carl9170 firmware: import 1.7.0 1.7.0
authorChristian Lamparter <chunkeey@googlemail.com>
Mon, 19 Jul 2010 23:16:34 +0000 (01:16 +0200)
committerChristian Lamparter <chunkeey@googlemail.com>
Mon, 19 Jul 2010 23:16:34 +0000 (01:16 +0200)
The carl9170 project aims to provide more than just a
working substitute firmware+driver for Atheros' AR9170
USB solutions.

Signed-off-by: Christian Lamparter <chunkeey@googlemail.com>
109 files changed:
.gitignore [new file with mode: 0644]
CMakeLists.txt [new file with mode: 0644]
COPYRIGHT [new file with mode: 0644]
GPL [new file with mode: 0644]
Kconfig [new file with mode: 0644]
README [new file with mode: 0644]
autogen.sh [new file with mode: 0755]
carlfw/CMakeLists.txt [new file with mode: 0644]
carlfw/Kconfig [new file with mode: 0644]
carlfw/carl9170.lds [new file with mode: 0644]
carlfw/include/cam.h [new file with mode: 0644]
carlfw/include/carl9170.h [new file with mode: 0644]
carlfw/include/cmd.h [new file with mode: 0644]
carlfw/include/config.h [new file with mode: 0644]
carlfw/include/dma.h [new file with mode: 0644]
carlfw/include/fwdsc.h [new file with mode: 0644]
carlfw/include/gpio.h [new file with mode: 0644]
carlfw/include/hostif.h [new file with mode: 0644]
carlfw/include/io.h [new file with mode: 0644]
carlfw/include/printf.h [new file with mode: 0644]
carlfw/include/rf.h [new file with mode: 0644]
carlfw/include/rom.h [new file with mode: 0644]
carlfw/include/timer.h [new file with mode: 0644]
carlfw/include/uart.h [new file with mode: 0644]
carlfw/include/usb.h [new file with mode: 0644]
carlfw/include/usb_fifo.h [new file with mode: 0644]
carlfw/include/wl.h [new file with mode: 0644]
carlfw/src/ashlsi3.S [new file with mode: 0644]
carlfw/src/cam.c [new file with mode: 0644]
carlfw/src/cmd.c [new file with mode: 0644]
carlfw/src/dma.c [new file with mode: 0644]
carlfw/src/fw.c [new file with mode: 0644]
carlfw/src/gpio.c [new file with mode: 0644]
carlfw/src/hostif.c [new file with mode: 0644]
carlfw/src/main.c [new file with mode: 0644]
carlfw/src/memcpy.S [new file with mode: 0644]
carlfw/src/memset.S [new file with mode: 0644]
carlfw/src/printf.c [new file with mode: 0755]
carlfw/src/reboot.S [new file with mode: 0644]
carlfw/src/rf.c [new file with mode: 0644]
carlfw/src/timer.c [new file with mode: 0644]
carlfw/src/uart.c [new file with mode: 0644]
carlfw/src/wlan.c [new file with mode: 0644]
carlfw/usb/Kconfig [new file with mode: 0644]
carlfw/usb/fifo.c [new file with mode: 0644]
carlfw/usb/main.c [new file with mode: 0644]
carlfw/usb/usb.c [new file with mode: 0644]
config/.gitignore [new file with mode: 0644]
config/Makefile [new file with mode: 0644]
config/conf.c [new file with mode: 0644]
config/confdata.c [new file with mode: 0644]
config/expr.c [new file with mode: 0644]
config/expr.h [new file with mode: 0644]
config/lkc.h [new file with mode: 0644]
config/lkc_proto.h [new file with mode: 0644]
config/menu.c [new file with mode: 0644]
config/symbol.c [new file with mode: 0644]
config/util.c [new file with mode: 0644]
config/zconf.gperf [new file with mode: 0644]
config/zconf.l [new file with mode: 0644]
config/zconf.y [new file with mode: 0644]
extra/FindUSB-1.0.cmake [new file with mode: 0644]
extra/GCCVersion.cmake [new file with mode: 0644]
extra/libusb-zeropacket.diff [new file with mode: 0644]
extra/sh-elf-linux.cmake [new file with mode: 0644]
genapi.sh [new file with mode: 0755]
include/generated/defconfig [new file with mode: 0644]
include/linux/ch9.h [new file with mode: 0644]
include/linux/compiler.h [new file with mode: 0644]
include/linux/ieee80211.h [new file with mode: 0644]
include/linux/types.h [new file with mode: 0644]
include/shared/eeprom.h [new file with mode: 0644]
include/shared/fwcmd.h [new file with mode: 0644]
include/shared/fwdesc.h [new file with mode: 0644]
include/shared/hw.h [new file with mode: 0644]
include/shared/phy.h [new file with mode: 0644]
include/shared/version.h [new file with mode: 0644]
include/shared/wlan.h [new file with mode: 0644]
minifw/CMakeLists.txt [new file with mode: 0644]
minifw/Kconfig [new file with mode: 0644]
minifw/miniboot.S [new file with mode: 0644]
minifw/miniboot.lds [new file with mode: 0644]
toolchain/Makefile [new file with mode: 0644]
tools/.gitignore [new file with mode: 0644]
tools/CMakeLists.txt [new file with mode: 0644]
tools/Kconfig [new file with mode: 0644]
tools/carlu/CMakeLists.txt [new file with mode: 0644]
tools/carlu/src/carlu.h [new file with mode: 0644]
tools/carlu/src/cmd.c [new file with mode: 0644]
tools/carlu/src/debug.c [new file with mode: 0644]
tools/carlu/src/debug.h [new file with mode: 0644]
tools/carlu/src/fw.c [new file with mode: 0644]
tools/carlu/src/main.c [new file with mode: 0644]
tools/carlu/src/rx.c [new file with mode: 0644]
tools/carlu/src/test.c [new file with mode: 0644]
tools/carlu/src/test.h [new file with mode: 0644]
tools/carlu/src/tx.c [new file with mode: 0644]
tools/carlu/src/usb.c [new file with mode: 0644]
tools/carlu/src/usb.h [new file with mode: 0644]
tools/include/frame.h [new file with mode: 0644]
tools/include/list.h [new file with mode: 0644]
tools/lib/CMakeLists.txt [new file with mode: 0644]
tools/lib/carlfw.c [new file with mode: 0644]
tools/lib/carlfw.h [new file with mode: 0644]
tools/src/CMakeLists.txt [new file with mode: 0644]
tools/src/checksum.c [new file with mode: 0644]
tools/src/eeprom_fix.c [new file with mode: 0644]
tools/src/fwinfo.c [new file with mode: 0644]
tools/src/miniboot.c [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..f7a6e67
--- /dev/null
@@ -0,0 +1,26 @@
+*.o
+*.so
+*.orig
+*.fw
+*.dsc
+*.elf
+*.bin
+*.a
+fw.map
+*~
+*.dis
+a.out
+*.cmake
+*.conf
+*.conf.cmd
+*old
+.config
+CMakeCache.txt
+include/generated
+CMakeFiles/
+toolchain
+toolchain/src
+toolchain/build
+toolchain/inst
+Makefile
+include/autoconf.h
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644 (file)
index 0000000..c017bbb
--- /dev/null
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8)
+
+project(carl9170)
+
+#if you don't want the full compiler output, remove the following line
+#set(CMAKE_VERBOSE_MAKEFILE ON)
+
+include("config.cmake")
+
+add_subdirectory(carlfw)
+
+if (CONFIG_CARL9170FW_BUILD_MINIBOOT)
+       add_subdirectory(minifw)
+endif (CONFIG_CARL9170FW_BUILD_MINIBOOT)
+
+if (CONFIG_CARL9170FW_BUILD_TOOLS)
+       add_subdirectory(tools)
+endif (CONFIG_CARL9170FW_BUILD_TOOLS)
+
diff --git a/COPYRIGHT b/COPYRIGHT
new file mode 100644 (file)
index 0000000..cd0a354
--- /dev/null
+++ b/COPYRIGHT
@@ -0,0 +1,18 @@
+Atheros ar9170 firmware - used by the ar9170 wireless device
+
+Copyright (c) 2000-2005  ZyDAS Technology Corporation
+Copyright (c) 2007-2009 Atheros Communications, Inc.
+
+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.
diff --git a/GPL b/GPL
new file mode 100644 (file)
index 0000000..d511905
--- /dev/null
+++ b/GPL
@@ -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/Kconfig b/Kconfig
new file mode 100644 (file)
index 0000000..03fd2cd
--- /dev/null
+++ b/Kconfig
@@ -0,0 +1,5 @@
+mainmenu "CARL9170 Firmware Configuration"
+
+source "carlfw/Kconfig"
+source "minifw/Kconfig"
+source "tools/Kconfig"
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..32e764d
--- /dev/null
+++ b/README
@@ -0,0 +1,19 @@
+Community AR9170 Linux firmware
+-----------------------
+
+This is the firmware for the Atheros ar9170 802.11n devices.
+
+To build the firmware you will need an SH-2 toolchain.
+You can build your own toolchain:
+
+make -C toolchain
+
+but be aware that this will take some time and requires
+about 1.2 GiB disk space.
+
+The resulting firmware, carl9170.fw, can be used only
+with the carl9170 Linux driver.
+
+After getting a toolchain, simply build:
+
+autogen.sh
diff --git a/autogen.sh b/autogen.sh
new file mode 100755 (executable)
index 0000000..43dbce6
--- /dev/null
@@ -0,0 +1,42 @@
+#!/bin/sh
+
+set -e
+
+case "$1" in
+       config)
+               echo "Configuring..."
+               make -C config
+               config/conf Kconfig
+               cmake .
+       ;;
+
+       compile)
+               echo "Compile time..."
+               make
+
+       ;;
+
+       install)
+               if [ ! -e .config ]; then
+                       exit 1
+               fi
+
+               . ./.config
+
+
+               if [ "$CONFIG_CARL9170FW_MAKE_RELEASE" = "y" ]; then
+                       echo "Installing firmware..."
+                       tmpfwfile=`mktemp`
+                       cat carlfw/carl9170.fw carlfw/carl9170.dsc > $tmpfwfile
+                       install $tmpfwfile /lib/firmware/carl9170-$CONFIG_CARL9170FW_RELEASE_VERSION.fw
+                       rm $tmpfwfile
+               fi
+       ;;
+
+       *)
+               $0 config
+               $0 compile
+       ;;
+
+
+esac
diff --git a/carlfw/CMakeLists.txt b/carlfw/CMakeLists.txt
new file mode 100644 (file)
index 0000000..b705717
--- /dev/null
@@ -0,0 +1,49 @@
+cmake_minimum_required(VERSION 2.8)
+
+project(carl9170.fw)
+
+include("../extra/sh-elf-linux.cmake")
+include("../config.cmake")
+
+set(CARLFW_CFLAGS_WARNING "-W -Wall -Wextra -Wunreachable-code -Winline -Wlogical-op -Wno-packed-bitfield-compat -Winit-self -Wshadow -Wwrite-strings -Waggregate-return -Wstrict-prototypes -Wformat=2 -Wcast-align -Wmissing-format-attribute -Wmissing-prototypes -Wtype-limits -Wmissing-declarations -Wmissing-noreturn -Wredundant-decls -Wnested-externs -Wdisabled-optimization -Wpointer-arith -Wvolatile-register-var -Waddress -Wbad-function-cast -Wunsafe-loop-optimizations")
+set(CARLFW_CFLAGS_EXTRA "-mbitops -std=gnu99 -ffunction-sections")
+
+if (CONFIG_CARL9170FW_AGGRESSIVE_CFLAGS)
+       set(CARLFW_CFLAGS_AGGRESSIVE "-fomit-frame-pointer -fsee -frename-registers -ftree-vectorize")
+endif (CONFIG_CARL9170FW_AGGRESSIVE_CFLAGS)
+
+include_directories (../include/linux ../include/shared ../include include)
+
+set(carl9170_main_src src/main.c src/timer.c src/wlan.c src/fw.c src/gpio.c
+                     src/cmd.c src/uart.c src/dma.c src/hostif.c src/reboot.S
+                     src/printf.c src/rf.c src/cam.c)
+
+set(carl9170_lib_src src/ashlsi3.S src/memcpy.S src/memset.S)
+set(carl9170_usb_src usb/main.c usb/usb.c usb/fifo.c)
+
+set(carl9170_src ${carl9170_main_src} ${carl9170_lib_src} ${carl9170_usb_src})
+
+set_source_files_properties(src/ashlsi3.S PROPERTIES LANGUAGE C)
+set_source_files_properties(src/memcpy.S PROPERTIES LANGUAGE C)
+set_source_files_properties(src/memset.S PROPERTIES LANGUAGE C)
+set_source_files_properties(src/reboot.S PROPERTIES LANGUAGE C)
+
+add_executable(carl9170.elf ${carl9170_src})
+
+set_target_properties(carl9170.elf PROPERTIES LINKER_LANGUAGE C)
+
+set_target_properties(carl9170.elf PROPERTIES COMPILE_FLAGS
+       "${CARLFW_CFLAGS_EXTRA} ${CARLFW_CFLAGS_AGGRESSIVE} ${CARLFW_CFLAGS_WARNING}")
+set_target_properties(carl9170.elf PROPERTIES LINK_FLAGS "-Tcarl9170.lds")
+
+add_custom_target(
+       carl9170.fw ALL
+       ${OBJCOPY} --strip-unneeded -O binary -R .sram -R .eeprom -R .fwdsc carl9170.elf carl9170.fw
+       DEPENDS carl9170.elf)
+
+
+add_custom_target(
+       carl9170.dsc ALL
+       ${OBJCOPY} --strip-unneeded -O binary -j .fwdsc carl9170.elf carl9170.dsc
+       DEPENDS carl9170.elf)
+
diff --git a/carlfw/Kconfig b/carlfw/Kconfig
new file mode 100644 (file)
index 0000000..c8f3cca
--- /dev/null
@@ -0,0 +1,259 @@
+menu "General"
+
+config CARL9170FW_MAKE_RELEASE
+       def_bool n
+       prompt "Update/Generate new release/revision files"
+
+config CARL9170FW_RELEASE_VERSION
+       int
+       depends on CARL9170FW_MAKE_RELEASE
+       prompt "Major CARL9170 Firmware Revision"
+       default 1
+
+menu "Selectable Hardware Options"
+
+choice
+       prompt "Receiver Max. Frame Length"
+       default CARL9170FW_RX_FRAME_LEN_8192
+
+       config CARL9170FW_RX_FRAME_LEN_4096
+               bool "4096"
+
+       config CARL9170FW_RX_FRAME_LEN_8192
+               bool "8192"
+
+       config CARL9170FW_RX_FRAME_LEN_16384
+               bool "16384"
+
+       config CARL9170FW_RX_FRAME_LEN_32768
+               bool "32768"
+
+endchoice
+
+config CARL9170FW_RX_FRAME_LEN
+       int
+       default 4096 if CARL9170FW_RX_FRAME_LEN_4096
+       default 8192 if CARL9170FW_RX_FRAME_LEN_8192
+       default 16384 if CARL9170FW_RX_FRAME_LEN_16384
+       default 32768 if CARL9170FW_RX_FRAME_LEN_32768
+
+config CARL9170FW_WATCHDOG
+       def_bool y
+       prompt "Activate HW Watchdog"
+       ---help---
+        The watchdog will notify the application as soon as the firmware
+        has stalled.
+
+        Say Y.
+
+config CARL9170FW_GPIO_INTERRUPT
+       def_bool y
+       prompt "GPIO Software Interrupt"
+       ---help---
+        When this option is enabled, the firmware will poll the GPIO
+        registers and reports to the driver whenever the GPIO state
+        has changed from a previous state.
+
+        Note: This feature is necessary to monitor the WPS button,
+              if you have one on your device, then say Y.
+
+config CARL9170FW_SECURITY_ENGINE
+       def_bool y
+       prompt "Support Hardware Crypto Engine"
+       ---help---
+        This options controls if the firmware will allow the driver
+        to program the security engine / CAM through a firmware
+        interface.
+
+        Say Y. Unless you want to do the en- and decryption for
+        CCMP(AES), TKIP/WEP(RC4) in the application anyway.
+
+config CARL9170FW_RADIO_FUNCTIONS
+       def_bool y
+       prompt "Enable Firmware-supported Radio/RF functions"
+       ---help---
+        Some PHY/RF functions (e.g.: AGC and Noise calibration) need
+        to be done in the firmware.
+
+        Say Y, unless you really don't need the Radio/RF for
+        your project.
+
+endmenu
+
+menu "802.11 Firmware Features"
+
+config CARL9170FW_HANDLE_BACK_REQ
+       def_bool y
+       prompt "Handle BlockACK Requests in Firmware"
+       ---help---
+        Enables a firmware mechanism to answer incoming BlockACK requests.
+        This feature is necessary to comply with 802.11n. So say Y,
+        if you have plans to support this operation mode.
+
+config CARL9170FW_BACK_REQS_NUM
+       default 4
+       int
+       prompt "Number of max. active BlockACK Request"
+       depends on CARL9170FW_HANDLE_BACK_REQ
+
+config CARL9170FW_CAB_QUEUE
+       def_bool y
+       prompt "Support software-based Content after Beacon Queue"
+       ---help---
+        This (software) queue is used to send any broad-/multi-cast buffered
+        frames after the next DTIM beacon.
+
+        This feature is required for Accesspoint mode operation.
+
+        Say Y.
+
+endmenu
+
+source "carlfw/usb/Kconfig"
+
+menu "Experimental, Unstable & Testing Extensions"
+
+config CARL9170FW_PRINTF
+       def_bool y
+       prompt "Advanced printf"
+       depends on CARL9170FW_DEBUG_UART || CARL9170FW_DEBUG_USB
+       ---help---
+        Advanced printf (very useful for debugging purposes)
+        The formats supported by this implementation are:
+               'd' 'u' 'c' 's' 'x' 'X' 'p'.
+
+        Note: If this option is disabled, the firmware will be only
+              capable of reported _preformated_ string.
+
+config CARL9170FW_EXPERIMENTAL
+       def_bool y
+       prompt "Experimental Features"
+
+config CARL9170FW_PSM
+       def_bool y
+       prompt "Firmware Supported Power-saving Management"
+       depends on CARL9170FW_EXPERIMENTAL && CARL9170FW_RADIO_FUNCTIONS
+       ---help---
+        This options enables a interface for the application to
+        switch off the RF/PHY (in order to save power). And the
+        Firmware will automatically turn it on again, when the
+        PRETBTT event fires.
+
+config CARL9170FW_DELAYED_TX
+       def_bool y
+       prompt "Delay and reorder TX"
+       depends on CARL9170FW_EXPERIMENTAL
+       ---help---
+        Delay incoming TX' from the application until wlan_complete
+        has finished.
+
+        This feature is necessary to keep A-MPDUs partially ordered.
+
+        Doesn't work 100% yet, but in most cases other HW designs can
+        deal with the fallout.
+
+config CARL9170FW_BROKEN_FEATURES
+       def_bool n
+       prompt "Broken Featurs"
+
+config CARL9170FW_DEBUG
+       def_bool n
+       depends on CARL9170FW_BROKEN_FEATURES && CARL9170FW_PRINTF
+       prompt "Enable verbose debugging messages"
+
+config CARL9170FW_DEBUG_LED_HEARTBEAT
+       def_bool n
+       prompt "LED Heartbeat"
+       depends on CARL9170FW_BROKEN_FEATURES
+       ---help---
+        This option conflicts with the application's LED code.
+        Also, it assumes that you have two LEDs, which is not
+        necessarily true.
+
+config CARL9170FW_DEBUG_UART
+       def_bool n
+       prompt "Pass debug messages through Highspeed UART"
+       depends on CARL9170FW_BROKEN_FEATURES
+       ---help---
+        This option allows the firmware to send BUG/ERR/INFO/DBG and
+        hexdumps through the UART _as well_. However, first: you must
+        connect a working logger.
+
+config CARL9170FW_WATCHDOG_BUTTON
+       def_bool n
+       depends on CARL9170FW_BROKEN && CARL9170FW_WATCHDOG && CARL9170FW_GPIO_INTERRUPT
+       prompt "Trigger Watchdog by pressing the WPS button"
+
+config CARL9170FW_VIFS_NUM
+       default 0
+       int
+       prompt "Number of additional pseudo virtual interfaces"
+       depends on CARL9170FW_BROKEN_FEATURES
+
+choice CARL9170FW_UART_CLOCK
+       prompt "UART Clock"
+       depends on CARL9170FW_DEBUG_UART
+       default CARL9170FW_UART_CLOCK_40M
+
+config CARL9170FW_UART_CLOCK_25M
+       bool "25"
+
+config CARL9170FW_UART_CLOCK_40M
+       bool "40"
+
+endchoice
+
+
+choice
+       prompt "TX / RX Path"
+
+       default CARL9170FW_NORMAL_TX_RX
+
+       config CARL9170FW_NORMAL_TX_RX
+               bool
+               prompt "Normal WLAN TX/RX routines"
+
+       config CARL9170FW_LOOPBACK
+               bool
+               depends on CARL9170FW_BROKEN_FEATURES
+               prompt "TX->(tx feedback)->RX loopback"
+               ---help---
+                Useful carlu testcase.
+
+       config CARL9170FW_DISCARD
+               bool
+               depends on CARL9170FW_BROKEN_FEATURES
+               prompt "TX->(tx feedback)"
+               ---help---
+                Useful carlu testcase
+
+endchoice
+
+config CARL9170FW_UNUSABLE
+       def_bool y
+       depends on CARL9170FW_BROKEN || CARL9170FW_LOOPBACK
+
+config CARL9170FW_USB_MODESWITCH
+       def_bool n
+       prompt "USB 1.1 / 2.0 switching support"
+       depends on CARL9170FW_BROKEN_FEATURES
+       ---help---
+        Mostly implemented, but untested and some serious
+        doubts remain.
+
+menu "Build Options"
+config CARL9170FW_AGGRESSIVE_CFLAGS
+       def_bool y
+       prompt "Enable aggressive size optimization"
+       ---help---
+        This option adds several more optimization compiler flags,
+        which can greatly reduce the firmware size... at the expense
+        of machine-code readability.
+
+         Say Y. Else the firmware might not fit onto the device!
+
+endmenu
+
+endmenu
+
+endmenu
diff --git a/carlfw/carl9170.lds b/carlfw/carl9170.lds
new file mode 100644 (file)
index 0000000..0c4ebd6
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * The carl9170 firwmare gets copied into the device's
+ * Program RAM (pram), which has a size of 32K, but
+ * also has to accomodate the stack the device uses,
+ * which starts at the top of the 32k, so we pretend
+ * that we just have 16k of pram.
+ *
+ * This section documents some of the other areas
+ * mapped into the firmware processor's address space
+ * as well.
+ */
+
+ENTRY(_start);
+
+MEMORY
+{
+    eeprom   : ORIGIN = 0x000000, LENGTH = 1024k
+    sram     : ORIGIN = 0x100000, LENGTH = 96k
+    uart     : ORIGIN = 0x1c0000, LENGTH = 4k
+    timer    : ORIGIN = 0x1c1000, LENGTH = 4k
+    vflash   : ORIGIN = 0x1c2000, LENGTH = 4k
+    wlan     : ORIGIN = 0x1c3000, LENGTH = 4k
+    pci2ahb  : ORIGIN = 0x1c4000, LENGTH = 4k
+    security : ORIGIN = 0x1c5000, LENGTH = 4k
+    gpio     : ORIGIN = 0x1d0000, LENGTH = 4k
+    memctl   : ORIGIN = 0x1d1000, LENGTH = 4k
+    irqctl   : ORIGIN = 0x1d2000, LENGTH = 4k
+    usb      : ORIGIN = 0x1e1000, LENGTH = 4k
+    pta      : ORIGIN = 0x1e2000, LENGTH = 4k
+    pram     : ORIGIN = 0x200000, LENGTH = 16k
+}
+
+SECTIONS
+{
+       /*
+        * The ar9170 boot code will execute the code
+        * at address 0x04 from the loaded firmware as
+        * such we must ensure our starting routine
+        * is kept at that address.
+        */
+       .padding : {
+               /* NOP NOP just in case */
+               LONG(0x00090009)
+       } > pram
+
+       .boot     : { *(.boot) } > pram
+       /* anything else can be anywhere */
+
+       .text   : { *(.text*) } > pram
+       .rodata : { *(.rodata*) } > pram
+       .bss    : { *(.bss) } > pram
+       .data   : { *(.data*) } > pram
+       .fwdsc  : { KEEP(*(.fwdsc)) } > pram
+
+       .sram   : { *(.sram*) } > sram
+       .eeprom : { *(.eeprom*) } > eeprom
+}
diff --git a/carlfw/include/cam.h b/carlfw/include/cam.h
new file mode 100644 (file)
index 0000000..ad34e44
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * carl9170 firmware - used by the ar9170 wireless device
+ *
+ * CAM (Security Engine) definitions
+ *
+ * Copyright (c) 2000-2005 ZyDAS Technology Corporation
+ * Copyright (c) 2007-2009 Atheros Communications, Inc.
+ * Copyright   2009    Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2009, 2010 Christian Lamparter <chunkeey@googlemail.com>
+ *
+ * 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.
+ */
+
+#ifndef __CARL9170FW_CAM_H
+#define __CARL9170FW_CAM_H
+
+#include "config.h"
+#include "cmd.h"
+
+#ifdef CONFIG_CARL9170FW_SECURITY_ENGINE
+
+#define ENCRY_TYPE_START_ADDR  24
+#define DEFAULT_ENCRY_TYPE     26
+#define KEY_START_ADDR         27
+#define STA_KEY_START_ADDR     155
+#define COUNTER_START_ADDR      163
+#define STA_COUNTER_START_ADDR 165
+
+/* CAM */
+#define MIC_FINISH                     0x1
+
+void set_key(const struct carl9170_set_key_cmd *key);
+void disable_key(const struct carl9170_disable_key_cmd *key);
+
+#endif /* CONFIG_CARL9170FW_SECURITY_ENGINE */
+
+#endif /* __CARL9170FW_CAM_H */
diff --git a/carlfw/include/carl9170.h b/carlfw/include/carl9170.h
new file mode 100644 (file)
index 0000000..6c19c32
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * carl9170 firmware - used by the ar9170 wireless device
+ *
+ * Firmware context definition
+ *
+ * Copyright 2009, 2010 Christian Lamparter <chunkeey@googlemail.com>
+ *
+ * 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.
+ */
+
+#ifndef __CARL9170FW_CARL9170_H
+#define __CARL9170FW_CARL9170_H
+
+#include "generated/autoconf.h"
+#include "version.h"
+#include "config.h"
+#include "types.h"
+#include "compiler.h"
+#include "fwcmd.h"
+#include "hw.h"
+#include "dma.h"
+#include "usb.h"
+#include "cmd.h"
+
+struct carl9170_bar_ctx {
+       uint8_t ta[6];
+       uint8_t ra[6];
+       __le16 start_seq_num;
+       __le16 control;
+       __le32 phy;
+};
+
+#ifdef CONFIG_CARL9170FW_CAB_QUEUE
+enum carl9170_cab_trigger {
+       CARL9170_CAB_TRIGGER_EMPTY      = 0,
+       CARL9170_CAB_TRIGGER_ARMED      = BIT(0),
+       CARL9170_CAB_TRIGGER_DEFER      = BIT(1),
+};
+#endif /* CONFIG_CARL9170FW_CAB_QUEUE */
+
+enum carl9170_ep0_action {
+       CARL9170_EP0_NO_ACTION          = 0,
+       CARL9170_EP0_STALL              = BIT(0),
+       CARL9170_EP0_TRIGGER            = BIT(1),
+};
+
+enum carl9170_mac_reset_state {
+       CARL9170_MAC_RESET_OFF          = 0,
+       CARL9170_MAC_RESET_ARMED,
+       CARL9170_MAC_RESET_RESET,
+       CARL9170_MAC_RESET_FORCE,
+};
+
+/*
+ * This platform - being an odd 32-bit architecture - prefers to
+ * have 32-Bit variables.
+ */
+
+struct firmware_context_struct {
+       /* timer / clocks */
+       unsigned int bogoclock;                 /* supposed to be CPU clock in KHz */
+       unsigned int counter;                   /* main() cycles */
+
+       /* misc */
+       unsigned int watchdog_enable;
+
+       struct {
+               /* Host Interface DMA queues */
+               struct dma_queue up_queue;      /* used to send frames to the host */
+               struct dma_queue down_queue;    /* stores incoming frames from the host */
+       } pta;
+
+       struct {
+               /* Hardware DMA queues */
+               struct dma_queue tx_queue[__AR9170_NUM_TX_QUEUES];      /* wlan tx queue */
+               struct dma_queue rx_queue;                              /* wlan rx queue */
+
+#ifdef CONFIG_CARL9170FW_DELAYED_TX
+               struct dma_queue tx_delay[__AR9170_NUM_TX_QUEUES];
+               struct dma_queue tx_retry;
+               unsigned int tx_trigger;
+#endif /* CONFIG_CARL9170FW_DELAYED_TX */
+
+               /* Hardware DMA queue unstuck/fix detection */
+               unsigned int last_tx_desc_num[__AR9170_NUM_TX_QUEUES];
+               struct dma_desc *last_tx_desc[__AR9170_NUM_TX_QUEUES];
+               unsigned int rx_total;
+               unsigned int rx_overruns;
+               unsigned int mac_reset;
+
+#ifdef CONFIG_CARL9170FW_CAB_QUEUE
+               /* CAB */
+               struct dma_queue cab_queue;
+               unsigned int cab_queue_len,
+                            cab_flush_time;
+               enum carl9170_cab_trigger cab_flush_trigger;
+#endif /* CONFIG_CARL9170FW_CAB_QUEUE */
+
+               /* tx status */
+               unsigned int tx_status_pending,
+                            tx_status_head_idx,
+                            tx_status_tail_idx;
+               struct carl9170_tx_status tx_status_cache[CARL9170_TX_STATUS_NUM];
+
+#ifdef CONFIG_CARL9170FW_HANDLE_BACK_REQ
+               /* BA(R) Request Handler */
+               struct dma_desc *ba_desc;
+               struct carl9170_bar_ctx ba_cache[CONFIG_CARL9170FW_BACK_REQS_NUM];
+               unsigned int ba_desc_available,
+                            ba_tail_idx,
+                            ba_head_idx;
+#endif /* CONFIG_CARL9170FW_HANDLE_BACK_REQ */
+       } wlan;
+
+       struct {
+               unsigned int config,
+                            interface_setting,
+                            alternate_interface_setting;
+               enum carl9170_ep0_action ep0_action;
+
+               void *ep0_txrx_buffer;
+               unsigned int ep0_txrx_len,
+                            ep0_txrx_pos;
+
+               struct ar9170_usb_config *cfg_desc;
+               struct ar9170_usb_config *os_cfg_desc;
+
+               /*
+                * special buffers for command & response handling
+                *
+                * the firmware uses a sort of ring-buffer to communicate
+                * to the host.
+                */
+               unsigned int int_pending,
+                            int_desc_available,
+                            int_head_index,
+                            int_tail_index;
+               struct dma_desc *int_desc;
+               struct carl9170_rsp int_buf[CARL9170_INT_RQ_CACHES];
+
+#ifdef CONFIG_CARL9170FW_DEBUG_USB
+               /* USB printf */
+               unsigned int put_index;
+               uint8_t put_buffer[CARL9170_MAX_CMD_PAYLOAD_LEN];
+#endif /* CONFIG_CARL9170FW_DEBUG_USB */
+
+#ifdef CONFIG_CARL9170FW_USB_WATCHDOG
+               struct carl9170_watchdog_cmd watchdog;
+#endif /* CONFIG CARL9170FW_USB_WATCHDOG */
+       } usb;
+
+       struct {
+#ifdef CONFIG_CARL9170FW_RADIO_FUNCTIONS
+               /* (cached) ar9170_rf_init */
+
+               /* PHY/RF state */
+               unsigned int frequency;
+               unsigned int ht_settings;
+
+#ifdef CONFIG_CARL9170FW_PSM
+               struct carl9170_psm psm;
+#endif /* CONFIG_CARL9170FW_PSM */
+#endif /* CONFIG_CARL9170FW_RADIO_FUNCTIONS */
+       } phy;
+
+#ifdef CONFIG_CARL9170FW_GPIO_INTERRUPT
+       struct carl9170_gpio cached_gpio_state;
+#endif /*CONFIG_CARL9170FW_GPIO_INTERRUPT */
+};
+
+/*
+ * global firmware context struct.
+ *
+ * NOTE: This struct will zeroed out in start()
+ */
+extern struct firmware_context_struct fw;
+#endif /* __CARL9170FW_CARL9170_H */
diff --git a/carlfw/include/cmd.h b/carlfw/include/cmd.h
new file mode 100644 (file)
index 0000000..809a6c2
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * carl9170 firmware - used by the ar9170 wireless device
+ *
+ * Firmware command interface definition
+ *
+ * Copyright (c) 2000-2005 ZyDAS Technology Corporation
+ * Copyright (c) 2007-2009 Atheros Communications, Inc.
+ * Copyright   2009    Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2009, 2010 Christian Lamparter <chunkeey@googlemail.com>
+ *
+ * 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.
+ */
+
+#ifndef __CARL9170FW_CMD_H
+#define __CARL9170FW_CMD_H
+
+#include "config.h"
+#include "compiler.h"
+#include "types.h"
+
+#include "fwcmd.h"
+
+static inline void __check(void)
+{
+       BUILD_BUG_ON(sizeof(struct carl9170_cmd) != CARL9170_MAX_CMD_LEN);
+       BUILD_BUG_ON(sizeof(struct carl9170_rsp) != CARL9170_MAX_CMD_LEN);
+       BUILD_BUG_ON(sizeof(struct carl9170_set_key_cmd) != CARL9170_SET_KEY_CMD_SIZE);
+       BUILD_BUG_ON(sizeof(struct carl9170_disable_key_cmd) != CARL9170_DISABLE_KEY_CMD_SIZE);
+       BUILD_BUG_ON(sizeof(struct carl9170_rf_init) != CARL9170_RF_INIT_SIZE);
+       BUILD_BUG_ON(sizeof(struct carl9170_rf_init_result) != CARL9170_RF_INIT_RESULT_SIZE);
+       BUILD_BUG_ON(sizeof(struct carl9170_watchdog_cmd) != CARL9170_WATCHDOG_CMD_SIZE);
+       BUILD_BUG_ON(sizeof(struct carl9170_psm) != CARL9170_PSM_SIZE);
+       BUILD_BUG_ON(sizeof(struct carl9170_tsf_rsp) != CARL9170_TSF_RSP_SIZE);
+       BUILD_BUG_ON(sizeof(struct carl9170_cab_flush_cmd) != CARL9170_CAB_FLUSH_CMD_SIZE);
+       BUILD_BUG_ON(sizeof(struct carl9170_tx_status) != CARL9170_TX_STATUS_SIZE);
+       BUILD_BUG_ON(sizeof(struct _carl9170_tx_status) != CARL9170_TX_STATUS_SIZE);
+       BUILD_BUG_ON(sizeof(struct carl9170_gpio) != CARL9170_GPIO_SIZE);
+}
+
+void handle_cmd(struct carl9170_rsp *resp);
+void __attribute__((noreturn)) reboot(void);
+
+#endif /* __CARL9170FW_CMD_H */
diff --git a/carlfw/include/config.h b/carlfw/include/config.h
new file mode 100644 (file)
index 0000000..b947f59
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ */
+
+#include "generated/autoconf.h"
+#include "version.h"
+#include "types.h"
+#include "compiler.h"
+#include "fwcmd.h"
+
+#ifndef __CARL9170FW_CONFIG_H
+#define __CARL9170FW_CONFIG_H
+
+#define __CARL9170FW__
+
+#define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
+
+#if GCC_VERSION < 40400
+# error "This firmware will not work if it is compiled with gcc versions < 4.4"
+# error "See: http://gcc.gnu.org/gcc-4.4/changes.html / Caveats No. 4"
+#endif
+
+#if ((defined CONFIG_CARL9170FW_PRINTF) &&             \
+     (!defined CONFIG_CARL9170FW_DEBUG_USB) &&         \
+    (!defined CONFIG_CARL9170FW_DEBUG_UART))
+# warning "You have disabled all debug message transports."
+# warning "However CONFIG_CARL9170FW_PRINTF is still set..."
+# warning "Which is a waste of firmware space, if you ask me."
+#endif
+
+#define CARL9170_TX_STATUS_NUM         (CARL9170_RSP_TX_STATUS_NUM)
+#define CARL9170_INT_RQ_CACHES         16
+#define AR9170_INT_MAGIC_HEADER_SIZE   12
+#define CARL9170_USB_WATCHDOG_TRIGGER_THRESHOLD        4
+#define CARL9170_TBTT_DELTA            (CARL9170_PRETBTT_KUS + 1)
+
+#define CARL9170_GPIO_MASK             (AR9170_GPIO_PORT_WPS_BUTTON_PRESSED)
+
+#ifdef CONFIG_CARL9170FW_VIFS_NUM
+#define CARL9170_INTF_NUM              (1 + CONFIG_CARL9170FW_VIFS_NUM)
+#else
+#define CARL9170_INTF_NUM              (1)
+#endif /* CONFIG_CARL9170FW_VIFS_NUM */
+
+#if ((defined CONFIG_CARL9170FW_DEBUG) ||      \
+     (defined CONFIG_CARL9170FW_LOOPBACK))
+#define CARL9170FW_UNUSABLE    y
+#endif
+
+static inline void __config_check(void)
+{
+       BUILD_BUG_ON(!CARL9170_TX_STATUS_NUM);
+       BUILD_BUG_ON(CARL9170_USB_WATCHDOG_TRIGGER_THRESHOLD < 2);
+       BUILD_BUG_ON(CARL9170_INTF_NUM < 1);
+
+#ifdef CONFIG_CARL9170FW_HANDLE_BACK_REQ
+       BUILD_BUG_ON(!CONFIG_CARL9170FW_BACK_REQS_NUM);
+#endif /* CONFIG_CARL9170FW_HANDLE_BACK_REQ */
+}
+
+#endif /* __CARL9170FW_CONFIG_H */
diff --git a/carlfw/include/dma.h b/carlfw/include/dma.h
new file mode 100644 (file)
index 0000000..079b3b7
--- /dev/null
@@ -0,0 +1,334 @@
+/*
+ * carl9170 firmware - used by the ar9170 wireless device
+ *
+ * This module contains DMA descriptor related definitions.
+ *
+ * Copyright (c) 2000-2005 ZyDAS Technology Corporation
+ * Copyright (c) 2007-2009 Atheros Communications, Inc.
+ * Copyright   2009    Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2009, 2010 Christian Lamparter <chunkeey@googlemail.com>
+ *
+ * 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.
+ */
+
+#ifndef __CARL9170FW_DMA_H
+#define __CARL9170FW_DMA_H
+
+#include "config.h"
+#include "types.h"
+#include "compiler.h"
+#include "hw.h"
+#include "ieee80211.h"
+#include "wlan.h"
+
+struct dma_desc {
+       volatile uint16_t status;       /* Descriptor status */
+       volatile uint16_t ctrl;         /* Descriptor control */
+       volatile uint16_t dataSize;     /* Data size */
+       volatile uint16_t totalLen;     /* Total length */
+       struct dma_desc *lastAddr;      /* Last address of this chain */
+       union {
+               uint8_t *_dataAddr;     /* Data buffer address */
+               void *dataAddr;
+       } __packed;
+       struct dma_desc *nextAddr;      /* Next TD address */
+} __packed;
+
+/* (Up, Dn, 5x Tx, Rx), USB Int, (5x delayed Tx + retry), CAB, BA */
+#define AR9170_TERMINATOR_NUMBER_B     8
+
+#define AR9170_TERMINATOR_NUMBER_INT   1
+
+#ifdef CONFIG_CARL9170FW_DELAYED_TX
+#define AR9170_TERMINATOR_NUMBER_DELAY 6
+#else
+#define AR9170_TERMINATOR_NUMBER_DELAY 0
+#endif /* CONFIG_CARL9170FW_DELAYED_TX */
+
+#ifdef CONFIG_CARL9170FW_CAB_QUEUE
+#define AR9170_TERMINATOR_NUMBER_CAB   1
+#else
+#define AR9170_TERMINATOR_NUMBER_CAB   0
+#endif /* CONFIG_CARL9170FW_CAB_QUEUE */
+
+#ifdef CONFIG_CARL9170FW_HANDLE_BACK_REQ
+#define AR9170_TERMINATOR_NUMBER_BA    1
+#else
+#define AR9170_TERMINATOR_NUMBER_BA    0
+#endif /* CONFIG_CARL9170FW_HANDLE_BACK_REQ */
+#define AR9170_TERMINATOR_NUMBER (AR9170_TERMINATOR_NUMBER_B + \
+                                 AR9170_TERMINATOR_NUMBER_INT + \
+                                 AR9170_TERMINATOR_NUMBER_DELAY + \
+                                 AR9170_TERMINATOR_NUMBER_CAB + \
+                                 AR9170_TERMINATOR_NUMBER_BA)
+
+#define AR9170_BLOCK_SIZE           (256 + 64)
+
+#define AR9170_DESCRIPTOR_SIZE      (sizeof(struct dma_desc))
+
+struct ar9170_tx_ba_frame {
+       struct ar9170_tx_hwdesc hdr;
+       struct ieee80211_ba ba;
+} __packed;
+
+struct carl9170_tx_ba_superframe {
+       struct carl9170_tx_superdesc s;
+       struct ar9170_tx_ba_frame f;
+} __packed;
+
+#define CARL9170_BA_BUFFER_LEN (__roundup(sizeof(struct carl9170_tx_ba_superframe), 16))
+#define CARL9170_RSP_BUFFER_LEN        AR9170_BLOCK_SIZE
+
+struct carl9170_sram_reserved {
+#ifdef CONFIG_CARL9170FW_HANDLE_BACK_REQ
+       union {
+               uint32_t buf[CARL9170_BA_BUFFER_LEN / sizeof(uint32_t)];
+               struct carl9170_tx_ba_superframe ba;
+       } ba;
+#endif /* CONFIG_CARL9170FW_HANDLE_BACK_REQ */
+       union {
+               uint32_t buf[CARL9170_MAX_CMD_LEN / sizeof(uint32_t)];
+               struct carl9170_cmd cmd;
+       } cmd;
+
+       union {
+               uint32_t buf[CARL9170_RSP_BUFFER_LEN / sizeof(uint32_t)];
+               struct carl9170_rsp rsp;
+       } rsp;
+
+       union {
+               uint32_t buf[CARL9170_INTF_NUM][AR9170_MAC_BCN_LENGTH_MAX / sizeof(uint32_t)];
+       } bcn;
+};
+
+/*
+ * Memory layout in RAM:
+ *
+ * 0x100000                    +--
+ *                             | terminator descriptors (dma_desc)
+ *                             |  - Up (to USB host)
+ *                             |  - Down (from USB host)
+ *                             |  - TX (5x, to wifi)
+ *                             |  - RX (from wifi)
+ *                             |  - CAB Queue
+ *                             |  - FW cmd & req descriptor
+ *                             |  - BlockAck descriptor
+ *                             |  - Delayed TX (5x)
+ *                             | total: AR9170_TERMINATOR_NUMBER
+ *                             +--
+ *                             | block descriptors (dma_desc)
+ *                             | (AR9170_BLOCK_NUMBER)
+ * AR9170_BLOCK_BUFFER_BASE    +-- align to multiple of 64
+ *                             | block buffers (AR9170_BLOCK_SIZE each)
+ *                             | (AR9170_BLOCK_NUMBER)
+ * approx. 0x117c00            +--
+ *                             | BA buffer (128 bytes)
+ *                             +--
+ *                             | CMD buffer (128 bytes)
+ *                             +--
+ *                             | RSP buffer (320 bytes)
+ *                             +--
+ *                             | BEACON buffer (256 bytes)
+ *                             +--
+ *                             | unaccounted space / padding
+ *                             +--
+ * 0x18000
+ */
+
+#define AR9170_SRAM_SIZE               0x18000
+#define CARL9170_SRAM_RESERVED         (sizeof(struct carl9170_sram_reserved))
+
+#define AR9170_FRAME_MEMORY_SIZE       (AR9170_SRAM_SIZE - CARL9170_SRAM_RESERVED)
+
+#define BLOCK_ALIGNMENT                64
+
+#define NONBLOCK_DESCRIPTORS_SIZE      \
+       (AR9170_DESCRIPTOR_SIZE * (AR9170_TERMINATOR_NUMBER))
+
+#define NONBLOCK_DESCRIPTORS_SIZE_ALIGNED      \
+       (ALIGN(NONBLOCK_DESCRIPTORS_SIZE, BLOCK_ALIGNMENT))
+
+#define AR9170_BLOCK_NUMBER    ((AR9170_FRAME_MEMORY_SIZE - NONBLOCK_DESCRIPTORS_SIZE_ALIGNED) / \
+                                (AR9170_BLOCK_SIZE + AR9170_DESCRIPTOR_SIZE))
+
+struct ar9170_data_block {
+       uint8_t data[AR9170_BLOCK_SIZE];
+};
+
+struct ar9170_dma_memory {
+       struct dma_desc                 terminator[AR9170_TERMINATOR_NUMBER];
+       struct dma_desc                 block[AR9170_BLOCK_NUMBER];
+       struct ar9170_data_block        data[AR9170_BLOCK_NUMBER] __attribute__((aligned(BLOCK_ALIGNMENT)));
+       struct carl9170_sram_reserved   reserved __attribute__((aligned(BLOCK_ALIGNMENT)));
+};
+
+extern struct ar9170_dma_memory dma_mem;
+
+#define AR9170_DOWN_BLOCK_RATIO     2
+#define AR9170_RX_BLOCK_RATIO       1
+/* Tx 16*2 = 32 packets => 32*(5*320) */
+#define AR9170_TX_BLOCK_NUMBER     (AR9170_BLOCK_NUMBER * AR9170_DOWN_BLOCK_RATIO / \
+                                  (AR9170_RX_BLOCK_RATIO + AR9170_DOWN_BLOCK_RATIO))
+#define AR9170_RX_BLOCK_NUMBER     (AR9170_BLOCK_NUMBER - AR9170_TX_BLOCK_NUMBER)
+
+/* Error code */
+#define AR9170_ERR_FS_BIT           1
+#define AR9170_ERR_LS_BIT           2
+#define AR9170_ERR_OWN_BITS         3
+#define AR9170_ERR_DATA_SIZE        4
+#define AR9170_ERR_TOTAL_LEN        5
+#define AR9170_ERR_DATA             6
+#define AR9170_ERR_SEQ              7
+#define AR9170_ERR_LEN              8
+
+/* Status bits definitions */
+/* Own bits definitions */
+#define AR9170_OWN_BITS_MASK        0x3
+#define AR9170_OWN_BITS_SW          0x0
+#define AR9170_OWN_BITS_HW          0x1
+#define AR9170_OWN_BITS_SE          0x2
+
+/* Control bits definitions */
+#define AR9170_CTRL_TXFAIL     1
+#define AR9170_CTRL_BAFAIL     2
+#define AR9170_CTRL_FAIL_MASK  (AR9170_CTRL_TXFAIL | AR9170_CTRL_BAFAIL)
+
+/* First segament bit */
+#define AR9170_CTRL_LS_BIT               0x100
+/* Last segament bit */
+#define AR9170_CTRL_FS_BIT               0x200
+
+struct dma_queue {
+       struct dma_desc *head;
+       struct dma_desc *terminator;
+};
+
+#define DESC_PAYLOAD(a)                        ((void *)a->dataAddr)
+#define DESC_PAYLOAD_OFF(a, offset)    ((void *)((unsigned long)(a->_dataAddr) + offset))
+
+struct dma_desc *dma_unlink_head(struct dma_queue *queue);
+void dma_init_descriptors(void);
+void dma_reclaim(struct dma_queue *q, struct dma_desc *desc);
+void dma_put(struct dma_queue *q, struct dma_desc *desc);
+void dma_queue_reclaim(struct dma_queue *dst, struct dma_queue *src);
+void queue_dump(void);
+void wlan_txq_hangfix(const unsigned int queue);
+
+static inline __inline bool queue_empty(struct dma_queue *q)
+{
+       return q->head == q->terminator;
+}
+
+/*
+ * Get a completed packet with # descriptors. Return the first
+ * descriptor and pointer the head directly by lastAddr->nextAddr
+ */
+static inline __inline struct dma_desc *dma_dequeue_bits(struct dma_queue *q,
+                                               uint16_t bits)
+{
+       struct dma_desc *desc = NULL;
+
+       if ((q->head->status & AR9170_OWN_BITS_MASK) == bits)
+               desc = dma_unlink_head(q);
+
+       return desc;
+}
+
+static inline __inline struct dma_desc *dma_dequeue_not_bits(struct dma_queue *q,
+                                                   uint16_t bits)
+{
+       struct dma_desc *desc = NULL;
+
+       /* AR9170_OWN_BITS_HW will be filtered out here too. */
+       if ((q->head->status & AR9170_OWN_BITS_MASK) != bits)
+               desc = dma_unlink_head(q);
+
+       return desc;
+}
+
+#define for_each_desc_bits(desc, queue, bits)                          \
+       while ((desc = dma_dequeue_bits(queue, bits)))
+
+#define for_each_desc_not_bits(desc, queue, bits)                      \
+       while ((desc = dma_dequeue_not_bits(queue, bits)))
+
+#define for_each_desc(desc, queue)                                     \
+       while ((desc = dma_unlink_head(queue)))
+
+#define __for_each_desc_bits(desc, queue, bits)                                \
+       for (desc = (queue)->head;                                      \
+            (desc != (queue)->terminator &&                            \
+            (desc->status & AR9170_OWN_BITS_MASK) == bits);            \
+            desc = desc->lastAddr->nextAddr)
+
+#define __while_desc_bits(desc, queue, bits)                           \
+       for (desc = (queue)->head;                                      \
+            (!queue_empty(queue) &&                                    \
+            (desc->status & AR9170_OWN_BITS_MASK) == bits);            \
+            desc = (queue)->head)
+
+#define __for_each_desc(desc, queue)                                   \
+       for (desc = (queue)->head;                                      \
+            desc != (queue)->terminator;                               \
+            desc = (desc)->lastAddr->nextAddr)
+
+#define __for_each_desc_safe(desc, tmp, queue)                         \
+       for (desc = (queue)->head, tmp = desc->lastAddr->nextAddr;      \
+            desc != (queue)->terminator;                               \
+            desc = tmp, tmp = tmp->lastAddr->nextAddr)
+
+#define __while_subdesc(desc, queue)                                   \
+       for (desc = (queue)->head;                                      \
+            desc != (queue)->terminator;                               \
+            desc = (desc)->nextAddr)
+
+static inline __inline unsigned int queue_len(struct dma_queue *q)
+{
+       struct dma_desc *desc;
+       unsigned int i = 0;
+
+       __while_subdesc(desc, q)
+               i++;
+
+       return i;
+}
+
+/*
+ * rearm a completed packet, so it will be processed agian.
+ */
+static inline __inline void dma_rearm(struct dma_desc *desc)
+{
+       /* Set OWN bit to HW */
+       desc->status = ((desc->status & (~AR9170_OWN_BITS_MASK)) |
+                       AR9170_OWN_BITS_HW);
+}
+
+static inline void __check_desc(void)
+{
+       struct ar9170_dma_memory mem;
+       BUILD_BUG_ON(sizeof(struct ar9170_data_block) != AR9170_BLOCK_SIZE);
+       BUILD_BUG_ON(sizeof(struct dma_desc) != 20);
+
+       BUILD_BUG_ON(sizeof(mem) > AR9170_SRAM_SIZE);
+
+#ifdef CONFIG_CARL9170FW_HANDLE_BACK_REQ
+       BUILD_BUG_ON(offsetof(struct carl9170_sram_reserved, ba.buf) & (BLOCK_ALIGNMENT - 1));
+#endif /* CONFIG_CARL9170FW_HANDLE_BACK_REQ */
+       BUILD_BUG_ON(offsetof(struct carl9170_sram_reserved, cmd.buf) & (BLOCK_ALIGNMENT - 1));
+       BUILD_BUG_ON(offsetof(struct carl9170_sram_reserved, rsp.buf) & (BLOCK_ALIGNMENT - 1));
+       BUILD_BUG_ON(offsetof(struct carl9170_sram_reserved, bcn.buf) & (BLOCK_ALIGNMENT - 1));
+}
+
+#endif /* __CARL9170FW_DMA_H */
diff --git a/carlfw/include/fwdsc.h b/carlfw/include/fwdsc.h
new file mode 100644 (file)
index 0000000..bebe970
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * carl9170 firmware - used by the ar9170 wireless device
+ *
+ * Firmware definition
+ *
+ * Copyright 2009, 2010 Christian Lamparter <chunkeey@googlemail.com>
+ *
+ * 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.
+ */
+
+#ifndef __CARL9170FW_FWDSC_H
+#define __CARL9170FW_FWDSC_H
+
+#include "config.h"
+#include "compiler.h"
+#include "types.h"
+#include "fwdesc.h"
+
+struct carl9170_firmware_descriptor {
+       struct carl9170fw_otus_desc otus;
+       struct carl9170fw_usb_desc  usb;
+       struct carl9170fw_motd_desc motd;
+       struct carl9170fw_dbg_desc  dbg;
+       struct carl9170fw_last_desc last;
+} __packed;
+
+extern const struct carl9170_firmware_descriptor carl9170fw_desc;
+
+static inline void __check_fw(void)
+{
+       BUILD_BUG_ON(sizeof(carl9170fw_desc) & 0x3);
+       BUILD_BUG_ON(sizeof(carl9170fw_desc) > CARL9170FW_DESC_MAX_LENGTH);
+}
+
+#endif /* __CARL9170FW_FWDSC_H */
diff --git a/carlfw/include/gpio.h b/carlfw/include/gpio.h
new file mode 100644 (file)
index 0000000..1ed816c
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * carl9170 firmware - used by the ar9170 wireless device
+ *
+ * GPIO definitions
+ *
+ * Copyright 2009, 2010 Christian Lamparter <chunkeey@googlemail.com>
+ *
+ * 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.
+ */
+
+#ifndef __CARL9170FW_GPIO_H
+#define __CARL9170FW_GPIO_H
+
+#include "config.h"
+#include "hw.h"
+#include "io.h"
+
+static inline __inline void led_init(void)
+{
+       set(AR9170_GPIO_REG_PORT_TYPE, 3);
+}
+
+static inline __inline void led_set(const unsigned int ledstate)
+{
+       set(AR9170_GPIO_REG_PORT_DATA, ledstate);
+}
+
+#ifdef CONFIG_CARL9170FW_GPIO_INTERRUPT
+
+void gpio_timer(void);
+
+#endif /* CONFIG_CARL9170FW_GPIO_INTERRUPT */
+#endif /* __CARL9170FW_GPIO_H */
diff --git a/carlfw/include/hostif.h b/carlfw/include/hostif.h
new file mode 100644 (file)
index 0000000..11a0d76
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * carl9170 firmware - used by the ar9170 wireless device
+ *
+ * HostIF definition
+ *
+ * Copyright (c) 2000-2005 ZyDAS Technology Corporation
+ * Copyright (c) 2007-2009 Atheros Communications, Inc.
+ * Copyright   2009    Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2009, 2010 Christian Lamparter <chunkeey@googlemail.com>
+ *
+ * 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.
+ */
+
+#ifndef __CARL9170FW_HOSTIF_H
+#define __CARL9170FW_HOSTIF_H
+
+#include "config.h"
+#include "compiler.h"
+#include "types.h"
+#include "hw.h"
+#include "io.h"
+
+static inline __inline void down_trigger(void)
+{
+       set(AR9170_PTA_REG_DN_DMA_TRIGGER, 1);
+}
+
+static inline __inline void up_trigger(void)
+{
+       set(AR9170_PTA_REG_UP_DMA_TRIGGER, 1);
+}
+
+void handle_host_interface(void);
+
+#endif /* __CARL9170FW_HOSTIF_H */
diff --git a/carlfw/include/io.h b/carlfw/include/io.h
new file mode 100644 (file)
index 0000000..f594a3f
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * 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.
+ */
+
+#ifndef __CARL9170FW_IO_H
+#define __CARL9170FW_IO_H
+
+#include "config.h"
+#include "types.h"
+#include "compiler.h"
+
+static inline __inline uint8_t readb(const volatile void *addr)
+{
+       return *(const volatile uint8_t *) addr;
+}
+
+static inline __inline uint16_t readw(const volatile void *addr)
+{
+       return *(const volatile uint16_t *) addr;
+}
+
+static inline __inline void *readp(const volatile void *addr)
+{
+       return *(void **) addr;
+}
+
+static inline __inline uint32_t readl(const volatile void *addr)
+{
+       return (uint32_t) (const unsigned int *) readp(addr);
+}
+
+static inline __inline void writeb(volatile void *addr, const volatile uint8_t val)
+{
+       *(volatile uint8_t *) addr = val;
+}
+
+static inline __inline void writew(volatile void *addr, const volatile uint16_t val)
+{
+       *(volatile uint16_t *) addr = val;
+}
+
+static inline __inline void writel(volatile void *addr, const volatile uint32_t val)
+{
+       *(volatile uint32_t *) addr = val;
+}
+
+static inline __inline void __orl(volatile void *addr, const volatile uint32_t val)
+{
+       *(volatile uint32_t *) addr |= val;
+}
+
+static inline __inline void __andl(volatile void *addr, const volatile uint32_t val)
+{
+       *(volatile uint32_t *) addr &= val;
+}
+
+static inline __inline void __xorl(volatile void *addr, const volatile uint32_t val)
+{
+       *(volatile uint32_t *) addr ^= val;
+}
+
+static inline __inline void __incl(volatile void *addr)
+{
+       (*(volatile uint32_t *)addr)++;
+}
+
+static inline __inline uint32_t readl_async(const volatile void *addr)
+{
+       uint32_t i = 0, read, tmp;
+
+       read = readl(addr);
+       while (i++ < 10) {
+               tmp = readl(addr);
+               if (tmp == read)
+                       break;
+               else
+                       read = tmp;
+       }
+
+       return read;
+}
+
+static inline __inline void set(const volatile uint32_t addr, const volatile uint32_t val)
+{
+       writel((volatile void *) addr, val);
+}
+
+static inline __inline void orl(volatile uint32_t addr, const volatile uint32_t val)
+{
+       __orl((volatile void *) addr, val);
+}
+
+static inline __inline void xorl(const volatile uint32_t addr, const volatile uint32_t val)
+{
+       __xorl((volatile void *) addr, val);
+}
+
+static inline __inline void andl(const volatile uint32_t addr, const volatile uint32_t val)
+{
+       __andl((volatile void *) addr, val);
+}
+
+static inline __inline void incl(const volatile uint32_t addr)
+{
+       __incl((volatile void *) addr);
+}
+
+static inline __inline uint32_t get(const volatile uint32_t addr)
+{
+       return readl((const volatile void *) addr);
+}
+
+static inline __inline void *getp(const volatile uint32_t addr)
+{
+       return readp((const volatile void *) addr);
+}
+
+static inline __inline uint32_t get_async(const volatile uint32_t addr)
+{
+       return readl_async((const volatile void *) addr);
+}
+
+static inline __inline void setb(const volatile uint32_t addr, const volatile uint8_t val)
+{
+       writeb((volatile void *) addr, val);
+}
+
+static inline __inline uint8_t getb(const volatile uint32_t addr)
+{
+       return readb((const volatile void *) addr);
+}
+
+static inline __inline void andb(const volatile uint32_t addr, const volatile uint8_t val)
+{
+       setb(addr, getb(addr) & val);
+}
+
+static inline __inline void orb(const volatile uint32_t addr, const volatile uint32_t val)
+{
+       setb(addr, getb(addr) | val);
+}
+
+#endif /* __CARL9170FW_IO_H */
diff --git a/carlfw/include/printf.h b/carlfw/include/printf.h
new file mode 100644 (file)
index 0000000..f4fbcf1
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * carl9170 firmware - used by the ar9170 wireless device
+ *
+ * printf and his friends...
+ *
+ * Copyright (c) 2000-2005 ZyDAS Technology Corporation
+ * Copyright (c) 2007-2009 Atheros Communications, Inc.
+ * Copyright   2009    Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2009, 2010 Christian Lamparter <chunkeey@googlemail.com>
+ *
+ * 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.
+ */
+
+#ifndef __CARL9170FW_PRINTF_H
+#define __CARL9170FW_PRINTF_H
+
+#include <stdarg.h>
+#include <string.h>
+#include "config.h"
+#include "carl9170.h"
+#include "uart.h"
+#include "fwcmd.h"
+
+#ifdef CONFIG_CARL9170FW_PRINTF
+void __attribute__((format (printf, 1, 2))) tfp_printf(const char *fmt, ...);
+
+#define printf tfp_printf
+
+#else
+void __attribute__((format (printf, 1, 2))) min_printf(const char *fmt, ...);
+
+#define printf min_printf
+#endif /* CONFIG_CARL9170FW_PRINTF */
+
+#define PRINT(fmt, args...)                                            \
+       do {                                                            \
+               printf(fmt, ## args);                                   \
+       } while (0)
+
+#define INFO(fmt, args...)     PRINT(fmt, ## args)
+
+#define ERR(fmt, args...)      PRINT(CARL9170_ERR_MAGIC fmt, ## args)
+
+#ifdef CONFIG_CARL9170FW_DEBUG
+#define DBG(fmt, args...)      PRINT(fmt, ## args)
+#else
+#define DBG(...)               do { } while (0);
+#endif
+
+/*
+ * NB: even though the MACRO is called "stall". It isn't supposed
+ * to stall since this will render the device unresponsive, until
+ * someone pulls the plug.
+ */
+#define STALL()
+
+#define BUG(fmt, args...)                                              \
+       do {                                                            \
+               PRINT(CARL9170_BUG_MAGIC" %s()@%d \"" fmt "\"" ,        \
+                     __func__, __LINE__, ## args);                     \
+               STALL()                                                 \
+       } while (0);
+
+#define BUG_ON(condition)                                              \
+       ({                                                              \
+               int __ret = !!(condition);                              \
+               if (unlikely(!!(__ret)))                                \
+                       BUG(#condition);                                \
+               (__ret);                                                \
+       })
+
+static inline __inline void putcharacter(const char c __unused)
+{
+#ifdef CONFIG_CARL9170FW_DEBUG_USB
+       usb_putc(c);
+#endif /* CONFIG_CARL9170FW_DEBUG_USB */
+
+#ifdef CONFIG_CARL9170FW_DEBUG_UART
+       uart_putc(c);
+#endif /* CONFIG_CARL9170FW_DEBUG_UART */
+}
+
+static inline __inline void print_hex_dump(const void *buf __unused, int len __unused)
+{
+#ifdef CONFIG_CARL9170FW_DEBUG_USB
+       usb_print_hex_dump(buf, len);
+#endif /* CONFIG_CARL9170FW_DEBUG_USB */
+
+#ifdef CONFIG_CARL9170FW_DEBUG_UART
+       uart_print_hex_dump(buf, len);
+#endif /* CONFIG_CARL9170FW_DEBUG_UART */
+}
+
+#endif /* __CARL9170FW_PRINTF_H */
+
diff --git a/carlfw/include/rf.h b/carlfw/include/rf.h
new file mode 100644 (file)
index 0000000..8252740
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * carl9170 firmware - used by the ar9170 wireless device
+ *
+ * RF routine definitions
+ *
+ * Copyright (c) 2000-2005 ZyDAS Technology Corporation
+ * Copyright (c) 2007-2009 Atheros Communications, Inc.
+ * Copyright   2009    Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2009, 2010 Christian Lamparter <chunkeey@googlemail.com>
+ *
+ * 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.
+ */
+
+#ifndef __CARL9170FW_RF_H
+#define __CARL9170FW_RF_H
+
+#include "config.h"
+
+#ifdef CONFIG_CARL9170FW_RADIO_FUNCTIONS
+void rf_notify_set_channel(void);
+void rf_cmd(const struct carl9170_cmd *cmd, struct carl9170_rsp *resp);
+void rf_psm(void);
+#endif /* CONFIG_CARL9170FW_RADIO_FUNCTIONS */
+
+#endif /* __CARL9170FW_RF_H */
diff --git a/carlfw/include/rom.h b/carlfw/include/rom.h
new file mode 100644 (file)
index 0000000..d97677c
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * carl9170 firmware - used by the ar9170 wireless device
+ *
+ * ROM layout
+ *
+ * Copyright (c) 2000-2005  ZyDAS Technology Corporation
+ * Copyright (c) 2007-2009 Atheros Communications, Inc.
+ * Copyright   2009    Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2009, 2010 Christian Lamparter <chunkeey@googlemail.com>
+ *
+ * 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.
+ */
+
+#ifndef __CARL9170FW_ROM_H
+#define __CARL9170FW_ROM_H
+
+#include "types.h"
+#include "config.h"
+#include "compiler.h"
+#include "usb.h"
+#include "eeprom.h"
+
+struct ar9170_hwtype {
+       /* 0x00001370 */
+       uint8_t data[4];
+
+       /* 0x00001374 */
+       struct ar9170_led_mode led_mode[AR9170_NUM_LEDS];
+
+       /* 0x00001378 */
+       uint8_t nulldata[2];
+
+       struct {
+               /* 0x0000137a */
+               struct usb_device_descriptor device_desc;
+
+               /* 0x0000138c */
+               uint8_t string0_desc[4];
+
+               /* 0x00001390 */
+               uint8_t string1_desc[32];
+
+               /* 0x000013b0 */
+               uint8_t string2_desc[48];
+
+               /* 0x000013e0 */
+               uint8_t string3_desc[32];
+       } usb;
+} __packed;
+
+struct ar9170_rom {
+       /* 0x00000000 */
+       uint8_t __nulldata[8];
+
+       /* 0x00000008 */
+       uint8_t bootcode[4968];
+
+       /* 0x00001370 */
+       struct ar9170_hwtype hw;
+
+       /* 0x00001400 */
+       uint8_t data[512];
+
+       /* eeprom */
+       struct ar9170_eeprom sys;
+} __packed;
+
+static const struct ar9170_rom rom __section(eeprom);
+
+#endif /* __CARL9170FW_ROM_H */
diff --git a/carlfw/include/timer.h b/carlfw/include/timer.h
new file mode 100644 (file)
index 0000000..8c3df1b
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * carl9170 firmware - used by the ar9170 wireless device
+ *
+ * Clock, Timer & Timing
+ *
+ * Copyright (c) 2000-2005 ZyDAS Technology Corporation
+ * Copyright (c) 2007-2009 Atheros Communications, Inc.
+ * Copyright   2009    Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2009, 2010 Christian Lamparter <chunkeey@googlemail.com>
+ *
+ * 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.
+ */
+
+#ifndef __CARL9170FW_TIMER_H
+#define __CARL9170FW_TIMER_H
+
+#include "config.h"
+
+enum cpu_clock_t {
+       AHB_40MHZ_OSC   = 0,
+       AHB_20_22MHZ    = 1,
+       AHB_40_44MHZ    = 2,
+       AHB_80_88MHZ    = 3
+};
+
+#define AR9170_TICKS_PER_MICROSECOND   80
+
+void handle_timer(void);
+void timer_init(const unsigned int timer, const unsigned int interval);
+void clock_set(const bool on, const enum cpu_clock_t _clock);
+
+static inline __inline uint32_t get_clock_counter(void)
+{
+       return (get(AR9170_TIMER_REG_CLOCK_HIGH) << 16) | get(AR9170_TIMER_REG_CLOCK_LOW);
+}
+
+static inline __inline bool is_after_msecs(uint32_t t0, uint32_t msecs)
+{
+       return (get_clock_counter() - t0) / (AR9170_TICKS_PER_MICROSECOND * 1000) > msecs;
+}
+
+static inline __inline void delay(uint32_t msec)
+{
+       uint32_t t1, t2, dt;
+
+       t1 = get_clock_counter();
+       while (1) {
+               t2 = get_clock_counter();
+               dt = (t2 - t1) / AR9170_TICKS_PER_MICROSECOND / 1000;
+               if (dt >= msec)
+                       break;
+       }
+}
+
+static inline __inline void udelay(uint32_t usec)
+{
+       uint32_t t1, t2, dt;
+
+       t1 = get_clock_counter();
+       while (1) {
+               t2 = get_clock_counter();
+               dt = (t2 - t1) / AR9170_TICKS_PER_MICROSECOND;
+               if (dt >= usec)
+                       break;
+       }
+}
+
+#endif /* __CARL9170FW_TIMER_H */
diff --git a/carlfw/include/uart.h b/carlfw/include/uart.h
new file mode 100644 (file)
index 0000000..5f7ef67
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * carl9170 firmware - used by the ar9170 wireless device
+ *
+ * UART functions definition
+ *
+ * Copyright (c) 2000-2005 ZyDAS Technology Corporation
+ * Copyright (c) 2007-2009 Atheros Communications, Inc.
+ * Copyright   2009    Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2009, 2010 Christian Lamparter <chunkeey@googlemail.com>
+ *
+ * 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.
+ */
+
+#ifndef __CARL9170FW_UART_H
+#define __CARL9170FW_UART_H
+
+#include "config.h"
+
+#ifdef CONFIG_CARL9170FW_DEBUG_UART
+void uart_putc(const char c);
+void uart_print_hex_dump(const void *buf, const int len);
+void uart_init(void);
+#endif /* CONFIG_CARL9170FW_DEBUG_UART */
+
+#endif /* __CARL9170FW_UART_H */
diff --git a/carlfw/include/usb.h b/carlfw/include/usb.h
new file mode 100644 (file)
index 0000000..6501e19
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * carl9170 firmware - used by the ar9170 wireless device
+ *
+ * USB definitions
+ *
+ * Copyright (c) 2000-2005 ZyDAS Technology Corporation
+ * Copyright (c) 2007-2009 Atheros Communications, Inc.
+ * Copyright   2009    Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2009, 2010 Christian Lamparter <chunkeey@googlemail.com>
+ *
+ * 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.
+ */
+
+#ifndef __CARL9170FW_USB_H
+#define __CARL9170FW_USB_H
+
+#include "config.h"
+#include "types.h"
+#include "io.h"
+#include "hw.h"
+#include "ch9.h"
+
+struct ar9170_usb_config {
+       struct usb_config_descriptor cfg;
+       struct usb_interface_descriptor intf;
+       struct usb_endpoint_descriptor ep[AR9170_USB_NUM_EXTRA_EP];
+} __packed;
+
+static inline __inline bool usb_detect_highspeed(void)
+{
+       return !!(getb(AR9170_USB_REG_MAIN_CTRL) &
+                 AR9170_USB_MAIN_CTRL_HIGHSPEED);
+}
+
+static inline __inline bool usb_configured(void)
+{
+       return !!(getb(AR9170_USB_REG_DEVICE_ADDRESS) &
+                 AR9170_USB_DEVICE_ADDRESS_CONFIGURE);
+}
+
+static inline __inline void usb_remote_wakeup(void)
+{
+       orb(AR9170_USB_REG_MAIN_CTRL, AR9170_USB_MAIN_CTRL_REMOTE_WAKEUP);
+}
+
+static inline __inline void usb_enable_global_int(void)
+{
+       orb(AR9170_USB_REG_MAIN_CTRL, AR9170_USB_MAIN_CTRL_ENABLE_GLOBAL_INT);
+}
+
+static inline __inline void usb_trigger_out(void)
+{
+       andb(AR9170_USB_REG_INTR_MASK_BYTE_4,
+               (uint8_t) ~AR9170_USB_INTR_DISABLE_OUT_INT);
+}
+
+static inline __inline void usb_reset_out(void)
+{
+       orb(AR9170_USB_REG_INTR_MASK_BYTE_4, AR9170_USB_INTR_DISABLE_OUT_INT);
+}
+
+static inline __inline void usb_trigger_in(void)
+{
+       andb(AR9170_USB_REG_INTR_MASK_BYTE_6, ~AR9170_USB_INTR_DISABLE_IN_INT);
+}
+
+static inline __inline void usb_reset_in(void)
+{
+       orb(AR9170_USB_REG_INTR_MASK_BYTE_6, AR9170_USB_INTR_DISABLE_IN_INT);
+}
+
+static inline __inline void usb_ep3_xfer_done(void)
+{
+       orb(AR9170_USB_REG_EP3_BYTE_COUNT_HIGH, 0x08);
+}
+
+static inline __inline void usb_suspend_ack(void)
+{
+       /*
+        * uP must do-over everything it should handle
+        * and do before into the suspend mode
+        */
+       andb(AR9170_USB_REG_INTR_SOURCE_7, ~BIT(2));
+}
+
+static inline __inline void usb_resume_ack(void)
+{
+       /*
+        * uP must do-over everything it should handle
+        * and do before into the suspend mode
+        */
+
+       andb(AR9170_USB_REG_INTR_SOURCE_7, ~BIT(3));
+}
+
+static inline __inline void usb_reset_ack(void)
+{
+       andb(AR9170_USB_REG_INTR_SOURCE_7, ~BIT(1));
+}
+
+static inline __inline void usb_data_out0Byte(void)
+{
+       andb(AR9170_USB_REG_INTR_SOURCE_7, (uint8_t) ~BIT(7));
+}
+
+static inline __inline void usb_data_in0Byte(void)
+{
+       andb(AR9170_USB_REG_INTR_SOURCE_7, ~BIT(6));
+}
+
+static inline __inline void usb_stop_down_queue(void)
+{
+       andl(AR9170_USB_REG_DMA_CTL, ~AR9170_DMA_CTL_ENABLE_TO_DEVICE);
+}
+
+static inline __inline void usb_start_down_queue(void)
+{
+       orl(AR9170_USB_REG_DMA_CTL, AR9170_DMA_CTL_ENABLE_TO_DEVICE);
+}
+
+static inline __inline void usb_clear_input_ep_toggle(unsigned int ep)
+{
+       andl(AR9170_USB_REG_EP_IN_MAX_SIZE_HIGH + (ep << 1),
+            ~AR9170_USB_EP_IN_TOGGLE);
+}
+
+static inline __inline void usb_set_input_ep_toggle(unsigned int ep)
+{
+       orl(AR9170_USB_REG_EP_IN_MAX_SIZE_HIGH + (ep << 1),
+           AR9170_USB_EP_IN_TOGGLE);
+}
+
+static inline __inline void usb_clear_output_ep_toggle(unsigned int ep)
+{
+       andl(AR9170_USB_REG_EP_OUT_MAX_SIZE_HIGH + (ep << 1),
+            ~AR9170_USB_EP_OUT_TOGGLE);
+}
+
+static inline __inline void usb_set_output_ep_toggle(unsigned int ep)
+{
+       orl(AR9170_USB_REG_EP_OUT_MAX_SIZE_HIGH + (ep << 1),
+           AR9170_USB_EP_OUT_TOGGLE);
+}
+
+static inline void usb_structure_check(void)
+{
+       BUILD_BUG_ON(sizeof(struct usb_config_descriptor) != USB_DT_CONFIG_SIZE);
+       BUILD_BUG_ON(sizeof(struct usb_device_descriptor) != USB_DT_DEVICE_SIZE);
+       BUILD_BUG_ON(sizeof(struct usb_endpoint_descriptor) != USB_DT_ENDPOINT_SIZE);
+       BUILD_BUG_ON(sizeof(struct usb_interface_descriptor) != USB_DT_INTERFACE_SIZE);
+}
+
+void __attribute__((noreturn)) jump_to_bootcode(void);
+
+void send_cmd_to_host(const uint8_t len, const uint8_t type,
+                     const uint8_t ext, const uint8_t *body);
+
+void usb_init(void);
+void usb_ep0rx(void);
+void usb_ep0tx(void);
+void usb_ep0setup(void);
+void handle_usb(void);
+
+void usb_timer(void);
+void usb_putc(const char c);
+void usb_print_hex_dump(const void *buf, int len);
+
+void usb_init_highspeed_fifo_cfg(void);
+void usb_init_fullspeed_fifo_cfg(void);
+
+void start(void);
+
+#ifdef CONFIG_CARL9170FW_USB_WATCHDOG
+void usb_watchdog_timer(void);
+#endif /* CONFIG_CARL9170FW_USB_WATCHDOG */
+
+#endif /* __CARL9170FW_USB_H */
diff --git a/carlfw/include/usb_fifo.h b/carlfw/include/usb_fifo.h
new file mode 100644 (file)
index 0000000..1115168
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+ * carl9170 firmware - used by the ar9170 wireless device
+ *
+ * USB definitions
+ *
+ * Copyright (c) 2000-2005 ZyDAS Technology Corporation
+ * Copyright (c) 2007-2009 Atheros Communications, Inc.
+ * Copyright   2009    Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2009, 2010 Christian Lamparter <chunkeey@googlemail.com>
+ *
+ * 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.
+ */
+
+#ifndef __CARL9170FW_USB_FIFO_H
+#define __CARL9170FW_USB_FIFO_H
+
+#include "config.h"
+
+#define MASK_F0             0xf0
+
+/* Block Size define */
+#define BLK512BYTE      1
+#define BLK1024BYTE     2
+
+#define BLK64BYTE       1
+#define BLK128BYTE      2
+
+/* Block toggle number define */
+#define SINGLE_BLK      1
+#define DOUBLE_BLK      2
+#define TRIBLE_BLK      3
+
+/* Endpoint transfer type */
+#define TF_TYPE_ISOCHRONOUS     1
+#define TF_TYPE_BULK            2
+#define TF_TYPE_INTERRUPT       3
+
+/* Endpoint or FIFO direction define */
+#define DIRECTION_IN    0
+#define DIRECTION_OUT   1
+
+#define HS_C1_I0_A0_EP1_MAX_PACKET              512
+#define HS_C1_I0_A0_EP1_bInterval               0
+
+#define HS_C1_I0_A0_EP_NUMBER                   0x04
+#define HS_C1_I0_A0_EP_LENGTH                   (EP_LENGTH * HS_C1_I0_A0_EP_NUMBER)
+#define HS_C1_I0_ALT_LENGTH                     (HS_C1_I0_A0_EP_LENGTH)
+#define HS_C1_INTERFACE_LENGTH                  (HS_C1_I0_ALT_LENGTH)
+
+#define HS_C1_CONFIG_TOTAL_LENGTH               (CONFIG_LENGTH + INTERFACE_LENGTH +  HS_C1_INTERFACE_LENGTH)
+#define FS_C1_CONFIG_TOTAL_LENGTH               (CONFIG_LENGTH + INTERFACE_LENGTH +  FS_C1_INTERFACE_LENGTH)
+
+#define FS_C1_I0_A0_EP1_MAX_PACKET              64
+/* #define FS_C1_I0_A0_EP1_bInterval               HS_C1_I0_A0_EP1_bInterval */
+
+#define HS_CONFIGURATION_NUMBER                 1
+#define FS_CONFIGURATION_NUMBER                 1
+
+#define fDOUBLE_BUF                             1
+#define fDOUBLE_BUF_IN                          0
+
+#define fFLASH_DISK                             0
+#define fENABLE_ISO                             0
+
+#define HS_C1_INTERFACE_NUMBER  0x01
+#define HS_C1                   0x01
+#define HS_C1_iConfiguration    0x00
+#define HS_C1_bmAttribute       0x80
+
+#define HS_C1_iMaxPower         0xFA
+
+/* Interface 0 */
+#define HS_C1_I0_ALT_NUMBER    0X01
+/* AlternateSetting 0 */
+#define HS_C1_I0_A0_bInterfaceNumber   0x00
+#define HS_C1_I0_A0_bAlternateSetting  0x00
+/* JWEI 2003/07/14 */
+#define HS_C1_I0_A0_EP_NUMBER          0x04
+#define HS_C1_I0_A0_bInterfaceClass    0xff
+#define HS_C1_I0_A0_bInterfaceSubClass 0x00
+#define HS_C1_I0_A0_bInterfaceProtocol 0x00
+#define HS_C1_I0_A0_iInterface         0x00
+
+/* EP 1 */
+#define HS_C1_I0_A0_EP1_BLKSIZE    512
+#define HS_C1_I0_A0_EP1_BLKNO      DOUBLE_BLK
+#define HS_C1_I0_A0_EP1_DIRECTION  DIRECTION_OUT
+#define HS_C1_I0_A0_EP1_TYPE       TF_TYPE_BULK
+
+#define HS_C1_I0_A0_EP1_MAX_PACKET 512
+#define HS_C1_I0_A0_EP1_bInterval  0
+
+/* EP 2 */
+#define HS_C1_I0_A0_EP2_BLKSIZE    512
+/* JWEI 2003/08/20 */
+#define HS_C1_I0_A0_EP2_BLKNO      SINGLE_BLK
+#define HS_C1_I0_A0_EP2_DIRECTION  DIRECTION_IN
+#define HS_C1_I0_A0_EP2_TYPE       TF_TYPE_BULK
+#define HS_C1_I0_A0_EP2_MAX_PACKET 512
+#define HS_C1_I0_A0_EP2_bInterval  0
+
+/* EP 3 */
+#define HS_C1_I0_A0_EP3_BLKSIZE    64
+#define HS_C1_I0_A0_EP3_BLKNO      SINGLE_BLK
+#define HS_C1_I0_A0_EP3_DIRECTION  DIRECTION_IN
+#define HS_C1_I0_A0_EP3_TYPE       TF_TYPE_INTERRUPT
+#define HS_C1_I0_A0_EP3_MAX_PACKET 0x0040
+#define HS_C1_I0_A0_EP3_bInterval  01
+
+/*
+ * Note: HS Bulk type require max pkt size = 512
+ *       ==> must use Interrupt type for max pkt size = 64
+ */
+
+/* EP 4 */
+#define HS_C1_I0_A0_EP4_BLKSIZE    64
+#define HS_C1_I0_A0_EP4_BLKNO      SINGLE_BLK
+#define HS_C1_I0_A0_EP4_DIRECTION  DIRECTION_OUT
+#define HS_C1_I0_A0_EP4_TYPE       TF_TYPE_INTERRUPT
+#define HS_C1_I0_A0_EP4_MAX_PACKET 0x0040
+#define HS_C1_I0_A0_EP4_bInterval  01
+
+#define HS_C1_I0_A0_EP_LENGTH           (EP_LENGTH * HS_C1_I0_A0_EP_NUMBER)
+/* EP 1 */
+#define HS_C1_I0_A0_EP1_FIFO_START  0
+#define HS_C1_I0_A0_EP1_FIFO_NO     (HS_C1_I0_A0_EP1_BLKNO * HS_C1_I0_A0_EP1_BLKSIZE)
+#define HS_C1_I0_A0_EP1_FIFO_CONFIG (uint8_t)(0x80 | ((HS_C1_I0_A0_EP1_BLKSIZE - 1) << 4) | ((HS_C1_I0_A0_EP1_BLKNO - 1) << 2) | HS_C1_I0_A0_EP1_TYPE)
+#define HS_C1_I0_A0_EP1_FIFO_MAP    (((1 - HS_C1_I0_A0_EP1_DIRECTION) << 4) | 1)
+#define HS_C1_I0_A0_EP1_MAP         (HS_C1_I0_A0_EP1_FIFO_START |   (HS_C1_I0_A0_EP1_FIFO_START << 4)   | (MASK_F0 >> (4*HS_C1_I0_A0_EP1_DIRECTION)))
+
+/* EP 2 */
+#define HS_C1_I0_A0_EP2_FIFO_START  (uint8_t)(HS_C1_I0_A0_EP1_FIFO_START + HS_C1_I0_A0_EP1_FIFO_NO)
+#define HS_C1_I0_A0_EP2_FIFO_NO     (uint8_t)(HS_C1_I0_A0_EP2_BLKNO * HS_C1_I0_A0_EP2_BLKSIZE)
+#define HS_C1_I0_A0_EP2_FIFO_CONFIG (uint8_t)(0x80 | ((HS_C1_I0_A0_EP2_BLKSIZE - 1) << 4) | ((HS_C1_I0_A0_EP2_BLKNO - 1) << 2) | HS_C1_I0_A0_EP2_TYPE)
+#define HS_C1_I0_A0_EP2_FIFO_MAP    (uint8_t)(((1 - HS_C1_I0_A0_EP2_DIRECTION) << 4) | 2)
+#define HS_C1_I0_A0_EP2_MAP         (uint8_t)(HS_C1_I0_A0_EP2_FIFO_START |   (HS_C1_I0_A0_EP2_FIFO_START << 4)   | (MASK_F0 >> (4*HS_C1_I0_A0_EP2_DIRECTION)))
+
+/* EP 3 */
+#define HS_C1_I0_A0_EP3_FIFO_START  14
+#define HS_C1_I0_A0_EP3_FIFO_NO     (HS_C1_I0_A0_EP3_BLKNO * HS_C1_I0_A0_EP3_BLKSIZE)
+#define HS_C1_I0_A0_EP3_FIFO_CONFIG (uint8_t)(0x80 | ((HS_C1_I0_A0_EP3_BLKSIZE - 1) << 4) | ((HS_C1_I0_A0_EP3_BLKNO - 1) << 2) | HS_C1_I0_A0_EP3_TYPE)
+#define HS_C1_I0_A0_EP3_FIFO_MAP    (uint8_t)(((1 - HS_C1_I0_A0_EP3_DIRECTION) << 4) | 3)
+#define HS_C1_I0_A0_EP3_MAP         (uint8_t)(HS_C1_I0_A0_EP3_FIFO_START |   (HS_C1_I0_A0_EP3_FIFO_START << 4)   | (MASK_F0 >> (4*HS_C1_I0_A0_EP3_DIRECTION)))
+
+/* EP 4 */
+#define HS_C1_I0_A0_EP4_FIFO_START  (HS_C1_I0_A0_EP3_FIFO_START + HS_C1_I0_A0_EP3_FIFO_NO)
+#define HS_C1_I0_A0_EP4_FIFO_NO     (HS_C1_I0_A0_EP4_BLKNO * HS_C1_I0_A0_EP4_BLKSIZE)
+#define HS_C1_I0_A0_EP4_FIFO_CONFIG (uint8_t)(0x80 | ((HS_C1_I0_A0_EP4_BLKSIZE - 1) << 4) | ((HS_C1_I0_A0_EP4_BLKNO - 1) << 2) | HS_C1_I0_A0_EP4_TYPE)
+#define HS_C1_I0_A0_EP4_FIFO_MAP    (((1 - HS_C1_I0_A0_EP4_DIRECTION) << 4) | 4)
+#define HS_C1_I0_A0_EP4_MAP         (uint8_t)(HS_C1_I0_A0_EP4_FIFO_START |   (HS_C1_I0_A0_EP4_FIFO_START << 4)   | (MASK_F0 >> (4*HS_C1_I0_A0_EP4_DIRECTION)))
+
+/* Configuration 1 */
+#define FS_C1_INTERFACE_NUMBER         0x01
+#define FS_C1                          0x01
+#define FS_C1_iConfiguration           0x00
+#define FS_C1_bmAttribute              0x80
+#define FS_C1_iMaxPower                        0xfa
+
+/* Interface 0 */
+#define FS_C1_I0_ALT_NUMBER            0x01
+/* AlternateSetting 0x00 */
+#define FS_C1_I0_A0_bInterfaceNumber   0x00
+#define FS_C1_I0_A0_bAlternateSetting  0x00
+#define FS_C1_I0_A0_EP_NUMBER          0x04
+#define FS_C1_I0_A0_bInterfaceClass    0xff
+#define FS_C1_I0_A0_bInterfaceSubClass 0x00
+#define FS_C1_I0_A0_bInterfaceProtocol 0x00
+
+/* EP 1 */
+#define FS_C1_I0_A0_EP1_BLKSIZE    512
+/* JWEI 2003/05/19 */
+#define FS_C1_I0_A0_EP1_BLKNO      DOUBLE_BLK
+#define FS_C1_I0_A0_EP1_DIRECTION  DIRECTION_OUT
+#define FS_C1_I0_A0_EP1_TYPE       TF_TYPE_BULK
+#define FS_C1_I0_A0_EP1_MAX_PACKET 64
+#define FS_C1_I0_A0_EP1_bInterval  0
+
+/* EP 2 */
+#define FS_C1_I0_A0_EP2_BLKSIZE    512
+/* JWEI 2003/08/20 */
+#define FS_C1_I0_A0_EP2_BLKNO      SINGLE_BLK
+#define FS_C1_I0_A0_EP2_DIRECTION  DIRECTION_IN
+#define FS_C1_I0_A0_EP2_TYPE       TF_TYPE_BULK
+#define FS_C1_I0_A0_EP2_MAX_PACKET 64
+#define FS_C1_I0_A0_EP2_bInterval  0
+
+/* EP 3 */
+#define FS_C1_I0_A0_EP3_BLKSIZE    64
+#define FS_C1_I0_A0_EP3_BLKNO      SINGLE_BLK
+#define FS_C1_I0_A0_EP3_DIRECTION  DIRECTION_IN
+#define FS_C1_I0_A0_EP3_TYPE       TF_TYPE_INTERRUPT
+#define FS_C1_I0_A0_EP3_MAX_PACKET 0x0040
+#define FS_C1_I0_A0_EP3_bInterval  1
+
+/* EP 4 */
+#define FS_C1_I0_A0_EP4_BLKSIZE    64
+#define FS_C1_I0_A0_EP4_BLKNO      SINGLE_BLK
+#define FS_C1_I0_A0_EP4_DIRECTION  DIRECTION_OUT
+#define FS_C1_I0_A0_EP4_TYPE       TF_TYPE_BULK
+#define FS_C1_I0_A0_EP4_MAX_PACKET 0x0040
+#define FS_C1_I0_A0_EP4_bInterval  0
+
+#define FS_C1_I0_A0_EP_LENGTH           (EP_LENGTH * FS_C1_I0_A0_EP_NUMBER)
+/* EP 1 */
+#define FS_C1_I0_A0_EP1_FIFO_START  0
+#define FS_C1_I0_A0_EP1_FIFO_NO     (uint8_t)(FS_C1_I0_A0_EP1_BLKNO * FS_C1_I0_A0_EP1_BLKSIZE)
+#define FS_C1_I0_A0_EP1_FIFO_CONFIG (uint8_t)(0x80 | ((FS_C1_I0_A0_EP1_BLKSIZE - 1) << 4) | ((FS_C1_I0_A0_EP1_BLKNO - 1) << 2) | FS_C1_I0_A0_EP1_TYPE)
+#define FS_C1_I0_A0_EP1_FIFO_MAP    (uint8_t)(((1 - FS_C1_I0_A0_EP1_DIRECTION) << 4) | 1)
+#define FS_C1_I0_A0_EP1_MAP         (uint8_t)(FS_C1_I0_A0_EP1_FIFO_START |   (FS_C1_I0_A0_EP1_FIFO_START << 4)   | (MASK_F0 >> (4*FS_C1_I0_A0_EP1_DIRECTION)))
+
+/* EP 2 */
+#define FS_C1_I0_A0_EP2_FIFO_START  (uint8_t)(FS_C1_I0_A0_EP1_FIFO_START + FS_C1_I0_A0_EP1_FIFO_NO)
+#define FS_C1_I0_A0_EP2_FIFO_NO     (uint8_t)(FS_C1_I0_A0_EP2_BLKNO * FS_C1_I0_A0_EP2_BLKSIZE)
+#define FS_C1_I0_A0_EP2_FIFO_CONFIG (uint8_t)(0x80 | ((FS_C1_I0_A0_EP2_BLKSIZE - 1) << 4) | ((FS_C1_I0_A0_EP2_BLKNO - 1) << 2) | FS_C1_I0_A0_EP2_TYPE)
+#define FS_C1_I0_A0_EP2_FIFO_MAP    (uint8_t)(((1 - FS_C1_I0_A0_EP2_DIRECTION) << 4) | 2)
+#define FS_C1_I0_A0_EP2_MAP         (uint8_t)(FS_C1_I0_A0_EP2_FIFO_START |   (FS_C1_I0_A0_EP2_FIFO_START << 4)   | (MASK_F0 >> (4*FS_C1_I0_A0_EP2_DIRECTION)))
+
+/* EP 3 */
+#define FS_C1_I0_A0_EP3_FIFO_START  14
+#define FS_C1_I0_A0_EP3_FIFO_NO     (uint8_t)(FS_C1_I0_A0_EP3_BLKNO * FS_C1_I0_A0_EP3_BLKSIZE)
+#define FS_C1_I0_A0_EP3_FIFO_CONFIG (uint8_t)(0x80 | ((FS_C1_I0_A0_EP3_BLKSIZE - 1) << 4) | ((FS_C1_I0_A0_EP3_BLKNO - 1) << 2) | FS_C1_I0_A0_EP3_TYPE)
+#define FS_C1_I0_A0_EP3_FIFO_MAP    (uint8_t)(((1 - FS_C1_I0_A0_EP3_DIRECTION) << 4) | 3)
+#define FS_C1_I0_A0_EP3_MAP         (uint8_t)(FS_C1_I0_A0_EP3_FIFO_START |   (FS_C1_I0_A0_EP3_FIFO_START << 4)   | (MASK_F0 >> (4*FS_C1_I0_A0_EP3_DIRECTION)))
+
+/* EP 4 */
+#define FS_C1_I0_A0_EP4_FIFO_START  (uint8_t)(FS_C1_I0_A0_EP3_FIFO_START + FS_C1_I0_A0_EP3_FIFO_NO)
+#define FS_C1_I0_A0_EP4_FIFO_NO     (uint8_t)(FS_C1_I0_A0_EP4_BLKNO * FS_C1_I0_A0_EP4_BLKSIZE)
+#define FS_C1_I0_A0_EP4_FIFO_CONFIG (uint8_t)(0x80 | ((FS_C1_I0_A0_EP4_BLKSIZE - 1) << 4) | ((FS_C1_I0_A0_EP4_BLKNO - 1) << 2) | FS_C1_I0_A0_EP4_TYPE)
+#define FS_C1_I0_A0_EP4_FIFO_MAP    (uint8_t)(((1 - FS_C1_I0_A0_EP4_DIRECTION) << 4) | 4)
+#define FS_C1_I0_A0_EP4_MAP         (uint8_t)(FS_C1_I0_A0_EP4_FIFO_START |   (FS_C1_I0_A0_EP4_FIFO_START << 4)   | (MASK_F0 >> (4*FS_C1_I0_A0_EP4_DIRECTION)))
+
+#endif /* __CARL9170FW_USB_FIFO_H */
diff --git a/carlfw/include/wl.h b/carlfw/include/wl.h
new file mode 100644 (file)
index 0000000..178679a
--- /dev/null
@@ -0,0 +1,274 @@
+/*
+ * carl9170 firmware - used by the ar9170 wireless device
+ *
+ * WLAN
+ *
+ * Copyright (c) 2000-2005 ZyDAS Technology Corporation
+ * Copyright (c) 2007-2009 Atheros Communications, Inc.
+ * Copyright   2009    Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2009, 2010 Christian Lamparter <chunkeey@googlemail.com>
+ *
+ * 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.
+ */
+
+#ifndef __CARL9170FW_WLAN_H
+#define __CARL9170FW_WLAN_H
+
+#include "config.h"
+#include "carl9170.h"
+#include "io.h"
+
+struct ieee80211_hdr;
+
+static inline __inline void set_wlan_txq_dma_addr(const unsigned int q, const uint32_t v)
+{
+       set(AR9170_MAC_REG_DMA_TXQ_ADDR + (q << 3), v);
+}
+
+static inline __inline void set_wlan_txq_dma_curr_addr(const unsigned int q, const uint32_t v)
+{
+       set(AR9170_MAC_REG_DMA_TXQ_CURR_ADDR + (q << 3), v);
+}
+
+static inline __inline struct dma_desc *get_wlan_txq_dma_addr(const unsigned int q)
+{
+       return getp(AR9170_MAC_REG_DMA_TXQ_ADDR + (q << 3));
+}
+
+static inline __inline struct dma_desc *get_wlan_txq_addr(const unsigned int q)
+{
+       return getp(AR9170_MAC_REG_DMA_TXQ_CURR_ADDR + (q << 3));
+}
+
+static inline __inline void _wlan_trigger(const uint32_t queue_bit)
+{
+       set(AR9170_MAC_REG_DMA_TRIGGER, queue_bit);
+}
+
+#ifdef CONFIG_CARL9170FW_DELAYED_TX
+static inline __inline void wlan_trigger(const uint32_t queue_bit)
+{
+       fw.wlan.tx_trigger |= queue_bit;
+}
+#else
+static inline __inline void wlan_trigger(const uint32_t queue_bit)
+{
+       _wlan_trigger(queue_bit);
+}
+#endif /* CONFIG_CARL9170FW_DELAYED_TX */
+
+static inline __inline uint8_t ar9170_get_rx_macstatus_status(struct dma_desc *desc)
+{
+       return *((uint8_t *) DESC_PAYLOAD_OFF(desc->lastAddr,
+               (unsigned int) desc->lastAddr->dataSize - 1));
+}
+
+static inline __inline uint8_t ar9170_get_rx_macstatus_error(struct dma_desc *desc)
+{
+       unsigned int offset;
+
+       if (desc->lastAddr->dataSize == 1) {
+               while (desc->lastAddr != desc->nextAddr)
+                       desc = desc->nextAddr;
+
+               offset = (unsigned int) (desc->dataSize - 1);
+       } else {
+               desc = desc->lastAddr;
+               offset = desc->dataSize -
+                       (sizeof(struct ar9170_rx_macstatus) -
+                        offsetof(struct ar9170_rx_macstatus, error));
+       }
+
+       return *((uint8_t *) DESC_PAYLOAD_OFF(desc, offset));
+}
+
+static inline __inline struct ieee80211_hdr *ar9170_get_rx_i3e(struct dma_desc *desc)
+{
+       if (!((ar9170_get_rx_macstatus_status(desc) &
+               AR9170_RX_STATUS_MPDU_MASK) & AR9170_RX_STATUS_MPDU_LAST)) {
+               return (void *)(DESC_PAYLOAD_OFF(desc,
+                       offsetof(struct ar9170_rx_frame_head, i3e)));
+       } else {
+               return (void *)(DESC_PAYLOAD_OFF(desc,
+                       offsetof(struct ar9170_rx_frame_tail, i3e)));
+       }
+}
+
+static inline __inline struct ar9170_rx_head *ar9170_get_rx_head(struct dma_desc *desc)
+{
+       if (!((ar9170_get_rx_macstatus_status(desc) &
+               AR9170_RX_STATUS_MPDU_MASK) & AR9170_RX_STATUS_MPDU_LAST)) {
+               return (void *)((uint8_t *)DESC_PAYLOAD(desc) +
+                       offsetof(struct ar9170_rx_frame_head, phy_head));
+       } else {
+               return (void *) NULL;
+       }
+}
+
+static inline __inline uint32_t ar9170_rx_to_phy(struct dma_desc *rx)
+{
+       struct ar9170_tx_hw_phy_control phy;
+       struct ar9170_rx_head *head;
+       uint8_t mac_status;
+
+       phy.set = 0;
+
+       head = ar9170_get_rx_head(rx);
+       if (!head)
+               return le32_to_cpu(phy.set);
+
+       mac_status = ar9170_get_rx_macstatus_status(rx);
+
+       phy.modulation = mac_status & AR9170_RX_STATUS_MODULATION_MASK;
+       phy.chains = AR9170_TX_PHY_TXCHAIN_1;
+
+       switch (phy.modulation) {
+       case AR9170_RX_STATUS_MODULATION_CCK:
+               if (mac_status & AR9170_RX_STATUS_SHORT_PREAMBLE)
+                       phy.preamble = 1;
+
+               switch (head->plcp[0]) {
+               case AR9170_RX_PHY_RATE_CCK_2M:
+                       phy.mcs = AR9170_TX_PHY_RATE_CCK_2M;
+                       break;
+
+               case AR9170_RX_PHY_RATE_CCK_5M:
+                       phy.mcs = AR9170_TX_PHY_RATE_CCK_5M;
+                       break;
+
+               case AR9170_RX_PHY_RATE_CCK_11M:
+                       phy.mcs = AR9170_TX_PHY_RATE_CCK_11M;
+                       break;
+
+               case AR9170_RX_PHY_RATE_CCK_1M:
+               default:
+                       phy.mcs = AR9170_TX_PHY_RATE_CCK_1M;
+                       break;
+
+               }
+               break;
+
+       case AR9170_RX_STATUS_MODULATION_DUPOFDM:
+       case AR9170_RX_STATUS_MODULATION_OFDM:
+               phy.mcs = head->plcp[0] & 0xf;
+               break;
+
+       case AR9170_RX_STATUS_MODULATION_HT:
+               if (head->plcp[3] & 0x80)
+                       phy.bandwidth = 2;
+
+               if (head->plcp[6] & 0x80)
+                       phy.short_gi = 1;
+
+               /* TODO: Enable both chains for MCS > 7 */
+               phy.mcs = head->plcp[6] & 0x7;
+               break;
+       }
+
+       return le32_to_cpu(phy.set);
+}
+
+static inline __inline unsigned int ar9170_get_rx_mpdu_len(struct dma_desc *desc)
+{
+       /*
+        * WARNING: you have to check the error bits in macstatus first!
+        */
+
+       unsigned int mpdu_len = desc->totalLen;
+
+       mpdu_len -= sizeof(struct ar9170_rx_macstatus);
+
+       switch (ar9170_get_rx_macstatus_status(desc) & AR9170_RX_STATUS_MPDU_MASK) {
+       case AR9170_RX_STATUS_MPDU_LAST:
+               mpdu_len -= sizeof(struct ar9170_rx_phystatus);
+               break;
+
+       case AR9170_RX_STATUS_MPDU_SINGLE:
+               mpdu_len -= sizeof(struct ar9170_rx_phystatus);
+
+       case AR9170_RX_STATUS_MPDU_FIRST:
+               mpdu_len -= sizeof(struct ar9170_rx_head);
+               break;
+
+       case AR9170_RX_STATUS_MPDU_MIDDLE:
+       default:
+               break;
+       }
+
+       return mpdu_len;
+}
+
+static inline __inline bool ar9170_tx_length_check(const uint16_t len)
+{
+       return len > (sizeof(struct carl9170_tx_superframe) + 24 +
+                        FCS_LEN);
+}
+
+static inline __inline struct carl9170_tx_superframe *get_super(struct dma_desc *desc)
+{
+       return container_of(DESC_PAYLOAD(desc), struct carl9170_tx_superframe,
+                           f);
+}
+
+static inline __inline void hide_super(struct dma_desc *desc)
+{
+       desc->dataAddr = (uint8_t *)
+               (((unsigned long)(DESC_PAYLOAD(desc)) +
+               offsetof(struct carl9170_tx_superframe, f)));
+
+       desc->dataSize -= sizeof(struct carl9170_tx_superdesc);
+       desc->totalLen -= sizeof(struct carl9170_tx_superdesc);
+}
+
+static inline __inline void unhide_super(struct dma_desc *desc)
+{
+       desc->dataAddr = (uint8_t *) get_super(desc);
+       desc->dataSize += sizeof(struct carl9170_tx_superdesc);
+       desc->totalLen += sizeof(struct carl9170_tx_superdesc);
+}
+
+static inline __inline __hot void read_tsf(uint32_t *tsf)
+{
+       /*
+        * "According to the [hardware] documentation:
+        *  > when TSF_LOW is read, TSF_HI is automatically concurrently
+        *  > copied into a temporary register so that an immediate read
+        *  > of TSF_HI will get the value that was present when TSF_LOW
+        *  > was read. "
+        *
+        * (David H. Lynch Jr. - mail from 2010-05-22)
+        * http://permalink.gmane.org/gmane.linux.kernel.wireless.general/51249
+        */
+
+       tsf[0] = get(AR9170_MAC_REG_TSF_L);
+       tsf[1] = get(AR9170_MAC_REG_TSF_H);
+}
+
+void wlan_tx(struct dma_desc *desc);
+void wlan_timer(void);
+void handle_wlan(void);
+void wlan_tx_stuck(const struct carl9170_cmd *cmd, struct carl9170_rsp *rsp);
+
+static inline void __check_wlantx(void)
+{
+       BUILD_BUG_ON(sizeof(struct carl9170_tx_superdesc) != CARL9170_TX_SUPERDESC_LEN);
+       BUILD_BUG_ON((offsetof(struct carl9170_tx_superframe, f) & 3) != 0);
+       BUILD_BUG_ON(sizeof(struct ar9170_tx_hwdesc) != AR9170_TX_HWDESC_LEN);
+       BUILD_BUG_ON(sizeof(struct ar9170_rx_head) != 12);
+       BUILD_BUG_ON(sizeof(struct ar9170_rx_phystatus) != 20);
+       BUILD_BUG_ON(sizeof(struct ar9170_rx_macstatus) != 4);
+}
+
+#endif /* __CARL9170FW_WLAN_H */
diff --git a/carlfw/src/ashlsi3.S b/carlfw/src/ashlsi3.S
new file mode 100644 (file)
index 0000000..593d902
--- /dev/null
@@ -0,0 +1,193 @@
+/* Copyright (C) 1994, 1995, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
+   2004, 2005, 2006
+   Free Software Foundation, Inc.
+
+This file 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, or (at your option) any
+later version.
+
+In addition to the permissions in the GNU General Public License, the
+Free Software Foundation gives you unlimited permission to link the
+compiled version of this file into combinations with other programs,
+and to distribute those combinations without any restriction coming
+from the use of this file.  (The General Public License restrictions
+do apply in other respects; for example, they cover modification of
+the file, and distribution when not linked into a combine
+executable.)
+
+This file 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, 51 Franklin Street, Fifth Floor,
+Boston, MA 02110-1301, USA.  */
+
+!! libgcc routines for the Renesas / SuperH SH CPUs.
+!! Contributed by Steve Chamberlain.
+!! sac@cygnus.com
+
+!! ashiftrt_r4_x, ___ashrsi3, ___ashlsi3, ___lshrsi3 routines
+!! recoded in assembly by Toshiyasu Morita
+!! tm@netcom.com
+
+/* SH2 optimizations for ___ashrsi3, ___ashlsi3, ___lshrsi3 and
+   ELF local label prefixes by J"orn Rennecke
+   amylaar@cygnus.com  */
+
+!
+! __ashlsi3
+!
+! Entry:
+!
+! r4: Value to shift
+! r5: Shifts
+!
+! Exit:
+!
+! r0: Result
+!
+! Destroys:
+!
+! (none)
+!
+       .global ___ashlsi3
+
+       .align  2
+___ashlsi3:
+       mov     #31,r0
+       and     r0,r5
+       mova    ashlsi3_table,r0
+       mov.b   @(r0,r5),r5
+#ifdef __sh1__
+       add     r5,r0
+       jmp     @r0
+#else
+       braf    r5
+#endif
+       mov     r4,r0
+
+       .align  2
+ashlsi3_table:
+       .byte           ashlsi3_0-ashlsi3_table
+       .byte           ashlsi3_1-ashlsi3_table
+       .byte           ashlsi3_2-ashlsi3_table
+       .byte           ashlsi3_3-ashlsi3_table
+       .byte           ashlsi3_4-ashlsi3_table
+       .byte           ashlsi3_5-ashlsi3_table
+       .byte           ashlsi3_6-ashlsi3_table
+       .byte           ashlsi3_7-ashlsi3_table
+       .byte           ashlsi3_8-ashlsi3_table
+       .byte           ashlsi3_9-ashlsi3_table
+       .byte           ashlsi3_10-ashlsi3_table
+       .byte           ashlsi3_11-ashlsi3_table
+       .byte           ashlsi3_12-ashlsi3_table
+       .byte           ashlsi3_13-ashlsi3_table
+       .byte           ashlsi3_14-ashlsi3_table
+       .byte           ashlsi3_15-ashlsi3_table
+       .byte           ashlsi3_16-ashlsi3_table
+       .byte           ashlsi3_17-ashlsi3_table
+       .byte           ashlsi3_18-ashlsi3_table
+       .byte           ashlsi3_19-ashlsi3_table
+       .byte           ashlsi3_20-ashlsi3_table
+       .byte           ashlsi3_21-ashlsi3_table
+       .byte           ashlsi3_22-ashlsi3_table
+       .byte           ashlsi3_23-ashlsi3_table
+       .byte           ashlsi3_24-ashlsi3_table
+       .byte           ashlsi3_25-ashlsi3_table
+       .byte           ashlsi3_26-ashlsi3_table
+       .byte           ashlsi3_27-ashlsi3_table
+       .byte           ashlsi3_28-ashlsi3_table
+       .byte           ashlsi3_29-ashlsi3_table
+       .byte           ashlsi3_30-ashlsi3_table
+       .byte           ashlsi3_31-ashlsi3_table
+
+ashlsi3_6:
+       shll2   r0
+ashlsi3_4:
+       shll2   r0
+ashlsi3_2:
+       rts
+       shll2   r0
+
+ashlsi3_7:
+       shll2   r0
+ashlsi3_5:
+       shll2   r0
+ashlsi3_3:
+       shll2   r0
+ashlsi3_1:
+       rts
+       shll    r0
+
+ashlsi3_14:
+       shll2   r0
+ashlsi3_12:
+       shll2   r0
+ashlsi3_10:
+       shll2   r0
+ashlsi3_8:
+       rts
+       shll8   r0
+
+ashlsi3_15:
+       shll2   r0
+ashlsi3_13:
+       shll2   r0
+ashlsi3_11:
+       shll2   r0
+ashlsi3_9:
+       shll8   r0
+       rts
+       shll    r0
+
+ashlsi3_22:
+       shll2   r0
+ashlsi3_20:
+       shll2   r0
+ashlsi3_18:
+       shll2   r0
+ashlsi3_16:
+       rts
+       shll16  r0
+
+ashlsi3_23:
+       shll2   r0
+ashlsi3_21:
+       shll2   r0
+ashlsi3_19:
+       shll2   r0
+ashlsi3_17:
+       shll16  r0
+       rts
+       shll    r0
+
+ashlsi3_30:
+       shll2   r0
+ashlsi3_28:
+       shll2   r0
+ashlsi3_26:
+       shll2   r0
+ashlsi3_24:
+       shll16  r0
+       rts
+       shll8   r0
+
+ashlsi3_31:
+       shll2   r0
+ashlsi3_29:
+       shll2   r0
+ashlsi3_27:
+       shll2   r0
+ashlsi3_25:
+       shll16  r0
+       shll8   r0
+       rts
+       shll    r0
+
+ashlsi3_0:
+       rts
+       nop
diff --git a/carlfw/src/cam.c b/carlfw/src/cam.c
new file mode 100644 (file)
index 0000000..2e0d996
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * carl9170 firmware - used by the ar9170 wireless device
+ *
+ * Security Engine
+ *
+ * Copyright (c) 2000-2005 ZyDAS Technology Corporation
+ * Copyright (c) 2007-2009 Atheros Communications, Inc.
+ * Copyright   2009    Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2009, 2010 Christian Lamparter <chunkeey@googlemail.com>
+ *
+ * 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.
+ */
+
+#include "carl9170.h"
+#include "cam.h"
+
+#ifdef CONFIG_CARL9170FW_SECURITY_ENGINE
+static void disable_cam_user(const uint16_t userId)
+{
+       if (userId <= 31)
+               andl(AR9170_MAC_REG_CAM_ROLL_CALL_TBL_L, (~((uint32_t) 1 << userId)));
+       else if (userId <= 63)
+               andl(AR9170_MAC_REG_CAM_ROLL_CALL_TBL_H, (~((uint32_t) 1 << (userId - 32))));
+}
+
+static void enable_cam_user(const uint16_t userId)
+{
+       if (userId <= 31)
+               orl(AR9170_MAC_REG_CAM_ROLL_CALL_TBL_L, (((uint32_t) 1) << userId));
+       else if (userId <= 63)
+               orl(AR9170_MAC_REG_CAM_ROLL_CALL_TBL_H, (((uint32_t) 1) << (userId - 32)));
+}
+
+static void wait_for_cam_read_ready(void)
+{
+       while ((get(AR9170_MAC_REG_CAM_STATE) & AR9170_MAC_CAM_STATE_READ_PENDING) == 0) {
+               /*
+                * wait
+                */
+       }
+}
+
+static void wait_for_cam_write_ready(void)
+{
+       while ((get(AR9170_MAC_REG_CAM_STATE) & AR9170_MAC_CAM_STATE_WRITE_PENDING) == 0) {
+               /*
+                * wait some more
+                */
+       }
+}
+
+static void HW_CAM_Avail(void)
+{
+       uint32_t tmpValue;
+
+       do {
+               tmpValue = get(AR9170_MAC_REG_CAM_MODE);
+       } while (tmpValue & AR9170_MAC_CAM_HOST_PENDING);
+}
+
+static void HW_CAM_Write128(const uint32_t address, const uint32_t *data)
+{
+       HW_CAM_Avail();
+
+       set(AR9170_MAC_REG_CAM_DATA0, data[0]);
+       set(AR9170_MAC_REG_CAM_DATA1, data[1]);
+       set(AR9170_MAC_REG_CAM_DATA2, data[2]);
+       set(AR9170_MAC_REG_CAM_DATA3, data[3]);
+
+       set(AR9170_MAC_REG_CAM_ADDR, address | AR9170_MAC_CAM_ADDR_WRITE);
+
+       wait_for_cam_write_ready();
+}
+
+static void HW_CAM_Read128(const uint32_t address, uint32_t *data)
+{
+
+       HW_CAM_Avail();
+       set(AR9170_MAC_REG_CAM_ADDR, address);
+
+       wait_for_cam_read_ready();
+       HW_CAM_Avail();
+       data[0] = get(AR9170_MAC_REG_CAM_DATA0);
+       data[1] = get(AR9170_MAC_REG_CAM_DATA1);
+       data[2] = get(AR9170_MAC_REG_CAM_DATA2);
+       data[3] = get(AR9170_MAC_REG_CAM_DATA3);
+}
+
+void set_key(const struct carl9170_set_key_cmd *key)
+{
+       uint32_t data[4];
+       uint16_t row, wordId, nibbleId, i;
+
+       if (key->user > (AR9170_CAM_MAX_USER + 3))
+               return ;
+
+       if (key->keyId > 1)
+               return ;
+
+       /* Disable Key */
+       disable_cam_user(key->user);
+
+       /* Set encrypt type */
+       if (key->user >= AR9170_CAM_MAX_USER) {
+               /* default */
+               row = DEFAULT_ENCRY_TYPE;
+               wordId = 0;
+               nibbleId = (key->user - AR9170_CAM_MAX_USER) & 0x7;
+       } else {
+               row = ENCRY_TYPE_START_ADDR + (key->user >> 5);
+               wordId = (key->user >> 3) & 0x3;
+               nibbleId = key->user & 0x7;
+       }
+
+       HW_CAM_Read128(row, data);
+       data[wordId] &= (~(0xf << ((uint32_t) nibbleId * 4)));
+       data[wordId] |= (key->type << ((uint32_t) nibbleId * 4));
+       HW_CAM_Write128(row, data);
+
+       /* Set MAC address */
+       if (key->user < AR9170_CAM_MAX_USER) {
+               uint16_t byteId;
+               wordId = (key->user >> 2) & 0x3;
+               byteId = key->user & 0x3;
+               row = (key->user >> 4) * 6;
+
+               for (i = 0; i < 6; i++) {
+                       HW_CAM_Read128(row + i, data);
+                       data[wordId] &= (~(0xff << ((uint32_t) byteId * 8)));
+                       data[wordId] |= (key->macAddr[i] << ((uint32_t) byteId * 8));
+                       HW_CAM_Write128(row + i, data);
+               }
+       }
+
+       /* Set key */
+       row = KEY_START_ADDR + (key->user * 2) + key->keyId;
+
+       HW_CAM_Write128(row, key->key);
+
+       /* Enable Key */
+       enable_cam_user(key->user);
+}
+
+void disable_key(const struct carl9170_disable_key_cmd *key)
+{
+       disable_cam_user(key->user);
+}
+
+#endif /* CONFIG_CARL9170FW_SECURITY_ENGINE */
diff --git a/carlfw/src/cmd.c b/carlfw/src/cmd.c
new file mode 100644 (file)
index 0000000..f5bbad8
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * carl9170 firmware - used by the ar9170 wireless device
+ *
+ * Code to handle commands from the host driver.
+ *
+ * Copyright (c) 2000-2005 ZyDAS Technology Corporation
+ * Copyright (c) 2007-2009 Atheros Communications, Inc.
+ * Copyright   2009    Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2009, 2010 Christian Lamparter <chunkeey@googlemail.com>
+ *
+ * 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.
+ */
+
+#include "carl9170.h"
+#include "io.h"
+#include "cam.h"
+#include "rf.h"
+#include "printf.h"
+#include "timer.h"
+#include "wl.h"
+
+void handle_cmd(struct carl9170_rsp *resp)
+{
+       struct carl9170_cmd *cmd = &dma_mem.reserved.cmd.cmd;
+       unsigned int i;
+
+       /* copies cmd, len and extra fields */
+       resp->hdr.hdr_data = cmd->hdr.hdr_data;
+
+       switch (cmd->hdr.cmd) {
+       case CARL9170_CMD_RREG:
+               for (i = 0; i < (cmd->hdr.len / 4); i++)
+                       resp->rreg_res.vals[i] = get(cmd->rreg.regs[i]);
+               break;
+
+       case CARL9170_CMD_WREG:
+               resp->hdr.len = 0;
+               for (i = 0; i < (cmd->hdr.len / 8); i++)
+                       set(cmd->wreg.regs[i].addr, cmd->wreg.regs[i].val);
+               break;
+
+       case CARL9170_CMD_ECHO:
+               memcpy(resp->echo.vals, cmd->echo.vals, cmd->hdr.len);
+               break;
+
+       case CARL9170_CMD_SWRST:
+               resp->hdr.len = 0;
+               fw.wlan.mac_reset = CARL9170_MAC_RESET_FORCE;
+               break;
+
+       case CARL9170_CMD_REBOOT:
+               /*
+                * reboot does not return and generates no response
+                * resp->len = 0;
+                */
+
+               reboot();
+               break;
+
+       case CARL9170_CMD_READ_TSF:
+               resp->hdr.len = 8;
+               read_tsf((uint32_t *)resp->tsf.tsf);
+               break;
+
+#ifdef CONFIG_CARL9170FW_CAB_QUEUE
+       case CARL9170_CMD_FLUSH_CAB:
+               resp->hdr.len = 0;
+               fw.wlan.cab_flush_trigger = CARL9170_CAB_TRIGGER_ARMED;
+               fw.wlan.cab_flush_time = get_clock_counter() +
+                                        CARL9170_TBTT_DELTA;
+               break;
+#endif /* CONFIG_CARL9170FW_CAB_QUEUE */
+
+#ifdef CONFIG_CARL9170FW_SECURITY_ENGINE
+       case CARL9170_CMD_EKEY:
+               resp->hdr.len = 1;
+               set_key(&cmd->setkey);
+               break;
+
+       case CARL9170_CMD_DKEY:
+               /* Disable Key */
+               resp->hdr.len = 1;
+               disable_key(&cmd->disablekey);
+               break;
+#endif /* CONFIG_CARL9170FW_SECURIT_ENGINE */
+
+#ifdef CONFIG_CARL9170FW_RADIO_FUNCTIONS
+       case CARL9170_CMD_FREQUENCY:
+       case CARL9170_CMD_RF_INIT:
+               rf_cmd(cmd, resp);
+               break;
+
+       case CARL9170_CMD_FREQ_START:
+               resp->hdr.len = 0;
+               rf_notify_set_channel();
+               break;
+
+# ifdef CONFIG_CARL9170FW_PSM
+       case CARL9170_CMD_PSM:
+               resp->hdr.len = 0;
+               fw.phy.psm.state = le32_to_cpu(cmd->psm.state);
+               rf_psm();
+               break;
+# endif /* CONFIG_CARL9170FW_PSM */
+#endif /* CONFIG_CARL9170FW_RADIO_FUNCTIOS */
+
+#ifdef CONFIG_CARL9170FW_USB_WATCHDOG
+       case CARL9170_CMD_USB_WD:
+               resp->hdr.len = 4;
+               fw.usb.watchdog.state = le32_to_cpu(cmd->watchdog.state);
+               break;
+
+#endif /* CONFIG_CARL9170FW_USB_WATCHDOG */
+
+       default:
+               break;
+       }
+}
diff --git a/carlfw/src/dma.c b/carlfw/src/dma.c
new file mode 100644 (file)
index 0000000..3da9f8c
--- /dev/null
@@ -0,0 +1,280 @@
+/*
+ * carl9170 firmware - used by the ar9170 wireless device
+ *
+ * DMA descriptor handling functions
+ *
+ * Copyright (c) 2000-2005 ZyDAS Technology Corporation
+ * Copyright (c) 2007-2009 Atheros Communications, Inc.
+ * Copyright   2009    Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2009, 2010 Christian Lamparter <chunkeey@googlemail.com>
+ *
+ * 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.
+ */
+
+#include "carl9170.h"
+#include "wl.h"
+#include "printf.h"
+
+struct ar9170_dma_memory dma_mem __section(sram);
+
+static void copy_dma_desc(struct dma_desc *dst,
+                         struct dma_desc *src)
+{
+       memcpy(dst, src, sizeof(struct dma_desc));
+}
+
+static void clear_descriptor(struct dma_desc *d)
+{
+       d->status = AR9170_OWN_BITS_SW;
+       d->ctrl = 0;
+       d->dataSize = 0;
+       d->totalLen = 0;
+       d->lastAddr = d;
+       d->dataAddr = NULL;
+       d->nextAddr = d;
+}
+
+static void fill_descriptor(struct dma_desc *d, uint16_t size, uint8_t *data)
+{
+       d->status = AR9170_OWN_BITS_SW;
+       d->ctrl = 0;
+       d->dataSize = size;
+       d->totalLen = 0;
+       d->lastAddr = d;
+       d->dataAddr = data;
+       d->nextAddr = NULL;
+}
+
+/*
+ *  - Init up_queue, down_queue, tx_queue[5], rx_queue.
+ *  - Setup descriptors and data buffer address.
+ *  - Ring descriptors rx_queue and down_queue by dma_reclaim().
+ *
+ * NOTE: LastAddr tempary point (same) to nextAddr after initialize.
+ *      Because LastAddr is don't care in function dma_reclaim().
+ */
+void dma_init_descriptors(void)
+{
+       unsigned int i, j;
+
+       for (i = 0; i < ARRAY_SIZE(dma_mem.terminator); i++)
+               clear_descriptor(&dma_mem.terminator[i]);
+
+       /* Assign terminators to DMA queues */
+       i = 0;
+       fw.pta.up_queue.head = fw.pta.up_queue.terminator = &dma_mem.terminator[i++];
+       fw.pta.down_queue.head = fw.pta.down_queue.terminator = &dma_mem.terminator[i++];
+       for (j = 0; j < __AR9170_NUM_TX_QUEUES; j++)
+               fw.wlan.tx_queue[j].head = fw.wlan.tx_queue[j].terminator = &dma_mem.terminator[i++];
+       fw.wlan.rx_queue.head = fw.wlan.rx_queue.terminator = &dma_mem.terminator[i++];
+       fw.usb.int_desc = &dma_mem.terminator[i++];
+
+#ifdef CONFIG_CARL9170FW_CAB_QUEUE
+       fw.wlan.cab_queue.head = fw.wlan.cab_queue.terminator = &dma_mem.terminator[i++];
+#endif /* CONFIG_CARL9170FW_CAB_QUEUE */
+
+#ifdef CONFIG_CARL9170FW_HANDLE_BACK_REQ
+       fw.wlan.ba_desc = &dma_mem.terminator[i++];
+#endif /* CONFIG_CARL9170FW_HANDLE_BACK_REQ */
+
+#ifdef CONFIG_CARL9170FW_DELAYED_TX
+       fw.wlan.tx_retry.head = fw.wlan.tx_retry.terminator = &dma_mem.terminator[i++];
+
+       for (j = 0; j < __AR9170_NUM_TX_QUEUES; j++)
+               fw.wlan.tx_delay[j].head = fw.wlan.tx_delay[j].terminator = &dma_mem.terminator[i++];
+#endif /* CONFIG_CARL9170FW_DELAYED_TX */
+
+       DBG("Blocks:%d [tx:%d, rx:%d] Terminators:%d/%d\n",
+           AR9170_BLOCK_NUMBER, AR9170_TX_BLOCK_NUMBER,
+           AR9170_RX_BLOCK_NUMBER, AR9170_TERMINATOR_NUMBER, i);
+
+       /* Init descriptors and memory blocks */
+       for (i = 0; i < AR9170_BLOCK_NUMBER; i++) {
+               fill_descriptor(&dma_mem.block[i], AR9170_BLOCK_SIZE, dma_mem.data[i].data);
+
+               if (i < AR9170_TX_BLOCK_NUMBER)
+                       dma_reclaim(&fw.pta.down_queue, &dma_mem.block[i]);
+               else
+                       dma_reclaim(&fw.wlan.rx_queue, &dma_mem.block[i]);
+       }
+
+       /* Set DMA address registers */
+       set(AR9170_PTA_REG_DN_DMA_ADDRH, (uint32_t) fw.pta.down_queue.head >> 16);
+       set(AR9170_PTA_REG_DN_DMA_ADDRL, (uint32_t) fw.pta.down_queue.head & 0xffff);
+       set(AR9170_PTA_REG_UP_DMA_ADDRH, (uint32_t) fw.pta.up_queue.head >> 16);
+       set(AR9170_PTA_REG_UP_DMA_ADDRL, (uint32_t) fw.pta.up_queue.head & 0xffff);
+
+       for (i = 0; i < __AR9170_NUM_TX_QUEUES; i++)
+               set_wlan_txq_dma_addr(i, (uint32_t) fw.wlan.tx_queue[i].head);
+
+       set(AR9170_MAC_REG_DMA_RXQ_ADDR, (uint32_t) fw.wlan.rx_queue.head);
+
+       fw.usb.int_desc->status = AR9170_OWN_BITS_SW;
+       fw.usb.int_desc->ctrl = (AR9170_CTRL_LS_BIT | AR9170_CTRL_FS_BIT);
+       fw.usb.int_desc->dataSize = AR9170_BLOCK_SIZE;
+       fw.usb.int_desc->totalLen = 0;
+       fw.usb.int_desc->lastAddr = fw.usb.int_desc;
+       fw.usb.int_desc->dataAddr = (void *) &dma_mem.reserved.rsp;
+       fw.usb.int_desc->nextAddr = (void *) 0;
+
+       memset(DESC_PAYLOAD(fw.usb.int_desc), 0xff,
+              AR9170_INT_MAGIC_HEADER_SIZE);
+       memset(DESC_PAYLOAD_OFF(fw.usb.int_desc, AR9170_INT_MAGIC_HEADER_SIZE),
+              0, AR9170_BLOCK_SIZE - AR9170_INT_MAGIC_HEADER_SIZE);
+
+       /* rsp is now available for use */
+       fw.usb.int_desc_available = 1;
+
+#ifdef CONFIG_CARL9170FW_HANDLE_BACK_REQ
+       fw.wlan.ba_desc->status = AR9170_OWN_BITS_SW;
+       fw.wlan.ba_desc->ctrl = (AR9170_CTRL_LS_BIT | AR9170_CTRL_FS_BIT);
+       fw.wlan.ba_desc->dataSize = fw.wlan.ba_desc->totalLen =
+               sizeof(struct carl9170_tx_superdesc) +
+               sizeof(struct ar9170_tx_hwdesc) +
+               sizeof(struct ieee80211_ba) + FCS_LEN;
+       fw.wlan.ba_desc->lastAddr = fw.wlan.ba_desc;
+       fw.wlan.ba_desc->nextAddr = fw.wlan.ba_desc;
+       fw.wlan.ba_desc->dataAddr = (void *) &dma_mem.reserved.ba;
+
+       memset(DESC_PAYLOAD(fw.wlan.ba_desc), 0, 128);
+
+       fw.wlan.ba_desc_available = 1;
+#endif /* CONFIG_CARL9170FW_HANDLE_BACK_REQ */
+}
+
+/*
+ * Free descriptor.
+ *
+ * Exchange the terminator and the first descriptor of the packet
+ * for hardware ascy...
+ */
+void dma_reclaim(struct dma_queue *q, struct dma_desc *desc)
+{
+       struct dma_desc *tmpDesc;
+       struct dma_desc tdesc;
+
+       /* 1. Set OWN bit to HW for all TDs to be added, clear ctrl and size */
+       tmpDesc = desc;
+       while (1) {
+               tmpDesc->status = AR9170_OWN_BITS_HW;
+               tmpDesc->ctrl = 0;
+               tmpDesc->totalLen = 0;
+               tmpDesc->dataSize = AR9170_BLOCK_SIZE;
+
+               /* TODO : Exception handle */
+
+               if (desc->lastAddr == tmpDesc)
+                       break;
+
+               tmpDesc->lastAddr = desc->lastAddr;
+               tmpDesc = tmpDesc->nextAddr;
+       }
+
+       /* 2. Next address of Last TD to be added = first TD */
+       desc->lastAddr->nextAddr = desc;
+
+       /* 3. Copy first TD to be added to TTD */
+       copy_dma_desc(&tdesc, desc);
+
+       /* 4. set first TD OWN bit to SW */
+       desc->status = AR9170_OWN_BITS_SW;
+
+       /* 5. Copy TTD to last TD */
+       tdesc.status &= (~AR9170_OWN_BITS_MASK);
+       copy_dma_desc((void *)q->terminator, (void *)&tdesc);
+       q->terminator->status |= AR9170_OWN_BITS_HW;
+
+       /* Update terminator pointer */
+       q->terminator = desc;
+}
+
+/*
+ * Put a complete packet into the tail of the Queue q.
+ * Exchange the terminator and the first descriptor of the packet
+ * for hardware ascy...
+ */
+void dma_put(struct dma_queue *q, struct dma_desc *desc)
+{
+       struct dma_desc *tmpDesc;
+       struct dma_desc tdesc;
+
+       tmpDesc = desc;
+
+       /* force correct CTRL_BITS */
+       tmpDesc->ctrl = 0;
+       tmpDesc->ctrl |= AR9170_CTRL_FS_BIT;
+       while (1) {
+               /* update totalLen */
+               tmpDesc->totalLen = desc->totalLen;
+
+               /* 1. Set OWN bit to HW for all TDs to be added */
+               tmpDesc->status = AR9170_OWN_BITS_HW;
+               /* TODO : Exception handle */
+
+               tmpDesc->lastAddr = desc->lastAddr;
+
+               if (desc->lastAddr == tmpDesc)
+                       break;
+
+               tmpDesc = tmpDesc->nextAddr;
+               tmpDesc->ctrl = 0;
+       }
+       tmpDesc->ctrl |= AR9170_CTRL_LS_BIT;
+
+       /* 2. Next address of Last TD to be added = first TD */
+       desc->lastAddr->nextAddr = desc;
+
+       /* If there is only one descriptor, update pointer of last descriptor */
+       if (desc->lastAddr == desc)
+               desc->lastAddr = q->terminator;
+
+       /* 3. Copy first TD to be added to TTD */
+       copy_dma_desc(&tdesc, desc);
+
+       /* 4. set first TD OWN bit to SW */
+       desc->status = AR9170_OWN_BITS_SW;
+       desc->ctrl = 0;
+       desc->totalLen = 0;
+       desc->dataSize = 0;
+       desc->lastAddr = desc;
+       desc->nextAddr = desc;
+       desc->dataAddr = NULL;
+
+       /* 5. Copy TTD to last TD */
+       tdesc.status &= (~AR9170_OWN_BITS_MASK);
+       copy_dma_desc((void *)q->terminator, (void *)&tdesc);
+       q->terminator->status |= AR9170_OWN_BITS_HW;
+
+       /* Update terminator pointer */
+       q->terminator = desc;
+}
+
+struct dma_desc *dma_unlink_head(struct dma_queue *queue)
+{
+       struct dma_desc *desc;
+
+       if (queue_empty(queue))
+               return NULL;
+
+       desc = queue->head;
+
+       queue->head = desc->lastAddr->nextAddr;
+
+       /* poison nextAddr address */
+       desc->lastAddr->nextAddr = desc->lastAddr;
+       desc->lastAddr->lastAddr = desc->lastAddr;
+
+       return desc;
+}
diff --git a/carlfw/src/fw.c b/carlfw/src/fw.c
new file mode 100644 (file)
index 0000000..00dd221
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * carl9170 firmware - used by the ar9170 wireless device
+ *
+ * Firmware descriptor
+ *
+ * Copyright 2009, 2010 Christian Lamparter <chunkeey@googlemail.com>
+ *
+ * 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.
+ */
+#include "carl9170.h"
+#include "fwdsc.h"
+
+#define FILL(small, big, more...)                                      \
+       .small = {                                                      \
+               CARL9170FW_FILL_DESC(big##_MAGIC,                       \
+                       sizeof(struct carl9170fw_## small##_desc),      \
+                       CARL9170FW_## big##_DESC_MIN_VER,               \
+                       CARL9170FW_## big##_DESC_CUR_VER),              \
+               more                                                    \
+       }
+
+const struct carl9170_firmware_descriptor __section(fwdsc) carl9170fw_desc = {
+       FILL(otus, OTUS,
+            .fw_feature_set = cpu_to_le32(BIT(CARL9170FW_DUMMY_FEATURE) |
+#ifdef CONFIG_CARL9170FW_RADIO_FUNCTIONS
+                                          BIT(CARL9170FW_COMMAND_PHY) |
+#endif /* CONFIG_CARL9170FW_RADIO_FUNCTIONS */
+#ifdef CONFIG_CARL9170FW_SECURITY_ENGINE
+                                          BIT(CARL9170FW_COMMAND_CAM) |
+#endif /* CONFIG_CARL9170FW_SECURITY_ENGINE */
+#ifdef CONFIG_CARL9170FW_CAB_QUEUE
+                                          BIT(CARL9170FW_WLANTX_CAB) |
+#endif /* CONFIG_CARL9170FW_CAB_QUEUE */
+#ifdef CONFIG_CARL9170FW_HANDLE_BACK_REQ
+                                          BIT(CARL9170FW_HANDLE_BACK_REQ) |
+#endif /* CONFIG_CARL9170FW_HANDLE_BACK_REQ */
+#ifdef CONFIG_CARL9170FW_UNUSABLE
+                                          BIT(CARL9170FW_UNUSABLE) |
+#endif /* CONFIG_CARL9170FW_UNUSABLE */
+#ifdef CONFIG_CARL9170FW_GPIO_INTERRUPT
+                                          BIT(CARL9170FW_GPIO_INTERRUPT) |
+#endif /* CONFIG_CARL9170FW_GPIO_INTERRUPT */
+#ifdef CONFIG_CARL9170FW_PSM
+                                          BIT(CARL9170FW_PSM) |
+#endif
+                                          (0)),
+            .bcn_addr = (__le32) cpu_to_le32(&dma_mem.reserved.bcn),
+            .bcn_len = (__le16) cpu_to_le16(sizeof(dma_mem.reserved.bcn)),
+            .vif_num = CARL9170_INTF_NUM,
+            .api_ver = CONFIG_CARL9170FW_RELEASE_VERSION,
+       ),
+
+       FILL(usb, USB,
+            .usb_feature_set = cpu_to_le32(BIT(CARL9170FW_USB_DUMMY_FEATURE) |
+                                           BIT(CARL9170FW_USB_RESP_EP2) |
+#ifdef CONFIG_CARL9170FW_USB_INIT_FIRMWARE
+                                           BIT(CARL9170FW_USB_INIT_FIRMWARE) |
+# ifdef CONFIG_CARL9170FW_USB_UP_STREAM
+                                           BIT(CARL9170FW_USB_UP_STREAM) |
+# endif /* CONFIG_CARL9170FW_USB_UP_STREAM */
+# ifdef CONFIG_CARL9170FW_USB_DOWN_STREAM
+                                           BIT(CARL9170FW_USB_DOWN_STREAM) |
+# endif /* CONFIG_CARL9170FW_USB_DOWN_STREAM */
+#endif /* CONFIG_CARL9170FW_USB_INIT_FIRMWARE */
+#ifdef CONFIG_CARL9170FW_USB_WATCHDOG
+                                           BIT(CARL9170FW_USB_WATCHDOG) |
+#endif /* CONFIG_CARL9170FW_USB_WATCHDOG */
+                                           (0)),
+
+            .miniboot_size = cpu_to_le16(0),
+            .tx_descs = AR9170_TX_BLOCK_NUMBER,
+            .rx_max_frame_len = cpu_to_le16(CONFIG_CARL9170FW_RX_FRAME_LEN),
+            .tx_frag_len = cpu_to_le16(AR9170_BLOCK_SIZE),
+            .fw_address = cpu_to_le32(AR9170_PRAM_OFFSET),
+       ),
+
+       FILL(motd, MOTD,
+            .fw_year_month_day = cpu_to_le32(
+                       CARL9170FW_SET_DAY(CARL9170FW_VERSION_DAY) +
+                       CARL9170FW_SET_MONTH(CARL9170FW_VERSION_MONTH) +
+                       CARL9170FW_SET_YEAR(CARL9170FW_VERSION_YEAR)),
+            .desc = "Community AR9170 Linux",
+            .release = CARL9170FW_VERSION_GIT),
+
+       FILL(dbg, DBG,
+            .bogoclock_addr = cpu_to_le32(&fw.bogoclock),
+            .counter_addr = cpu_to_le32(&fw.counter),
+            .rx_total_addr = cpu_to_le32(&fw.wlan.rx_total),
+            .rx_overrun_addr = cpu_to_le32(&fw.wlan.rx_overruns),
+       ),
+
+       FILL(last, LAST),
+};
+
+#undef FILL
+
+struct firmware_context_struct fw;
diff --git a/carlfw/src/gpio.c b/carlfw/src/gpio.c
new file mode 100644 (file)
index 0000000..6c2699b
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * carl9170 firmware - used by the ar9170 wireless device
+ *
+ * GPIO interrupt service
+ *
+ * Copyright (c) 2000-2005 ZyDAS Technology Corporation
+ * Copyright (c) 2007-2009 Atheros Communications, Inc.
+ * Copyright   2009    Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2009, 2010 Christian Lamparter <chunkeey@googlemail.com>
+ *
+ * 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.
+ */
+
+#include "carl9170.h"
+#include "gpio.h"
+
+#ifdef CONFIG_CARL9170FW_GPIO_INTERRUPT
+void gpio_timer(void)
+{
+       uint32_t cur;
+
+       cur = get(AR9170_GPIO_REG_PORT_DATA) & CARL9170_GPIO_MASK;
+
+       if (cur != fw.cached_gpio_state.gpio) {
+               fw.cached_gpio_state.gpio = cur;
+
+               send_cmd_to_host(sizeof(struct carl9170_gpio),
+                                CARL9170_RSP_GPIO, 0x00,
+                                (uint8_t *)&fw.cached_gpio_state);
+
+# ifdef CONFIG_CARL9170FW_WATCHDOG_BUTTON
+               for (;;) {
+                       /*
+                        * Loop forever... Until the watchdog triggers.
+                        */
+               }
+# endif /* CONFIG_CARL9170FW_WATCHDOG_BUTTON */
+       }
+}
+#endif /* CONFIG_CARL9170FW_GPIO_INTERRUPT */
diff --git a/carlfw/src/hostif.c b/carlfw/src/hostif.c
new file mode 100644 (file)
index 0000000..e005147
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * carl9170 firmware - used by the ar9170 wireless device
+ *
+ * Host interface routines
+ *
+ * Copyright (c) 2000-2005 ZyDAS Technology Corporation
+ * Copyright (c) 2007-2009 Atheros Communications, Inc.
+ * Copyright   2009    Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2009, 2010 Christian Lamparter <chunkeey@googlemail.com>
+ *
+ * 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.
+ */
+
+#include "carl9170.h"
+#include "hostif.h"
+#include "printf.h"
+#include "wl.h"
+
+static bool length_check(struct dma_desc *desc)
+{
+       volatile struct carl9170_tx_superframe *super = DESC_PAYLOAD(desc);
+
+       if (unlikely(desc->totalLen < sizeof(struct carl9170_tx_superdesc)))
+               return false;
+
+       /*
+        * check if the DMA is complete, or clipped.
+        *
+        * NB: The hardware aligns the descriptor length to
+        * a 4 byte boundary. This makes the direct comparison
+        * difficult, or unnecessary complex for a hot-path.
+        */
+       if (unlikely(super->s.len > desc->totalLen))
+               return false;
+
+       return true;
+}
+
+static void handle_download(void)
+{
+       struct dma_desc *desc;
+
+       /*
+        * Under normal conditions, all completed descs should have
+        * the AR9170_OWN_BITS_SE status flag set.
+        * However there seems to be a undocumented case where the flag
+        * is _SW...
+        */
+
+       for_each_desc_not_bits(desc, &fw.pta.down_queue, AR9170_OWN_BITS_HW) {
+               if (unlikely((length_check(desc) == false))) {
+                       /*
+                        * There is no easy way of telling what was lost.
+                        *
+                        * Therefore we just reclaim the data.
+                        * The driver has to have some sort frame
+                        * timeout mechanism.
+                        */
+
+                       dma_reclaim(&fw.pta.down_queue, desc);
+                       down_trigger();
+                       continue;
+               }
+
+               wlan_tx(desc);
+       }
+
+#ifdef CONFIG_CARL9170FW_DEBUG_LED_HEARTBEAT
+       xorl(AR9170_GPIO_REG_PORT_DATA, 2);
+#endif /* CONFIG_CARL9170FW_DEBUG_LED_HEARTBEAT */
+}
+
+static void handle_upload(void)
+{
+       struct dma_desc *desc;
+
+       for_each_desc_not_bits(desc, &fw.pta.up_queue, AR9170_OWN_BITS_HW) {
+               /*
+                * BIG FAT NOTE:
+                *
+                * DO NOT compare the descriptor addresses.
+                */
+               if (DESC_PAYLOAD(desc) == (void *) &dma_mem.reserved.rsp) {
+                       fw.usb.int_desc = desc;
+                       fw.usb.int_desc_available = 1;
+               } else {
+#ifdef CONFIG_CARL9170FW_LOOPBACK
+                       dma_reclaim(&fw.pta.down_queue, desc);
+                       down_trigger();
+#else
+                       dma_reclaim(&fw.wlan.rx_queue, desc);
+                       _wlan_trigger(AR9170_DMA_TRIGGER_RXQ);
+#endif /* CONFIG_CARL9170FW_LOOPBACK */
+               }
+       }
+
+#ifdef CONFIG_CARL9170FW_DEBUG_LED_HEARTBEAT
+       xorl(AR9170_GPIO_REG_PORT_DATA, 2);
+#endif /* CONFIG_CARL9170FW_DEBUG_LED_HEARTBEAT */
+}
+
+/* handle interrupts from DMA chip */
+void handle_host_interface(void)
+{
+       uint32_t pta_int;
+
+       pta_int = get(AR9170_PTA_REG_INT_FLAG);
+
+#define HANDLER(intr, flag, func)                      \
+       do {                                            \
+               if ((intr & flag) != 0) {               \
+                       func();                         \
+               }                                       \
+       } while (0)
+
+       HANDLER(pta_int, AR9170_PTA_INT_FLAG_DN, handle_download);
+
+       HANDLER(pta_int, AR9170_PTA_INT_FLAG_UP, handle_upload);
+
+#undef HANDLER
+}
diff --git a/carlfw/src/main.c b/carlfw/src/main.c
new file mode 100644 (file)
index 0000000..bdb68ba
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * carl9170 firmware - used by the ar9170 wireless device
+ *
+ * initialization and main() loop
+ *
+ * Copyright (c) 2000-2005 ZyDAS Technology Corporation
+ * Copyright (c) 2007-2009 Atheros Communications, Inc.
+ * Copyright   2009    Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2009, 2010 Christian Lamparter <chunkeey@googlemail.com>
+ *
+ * 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.
+ */
+
+#include "carl9170.h"
+#include "timer.h"
+#include "hostif.h"
+#include "printf.h"
+#include "gpio.h"
+#include "wl.h"
+
+#define AR9170_WATCH_DOG_TIMER            0x100
+
+static void init(void)
+{
+       led_init();
+
+#ifdef CONFIG_CARL9170FW_DEBUG_UART
+       uart_init();
+#endif /* CONFIG_CARL9170FW_DEBUG_UART */
+
+       /* 25/50/100ms timer (depends on cpu clock) */
+       timer_init(0, 50000);
+
+       /* turn off all leds */
+       led_set(0);
+
+       /* fill DMA rings */
+       dma_init_descriptors();
+
+       /* USB init */
+       usb_init();
+
+       /* clear all interrupt */
+       set(AR9170_MAC_REG_INT_CTRL, 0xffff);
+
+       orl(AR9170_MAC_REG_AFTER_PNP, 1);
+
+       /* Init watch dog control flag */
+#ifdef CONFIG_CARL9170FW_WATCHDOG
+       fw.watchdog_enable = 1;
+
+       set(AR9170_TIMER_REG_WATCH_DOG, AR9170_WATCH_DOG_TIMER);
+#else
+       fw.watchdog_enable = 0;
+       set(AR9170_TIMER_REG_WATCH_DOG, 0xffff);
+#endif /* CONFIG_CARL9170FW_WATCHDOG */
+
+#ifdef CONFIG_CARL9170FW_GPIO_INTERRUPT
+       fw.cached_gpio_state.gpio = get(AR9170_GPIO_REG_PORT_DATA) &
+                                   CARL9170_GPIO_MASK;
+#endif /* CONFIG_CARL9170FW_GPIO_INTERRUPT */
+
+       /* this will get the downqueue moving. */
+       down_trigger();
+}
+
+static void __attribute__((noreturn)) main_loop(void)
+{
+       clock_set(true, AHB_40MHZ_OSC);
+
+       /* main loop */
+       while (1) {
+               if (fw.watchdog_enable == 1)
+                       set(AR9170_TIMER_REG_WATCH_DOG, AR9170_WATCH_DOG_TIMER);
+
+               /*
+                * Due to frame order persevation, the wlan subroutines
+                * must be executed before handle_host_interface.
+                */
+               handle_wlan();
+
+               handle_host_interface();
+
+               handle_usb();
+
+               handle_timer();
+
+               fw.counter++;
+       }
+}
+
+/*
+ * The bootcode will work with the device driver to load the firmware
+ * onto the device's Program SRAM. The Program SRAM has a size of 32 KB
+ * and also contains the stack, which grows down from 0x208000.
+ *
+ * The Program SRAM starts at address 0x200000 on the device.
+ * The firmware entry point (0x200004) is located in boot.S.
+ * we put _start() there with the linker script carl9170.lds.
+ */
+
+void __attribute__((noreturn)) start(void)
+{
+       /* initialize firmware context and DMA memory */
+       memset(&fw, 0, sizeof(fw));
+       memset(&dma_mem, 0, sizeof(dma_mem));
+
+       /* watchdog magic pattern check */
+       if ((get(AR9170_PWR_REG_WATCH_DOG_MAGIC) & 0xffff0000) == 0x12340000) {
+               /* watch dog warm start */
+               incl(AR9170_PWR_REG_WATCH_DOG_MAGIC);
+               usb_trigger_out();
+       } else if ((get(AR9170_PWR_REG_WATCH_DOG_MAGIC) & 0xffff0000) == 0x98760000) {
+               /* suspend/resume */
+       }
+
+       /* write the magic pattern for watch dog */
+       andl(AR9170_PWR_REG_WATCH_DOG_MAGIC, 0xFFFF);
+       orl(AR9170_PWR_REG_WATCH_DOG_MAGIC, 0x12340000);
+
+       init();
+
+#ifdef CONFIG_CARL9170FW_DEBUG
+
+       BUG("TEST BUG");
+       BUG_ON(0x2b || !0x2b);
+       INFO("INFO MESSAGE");
+
+       /* a set of unique characters to detect transfer data corruptions */
+       DBG("AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz"
+           " ~`!1@2#3$4%%5^6&7*8(9)0_-+={[}]|\\:;\"'<,>.?/");
+#endif /* CONFIG_CARL9170FW_DEBUG */
+
+       main_loop();
+}
diff --git a/carlfw/src/memcpy.S b/carlfw/src/memcpy.S
new file mode 100644 (file)
index 0000000..9539a6b
--- /dev/null
@@ -0,0 +1,228 @@
+/* $Id: memcpy.S,v 1.3 2001/07/27 11:50:52 gniibe Exp $
+ *
+ * "memcpy" implementation of SuperH
+ *
+ * Copyright (C) 1999  Niibe Yutaka
+ *
+ */
+
+/*
+ * void *memcpy(void *dst, const void *src, size_t n);
+ * No overlap between the memory of DST and of SRC are assumed.
+ */
+
+       .globl _memcpy
+       .align 2
+       _memcpy:
+       tst     r6,r6
+       bt/s    9f              ! if n=0, do nothing
+        mov    r4,r0
+       sub     r4,r5           ! From here, r5 has the distance to r0
+       add     r6,r0           ! From here, r0 points the end of copying point
+       mov     #12,r1
+       cmp/gt  r6,r1
+       bt/s    7f              ! if it's too small, copy a byte at once
+        add    #-1,r5
+       add     #1,r5
+       !                       From here, r6 is free
+       !
+       !      r4   -->  [ ...  ] DST             [ ...  ] SRC
+       !                [ ...  ]                 [ ...  ]
+       !                  :                        :
+       !      r0   -->  [ ...  ]       r0+r5 --> [ ...  ]
+       !
+       !
+       mov     r5,r1
+       mov     #3,r2
+       and     r2,r1
+       shll2   r1
+       mov     r0,r3           ! Save the value on R0 to R3
+       mova    jmptable,r0
+       add     r1,r0
+       mov.l   @r0,r1
+       jmp     @r1
+        mov    r3,r0           ! and back to R0
+       .balign 4
+jmptable:
+       .long   case0
+       .long   case1
+       .long   case2
+       .long   case3
+
+       ! copy a byte at once
+7:     mov     r4,r2
+       add     #1,r2
+8:
+       cmp/hi  r2,r0
+       mov.b   @(r0,r5),r1
+       bt/s    8b                      ! while (r0>r2)
+        mov.b  r1,@-r0
+9:
+       rts
+        nop
+
+case0:
+       !
+       !       GHIJ KLMN OPQR -->  GHIJ KLMN OPQR
+       !
+       ! First, align to long word boundary
+       mov     r0,r3
+       and     r2,r3
+       tst     r3,r3
+       bt/s    2f
+        add    #-4,r5
+       add     #3,r5
+1:     dt      r3
+       mov.b   @(r0,r5),r1
+       bf/s    1b
+        mov.b  r1,@-r0
+       !
+       add     #-3,r5
+2:     ! Second, copy a long word at once
+       mov     r4,r2
+       add     #7,r2
+3:     mov.l   @(r0,r5),r1
+       cmp/hi  r2,r0
+       bt/s    3b
+        mov.l  r1,@-r0
+       !
+       ! Third, copy a byte at once, if necessary
+       cmp/eq  r4,r0
+       bt/s    9b
+        add    #3,r5
+       bra     8b
+        add    #-6,r2
+
+case1:
+       !
+       !       GHIJ KLMN OPQR -->  ...G HIJK LMNO PQR.
+       !
+       ! First, align to long word boundary
+       mov     r0,r3
+       and     r2,r3
+       tst     r3,r3
+       bt/s    2f
+        add    #-1,r5
+1:     dt      r3
+       mov.b   @(r0,r5),r1
+       bf/s    1b
+        mov.b  r1,@-r0
+       !
+2:     ! Second, read a long word and write a long word at once
+       mov.l   @(r0,r5),r1
+       add     #-4,r5
+       mov     r4,r2
+       add     #7,r2
+       !
+#ifdef __LITTLE_ENDIAN__
+3:     mov     r1,r3           ! RQPO
+       shll16  r3
+       shll8   r3              ! Oxxx
+       mov.l   @(r0,r5),r1     ! NMLK
+       mov     r1,r6
+       shlr8   r6              ! xNML
+       or      r6,r3           ! ONML
+       cmp/hi  r2,r0
+       bt/s    3b
+        mov.l  r3,@-r0
+#else
+3:     mov     r1,r3           ! OPQR
+       shlr16  r3
+       shlr8   r3              ! xxxO
+       mov.l   @(r0,r5),r1     ! KLMN
+       mov     r1,r6
+       shll8   r6              ! LMNx
+       or      r6,r3           ! LMNO
+       cmp/hi  r2,r0
+       bt/s    3b
+        mov.l  r3,@-r0
+#endif
+       !
+       ! Third, copy a byte at once, if necessary
+       cmp/eq  r4,r0
+       bt/s    9b
+        add    #4,r5
+       bra     8b
+        add    #-6,r2
+
+case2:
+       !
+       !       GHIJ KLMN OPQR -->  ..GH IJKL MNOP QR..
+       !
+       ! First, align to word boundary
+       tst     #1,r0
+       bt/s    2f
+        add    #-1,r5
+       mov.b   @(r0,r5),r1
+       mov.b   r1,@-r0
+       !
+2:     ! Second, read a word and write a word at once
+       add     #-1,r5
+       mov     r4,r2
+       add     #3,r2
+       !
+3:     mov.w   @(r0,r5),r1
+       cmp/hi  r2,r0
+       bt/s    3b
+        mov.w  r1,@-r0
+       !
+       ! Third, copy a byte at once, if necessary
+       cmp/eq  r4,r0
+       bt/s    9b
+        add    #1,r5
+       mov.b   @(r0,r5),r1
+       rts
+        mov.b  r1,@-r0
+
+case3:
+       !
+       !       GHIJ KLMN OPQR -->  .GHI JKLM NOPQ R...
+       !
+       ! First, align to long word boundary
+       mov     r0,r3
+       and     r2,r3
+       tst     r3,r3
+       bt/s    2f
+        add    #-1,r5
+1:     dt      r3
+       mov.b   @(r0,r5),r1
+       bf/s    1b
+        mov.b  r1,@-r0
+       !
+2:     ! Second, read a long word and write a long word at once
+       add     #-2,r5
+       mov.l   @(r0,r5),r1
+       add     #-4,r5
+       mov     r4,r2
+       add     #7,r2
+       !
+#ifdef __LITTLE_ENDIAN__
+3:     mov     r1,r3           ! RQPO
+       shll8   r3              ! QPOx
+       mov.l   @(r0,r5),r1     ! NMLK
+       mov     r1,r6
+       shlr16  r6
+       shlr8   r6              ! xxxN
+       or      r6,r3           ! QPON
+       cmp/hi  r2,r0
+       bt/s    3b
+        mov.l  r3,@-r0
+#else
+3:     mov     r1,r3           ! OPQR
+       shlr8   r3              ! xOPQ
+       mov.l   @(r0,r5),r1     ! KLMN
+       mov     r1,r6
+       shll16  r6
+       shll8   r6              ! Nxxx
+       or      r6,r3           ! NOPQ
+       cmp/hi  r2,r0
+       bt/s    3b
+        mov.l  r3,@-r0
+#endif
+       !
+       ! Third, copy a byte at once, if necessary
+       cmp/eq  r4,r0
+       bt/s    9b
+        add    #6,r5
+       bra     8b
+        add    #-6,r2
diff --git a/carlfw/src/memset.S b/carlfw/src/memset.S
new file mode 100644 (file)
index 0000000..d39c8a6
--- /dev/null
@@ -0,0 +1,58 @@
+/* $Id: memset.S,v 1.1 2000/04/14 16:49:01 mjd Exp $
+ *
+ * "memset" implementation of SuperH
+ *
+ * Copyright (C) 1999  Niibe Yutaka
+ *
+ */
+
+/*
+ *            void *memset(void *s, int c, size_t n);
+ */
+
+       .globl _memset
+       .align 2
+       _memset:
+       tst     r6,r6
+       bt/s    5f              ! if n=0, do nothing
+        add    r6,r4
+       mov     #12,r0
+       cmp/gt  r6,r0
+       bt/s    4f              ! if it's too small, set a byte at once
+        mov    r4,r0
+       and     #3,r0
+       cmp/eq  #0,r0
+       bt/s    2f              ! It's aligned
+        sub    r0,r6
+1:
+       dt      r0
+       bf/s    1b
+        mov.b  r5,@-r4
+2:                             ! make VVVV
+       extu.b  r5,r5
+       swap.b  r5,r0           !   V0
+       or      r0,r5           !   VV
+       swap.w  r5,r0           ! VV00
+       or      r0,r5           ! VVVV
+       !
+       mov     r6,r0
+       shlr2   r0
+       shlr    r0              ! r0 = r6 >> 3
+3:
+       dt      r0
+       mov.l   r5,@-r4         ! set 8-byte at once
+       bf/s    3b
+        mov.l  r5,@-r4
+       !
+       mov     #7,r0
+       and     r0,r6
+       tst     r6,r6
+       bt      5f
+       ! fill bytes
+4:
+       dt      r6
+       bf/s    4b
+        mov.b  r5,@-r4
+5:
+       rts
+        mov    r4,r0
diff --git a/carlfw/src/printf.c b/carlfw/src/printf.c
new file mode 100755 (executable)
index 0000000..3ff05c4
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2004,2008  Kustaa Nyholm
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "carl9170.h"
+#include "printf.h"
+
+#ifdef CONFIG_CARL9170FW_PRINTF
+static char *bf;
+static char buf[12];
+static unsigned int num;
+static char uc;
+static char zs;
+
+static void out(const char c)
+{
+       *bf++ = c;
+}
+
+static void outDgt(const char dgt)
+{
+       out(dgt + (dgt < 10 ? '0' : (uc ? 'A' : 'a') - 10));
+       zs = 1;
+}
+
+static void divOut(const unsigned int d)
+{
+       unsigned char dgt = 0;
+
+       while (num >= d) {
+               num -= d;
+               dgt++;
+       }
+
+       if (zs || dgt > 0)
+               outDgt(dgt);
+}
+
+void tfp_printf(const char *fmt, ...)
+{
+       va_list va;
+       char *p;
+       unsigned int i;
+       char ch;
+
+       va_start(va, fmt);
+
+       while ((ch = *(fmt++))) {
+               if (ch != '%') {
+                       putcharacter(ch);
+               } else {
+                       char lz = 0;
+                       char w = 0;
+                       ch = *(fmt++);
+
+                       if (ch == '0') {
+                               ch = *(fmt++);
+                               lz = 1;
+                       }
+
+                       if (ch >= '0' && ch <= '9') {
+                               w = 0;
+                               while (ch >= '0' && ch <= '9') {
+                                       w = (((w << 2) + w) << 1) + ch - '0';
+                                       ch = *fmt++;
+                                       }
+                       }
+
+                       bf = buf;
+                       p = bf;
+                       zs = 0;
+
+                       switch (ch) {
+                       case 0:
+                               goto abort;
+
+                       case 'u':
+                       case 'd':
+                               num = va_arg(va, unsigned int);
+                               if (ch == 'd' && (int) num < 0) {
+                                       num = -(int)num;
+                                       out('-');
+                               }
+
+                               for (i = 100000000; i != 1; i /= 10)
+                                       divOut(i);
+
+                               outDgt(num);
+                               break;
+
+                       case 'p':
+                       case 'x':
+                       case 'X':
+                               uc = ch == 'X';
+                               num = va_arg(va, unsigned int);
+                               for (i = 0x10000000; i != 0x1; i >>= 4)
+                                       divOut(i);
+
+                               outDgt(num);
+                               break;
+
+                       case 'c':
+                               out((char)(va_arg(va, int)));
+                               break;
+
+                       case 's':
+                               p = va_arg(va, char*);
+                               break;
+                       case '%':
+                               out('%');
+                               break;
+
+                       default:
+                               break;
+                               }
+
+                       *bf = 0;
+                       bf = p;
+                       while (*bf++ && w > 0)
+                               w--;
+
+                       while (w-- > 0)
+                               putcharacter(lz ? '0' : ' ');
+
+                       while ((ch = *p++))
+                               putcharacter(ch);
+               }
+       }
+
+abort:
+       putcharacter('\0');
+       va_end(va);
+}
+
+#else
+
+void min_printf(const char *fmt, ...)
+{
+       char ch;
+
+       do {
+               ch = *(fmt++);
+               putcharacter(ch);
+       } while (ch);
+}
+
+#endif /* CONFIG_CARL9170FW_PRINTF */
diff --git a/carlfw/src/reboot.S b/carlfw/src/reboot.S
new file mode 100644 (file)
index 0000000..a344ec3
--- /dev/null
@@ -0,0 +1,7 @@
+       .globl _jump_to_bootcode
+       .type  _jump_to_bootcode, @function
+       _jump_to_bootcode:
+               mov.l eeprom_start, r0
+               jmp @r0
+eeprom_start:  .long 0x00000008
+tmp:           .long
diff --git a/carlfw/src/rf.c b/carlfw/src/rf.c
new file mode 100644 (file)
index 0000000..be705a7
--- /dev/null
@@ -0,0 +1,297 @@
+/*
+ * carl9170 firmware - used by the ar9170 wireless device
+ *
+ * PHY and RF functions
+ *
+ * Copyright (c) 2000-2005 ZyDAS Technology Corporation
+ * Copyright (c) 2007-2009 Atheros Communications, Inc.
+ * Copyright   2009    Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2009, 2010 Christian Lamparter <chunkeey@googlemail.com>
+ *
+ * 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.
+ */
+
+#include "carl9170.h"
+#include "timer.h"
+#include "printf.h"
+#include "rf.h"
+#include "shared/phy.h"
+
+#ifdef CONFIG_CARL9170FW_RADIO_FUNCTIONS
+static void set_channel_start(void)
+{
+       /* Manipulate CCA threshold to stop transmission */
+       set(AR9170_PHY_REG_CCA_THRESHOLD, 0x300);
+       /* Enable Virtual CCA */
+       orl(AR9170_MAC_REG_QOS_PRIORITY_VIRTUAL_CCA,
+           AR9170_MAC_VIRTUAL_CCA_ALL);
+}
+
+static void set_channel_end(void)
+{
+       /* Manipulate CCA threshold to resume transmission */
+       set(AR9170_PHY_REG_CCA_THRESHOLD, 0x0);
+       /* Disable Virtual CCA */
+       andl(AR9170_MAC_REG_QOS_PRIORITY_VIRTUAL_CCA,
+            ~AR9170_MAC_VIRTUAL_CCA_ALL);
+}
+
+void rf_notify_set_channel(void)
+{
+       set_channel_start();
+}
+
+/*
+ * Update delta slope coeff man and exp
+ */
+static void hw_turn_off_dyn(const uint32_t delta_slope_coeff_exp,
+                           const uint32_t delta_slope_coeff_man,
+                           const uint32_t delta_slope_coeff_exp_shgi,
+                           const uint32_t delta_slope_coeff_man_shgi)
+{
+       uint32_t tmp;
+
+       tmp = get_async(AR9170_PHY_REG_TIMING3) & 0x00001fff;
+       tmp |= (delta_slope_coeff_man << AR9170_PHY_TIMING3_DSC_MAN_S) &
+               AR9170_PHY_TIMING3_DSC_MAN;
+       tmp |= (delta_slope_coeff_exp << AR9170_PHY_TIMING3_DSC_EXP_S) &
+               AR9170_PHY_TIMING3_DSC_EXP;
+
+       set(AR9170_PHY_REG_TIMING3, tmp);
+
+       tmp = (delta_slope_coeff_man_shgi << AR9170_PHY_HALFGI_DSC_MAN_S) &
+               AR9170_PHY_HALFGI_DSC_MAN;
+
+       tmp |= (delta_slope_coeff_exp_shgi << AR9170_PHY_HALFGI_DSC_EXP_S) &
+               AR9170_PHY_HALFGI_DSC_EXP;
+
+       set(AR9170_PHY_REG_HALFGI, tmp);
+}
+
+static void program_ADDAC(void)
+{
+       /* ??? Select Internal ADDAC ??? (is external radio) */
+       set(AR9170_PHY_REG_ADC_SERIAL_CTL, AR9170_PHY_ADC_SCTL_SEL_EXTERNAL_RADIO);
+
+       delay(10);
+
+       set(0x1c589c, 0x00000000);      /*# 7-0 */
+       set(0x1c589c, 0x00000000);      /*# 15-8 */
+       set(0x1c589c, 0x00000000);      /*# 23-16 */
+       set(0x1c589c, 0x00000000);      /*# 31- */
+
+       set(0x1c589c, 0x00000000);      /*# 39- */
+       set(0x1c589c, 0x00000000);      /*# 47- */
+       set(0x1c589c, 0x00000000);      /*# 55- [48]:doubles the xtalosc bias current */
+       set(0x1c589c, 0x00000000);      /*# 63- */
+
+       set(0x1c589c, 0x00000000);      /*# 71- */
+       set(0x1c589c, 0x00000000);      /*# 79- */
+       set(0x1c589c, 0x00000000);      /*# 87- */
+       set(0x1c589c, 0x00000000);      /*# 95- */
+
+       set(0x1c589c, 0x00000000);      /*# 103- */
+       set(0x1c589c, 0x00000000);      /*# 111- */
+       set(0x1c589c, 0x00000000);      /*# 119- */
+       set(0x1c589c, 0x00000000);      /*# 127- */
+
+       set(0x1c589c, 0x00000000);      /*# 135- */
+       set(0x1c589c, 0x00000000);      /*# 143- */
+       set(0x1c589c, 0x00000000);      /*# 151- */
+       set(0x1c589c, 0x00000030);      /*# 159- #[158:156]=xlnabufmode */
+
+       set(0x1c589c, 0x00000004);      /*# 167-  [162]:disable clkp_driver to flow */
+       set(0x1c589c, 0x00000000);      /*# 175- */
+       set(0x1c589c, 0x00000000);      /*# 183-176 */
+       set(0x1c589c, 0x00000000);      /*# 191-184 */
+
+       set(0x1c589c, 0x00000000);      /*# 199- */
+       set(0x1c589c, 0x00000000);      /*# 207- */
+       set(0x1c589c, 0x00000000);      /*# 215- */
+       set(0x1c589c, 0x00000000);      /*# 223- */
+
+       set(0x1c589c, 0x00000000);      /*# 231- */
+       set(0x1c58c4, 0x00000000);      /*# 233-232 */
+
+       delay(10);
+
+       /* Select External Flow ???? (is internal addac??) */
+       set(AR9170_PHY_REG_ADC_SERIAL_CTL, AR9170_PHY_ADC_SCTL_SEL_INTERNAL_ADDAC);
+}
+
+static uint32_t AGC_calibration(uint32_t loop)
+{
+       uint32_t wrdata;
+       uint32_t ret;
+
+#define AGC_CAL_NF     (AR9170_PHY_AGC_CONTROL_CAL | AR9170_PHY_AGC_CONTROL_NF);
+
+       wrdata = get_async(AR9170_PHY_REG_AGC_CONTROL) | AGC_CAL_NF;
+       set(AR9170_PHY_REG_AGC_CONTROL, wrdata);
+
+       ret = get_async(AR9170_PHY_REG_AGC_CONTROL) & AGC_CAL_NF;
+
+       /* sitesurvey : 100 ms / current connected 200 ms */
+       while (loop && ret != 0x0) {
+               ret = get_async(AR9170_PHY_REG_AGC_CONTROL) & AGC_CAL_NF;
+
+               if (ret == 0)
+                       break;
+
+               udelay(100);
+               loop--;
+       }
+
+       /* return the AGC/Noise calibration state to the driver */
+       return ret;
+}
+
+#define EIGHTY_FLAG (CARL9170FW_PHY_HT_ENABLE | CARL9170FW_PHY_HT_DYN2040)
+
+static uint32_t rf_init(const uint32_t delta_slope_coeff_exp,
+                       const uint32_t delta_slope_coeff_man,
+                       const uint32_t delta_slope_coeff_exp_shgi,
+                       const uint32_t delta_slope_coeff_man_shgi,
+                       const uint32_t finiteLoopCount,
+                       const bool initialize)
+{
+       uint32_t ret;
+
+       hw_turn_off_dyn(delta_slope_coeff_exp,
+                       delta_slope_coeff_man,
+                       delta_slope_coeff_exp_shgi,
+                       delta_slope_coeff_man_shgi);
+
+       if (initialize) {
+               /* Real Chip */
+               program_ADDAC();
+
+               /* inverse chain 0 <-> chain 2 */
+               set(AR9170_PHY_REG_ANALOG_SWAP, AR9170_PHY_ANALOG_SWAP_AB);
+
+               /* swap chain 0 and chain 2 */
+               set(AR9170_PHY_REG_ANALOG_SWAP, AR9170_PHY_ANALOG_SWAP_AB |
+                                               AR9170_PHY_ANALOG_SWAP_ALT_CHAIN);
+
+               /* configure mask */
+               set(AR9170_PHY_REG_RX_CHAINMASK, 0x5);  /* chain 0 + chain 2 */
+               set(AR9170_PHY_REG_CAL_CHAINMASK, 0x5); /* chain 0 + chain 2 */
+
+               /* Activate BB */
+               set(AR9170_PHY_REG_ACTIVE, AR9170_PHY_ACTIVE_EN);
+               delay(10);
+       }
+
+       ret = AGC_calibration(finiteLoopCount);
+
+       set_channel_end();
+       return ret;
+}
+
+void rf_cmd(const struct carl9170_cmd *cmd, struct carl9170_rsp *resp)
+{
+       uint32_t ret;
+
+       fw.phy.ht_settings = cmd->rf_init.ht_settings;
+       fw.phy.frequency = cmd->rf_init.freq;
+
+       if ((fw.phy.ht_settings & EIGHTY_FLAG) == EIGHTY_FLAG)
+               clock_set(true, AHB_80_88MHZ);
+       else
+               clock_set(true, AHB_40_44MHZ);
+
+       ret = rf_init(le32_to_cpu(cmd->rf_init.delta_slope_coeff_exp),
+                     le32_to_cpu(cmd->rf_init.delta_slope_coeff_man),
+                     le32_to_cpu(cmd->rf_init.delta_slope_coeff_exp_shgi),
+                     le32_to_cpu(cmd->rf_init.delta_slope_coeff_man_shgi),
+                     le32_to_cpu(cmd->rf_init.finiteLoopCount),
+                     cmd->hdr.cmd == CARL9170_CMD_RF_INIT);
+
+       resp->hdr.len = sizeof(struct carl9170_rf_init_result);
+       resp->rf_init_res.ret = cpu_to_le32(ret);
+
+       resp->rf_init_res.regs[0] = get(AR9170_PHY_REG_CCA);
+       resp->rf_init_res.regs[3] = get(AR9170_PHY_REG_EXT_CCA);
+
+       resp->rf_init_res.regs[1] = get(AR9170_PHY_REG_CH1_CCA);
+       resp->rf_init_res.regs[4] = get(AR9170_PHY_REG_CH1_EXT_CCA);
+
+       resp->rf_init_res.regs[2] = get(AR9170_PHY_REG_CH2_CCA);
+       resp->rf_init_res.regs[5] = get(AR9170_PHY_REG_CH2_EXT_CCA);
+}
+
+#ifdef CONFIG_CARL9170FW_PSM
+void rf_psm(void)
+{
+       u32 bank3;
+
+       /*
+        * FIXME: Does not work on 5GHz band!
+        */
+
+       if (fw.phy.psm.state == CARL9170_PSM_SOFTWARE) {
+               /* not enabled by the driver */
+               return;
+       }
+
+       if (fw.phy.psm.state & CARL9170_PSM_SLEEP) {
+               fw.phy.psm.state &= ~CARL9170_PSM_SLEEP;
+
+               /* disable all agc gain and offset updates to a2 */
+               set(AR9170_PHY_REG_TEST2, 0x8000000);
+
+               /* power down ADDAC */
+               set(AR9170_PHY_REG_ADC_CTL,
+                   AR9170_PHY_ADC_CTL_OFF_PWDDAC |
+                   AR9170_PHY_ADC_CTL_OFF_PWDADC |
+                   0xa0000000);
+
+               /* Synthesizer off + RX off */
+               bank3 = 0x00400018;
+
+               clock_set(true, AHB_20_22MHZ);
+       } else {
+               /* advance to the next PSM step */
+               fw.phy.psm.state--;
+
+               if (fw.phy.psm.state == CARL9170_PSM_WAKE) {
+                       /* wake up ADDAC */
+                       set(AR9170_PHY_REG_ADC_CTL,
+                           AR9170_PHY_ADC_CTL_OFF_PWDDAC |
+                           AR9170_PHY_ADC_CTL_OFF_PWDADC);
+
+                       /* enable all agc gain and offset updates to a2 */
+                       set(AR9170_PHY_REG_TEST2, 0x0);
+
+                       /* Synthesizer on + RX on */
+                       bank3 = 0x01420098;
+
+                       if ((fw.phy.ht_settings & EIGHTY_FLAG) == EIGHTY_FLAG)
+                               clock_set(true, AHB_80_88MHZ);
+                       else
+                               clock_set(true, AHB_40_44MHZ);
+               } else {
+                       return ;
+               }
+       }
+
+       if (fw.phy.frequency < 30000000)
+               bank3 |= 0x00800000;
+
+       set(0x1c58f0, bank3);
+}
+#endif /* CONFIG_CARL9170FW_PSM */
+
+#endif /* CONFIG_CARL9170FW_RADIO_FUNCTIONS */
diff --git a/carlfw/src/timer.c b/carlfw/src/timer.c
new file mode 100644 (file)
index 0000000..354eaf9
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * carl9170 firmware - used by the ar9170 wireless device
+ *
+ * Clock, Timer & Timing functions
+ *
+ * Copyright (c) 2000-2005 ZyDAS Technology Corporation
+ * Copyright (c) 2007-2009 Atheros Communications, Inc.
+ * Copyright   2009    Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2009, 2010 Christian Lamparter <chunkeey@googlemail.com>
+ *
+ * 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.
+ */
+
+#include "carl9170.h"
+#include "printf.h"
+#include "timer.h"
+#include "wl.h"
+#include "gpio.h"
+
+void timer_init(const unsigned int timer, const unsigned int interval)
+{
+       /* Set timer to periodic mode */
+       orl(AR9170_TIMER_REG_CONTROL, BIT(timer));
+
+       /* Set time interval */
+       set(AR9170_TIMER_REG_TIMER0 + (timer << 2), interval - 1);
+
+       /* Clear timer interrupt flag */
+       orl(AR9170_TIMER_REG_INTERRUPT, BIT(timer));
+}
+
+static void clock_calibrate(void)
+{
+       uint32_t t0, loop = 13;
+
+       t0 = get_clock_counter();
+
+       /*
+        * TODO:
+        * Write this code in assembler, so the reading is accurate
+        * and can be used to correct the timer intervals.
+        */
+       while (((get_clock_counter() - t0) & (BIT(18)-1)) < 1000)
+               loop += 9;      /* really rough uOP estimation */
+
+       fw.bogoclock = loop;
+}
+
+void clock_set(const bool on, const enum cpu_clock_t _clock)
+{
+       /*
+        * Word of Warning!
+        * This setting does more than just mess with the CPU Clock.
+        * So watch out, if you need _stable_ timer interrupts.
+        */
+
+       set(AR9170_PWR_REG_CLOCK_SEL, (uint32_t) ((on ? 0x70 : 0x600) | _clock));
+       clock_calibrate();
+}
+
+static void timer0_isr(void)
+{
+       wlan_timer();
+
+#ifdef CONFIG_CARL9170FW_GPIO_INTERRUPT
+       gpio_timer();
+#endif /* CONFIG_CARL9170FW_GPIO_INTERRUPT */
+
+#ifdef CONFIG_CARL9170FW_USB_WATCHDOG
+       usb_watchdog_timer();
+#endif /* CONFIG_CARL9170FW_USB_WATCHDOG */
+
+#ifdef CONFIG_CARL9170FW_DEBUG_LED_HEARTBEAT
+       set(AR9170_GPIO_REG_PORT_DATA, get(AR9170_GPIO_REG_PORT_DATA) ^ 1);
+#endif /* CONFIG_CARL9170FW_DEBUG_LED_HEARTBEAT */
+}
+
+void handle_timer(void)
+{
+       uint32_t intr;
+
+       intr = get(AR9170_TIMER_REG_INTERRUPT);
+
+       /* ACK timer interrupt */
+       set(AR9170_TIMER_REG_INTERRUPT, intr);
+
+#define HANDLER(intr, flag, func)                      \
+       do {                                            \
+               if ((intr & flag) != 0) {               \
+                       intr &= ~flag;                  \
+                       func();                         \
+               }                                       \
+       } while (0)
+
+       HANDLER(intr, BIT(0), timer0_isr);
+
+       if (intr)
+               DBG("Unhandled Timer Event %x", (unsigned int) intr);
+
+#undef HANDLER
+}
diff --git a/carlfw/src/uart.c b/carlfw/src/uart.c
new file mode 100644 (file)
index 0000000..cf6058e
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * carl9170 firmware - used by the ar9170 wireless device
+ *
+ * UART debug interface functions.
+ *
+ * Copyright (c) 2000-2005 ZyDAS Technology Corporation
+ * Copyright (c) 2007-2009 Atheros Communications, Inc.
+ * Copyright   2009    Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2009, 2010 Christian Lamparter <chunkeey@googlemail.com>
+ *
+ * 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.
+ */
+
+#include "carl9170.h"
+#include "uart.h"
+#include "io.h"
+
+#ifdef CONFIG_CARL9170FW_DEBUG_UART
+void uart_putc(const char c)
+{
+       set(AR9170_UART_REG_TX_HOLDING, c);
+
+       while (get(AR9170_UART_REG_LINE_STATUS) &
+              AR9170_UART_LINE_STS_TX_FIFO_ALMOST_EMPTY) {
+               /*
+                * wait until the byte has made it
+                */
+       }
+}
+
+void uart_print_hex_dump(const void *buf, const int len)
+{
+       unsigned int offset = 0;
+
+       uart_putc('H');
+       uart_putc('D');
+       uart_putc(':');
+
+       while (len > 0) {
+               uart_putc(*((uint8_t *) buf + offset));
+               offset++;
+       }
+}
+
+void uart_init(void)
+{
+       unsigned int timeout = 0;
+
+#ifdef CONFIG_CARL9170FW_UART_CLOCK_25M
+       set(AR9170_UART_REG_DIVISOR_LSB, 0xc);
+#elif CONFIG_CARL9170FW_UART_CLOCK_40M
+       set(AR9170_UART_REG_DIVISOR_LSB, 0x14); /* 40 MHz */
+       set(AR9170_UART_REG_REMAINDER, 0xb38e);
+#else
+#error "Unsupported UART clock"
+#endif /* CARL9170FW_UART_CLOCK_25M */
+
+       while (get(AR9170_UART_REG_LINE_STATUS) &
+              AR9170_UART_LINE_STS_TRANSMITTER_EMPTY) {
+               if (timeout++ >= 10000)
+                       break;
+       }
+}
+#endif /* CONFIG_CARL9170FW_DEBUG_UART */
diff --git a/carlfw/src/wlan.c b/carlfw/src/wlan.c
new file mode 100644 (file)
index 0000000..aaff3ee
--- /dev/null
@@ -0,0 +1,1038 @@
+/*
+ * carl9170 firmware - used by the ar9170 wireless device
+ *
+ * Interface to the WLAN part of the chip
+ *
+ * Copyright (c) 2000-2005 ZyDAS Technology Corporation
+ * Copyright (c) 2007-2009 Atheros Communications, Inc.
+ * Copyright   2009    Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2009, 2010 Christian Lamparter <chunkeey@googlemail.com>
+ *
+ * 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.
+ */
+
+#include "carl9170.h"
+#include "shared/phy.h"
+#include "hostif.h"
+#include "timer.h"
+#include "wl.h"
+#include "printf.h"
+#include "rf.h"
+#include "linux/ieee80211.h"
+
+static void wlan_txunstuck(unsigned int queue)
+{
+       set_wlan_txq_dma_addr(queue, ((uint32_t) fw.wlan.tx_queue[queue].head) | 1);
+       wlan_trigger(BIT(queue));
+}
+
+static void wlan_txupdate(unsigned int queue)
+{
+       set_wlan_txq_dma_addr(queue, ((uint32_t) fw.wlan.tx_queue[queue].head));
+       wlan_trigger(BIT(queue));
+}
+
+static void wlan_dma_bump(unsigned int qidx)
+{
+       unsigned int offset = qidx;
+       uint32_t status, trigger;
+
+       status = get(AR9170_MAC_REG_DMA_STATUS) >> 12;
+       trigger = get(AR9170_MAC_REG_DMA_TRIGGER) >> 12;
+
+       while (offset != 0) {
+               status >>= 4;
+               trigger >>= 4;
+               offset--;
+       }
+
+       status &= 0xf;
+       trigger &= 0xf;
+
+       if ((trigger == 0xa) && (status == 0x8)) {
+               DBG("UNSTUCK");
+               wlan_txunstuck(qidx);
+       } else {
+               DBG("UPDATE");
+               wlan_txupdate(qidx);
+       }
+}
+
+#ifdef CONFIG_CARL9170FW_DEBUG
+static void wlan_dump_queue(unsigned int qidx)
+{
+
+       struct dma_desc *desc;
+       struct carl9170_tx_superframe *super;
+       int entries = 0;
+
+       __for_each_desc(desc, &fw.wlan.tx_queue[qidx]) {
+               super = get_super(desc);
+               DBG("%d: %p s:%x c:%x tl:%x ds:%x n:%p l:%p ", entries, desc,
+                   desc->status, desc->ctrl, desc->totalLen,
+                   desc->dataSize, desc->nextAddr, desc->lastAddr);
+
+               DBG("c:%x tr:%d ri:%d l:%x m:%x p:%x fc:%x",
+                   super->s.cookie, super->s.cnt, super->s.rix,
+                   super->f.hdr.length, super->f.hdr.mac.set,
+                   (unsigned int) le32_to_cpu(super->f.hdr.phy.set),
+                   super->f.data.i3e.frame_control);
+
+               entries++;
+       }
+
+       desc = get_wlan_txq_addr(qidx);
+
+       DBG("Queue: %d: te:%d td:%d h:%p c:%p t:%p",
+           qidx, entries, queue_len(&fw.wlan.tx_queue[qidx]),
+           fw.wlan.tx_queue[qidx].head,
+           desc, fw.wlan.tx_queue[qidx].terminator);
+
+       DBG("HW: t:%x s:%x ac:%x c:%x",
+           (unsigned int) get(AR9170_MAC_REG_DMA_TRIGGER),
+           (unsigned int) get(AR9170_MAC_REG_DMA_STATUS),
+           (unsigned int) get(AR9170_MAC_REG_AMPDU_COUNT),
+           (unsigned int) get(AR9170_MAC_REG_DMA_TXQX_ADDR_CURR));
+}
+#endif /* CONFIG_CARL9170FW_DEBUG */
+
+static void wlan_send_buffered_tx_status(void)
+{
+       unsigned int len;
+
+       while (fw.wlan.tx_status_pending) {
+               len = min((unsigned int)fw.wlan.tx_status_pending,
+                         CARL9170_RSP_TX_STATUS_NUM);
+               len = min(len, CARL9170_TX_STATUS_NUM - fw.wlan.tx_status_head_idx);
+
+               /*
+                * rather than memcpy each individual request into a large buffer,
+                * we _splice_ them all together.
+                *
+                * The only downside is however that we have to be careful around
+                * the edges of the tx_status_cache.
+                *
+                * Note:
+                * Each tx_status is about 2 bytes. However every command package
+                * must have a size which is a multiple of 4.
+                */
+
+               send_cmd_to_host((len * sizeof(struct carl9170_tx_status) + 3) & ~3,
+                                CARL9170_RSP_TXCOMP, len, (void *)
+                                &fw.wlan.tx_status_cache[fw.wlan.tx_status_head_idx]);
+
+               fw.wlan.tx_status_pending -= len;
+               fw.wlan.tx_status_head_idx += len;
+               fw.wlan.tx_status_head_idx %= CARL9170_TX_STATUS_NUM;
+       }
+}
+
+static struct carl9170_tx_status *wlan_get_tx_status_buffer(void)
+{
+       struct carl9170_tx_status *tmp;
+
+       tmp = &fw.wlan.tx_status_cache[fw.wlan.tx_status_tail_idx++];
+       fw.wlan.tx_status_tail_idx %= CARL9170_TX_STATUS_NUM;
+
+       if (fw.wlan.tx_status_pending == CARL9170_TX_STATUS_NUM)
+               wlan_send_buffered_tx_status();
+
+       fw.wlan.tx_status_pending++;
+
+       return tmp;
+}
+
+/* generate _aggregated_ tx_status for the host */
+static void wlan_tx_complete(struct carl9170_tx_superframe *super,
+                            bool txs)
+{
+       struct carl9170_tx_status *status;
+
+       status = wlan_get_tx_status_buffer();
+
+       /*
+        * The *unique* cookie and AC_ID is used by the driver for
+        * frame lookup.
+        */
+       status->cookie = super->s.cookie;
+       status->queue = super->s.queue;
+
+       /*
+        * This field holds the number of tries of the rate in
+        * the rate index field (rix).
+        */
+       status->rix = super->s.rix;
+       status->tries = super->s.cnt;
+       status->success = (txs) ? 1 : 0;
+}
+
+static bool wlan_tx_consume_retry(struct carl9170_tx_superframe *super)
+{
+       /* check if this was the last possible retry with this rate */
+       if (unlikely(super->s.cnt >= super->s.tries[super->s.rix])) {
+               /* end of the road - indicate tx failure */
+               if (unlikely(super->s.rix == CARL9170_TX_MAX_RETRY_RATES))
+                       return false;
+
+               /* check if there are alternative rates available */
+               if (!super->s.rr[super->s.rix].set)
+                       return false;
+
+               /* try next retry rate */
+               super->f.hdr.phy.set = super->s.rr[super->s.rix].set;
+
+               /* finally - mark the old rate as USED */
+               super->s.rix++;
+
+               /* reinitialize try counter */
+               super->s.cnt = 1;
+       } else {
+               /* just increase retry counter */
+               super->s.cnt++;
+       }
+
+       return true;
+}
+
+/* for all tries */
+static void __wlan_tx(struct dma_desc *desc)
+{
+       struct carl9170_tx_superframe *super = get_super(desc);
+#ifdef CONFIG_CARL9170FW_NORMAL_TX_RX
+       unsigned int queue = super->s.queue;
+#endif /* CONFIG_CARL9170FW_LOOPBACK */
+
+       if (unlikely(super->s.fill_in_tsf)) {
+               struct ieee80211_mgmt *mgmt = (void *) &super->f.data.i3e;
+               uint32_t *tsf = (uint32_t *) &mgmt->u.probe_resp.timestamp;
+
+               /*
+                * Truth be told: this is a hack.
+                *
+                * The *real* TSF is definitely going to be higher/older.
+                * But this hardware emulation code is head and shoulders
+                * above anything a driver can possibly do.
+                *
+                * (even, if it's got an accurate atomic clock source).
+                */
+
+               read_tsf(tsf);
+       }
+
+#if (defined CONFIG_CARL9170FW_LOOPBACK) || (defined CONFIG_CARL9170FW_DISCARD)
+       wlan_tx_complete(super, true);
+       unhide_super(desc);
+# ifdef CONFIG_CARL9170FW_LOOPBACK
+       dma_put(&fw.pta.up_queue, desc);
+       up_trigger();
+# elif CONFIG_CARL9170FW_DISCARD
+       dma_reclaim(&fw.pta.down_queue, desc);
+       down_trigger();
+# endif
+#else /* CONFIG_CARL9170FW_LOOPBACK */
+
+# if ((defined CONFIG_CARL9170FW_DEBUG) && (defined CONFIG_CARL9170FW_PSM))
+       BUG_ON(fw.phy.psm.state != CARL9170_PSM_WAKE);
+# endif /* CONFIG_CARL9170FW_DEBUG && CONFIG_CARL9170FW_PSM */
+
+       /* insert desc into the right queue */
+       dma_put(&fw.wlan.tx_queue[queue], desc);
+       wlan_trigger(BIT(queue));
+#endif /* CONFIG_CARL9170FW_LOOPBACK */
+}
+
+/* prepares frame for the first transmission */
+static void _wlan_tx(struct dma_desc *desc)
+{
+       struct carl9170_tx_superframe *super = get_super(desc);
+
+       if (unlikely(super->s.ampdu_commit_density)) {
+               set(AR9170_MAC_REG_AMPDU_DENSITY,
+                   MOD_VAL(AR9170_MAC_AMPDU_DENSITY,
+                           get(AR9170_MAC_REG_AMPDU_DENSITY),
+                           super->s.ampdu_density));
+       }
+
+       if (unlikely(super->s.ampdu_commit_factor)) {
+               set(AR9170_MAC_REG_AMPDU_FACTOR,
+                   MOD_VAL(AR9170_MAC_AMPDU_FACTOR,
+                           get(AR9170_MAC_REG_AMPDU_FACTOR),
+                           8 << super->s.ampdu_factor));
+       }
+
+       __wlan_tx(desc);
+}
+
+/* propagate transmission status back to the driver */
+static bool wlan_tx_status(struct dma_queue *queue,
+                          struct dma_desc *desc)
+{
+       struct ar9170_tx_frame *frame = DESC_PAYLOAD(desc);
+       struct carl9170_tx_superframe *super = get_super(desc);
+       struct ieee80211_hdr *hdr = &super->f.data.i3e;
+       unsigned int qidx = super->s.queue;
+       bool txfail, success;
+
+       success = true;
+
+       if (!!(desc->ctrl & AR9170_CTRL_FAIL_MASK)) {
+               txfail = !!(desc->ctrl & AR9170_CTRL_TXFAIL);
+
+               /* reset retry indicator flags */
+               desc->ctrl &= ~(AR9170_CTRL_TXFAIL | AR9170_CTRL_BAFAIL);
+
+               if (wlan_tx_consume_retry(super)) {
+                       /*
+                        * retry for simple and aggregated 802.11 frames.
+                        *
+                        * Note: We must not mess up the original frame
+                        * order.
+                        */
+
+                       if (!frame->hdr.mac.ampdu) {
+                               /*
+                                * 802.11 - 7.1.3.1.5.
+                                * set "Retry Field" for consecutive attempts
+                                *
+                                * Note: For AMPDU see:
+                                * 802.11n 9.9.1.6 "Retransmit Procedures"
+                                */
+
+                               hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_RETRY);
+                       }
+
+                       if (txfail) {
+                               /* Normal TX Failure */
+
+                               /* demise descriptor ownership back to the hardware */
+                               dma_rearm(desc);
+
+                               /*
+                                * And this will get the queue going again.
+                                * To understand why: you have to get the HW
+                                * specs... But sadly I never saw them.
+                                */
+                               wlan_txunstuck(qidx);
+
+                               /* abort cycle - this is necessary due to HW design */
+                               return false;
+                       } else {
+                               /* (HT-) BlockACK failure */
+
+                               /*
+                                * Unlink the failed attempt and put it into
+                                * the retry queue. The caller routine must
+                                * be aware of this so the frames don't get lost.
+                                */
+
+                               dma_unlink_head(queue);
+#ifdef CONFIG_CARL9170FW_DELAYED_TX
+                               dma_put(&fw.wlan.tx_retry, desc);
+#else
+                               __wlan_tx(desc);
+#endif /* CONFIG_CARL9170FW_DELAYED_TX */
+                               return true;
+                       }
+               } else {
+                       /* out of frame attempts - discard frame */
+                       success = false;
+               }
+       }
+
+       dma_unlink_head(queue);
+       if (txfail) {
+               /*
+                * Issue the queue bump,
+                * We need to do this in case this was the frame's last
+                * possible retry attempt and it unfortunately: it failed.
+                */
+
+               wlan_txunstuck(qidx);
+       }
+
+       unhide_super(desc);
+
+       /* update hangcheck */
+       fw.wlan.last_tx_desc_num[qidx] = 0;
+
+#ifdef CONFIG_CARL9170FW_HANDLE_BACK_REQ
+       if (unlikely(super == (void *) &dma_mem.reserved.ba)) {
+               fw.wlan.ba_desc = desc;
+               fw.wlan.ba_desc_available = 1;
+               return true;
+       }
+#endif /* CONFIG_CARL9170FW_HANDLE_BACK_REQ */
+
+       wlan_tx_complete(super, success);
+
+#ifdef CONFIG_CARL9170FW_CAB_QUEUE
+       if (unlikely(super->s.cab))
+               fw.wlan.cab_queue_len--;
+#endif /* CONFIG_CARL9170FW_CAB_QUEUE */
+
+       /* recycle freed descriptors */
+       dma_reclaim(&fw.pta.down_queue, desc);
+       down_trigger();
+       return true;
+}
+
+static void handle_tx_completion(void)
+{
+       struct dma_desc *desc;
+       unsigned int i;
+
+       for (i = 0; i < __AR9170_NUM_TX_QUEUES; i++) {
+               __while_desc_bits(desc, &fw.wlan.tx_queue[i], AR9170_OWN_BITS_SW) {
+                       if (!wlan_tx_status(&fw.wlan.tx_queue[i], desc)) {
+                               /* termination requested. */
+                               break;
+                       }
+               }
+
+#ifdef CONFIG_CARL9170FW_DELAYED_TX
+               for_each_desc(desc, &fw.wlan.tx_retry)
+                       __wlan_tx(desc);
+
+               for_each_desc(desc, &fw.wlan.tx_delay[i])
+                       _wlan_tx(desc);
+#endif /* CONFIG_CARL9170FW_DELAYED_TX */
+       }
+}
+
+void __hot wlan_tx(struct dma_desc *desc)
+{
+       struct carl9170_tx_superframe *super = DESC_PAYLOAD(desc);
+
+       /* initialize rate control struct */
+       super->s.rix = 0;
+       super->s.cnt = 1;
+       hide_super(desc);
+
+#ifdef CONFIG_CARL9170FW_DELAYED_TX
+       if (!queue_empty(&fw.wlan.tx_queue[super->s.queue])) {
+               dma_put(&fw.wlan.tx_delay[super->s.queue], desc);
+               return;
+       }
+#endif /* CONFIG_CARL9170FW_DELAYED_TX */
+
+#ifdef CONFIG_CARL9170FW_CAB_QUEUE
+       if (unlikely(super->s.cab)) {
+               fw.wlan.cab_queue_len++;
+               dma_put(&fw.wlan.cab_queue, desc);
+               return;
+       }
+#endif /* CONFIG_CARL9170FW_CAB_QUEUE */
+
+       _wlan_tx(desc);
+}
+
+#ifdef CONFIG_CARL9170FW_HANDLE_BACK_REQ
+static void wlan_send_buffered_ba(void)
+{
+       struct carl9170_tx_ba_superframe *baf = &dma_mem.reserved.ba.ba;
+       struct ieee80211_ba *ba = (struct ieee80211_ba *) &baf->f.ba;
+       struct carl9170_bar_ctx *ctx;
+
+       if (likely(fw.wlan.ba_head_idx == fw.wlan.ba_tail_idx))
+               return;
+
+       /* there's no point to continue when the ba_desc is not available. */
+       if (!fw.wlan.ba_desc_available)
+               return;
+
+       ctx = &fw.wlan.ba_cache[fw.wlan.ba_head_idx % CONFIG_CARL9170FW_BACK_REQS_NUM];
+       fw.wlan.ba_head_idx++;
+
+       /* Format BlockAck */
+       fw.wlan.ba_desc->status = 0;
+       fw.wlan.ba_desc->ctrl = AR9170_CTRL_FS_BIT | AR9170_CTRL_LS_BIT;
+       fw.wlan.ba_desc_available = 0;
+       fw.wlan.ba_desc->nextAddr = fw.wlan.ba_desc->lastAddr =
+               fw.wlan.ba_desc;
+
+       baf->s.len = fw.wlan.ba_desc->totalLen = fw.wlan.ba_desc->dataSize =
+               sizeof(struct carl9170_tx_superdesc) +
+               sizeof(struct ar9170_tx_hwdesc) +
+               sizeof(struct ieee80211_ba);
+
+       baf->s.tries[0] = 3;
+       baf->s.queue = 0;
+       baf->f.hdr.length = sizeof(struct ieee80211_ba) + FCS_LEN;
+
+       /* HW Duration / Backoff */
+       baf->f.hdr.mac.backoff = 1;
+       baf->f.hdr.mac.hw_duration = 1;
+
+       /* take the TX rate from the RX'd BAR */
+       baf->f.hdr.phy.set = ctx->phy;
+       baf->f.hdr.phy.tx_power = 29; /* 14.5 dBm */
+
+       /* format outgoing BA */
+       ba->frame_control = cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_BACK);
+       ba->duration = cpu_to_le16(0);
+       memcpy(ba->ta, ctx->ta, 6);
+       memcpy(ba->ra, ctx->ra, 6);
+
+       /*
+        * Unfortunately, we cannot look into the hardware's scoreboard.
+        * Therefore we have to proceed as described in 802.11n 9.10.7.5
+        * and send a null BlockAck.
+        */
+       memset(ba->bitmap, 0x0, sizeof(ba->bitmap));
+
+       /*
+        * NB:
+        * not entirely sure if this is 100% correct?!
+        */
+       ba->control = ctx->control | cpu_to_le16(1);
+       ba->start_seq_num = ctx->start_seq_num;
+
+       wlan_tx(fw.wlan.ba_desc);
+}
+
+static struct carl9170_bar_ctx *wlan_get_bar_cache_buffer(void)
+{
+       struct carl9170_bar_ctx *tmp;
+
+       /* expire oldest entry, if we ran out of ba_ctx' */
+       if (fw.wlan.ba_head_idx + CONFIG_CARL9170FW_BACK_REQS_NUM < fw.wlan.ba_tail_idx)
+               fw.wlan.ba_head_idx++;
+
+       tmp = &fw.wlan.ba_cache[fw.wlan.ba_tail_idx % CONFIG_CARL9170FW_BACK_REQS_NUM];
+       fw.wlan.ba_tail_idx++;
+
+       return tmp;
+}
+
+static void handle_bar(struct dma_desc *desc)
+{
+       struct ieee80211_hdr *hdr;
+       struct ieee80211_bar *bar;
+       struct carl9170_bar_ctx *ctx;
+
+       hdr = ar9170_get_rx_i3e(desc);
+
+       /* check if this is a BAR for us */
+       if (likely(!ieee80211_is_back_req(hdr->frame_control)))
+               return ;
+
+       if (unlikely(ar9170_get_rx_macstatus_error(desc))) {
+               /*
+                * This check does a number of things:
+                * 1. checks if the frame is in good nick
+                * 2. checks if the RA (MAC) matches
+                */
+               return ;
+       }
+
+       if (unlikely(ar9170_get_rx_mpdu_len(desc) <
+           sizeof(struct ieee80211_bar))) {
+               /*
+                * Sneaky, corrupted BARs... but not with us!
+                */
+
+               return ;
+       }
+
+       bar = (void *) hdr;
+
+       if ((bar->control & cpu_to_le16(IEEE80211_BAR_CTRL_MULTI_TID)) ||
+           !(bar->control & cpu_to_le16(IEEE80211_BAR_CTRL_CBMTID_COMPRESSED_BA))) {
+               /* not implemented yet */
+
+               return ;
+       }
+
+       ctx = wlan_get_bar_cache_buffer();
+
+       /* Brilliant! The BAR provides all necessary MACs! */
+       memcpy(ctx->ra, bar->ta, 6);
+       memcpy(ctx->ta, bar->ra, 6);
+
+       /*
+        * NB:
+        * not entirely sure if this is 100% correct to force the
+        * imm ack bit or not...
+        */
+       ctx->control = bar->control | cpu_to_le16(1);
+       ctx->start_seq_num = bar->start_seq_num;
+       ctx->phy = ar9170_rx_to_phy(desc);
+       if (unlikely(!ctx->phy)) {
+               /* provide a backup, in case ar9170_rx_to_phy fails */
+               ctx->phy = cpu_to_le32(0x2cc301);
+       }
+}
+#endif /* CONFIG_CARL9170FW_HANDLE_BACK_REQ */
+
+static void wlan_check_rx_overrun(void)
+{
+       uint32_t overruns, total;
+
+       fw.wlan.rx_total += total = get(AR9170_MAC_REG_RX_TOTAL);
+       fw.wlan.rx_overruns += overruns = get(AR9170_MAC_REG_RX_OVERRUN);
+       if (unlikely(overruns)) {
+               if (overruns == total) {
+                       DBG("RX Overrun");
+                       fw.wlan.mac_reset++;
+               }
+       }
+}
+
+static void handle_rx(void)
+{
+       struct dma_desc *desc;
+
+       wlan_check_rx_overrun();
+
+       for_each_desc_not_bits(desc, &fw.wlan.rx_queue, AR9170_OWN_BITS_HW) {
+               if (unlikely(desc->totalLen < 26 ||
+                   desc->totalLen > CONFIG_CARL9170FW_RX_FRAME_LEN)) {
+                       /*
+                        * This frame is too damaged to do anything
+                        * useful with it.
+                        */
+                       dma_reclaim(&fw.wlan.rx_queue, desc);
+                       _wlan_trigger(AR9170_DMA_TRIGGER_RXQ);
+               } else {
+#ifdef CONFIG_CARL9170FW_HANDLE_BACK_REQ
+                       handle_bar(desc);
+#endif /* CONFIG_CARL9170FW_HANDLE_BACK_REQ */
+
+                       dma_put(&fw.pta.up_queue, desc);
+                       up_trigger();
+               }
+       }
+}
+
+#ifdef CONFIG_CARL9170FW_CAB_QUEUE
+static uint8_t *beacon_find_ie(uint8_t ie)
+{
+       struct ieee80211_mgmt *mgmt = getp(AR9170_MAC_REG_BCN_ADDR);
+       uint8_t *pos, *end;
+       unsigned int len;
+
+       len = get(AR9170_MAC_REG_BCN_LENGTH);
+
+       if (len < FCS_LEN + sizeof(mgmt))
+               return NULL;
+
+       pos = mgmt->u.beacon.variable;
+       end = (uint8_t *) ((unsigned long)mgmt + (len - FCS_LEN));
+       while (pos < end) {
+               if (pos + 2 + pos[1] > end)
+                       return NULL;
+
+               if (pos[0] == ie)
+                       return pos;
+
+               pos += pos[1] + 2;
+       }
+
+       return NULL;
+}
+
+static void wlan_cab_flush_queue(void)
+{
+       struct dma_desc *desc;
+       uint8_t *_ie;
+       struct ieee80211_tim_ie *ie;
+
+       /*
+        * This prevents the code from sending new BC/MC frames
+        * which were queued after the previous buffered traffic
+        * has been sent out... They will have to wait until the
+        * next DTIM beacon comes along.
+        */
+       if (unlikely(fw.wlan.cab_flush_trigger == CARL9170_CAB_TRIGGER_DEFER))
+               return ;
+
+       _ie = beacon_find_ie(WLAN_EID_TIM);
+       if (unlikely(!_ie))
+               return ;
+
+       ie = (struct ieee80211_tim_ie *) &_ie[2];
+
+       /* Ideally, check here for == AR9170_CAB_TRIGGER_ARMED */
+       if (fw.wlan.cab_flush_trigger) {
+               /* move queued frames into the main tx queues */
+               for_each_desc(desc, &fw.wlan.cab_queue) {
+                       struct carl9170_tx_superframe *super = get_super(desc);
+
+                       if (!queue_empty(&fw.wlan.cab_queue)) {
+                               /*
+                                * Set MOREDATA flag for all,
+                                * but the last queued frame.
+                                * see: 802.11-2007 11.2.1.5 f)
+                                *
+                                * This is actually the reason to why
+                                * we need to prevent the reentry.
+                                */
+
+                               super->f.data.i3e.frame_control |=
+                                       cpu_to_le16(IEEE80211_FCTL_MOREDATA);
+                       } else {
+                               super->f.data.i3e.frame_control &=
+                                       cpu_to_le16(~IEEE80211_FCTL_MOREDATA);
+                       }
+
+                       /* ready to roll! */
+                       _wlan_tx(desc);
+               }
+       }
+
+       /* Transfer finished - waiting for tx status */
+       fw.wlan.cab_flush_trigger = CARL9170_CAB_TRIGGER_DEFER;
+}
+
+static void wlan_cab_modify_dtim_beacon(void)
+{
+       uint8_t *_ie;
+       struct ieee80211_tim_ie *ie;
+
+       _ie = beacon_find_ie(WLAN_EID_TIM);
+       if (likely(_ie)) {
+               ie = (struct ieee80211_tim_ie *) &_ie[2];
+
+               if (!queue_empty(&fw.wlan.cab_queue) && (ie->dtim_count == 0)) {
+                       /* schedule DTIM transfer */
+                       fw.wlan.cab_flush_trigger = CARL9170_CAB_TRIGGER_ARMED;
+               } else if ((fw.wlan.cab_queue_len == 0) && (fw.wlan.cab_flush_trigger)) {
+                       /* undo all chances to the beacon structure */
+                       ie->bitmap_ctrl &= ~0x1;
+                       fw.wlan.cab_flush_trigger = CARL9170_CAB_TRIGGER_EMPTY;
+               }
+
+               if (fw.wlan.cab_flush_trigger) {
+                       /* Set the almighty Multicast Traffic Indication Bit. */
+                       ie->bitmap_ctrl |= 0x1;
+               }
+       }
+}
+#endif /* CONFIG_CARL9170FW_CAB_QUEUE */
+
+static void handle_beacon_config(void)
+{
+       uint32_t bcn_count;
+
+#ifdef CONFIG_CARL9170FW_CAB_QUEUE
+       /*
+        * The application has now updated the relevant beacon data.
+        * Now it should be the perfect time to apply the DTIM
+        * multicast information.
+        */
+
+       wlan_cab_modify_dtim_beacon();
+#endif /* CONFIG_CARL9170FW_CAB_QUEUE */
+
+       bcn_count = get(AR9170_MAC_REG_BCN_COUNT);
+       send_cmd_to_host(4, CARL9170_RSP_BEACON_CONFIG, 0x00,
+                        (uint8_t *) &bcn_count);
+}
+
+static void handle_pretbtt(void)
+{
+#ifdef CONFIG_CARL9170FW_CAB_QUEUE
+       fw.wlan.cab_flush_time = get_clock_counter();
+#endif /* CONFIG_CARL9170FW_CAB_QUEUE */
+
+#ifdef CONFIG_CARL9170FW_PSM
+       rf_psm();
+
+       send_cmd_to_host(4, CARL9170_RSP_PRETBTT, 0x00,
+                        (uint8_t *) &fw.phy.psm.state);
+#else
+       send_cmd_to_host(0, CARL9170_RSP_PRETBTT, 0x00, NULL);
+#endif /* CONFIG_CARL9170FW_PSM */
+
+}
+
+static void handle_atim(void)
+{
+       send_cmd_to_host(0, CARL9170_RSP_ATIM, 0x00, NULL);
+}
+
+#ifdef CONFIG_CARL9170FW_DEBUG
+static void handle_qos(void)
+{
+       /*
+        * What is the QoS Bit used for?
+        * Is it only an indicator for TXOP & Burst, or
+        * should we do something here?
+        */
+}
+
+static void handle_radar(void)
+{
+       send_cmd_to_host(0, CARL9170_RSP_RADAR, 0x00, NULL);
+}
+#endif /* CONFIG_CARL9170FW_DEBUG */
+
+static void wlan_janitor(void)
+{
+#ifdef CONFIG_CARL9170FW_CAB_QUEUE
+       if (unlikely(fw.wlan.cab_flush_trigger)) {
+               /*
+                * This is hardcoded into carl9170usb driver.
+                *
+                * The driver must set the PRETBTT event to beacon_interval -
+                * CARL9170_PRETBTT_KUS (usually 6) Kus.
+                *
+                * But still, we can only do so much about 802.11-2007 9.3.2.1 &
+                * 11.2.1.6. Let's hope the current solution is adequate enough.
+                */
+
+               if (is_after_msecs(fw.wlan.cab_flush_time,
+                   (CARL9170_TBTT_DELTA))) {
+                       wlan_cab_flush_queue();
+               }
+       }
+#endif /* CONFIG_CARL9170FW_CAB_QUEUE */
+
+#ifdef CONFIG_CARL9170FW_DELAYED_TX
+       if (fw.wlan.tx_trigger) {
+               _wlan_trigger(fw.wlan.tx_trigger);
+               fw.wlan.tx_trigger = 0;
+       }
+#endif /* CONFIG_CARL9170FW_DELAYED_TX */
+
+       wlan_send_buffered_tx_status();
+
+#ifdef CONFIG_CARL9170FW_HANDLE_BACK_REQ
+       wlan_send_buffered_ba();
+#endif /* CONFIG_CARL9170FW_HANDLE_BACK_REQ */
+}
+
+void handle_wlan(void)
+{
+       uint32_t intr;
+
+       intr = get(AR9170_MAC_REG_INT_CTRL);
+       /* ACK Interrupt */
+       set(AR9170_MAC_REG_INT_CTRL, intr);
+
+#define HANDLER(intr, flag, func)                      \
+       do {                                            \
+               if ((intr & flag) != 0) {               \
+                       func();                         \
+               }                                       \
+       } while (0)
+
+       HANDLER(intr, AR9170_MAC_INT_PRETBTT, handle_pretbtt);
+
+       HANDLER(intr, AR9170_MAC_INT_ATIM, handle_atim);
+
+       HANDLER(intr, AR9170_MAC_INT_RXC, handle_rx);
+
+       HANDLER(intr, (AR9170_MAC_INT_TXC | AR9170_MAC_INT_RETRY_FAIL),
+               handle_tx_completion);
+
+#ifdef CONFIG_CARL9170FW_DEBUG
+       HANDLER(intr, AR9170_MAC_INT_QOS, handle_qos);
+
+       HANDLER(intr, AR9170_MAC_INT_RADAR, handle_radar);
+#endif /* CONFIG_CARL9170FW_DEBUG */
+
+       HANDLER(intr, AR9170_MAC_INT_CFG_BCN, handle_beacon_config);
+
+       if (unlikely(intr))
+               DBG("Unhandled Interrupt %x\n", (unsigned int) intr);
+
+       wlan_janitor();
+
+#undef HANDLER
+}
+
+static void wlan_check_hang(void)
+{
+       struct dma_desc *desc;
+       unsigned int i;
+
+       for (i = 0; i < __AR9170_NUM_TX_QUEUES; i++) {
+               if (queue_empty(&fw.wlan.tx_queue[i])) {
+                       /* Nothing to do here... move along */
+                       continue;
+               }
+
+               /* fetch the current DMA queue position */
+               desc = get_wlan_txq_addr(i);
+
+               /* Stuck frame detection */
+               if (unlikely(desc == fw.wlan.last_tx_desc[i])) {
+                       fw.wlan.last_tx_desc_num[i]++;
+
+                       if (unlikely(fw.wlan.last_tx_desc_num[i] > 6)) {
+                               /*
+                                * schedule MAC reset (aka OFF/ON => dead)
+                                *
+                                * This will almost certainly kill
+                                * the device for good, but it's the
+                                * recommended thing to do...
+                                */
+
+                               fw.wlan.mac_reset++;
+                       }
+
+#ifdef CONFIG_CARL9170FW_DEBUG
+                       if (unlikely(fw.wlan.last_tx_desc_num[i] > 5)) {
+                               /*
+                                * Sigh, the queue is almost certainly
+                                * dead. Dump the queue content to the
+                                * user, maybe we find out why it got
+                                * so stuck.
+                                */
+
+                               wlan_dump_queue(i);
+                       }
+#endif /* CONFIG_CARL9170FW_DEBUG */
+
+                       if (unlikely(fw.wlan.last_tx_desc_num[i] > 3)) {
+                               /*
+                                * Hrrm, bump the queue a bit.
+                                * maybe this will get it going again.
+                                */
+
+                               wlan_dma_bump(i);
+                       }
+               } else {
+                       /* Nothing stuck */
+                       fw.wlan.last_tx_desc[i] = desc;
+                       fw.wlan.last_tx_desc_num[i] = 0;
+               }
+       }
+}
+
+/*
+ * NB: Resetting the MAC is a two-edged sword.
+ * On most occasions, it does what it is supposed to do.
+ * But there is a chance that this will make it
+ * even worse and the radio dies silently.
+ */
+static void wlan_mac_reset(void)
+{
+       uint32_t val;
+       uint32_t agg_wait_counter;
+       uint32_t agg_density;
+       uint32_t bcn_start_addr;
+       uint32_t rctl, rcth;
+       uint32_t cam_mode;
+       uint32_t ack_power;
+       uint32_t rts_cts_tpc;
+       unsigned int i;
+
+#ifdef CONFIG_CARL9170FW_RADIO_FUNCTIONS
+       uint32_t rx_BB;
+#endif /* CONFIG_CARL9170FW_RADIO_FUNCTIONS */
+
+       ERR("MAC RESET");
+
+       /* Save aggregation parameters */
+       agg_wait_counter = get(AR9170_MAC_REG_AMPDU_FACTOR);
+       agg_density = get(AR9170_MAC_REG_AMPDU_DENSITY);
+
+       bcn_start_addr = get(AR9170_MAC_REG_BCN_ADDR);
+
+       cam_mode = get(AR9170_MAC_REG_CAM_MODE);
+       rctl = get(AR9170_MAC_REG_CAM_ROLL_CALL_TBL_L);
+       rcth = get(AR9170_MAC_REG_CAM_ROLL_CALL_TBL_H);
+
+       ack_power = get(AR9170_MAC_REG_ACK_TPC);
+       rts_cts_tpc = get(AR9170_MAC_REG_RTS_CTS_TPC);
+
+#ifdef CONFIG_CARL9170FW_RADIO_FUNCTIONS
+       /* 0x1c8960 write only */
+       rx_BB = get(AR9170_PHY_REG_SWITCH_CHAIN_0);
+#endif /* CONFIG_CARL9170FW_RADIO_FUNCTIONS */
+
+       /* TX/RX must be stopped by now */
+       val = get(AR9170_MAC_REG_POWER_STATE_CTRL);
+
+       val |= AR9170_MAC_POWER_STATE_CTRL_RESET;
+
+       /*
+        * Manipulate CCA threshold to stop transmission
+        *
+        * set(AR9170_PHY_REG_CCA_THRESHOLD, 0x300);
+        */
+
+       /*
+        * check Rx state in 0(idle) 9(disable)
+        *
+        * chState = (get(AR9170_MAC_REG_MISC_684) >> 16) & 0xf;
+        * while( (chState != 0) && (chState != 9)) {
+        *      chState = (get(AR9170_MAC_REG_MISC_684) >> 16) & 0xf;
+        * }
+        */
+
+       set(AR9170_MAC_REG_POWER_STATE_CTRL, val);
+
+       delay(2);
+
+       /* Restore aggregation parameters */
+       set(AR9170_MAC_REG_AMPDU_FACTOR, agg_wait_counter);
+       set(AR9170_MAC_REG_AMPDU_DENSITY, agg_density);
+
+       set(AR9170_MAC_REG_BCN_ADDR, bcn_start_addr);
+       set(AR9170_MAC_REG_CAM_MODE, cam_mode);
+       set(AR9170_MAC_REG_CAM_ROLL_CALL_TBL_L, rctl);
+       set(AR9170_MAC_REG_CAM_ROLL_CALL_TBL_H, rcth);
+
+       set(AR9170_MAC_REG_RTS_CTS_TPC, rts_cts_tpc);
+       set(AR9170_MAC_REG_ACK_TPC, ack_power);
+
+#ifdef CONFIG_CARL9170FW_RADIO_FUNCTIONS
+       set(AR9170_PHY_REG_SWITCH_CHAIN_2, rx_BB);
+#endif /* CONFIG_CARL9170FW_RADIO_FUNCTIONS */
+
+       /*
+        * Manipulate CCA threshold to resume transmission
+        *
+        * set(AR9170_PHY_REG_CCA_THRESHOLD, 0x0);
+        */
+
+       for (i = 0; i < __AR9170_NUM_TX_QUEUES; i++) {
+               DBG("Q:%d l:%d h:%p t:%p\n", i, queue_len(&fw.wlan.tx_queue[i]),
+                    fw.wlan.tx_queue[i].head, fw.wlan.tx_queue[i].terminator);
+
+               set_wlan_txq_dma_addr(i, (uint32_t) fw.wlan.tx_queue[i].head);
+
+               if (!queue_empty(&fw.wlan.tx_queue[i]))
+                       wlan_trigger(BIT(i));
+       }
+
+       handle_rx();
+       set(AR9170_MAC_REG_DMA_RXQ_ADDR, (uint32_t) fw.wlan.rx_queue.head);
+       wlan_trigger(AR9170_DMA_TRIGGER_RXQ);
+}
+
+void __cold wlan_timer(void)
+{
+       unsigned int cached_mac_reset;
+
+       cached_mac_reset = fw.wlan.mac_reset;
+
+       /* TX Queue Hang check */
+       wlan_check_hang();
+
+       /* RX Overrun check */
+       wlan_check_rx_overrun();
+
+       if (unlikely(fw.wlan.mac_reset >= CARL9170_MAC_RESET_RESET)) {
+               wlan_mac_reset();
+               fw.wlan.mac_reset = CARL9170_MAC_RESET_OFF;
+       } else {
+               if (fw.wlan.mac_reset && cached_mac_reset == fw.wlan.mac_reset)
+                       fw.wlan.mac_reset--;
+       }
+}
diff --git a/carlfw/usb/Kconfig b/carlfw/usb/Kconfig
new file mode 100644 (file)
index 0000000..3d4709c
--- /dev/null
@@ -0,0 +1,54 @@
+menu "USB Firmware Configuration Settings"
+
+config CARL9170FW_USB_STANDARD_CMDS
+       def_bool y
+       prompt "Basic USB Interface"
+       ---help---
+        Allows the device to be queried about Standard USB 2.0 Device
+        Description Descriptors.
+
+        Say Y, unless you don't care if lsusb -v fails.
+
+config CARL9170FW_USB_INIT_FIRMWARE
+       def_bool y
+       prompt "USB Interface Setup"
+       ---help---
+        Firmware should initialize the USB chip.
+
+        Just say Y.
+
+config CARL9170FW_USB_UP_STREAM
+       def_bool y
+       prompt "USB Upload Stream"
+       ---help---
+        This features allows the USB silicon to combine small, single
+        frames into bigger transfers. This can help to reduce
+        some per-transfer overhead in the application.
+
+        Say Y, unless you have experienced strange rx corruptions.
+
+config CARL9170FW_USB_DN_STREAM
+       def_bool n
+       prompt "USB Download Stream"
+
+config CARL9170FW_USB_WATCHDOG
+       def_bool n
+       prompt "Trigger Watchdog if USB transport died"
+       depends on CARL9170FW_WATCHDOG
+       ---help---
+        The idea is that the firmware constantly monitors if the
+        application answers *firmware ping* requests. If it gets
+        no response the firmware stops.
+
+config CARL9170FW_DEBUG_USB
+       def_bool y
+       prompt "Pass debug messages through USB transport"
+       ---help---
+        Report all firmware messages through the USB transport.
+        But there is a catch: In case of a BUG, the USB transport
+        needs to be functional, otherwise the application won't
+        receive anything.
+
+        Say Y.
+
+endmenu
diff --git a/carlfw/usb/fifo.c b/carlfw/usb/fifo.c
new file mode 100644 (file)
index 0000000..24d2515
--- /dev/null
@@ -0,0 +1,206 @@
+/*
+ * carl9170 firmware - used by the ar9170 wireless device
+ *
+ * Copyright (c) 2000-2005  ZyDAS Technology Corporation
+ * Copyright (c) 2007-2009 Atheros Communications, Inc.
+ * Copyright   2009    Johannes Berg <johannes@sipsolutions.net>
+ * Copyright   2009    Christian Lamparter <chunkeey@googlemail.com>
+ *
+ * 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.
+ */
+
+#include "carl9170.h"
+#include "printf.h"
+#include "rom.h"
+#include "usb_fifo.h"
+
+/* TODO / TOTEST */
+#ifdef CONFIG_CARL9170FW_USB_MODESWITCH
+static inline void usb_ep_map(const uint8_t ep, const uint8_t map)
+{
+       setb(AR9170_USB_REG_EP_MAP + (ep - 1), map);
+}
+
+static inline void usb_fifo_map(const uint8_t fifo, const uint8_t map)
+{
+       setb(AR9170_USB_REG_FIFO_MAP + (fifo - 1), map);
+}
+
+static inline void usb_fifo_config(const uint8_t fifo, const uint8_t cfg)
+{
+       setb(AR9170_USB_REG_FIFO_CONFIG + (fifo - 1), cfg);
+}
+
+static inline void usb_ep_packet_size_hi(const uint8_t ep, const uint8_t dir,
+                            const uint16_t size)
+{
+       setb(AR9170_USB_REG_EP_IN_MAX_SIZE_HIGH + (((dir * 0x20) + ep) << 1),
+            (size >> 8) & 0xf);
+}
+
+static inline void usb_ep_packet_size_lo(const uint8_t ep, const uint8_t dir,
+                           const uint16_t size)
+{
+       setb(AR9170_USB_REG_EP_IN_MAX_SIZE_LOW + (((dir * 0x20) + ep) << 1),
+            size & 0xff);
+}
+
+static void usb_ep_in_highbandset(const uint8_t ep, const uint8_t dir,
+                               const uint16_t size)
+{
+       andb(AR9170_USB_REG_EP_IN_MAX_SIZE_HIGH + (ep << 1), ~(BIT(6) | BIT(5)));
+
+       switch (dir) {
+       case DIRECTION_IN:
+               setb(AR9170_USB_REG_EP_IN_MAX_SIZE_HIGH + (ep << 1),
+                    ((size >> 11) + 1) << 5);
+               break;
+       case DIRECTION_OUT:
+       default:
+               break;
+       }
+}
+
+/*
+ *      vUsbFIFO_EPxCfg_HS(void)
+ *      Description:
+ *          1. Configure the FIFO and EPx map
+ *      input: none
+ *      output: none
+ */
+
+void usb_init_highspeed_fifo_cfg(void)
+{
+       int i;
+
+       /* EP 1 */
+       usb_ep_map(1, HS_C1_I0_A0_EP1_MAP);
+       usb_fifo_map(HS_C1_I0_A0_EP1_FIFO_START, HS_C1_I0_A0_EP1_FIFO_MAP);
+       usb_fifo_config(HS_C1_I0_A0_EP1_FIFO_START, HS_C1_I0_A0_EP1_FIFO_CONFIG);
+
+       for (i = HS_C1_I0_A0_EP1_FIFO_START + 1;
+            i < HS_C1_I0_A0_EP1_FIFO_START + HS_C1_I0_A0_EP1_FIFO_NO; i++) {
+               usb_fifo_config(i, (HS_C1_I0_A0_EP1_FIFO_CONFIG & (~BIT(7))));
+       }
+
+       usb_ep_packet_size_hi(1, HS_C1_I0_A0_EP1_DIRECTION, (HS_C1_I0_A0_EP1_MAX_PACKET & 0x7ff));
+       usb_ep_packet_size_lo(1, HS_C1_I0_A0_EP1_DIRECTION, (HS_C1_I0_A0_EP1_MAX_PACKET & 0x7ff));
+       usb_ep_in_highbandset(1, HS_C1_I0_A0_EP1_DIRECTION, HS_C1_I0_A0_EP1_MAX_PACKET);
+
+       /* EP 2 */
+       usb_ep_map(2, HS_C1_I0_A0_EP2_MAP);
+       usb_fifo_map(HS_C1_I0_A0_EP2_FIFO_START, HS_C1_I0_A0_EP2_FIFO_MAP);
+       usb_fifo_config(HS_C1_I0_A0_EP2_FIFO_START, HS_C1_I0_A0_EP2_FIFO_CONFIG);
+
+       for (i = HS_C1_I0_A0_EP2_FIFO_START + 1;
+            i < HS_C1_I0_A0_EP2_FIFO_START + HS_C1_I0_A0_EP2_FIFO_NO; i++) {
+               usb_fifo_config(i, (HS_C1_I0_A0_EP2_FIFO_CONFIG & (~BIT(7))));
+       }
+
+       usb_ep_packet_size_hi(2, HS_C1_I0_A0_EP2_DIRECTION, (HS_C1_I0_A0_EP2_MAX_PACKET & 0x7ff));
+       usb_ep_packet_size_lo(2, HS_C1_I0_A0_EP2_DIRECTION, (HS_C1_I0_A0_EP2_MAX_PACKET & 0x7ff));
+       usb_ep_in_highbandset(2, HS_C1_I0_A0_EP2_DIRECTION, HS_C1_I0_A0_EP2_MAX_PACKET);
+
+       /* EP 3 */
+       usb_ep_map(3, HS_C1_I0_A0_EP3_MAP);
+       usb_fifo_map(HS_C1_I0_A0_EP3_FIFO_START, HS_C1_I0_A0_EP3_FIFO_MAP);
+       usb_fifo_config(HS_C1_I0_A0_EP3_FIFO_START, HS_C1_I0_A0_EP3_FIFO_CONFIG);
+
+       for (i = HS_C1_I0_A0_EP3_FIFO_START + 1;
+            i < HS_C1_I0_A0_EP3_FIFO_START + HS_C1_I0_A0_EP3_FIFO_NO; i++) {
+               usb_fifo_config(i, (HS_C1_I0_A0_EP3_FIFO_CONFIG & (~BIT(7))));
+       }
+
+       usb_ep_packet_size_hi(3, HS_C1_I0_A0_EP3_DIRECTION, (HS_C1_I0_A0_EP3_MAX_PACKET & 0x7ff));
+       usb_ep_packet_size_lo(3, HS_C1_I0_A0_EP3_DIRECTION, (HS_C1_I0_A0_EP3_MAX_PACKET & 0x7ff));
+       usb_ep_in_highbandset(3, HS_C1_I0_A0_EP3_DIRECTION, HS_C1_I0_A0_EP3_MAX_PACKET);
+
+       /* EP 4 */
+       usb_ep_map(4, HS_C1_I0_A0_EP4_MAP);
+       usb_fifo_map(HS_C1_I0_A0_EP4_FIFO_START, HS_C1_I0_A0_EP4_FIFO_MAP);
+       usb_fifo_config(HS_C1_I0_A0_EP4_FIFO_START, HS_C1_I0_A0_EP4_FIFO_CONFIG);
+
+       for (i = HS_C1_I0_A0_EP4_FIFO_START + 1;
+            i < HS_C1_I0_A0_EP4_FIFO_START + HS_C1_I0_A0_EP4_FIFO_NO; i++) {
+               usb_fifo_config(i, (HS_C1_I0_A0_EP4_FIFO_CONFIG & (~BIT(7))));
+       }
+
+       usb_ep_packet_size_hi(4, HS_C1_I0_A0_EP4_DIRECTION, (HS_C1_I0_A0_EP4_MAX_PACKET & 0x7ff));
+       usb_ep_packet_size_lo(4, HS_C1_I0_A0_EP4_DIRECTION, (HS_C1_I0_A0_EP4_MAX_PACKET & 0x7ff));
+       usb_ep_in_highbandset(4, HS_C1_I0_A0_EP4_DIRECTION, HS_C1_I0_A0_EP4_MAX_PACKET);
+}
+
+void usb_init_fullspeed_fifo_cfg(void)
+{
+       int i;
+
+       /* EP 1 */
+       usb_ep_map(1, FS_C1_I0_A0_EP1_MAP);
+       usb_fifo_map(FS_C1_I0_A0_EP1_FIFO_START, FS_C1_I0_A0_EP1_FIFO_MAP);
+       usb_fifo_config(FS_C1_I0_A0_EP1_FIFO_START, FS_C1_I0_A0_EP1_FIFO_CONFIG);
+
+       for (i = FS_C1_I0_A0_EP1_FIFO_START + 1;
+            i < FS_C1_I0_A0_EP1_FIFO_START + FS_C1_I0_A0_EP1_FIFO_NO; i++) {
+               usb_fifo_config(i, (FS_C1_I0_A0_EP1_FIFO_CONFIG & (~BIT(7))));
+       }
+
+       usb_ep_packet_size_hi(1, FS_C1_I0_A0_EP1_DIRECTION, (FS_C1_I0_A0_EP1_MAX_PACKET & 0x7ff));
+       usb_ep_packet_size_lo(1, FS_C1_I0_A0_EP1_DIRECTION, (FS_C1_I0_A0_EP1_MAX_PACKET & 0x7ff));
+       /* ``.JWEI 2003/04/29 */
+       usb_ep_in_highbandset(1, FS_C1_I0_A0_EP1_DIRECTION, FS_C1_I0_A0_EP1_MAX_PACKET);
+
+       /* EP 2 */
+       usb_ep_map(2, FS_C1_I0_A0_EP2_MAP);
+       usb_fifo_map(FS_C1_I0_A0_EP2_FIFO_START, FS_C1_I0_A0_EP2_FIFO_MAP);
+       usb_fifo_config(FS_C1_I0_A0_EP2_FIFO_START, FS_C1_I0_A0_EP2_FIFO_CONFIG);
+
+       for (i = FS_C1_I0_A0_EP2_FIFO_START + 1;
+            i < FS_C1_I0_A0_EP2_FIFO_START + FS_C1_I0_A0_EP2_FIFO_NO; i++) {
+               usb_fifo_config(i, (FS_C1_I0_A0_EP2_FIFO_CONFIG & (~BIT(7))));
+       }
+
+       usb_ep_packet_size_hi(2, FS_C1_I0_A0_EP2_DIRECTION, (FS_C1_I0_A0_EP2_MAX_PACKET & 0x7ff));
+       usb_ep_packet_size_lo(2, FS_C1_I0_A0_EP2_DIRECTION, (FS_C1_I0_A0_EP2_MAX_PACKET & 0x7ff));
+       usb_ep_in_highbandset(2, FS_C1_I0_A0_EP2_DIRECTION, FS_C1_I0_A0_EP2_MAX_PACKET);
+
+       /* EP 3 */
+       usb_ep_map(3, FS_C1_I0_A0_EP3_MAP);
+       usb_fifo_map(FS_C1_I0_A0_EP3_FIFO_START, FS_C1_I0_A0_EP3_FIFO_MAP);
+       usb_fifo_config(FS_C1_I0_A0_EP3_FIFO_START, FS_C1_I0_A0_EP3_FIFO_CONFIG);
+
+       for (i = FS_C1_I0_A0_EP3_FIFO_START + 1;
+            i < FS_C1_I0_A0_EP3_FIFO_START + FS_C1_I0_A0_EP3_FIFO_NO; i++) {
+               usb_fifo_config(i, (FS_C1_I0_A0_EP3_FIFO_CONFIG & (~BIT(7))));
+       }
+
+       usb_ep_packet_size_hi(3, FS_C1_I0_A0_EP3_DIRECTION, (FS_C1_I0_A0_EP3_MAX_PACKET & 0x7ff));
+       usb_ep_packet_size_lo(3, FS_C1_I0_A0_EP3_DIRECTION, (FS_C1_I0_A0_EP3_MAX_PACKET & 0x7ff));
+       usb_ep_in_highbandset(3, FS_C1_I0_A0_EP3_DIRECTION, FS_C1_I0_A0_EP3_MAX_PACKET);
+
+       /* EP 4 */
+       usb_ep_map(4, FS_C1_I0_A0_EP4_MAP);
+       usb_fifo_map(FS_C1_I0_A0_EP4_FIFO_START, FS_C1_I0_A0_EP4_FIFO_MAP);
+       usb_fifo_config(FS_C1_I0_A0_EP4_FIFO_START, FS_C1_I0_A0_EP4_FIFO_CONFIG);
+
+       for (i = FS_C1_I0_A0_EP4_FIFO_START + 1;
+            i < FS_C1_I0_A0_EP4_FIFO_START + FS_C1_I0_A0_EP4_FIFO_NO; i++) {
+               usb_fifo_config(i, (FS_C1_I0_A0_EP4_FIFO_CONFIG & (~BIT(7))));
+       }
+
+       usb_ep_packet_size_hi(4, FS_C1_I0_A0_EP4_DIRECTION, (FS_C1_I0_A0_EP4_MAX_PACKET & 0x7ff));
+       usb_ep_packet_size_lo(4, FS_C1_I0_A0_EP4_DIRECTION, (FS_C1_I0_A0_EP4_MAX_PACKET & 0x7ff));
+       usb_ep_in_highbandset(4, FS_C1_I0_A0_EP4_DIRECTION, FS_C1_I0_A0_EP4_MAX_PACKET);
+}
+#endif /* CONFIG_CARL9170FW_USB_MODESWITCH */
diff --git a/carlfw/usb/main.c b/carlfw/usb/main.c
new file mode 100644 (file)
index 0000000..f32efc2
--- /dev/null
@@ -0,0 +1,418 @@
+/*
+ * carl9170 firmware - used by the ar9170 wireless device
+ *
+ * Copyright (c) 2000-2005 ZyDAS Technology Corporation
+ * Copyright (c) 2007-2009 Atheros Communications, Inc.
+ * Copyright   2009    Johannes Berg <johannes@sipsolutions.net>
+ * Copyright   2009    Christian Lamparter <chunkeey@googlemail.com>
+ *
+ * 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.
+ */
+
+#include "carl9170.h"
+
+#include "hostif.h"
+#include "printf.h"
+#include "timer.h"
+#include "rom.h"
+#include "gpio.h"
+#include "shared/phy.h"
+
+#ifdef CONFIG_CARL9170FW_DEBUG_USB
+void usb_putc(const char c)
+{
+       fw.usb.put_buffer[fw.usb.put_index++] = (uint8_t) c;
+
+       if (fw.usb.put_index == CARL9170_MAX_CMD_PAYLOAD_LEN || c == '\0') {
+               fw.usb.put_buffer[fw.usb.put_index] = 0;
+
+               send_cmd_to_host(__roundup(fw.usb.put_index, 4),
+                                CARL9170_RSP_TEXT, fw.usb.put_index,
+                                fw.usb.put_buffer);
+               fw.usb.put_index = 0;
+       }
+}
+
+void usb_print_hex_dump(const void *buf, int len)
+{
+       unsigned int offset = 0, block = 0;
+       while (len > 0) {
+               block = min(__roundup(len, 4), CARL9170_MAX_CMD_PAYLOAD_LEN);
+
+               send_cmd_to_host(block, CARL9170_RSP_HEXDUMP, len,
+                                (const uint8_t *) buf + offset);
+
+               offset += block;
+               len -= block;
+       }
+}
+#endif /* CONFIG_CARL9170FW_DEBUG_USB */
+
+/* grab a buffer from the interrupt in queue ring-buffer */
+static struct carl9170_rsp *get_int_buf(void)
+{
+       struct carl9170_rsp *tmp;
+
+       tmp = &fw.usb.int_buf[fw.usb.int_tail_index++];
+       fw.usb.int_tail_index %= CARL9170_INT_RQ_CACHES;
+       if (fw.usb.int_pending != CARL9170_INT_RQ_CACHES)
+               fw.usb.int_pending++;
+
+       return tmp;
+}
+
+/* Pop up data from Interrupt IN Queue to USB Response buffer */
+static struct carl9170_rsp *dequeue_int_buf(unsigned int space)
+{
+       struct carl9170_rsp *tmp = NULL;
+
+       if (fw.usb.int_pending > 0) {
+               tmp = &fw.usb.int_buf[fw.usb.int_head_index];
+
+               if ((unsigned int)(tmp->hdr.len + 8) > space)
+                       return NULL;
+
+               fw.usb.int_head_index++;
+               fw.usb.int_head_index %= CARL9170_INT_RQ_CACHES;
+               fw.usb.int_pending--;
+       }
+
+       return tmp;
+}
+
+static void usb_data_in(void)
+{
+}
+
+static void usb_reg_out(void)
+{
+       uint32_t *regaddr = (uint32_t *) &dma_mem.reserved.cmd;
+       uint16_t usbfifolen, i;
+
+       usb_reset_out();
+
+       usbfifolen = getb(AR9170_USB_REG_EP4_BYTE_COUNT_LOW) |
+                    getb(AR9170_USB_REG_EP4_BYTE_COUNT_HIGH) << 8;
+
+       if (usbfifolen & 0x3)
+               usbfifolen = (usbfifolen >> 2) + 1;
+       else
+               usbfifolen = usbfifolen >> 2;
+
+       for (i = 0; i < usbfifolen; i++)
+               *regaddr++ = get(AR9170_USB_REG_EP4_DATA);
+
+       handle_cmd(get_int_buf());
+
+       usb_trigger_in();
+}
+
+static void usb_status_in(void)
+{
+       struct carl9170_rsp *rsp;
+       unsigned int rem, tlen, elen;
+
+       if (!fw.usb.int_desc_available)
+               return ;
+
+       fw.usb.int_desc_available = 0;
+
+       rem = AR9170_BLOCK_SIZE - AR9170_INT_MAGIC_HEADER_SIZE;
+       tlen = AR9170_INT_MAGIC_HEADER_SIZE;
+
+       usb_reset_in();
+
+       while (fw.usb.int_pending) {
+               rsp = dequeue_int_buf(rem);
+               if (!rsp)
+                       break;
+
+               elen = rsp->hdr.len + 4;
+
+               memcpy(DESC_PAYLOAD_OFF(fw.usb.int_desc, tlen), rsp, elen);
+
+               rem -= elen;
+               tlen += elen;
+       }
+
+       if (tlen == AR9170_INT_MAGIC_HEADER_SIZE) {
+               DBG("attempted to send an empty int response!\n");
+               goto reclaim;
+       }
+
+       fw.usb.int_desc->totalLen = tlen;
+       fw.usb.int_desc->dataSize = tlen;
+
+       /* Put to UpQ */
+       dma_put(&fw.pta.up_queue, fw.usb.int_desc);
+
+       /* Trigger PTA UP DMA */
+       set(AR9170_PTA_REG_UP_DMA_TRIGGER, 1);
+       usb_trigger_out();
+
+       return ;
+
+reclaim:
+       /* TODO: not sure what to do here */
+       fw.usb.int_desc_available = 1;
+}
+
+void send_cmd_to_host(const uint8_t len, const uint8_t type,
+                     const uint8_t ext, const uint8_t *body)
+{
+       struct carl9170_cmd *resp;
+
+#ifdef CONFIG_CARL9170FW_DEBUG
+       if (unlikely(len > sizeof(resp->data))) {
+               DBG("CMD too long:%x %d\n", type, len);
+               return ;
+       }
+
+       /* Element length must be a multiple of 4. */
+       if (unlikely(len & 0x3)) {
+               DBG("CMD length not mult. of 4:%x %d\n", type, len);
+               return ;
+       }
+#endif /* CONFIG_CARL9170FW_DEBUG */
+
+       resp = (struct carl9170_cmd *) get_int_buf();
+       if (unlikely(resp == NULL)) {
+               /* not very helpful for NON UART users */
+               DBG("out of msg buffers\n");
+               return ;
+       }
+
+       resp->hdr.len = len;
+       resp->hdr.cmd = type;
+       resp->hdr.ext = ext;
+
+       memcpy(resp->data, body, len);
+       usb_trigger_in();
+}
+
+/* Reset all the USB FIFO used for WLAN */
+static void usb_reset_FIFO(void)
+{
+       uint32_t val;
+
+       /*
+        * of course,
+        * simpley ORing AR9170_MAC_POWER_STATE_CTRL_RESET
+        * would be... I dunno, maybe: just to simple?
+        */
+
+       val = get(AR9170_MAC_REG_POWER_STATE_CTRL);
+       val |= AR9170_MAC_POWER_STATE_CTRL_RESET;
+       set(AR9170_MAC_REG_POWER_STATE_CTRL, val);
+
+       /* Reset USB FIFO */
+       set(AR9170_PWR_REG_ADDA_BB, AR9170_PWR_ADDA_BB_USB_FIFO_RESET);
+       set(AR9170_PWR_REG_ADDA_BB, 0x0);
+}
+
+/* Turn off ADDA/RF power, PLL */
+static void turn_power_off(void)
+{
+       set(AR9170_PHY_REG_ACTIVE, AR9170_PHY_ACTIVE_DIS);
+       set(AR9170_PHY_REG_ADC_CTL, 0xa0000000 |
+           AR9170_PHY_ADC_CTL_OFF_PWDADC | AR9170_PHY_ADC_CTL_OFF_PWDDAC);
+
+       set(AR9170_GPIO_REG_PORT_DATA, 0);
+       set(AR9170_GPIO_REG_PORT_TYPE, 0xf);
+
+       set(AR9170_PWR_REG_BASE, 0x40021);
+       set(AR9170_PWR_REG_ADDA_BB, 0);
+
+       clock_set(false, AHB_20_22MHZ);
+
+       set(AR9170_PWR_REG_PLL_ADDAC, 0x5163);  /* 0x502b; */
+       set(AR9170_PHY_REG_ADC_SERIAL_CTL, AR9170_PHY_ADC_SCTL_SEL_EXTERNAL_RADIO);
+       set(0x1c589c, 0);       /* 7-0 */
+       set(0x1c589c, 0);       /* 15-8 */
+       set(0x1c589c, 0);       /* 23-16 */
+       set(0x1c589c, 0);       /* 31- */
+       set(0x1c589c, 0);       /* 39- */
+       set(0x1c589c, 0);       /* 47- */
+       set(0x1c589c, 0);       /* 55- */
+       set(0x1c589c, 0xf8);    /* 63- */
+       set(0x1c589c, 0x27);    /* 0x24;        71-     modified */
+       set(0x1c589c, 0xf9);    /* 79- */
+       set(0x1c589c, 0x90);    /* 87- */
+       set(0x1c589c, 0x04);    /* 95- */
+       set(0x1c589c, 0x48);    /* 103- */
+       set(0x1c589c, 0x19);    /* 0;           111-    modified */
+       set(0x1c589c, 0);       /* 119- */
+       set(0x1c589c, 0);       /* 127- */
+       set(0x1c589c, 0);       /* 135- */
+       set(0x1c589c, 0);       /* 143- */
+       set(0x1c589c, 0);       /* 151- */
+       set(0x1c589c, 0x70);    /* 159- */
+       set(0x1c589c, 0x0c);    /* 167- */
+       set(0x1c589c, 0);       /* 175- */
+       set(0x1c589c, 0);       /* 183-176 */
+       set(0x1c589c, 0);       /* 191-184 */
+       set(0x1c589c, 0);       /* 199- */
+       set(0x1c589c, 0);       /* 207- */
+       set(0x1c589c, 0);       /* 215- */
+       set(0x1c589c, 0);       /* 223- */
+       set(0x1c589c, 0);       /* 231- */
+       set(0x1c58c4, 0);       /* 233- 232 */
+       set(AR9170_PHY_REG_ADC_SERIAL_CTL, AR9170_PHY_ADC_SCTL_SEL_INTERNAL_ADDAC);
+}
+
+void __attribute__((noreturn)) reboot(void)
+{
+       /* turn off leds */
+       led_set(0);
+
+       /* write watchdog magic pattern for suspend  */
+       andl(AR9170_PWR_REG_WATCH_DOG_MAGIC, 0xffff);
+       orl(AR9170_PWR_REG_WATCH_DOG_MAGIC, 0x98760000);
+
+       /* Disable watchdog */
+       orl(AR9170_TIMER_REG_WATCH_DOG, 0xffff);
+
+       /* Reset USB FIFO */
+       usb_reset_FIFO();
+
+       /* Turn off power */
+       turn_power_off();
+
+       /* add by ygwei for work around USB PHY chirp sequence problem */
+       set(0x10f100, 0x12345678);
+
+       /* Jump to boot code */
+       jump_to_bootcode();
+}
+
+/* service USB events and re-enable USB interrupt */
+static void usb_handler(uint8_t usb_interrupt_level1)
+{
+       uint8_t usb_interrupt_level2;
+
+       if (usb_interrupt_level1 & BIT(5))
+               usb_data_in();
+
+       if (usb_interrupt_level1 & BIT(4))
+               usb_reg_out();
+
+       if (usb_interrupt_level1 & BIT(6))
+               usb_status_in();
+
+       if (usb_interrupt_level1 & BIT(0)) {
+               usb_interrupt_level2 = getb(AR9170_USB_REG_INTR_SOURCE_0);
+
+               if (usb_interrupt_level2 & BIT(0))
+                       usb_ep0setup();
+
+               if (usb_interrupt_level2 & BIT(1))
+                       usb_ep0tx();
+
+               if (usb_interrupt_level2 & BIT(2))
+                       usb_ep0rx();
+
+               if (usb_interrupt_level2 & BIT(7)) {
+                       /* Clear the command abort interrupt */
+                       andb(AR9170_USB_REG_INTR_SOURCE_0, 0x7f);
+               }
+
+               if (usb_interrupt_level2 & BIT(3) ||
+                   fw.usb.ep0_action & CARL9170_EP0_STALL) {
+                       /*
+                        * transmission failure.
+                        * stall ep 0
+                        */
+                       setb(AR9170_USB_REG_CX_CONFIG_STATUS, BIT(2));
+                       fw.usb.ep0_action &= ~CARL9170_EP0_STALL;
+               }
+
+               if (usb_interrupt_level2 & BIT(4) ||
+                   fw.usb.ep0_action & CARL9170_EP0_TRIGGER) {
+                       /*
+                        * transmission done.
+                        * set DONE bit.
+                        */
+                       setb(AR9170_USB_REG_CX_CONFIG_STATUS, BIT(0));
+                       fw.usb.ep0_action &= ~CARL9170_EP0_TRIGGER;
+               }
+       }
+
+       if (usb_interrupt_level1 & BIT(7)) {
+               usb_interrupt_level2 = getb(AR9170_USB_REG_INTR_SOURCE_7);
+
+               if (usb_interrupt_level2 & BIT(7))
+                       usb_data_out0Byte();
+
+               if (usb_interrupt_level2 & BIT(6))
+                       usb_data_in0Byte();
+
+               if (usb_interrupt_level2 & BIT(1)) {
+                       usb_reset_ack();
+                       reboot();
+               }
+
+               if (usb_interrupt_level2 & BIT(2)) {
+                       /* ACK USB suspend interrupt */
+                       usb_suspend_ack();
+
+                       /* Set GO_TO_SUSPEND bit to USB main control register */
+                       setb(AR9170_USB_REG_MAIN_CTRL, BIT(3));
+
+                       /* add by ygwei for work around USB PHY chirp sequence problem */
+                       set(0x10f100, 0x12345678);
+
+                       reboot();
+               }
+
+               if (usb_interrupt_level2 & BIT(3))
+                       usb_resume_ack();
+       }
+}
+
+void handle_usb(void)
+{
+       uint8_t usb_interrupt_level1;
+
+       usb_interrupt_level1 = getb(AR9170_USB_REG_INTR_GROUP);
+
+       if (usb_interrupt_level1)
+               usb_handler(usb_interrupt_level1);
+
+       if (fw.usb.int_pending > 0)
+               usb_trigger_in();
+}
+
+#ifdef CONFIG_CARL9170FW_USB_WATCHDOG
+void usb_watchdog_timer(void)
+{
+       if (fw.usb.watchdog.state == cpu_to_le32(CARL9170_USB_WATCHDOG_INACTIVE))
+               return;
+
+       fw.usb.watchdog.state++;
+
+       if (le32_to_cpu(fw.usb.watchdog.state) >= CARL9170_USB_WATCHDOG_TRIGGER_THRESHOLD) {
+               for (;;) {
+                       /*
+                        * Simply wait until the HW watchdog
+                        * timer has elapsed.
+                        */
+               }
+       }
+
+       send_cmd_to_host(sizeof(fw.usb.watchdog), CARL9170_RSP_USB_WD,
+                        0x80, (uint8_t *) &fw.usb.watchdog);
+}
+#endif /* CONFIG_CARL9170FW_USB_WATCHDOG */
+
diff --git a/carlfw/usb/usb.c b/carlfw/usb/usb.c
new file mode 100644 (file)
index 0000000..49852fb
--- /dev/null
@@ -0,0 +1,723 @@
+/*
+ * carl9170 firmware - used by the ar9170 wireless device
+ *
+ * USB Controller
+ *
+ * Copyright (c) 2000-2005  ZyDAS Technology Corporation
+ * Copyright (c) 2007-2009 Atheros Communications, Inc.
+ * Copyright   2009    Johannes Berg <johannes@sipsolutions.net>
+ * Copyright   2009    Christian Lamparter <chunkeey@googlemail.com>
+ *
+ * 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.
+ */
+#include "carl9170.h"
+#include "usb.h"
+#include "printf.h"
+#include "rom.h"
+
+/*
+ * NB: The firmware has to write into these structures
+ * so don't try to make them "const".
+ */
+
+static struct ar9170_usb_config usb_config_highspeed = {
+       .cfg = {
+               .bLength = USB_DT_CONFIG_SIZE,
+               .bDescriptorType = USB_DT_CONFIG,
+               .wTotalLength = cpu_to_le16(sizeof(usb_config_highspeed)),
+               .bNumInterfaces = 1,
+               .bConfigurationValue = 1,
+               .iConfiguration = 0,
+               .bmAttributes = USB_CONFIG_ATT_ONE,
+               .bMaxPower = 0xfa, /* 500 mA */
+       },
+
+       .intf = {
+               .bLength = USB_DT_INTERFACE_SIZE,
+               .bDescriptorType = USB_DT_INTERFACE,
+               .bInterfaceNumber = 0,
+               .bAlternateSetting = 0,
+               .bNumEndpoints = AR9170_USB_NUM_EXTRA_EP,
+               .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+               .bInterfaceSubClass = USB_SUBCLASS_VENDOR_SPEC,
+               .bInterfaceProtocol = 0,
+               .iInterface = 0,
+       },
+
+       .ep = {
+               {       /* EP 1 */
+                       .bLength = USB_DT_ENDPOINT_SIZE,
+                       .bDescriptorType = USB_DT_ENDPOINT,
+                       .bEndpointAddress = USB_DIR_OUT | AR9170_USB_EP_TX,
+                       .bmAttributes = USB_ENDPOINT_XFER_BULK,
+                       .wMaxPacketSize = cpu_to_le16(512),
+                       .bInterval = 0,
+               },
+
+               {       /* EP 2 */
+                       .bLength = USB_DT_ENDPOINT_SIZE,
+                       .bDescriptorType = USB_DT_ENDPOINT,
+                       .bEndpointAddress = USB_DIR_IN | AR9170_USB_EP_RX,
+                       .bmAttributes = USB_ENDPOINT_XFER_BULK,
+                       .wMaxPacketSize = cpu_to_le16(512),
+                       .bInterval = 0,
+               },
+
+               {       /* EP 3 */
+                       .bLength = USB_DT_ENDPOINT_SIZE,
+                       .bDescriptorType = USB_DT_ENDPOINT,
+                       .bEndpointAddress = USB_DIR_IN | AR9170_USB_EP_IRQ,
+                       .bmAttributes = USB_ENDPOINT_XFER_INT,
+                       .wMaxPacketSize = cpu_to_le16(64),
+                       .bInterval = 1,
+               },
+
+               {       /* EP 4 */
+                       .bLength = USB_DT_ENDPOINT_SIZE,
+                       .bDescriptorType = USB_DT_ENDPOINT,
+                       .bEndpointAddress = USB_DIR_OUT | AR9170_USB_EP_CMD,
+                       .bmAttributes = USB_ENDPOINT_XFER_INT,
+                       .wMaxPacketSize = cpu_to_le16(64),
+                       .bInterval = 1,
+               },
+       },
+};
+
+static struct ar9170_usb_config usb_config_fullspeed = {
+       .cfg = {
+               .bLength = USB_DT_CONFIG_SIZE,
+               .bDescriptorType = USB_DT_CONFIG,
+               .wTotalLength = cpu_to_le16(sizeof(usb_config_fullspeed)),
+               .bNumInterfaces = 1,
+               .bConfigurationValue = 1,
+               .iConfiguration = 0,
+               .bmAttributes = USB_CONFIG_ATT_ONE,
+               .bMaxPower = 0xfa, /* 500 mA */
+       },
+
+       .intf = {
+               .bLength = USB_DT_INTERFACE_SIZE,
+               .bDescriptorType = USB_DT_INTERFACE,
+               .bInterfaceNumber = 0,
+               .bAlternateSetting = 0,
+               .bNumEndpoints = AR9170_USB_NUM_EXTRA_EP,
+               .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+               .bInterfaceSubClass = USB_SUBCLASS_VENDOR_SPEC,
+               .bInterfaceProtocol = 0,
+               .iInterface = 0,
+       },
+
+       .ep = {
+               {       /* EP 1 */
+                       .bLength = USB_DT_ENDPOINT_SIZE,
+                       .bDescriptorType = USB_DT_ENDPOINT,
+                       .bEndpointAddress = USB_DIR_OUT | AR9170_USB_EP_TX,
+                       .bmAttributes = USB_ENDPOINT_XFER_BULK,
+                       .wMaxPacketSize = cpu_to_le16(64),
+                       .bInterval = 0,
+               },
+
+               {       /* EP 2 */
+                       .bLength = USB_DT_ENDPOINT_SIZE,
+                       .bDescriptorType = USB_DT_ENDPOINT,
+                       .bEndpointAddress = USB_DIR_IN | AR9170_USB_EP_RX,
+                       .bmAttributes = USB_ENDPOINT_XFER_BULK,
+                       .wMaxPacketSize = cpu_to_le16(64),
+                       .bInterval = 0,
+               },
+
+               {       /* EP 3 */
+                       .bLength = USB_DT_ENDPOINT_SIZE,
+                       .bDescriptorType = USB_DT_ENDPOINT,
+                       .bEndpointAddress = USB_DIR_IN | AR9170_USB_EP_IRQ,
+                       .bmAttributes = USB_ENDPOINT_XFER_INT,
+                       .wMaxPacketSize = cpu_to_le16(64),
+                       .bInterval = 1,
+               },
+
+               {       /* EP 4 */
+                       .bLength = USB_DT_ENDPOINT_SIZE,
+                       .bDescriptorType = USB_DT_ENDPOINT,
+                       .bEndpointAddress = USB_DIR_OUT | AR9170_USB_EP_CMD,
+                       .bmAttributes = USB_ENDPOINT_XFER_INT,
+                       .wMaxPacketSize = cpu_to_le16(64),
+                       .bInterval = 1,
+               },
+       },
+};
+
+#ifdef CONFIG_CARL9170FW_USB_MODESWITCH
+static void usb_reset_eps(void)
+{
+       unsigned int i;
+
+       /* clear all EPs' toggle bit */
+       for (i = 1; i < __AR9170_USB_NUM_MAX_EP; i++) {
+               usb_set_input_ep_toggle(i);
+               usb_clear_input_ep_toggle(i);
+       }
+
+       /*
+        * NB: I've no idea why this cannot be integrated into the
+        * previous loop?
+        */
+       for (i = 1; i < __AR9170_USB_NUM_MAX_EP; i++) {
+               usb_set_output_ep_toggle(i);
+               usb_clear_output_ep_toggle(i);
+       }
+}
+#endif /* CONFIG_CARL9170FW_USB_MODESWITCH */
+
+
+#ifdef CONFIG_CARL9170FW_USB_INIT_FIRMWARE
+static void usb_pta_init(void)
+{
+       /* Set PTA mode to USB */
+       andl(AR9170_PTA_REG_DMA_MODE_CTRL,
+               ~AR9170_PTA_DMA_MODE_CTRL_DISABLE_USB);
+
+       /* Do a software reset to PTA component */
+       orl(AR9170_PTA_REG_DMA_MODE_CTRL, AR9170_PTA_DMA_MODE_CTRL_RESET);
+       andl(AR9170_PTA_REG_DMA_MODE_CTRL, ~AR9170_PTA_DMA_MODE_CTRL_RESET);
+
+       if (usb_detect_highspeed()) {
+               fw.usb.os_cfg_desc = &usb_config_fullspeed;
+               fw.usb.cfg_desc = &usb_config_highspeed;
+
+               /* 512 Byte DMA transfers */
+               orl(AR9170_USB_REG_DMA_CTL, AR9170_DMA_CTL_HIGH_SPEED);
+       } else {
+               fw.usb.cfg_desc = &usb_config_fullspeed;
+               fw.usb.os_cfg_desc = &usb_config_highspeed;
+       }
+
+#ifdef CONFIG_CARL9170FW_USB_UP_STREAM
+       /* Enable upload stream mode */
+       andl(AR9170_USB_REG_DMA_CTL, ~AR9170_DMA_CTL_UP_PACKET_MODE);
+
+       /* reset maximum transfer size */
+       andl(AR9170_USB_REG_DMA_CTL, ~(AR9170_DMA_CTL_UP_STREAM_MASK));
+
+# if (CONFIG_CARL9170FW_RX_FRAME_LEN == 4096)
+       orl(AR9170_USB_REG_DMA_CTL, AR9170_DMA_CTL_UP_STREAM_4K);
+# elif (CONFIG_CARL9170FW_RX_FRAME_LEN == 8192)
+       orl(AR9170_USB_REG_DMA_CTL, AR9170_DMA_CTL_UP_STREAM_8K);
+# elif (CONFIG_CARL9170FW_RX_FRAME_LEN == 16384)
+       orl(AR9170_USB_REG_DMA_CTL, AR9170_DMA_CTL_UP_STREAM_16K);
+# elif (CONFIG_CARL9170FW_RX_FRAME_LEN == 32768)
+       orl(AR9170_USB_REG_DMA_CTL, AR9170_DMA_CTL_UP_STREAM_32K);
+# else
+#      error "Invalid AR9170_RX_FRAME_LEN setting"
+# endif
+
+#else /* CONFIG_CARL9170FW_USB_UP_STREAM */
+       orl(AR9170_USB_REG_DMA_CTL, AR9170_DMA_CTL_UP_PACKET_MODE);
+#endif /* CONFIG_CARL9170FW_USB_UP_STREAM */
+
+#ifdef CONFIG_CARL9170FW_USB_DOWN_STREAM
+       /* Enable down stream mode */
+       orl(AR9170_USB_REG_DMA_CTL, AR9170_DMA_CTL_DOWN_STREAM);
+#endif /* CONFIG_CARL9170FW_USB_DOWN_STREAM */
+
+       /* Enable up stream and down stream */
+       orl(AR9170_USB_REG_DMA_CTL, AR9170_DMA_CTL_ENABLE_TO_DEVICE |
+           AR9170_DMA_CTL_ENABLE_FROM_DEVICE);
+
+#ifdef CONFIG_CARL9170FW_USB_UP_STREAM
+       /* Set the up stream mode maximum aggregate number */
+       set(AR9170_USB_REG_MAX_AGG_UPLOAD, 4);
+
+       /*
+        * Set the up stream mode timeout value.
+        * NB: The vendor driver (otus) set 0x80?
+        */
+       set(AR9170_USB_REG_UPLOAD_TIME_CTL, 0x80);
+#endif /* CONFIG_CARL9170FW_USB_UP_STREAM */
+
+}
+#endif /* CONFIG_CARL9170FW_USB_INIT_FIRMWARE */
+
+void usb_init(void)
+{
+#ifdef CONFIG_CARL9170FW_USB_INIT_FIRMWARE
+       usb_pta_init();
+#endif /* CONFIG_CARL9170FW_USB_INIT_FIRMWARE */
+
+       fw.usb.config = 1;
+       /*
+        * The fw structure is always initialized with "0"
+        * during boot(); No need to waste precious bytes here.
+        *
+        * fw.usb.interface_setting = 0;
+        * fw.usb.alternate_interface_setting = 0;
+        */
+}
+
+#define GET_ARRAY(a, o)        ((uint32_t *) (((unsigned long) data) + offset))
+
+static void usb_ep0rx_data(const void *data, const unsigned int len)
+{
+       unsigned int offset;
+       uint32_t value;
+
+       BUG_ON(len > AR9170_USB_EP_CTRL_MAX);
+       BUILD_BUG_ON(len > AR9170_USB_EP_CTRL_MAX);
+
+       for (offset = 0; offset < ((len + 3) & ~3); offset += 4) {
+               value = get(AR9170_USB_REG_EP0_DATA);
+               memcpy(GET_ARRAY(data, offset), &value,
+                      min(len - offset, (unsigned int)4));
+       }
+}
+
+static int usb_ep0tx_data(const void *data, const unsigned int len)
+{
+       unsigned int offset = 0, block, last_block = 0;
+       uint32_t value;
+
+       BUG_ON(len > AR9170_USB_EP_CTRL_MAX);
+       BUILD_BUG_ON(len > AR9170_USB_EP_CTRL_MAX);
+
+       block = min(len, (unsigned int) 4);
+       offset = 0;
+       while (offset < len) {
+
+               if (last_block != block || block < 4)
+                       setb(AR9170_USB_REG_FIFO_SIZE, (1 << block) - 1);
+
+               memcpy(&value, GET_ARRAY(data, offset), block);
+
+               set(AR9170_USB_REG_EP0_DATA, value);
+
+               offset += block;
+               last_block = block = min(len - offset, (unsigned int) 4);
+       }
+
+       setb(AR9170_USB_REG_FIFO_SIZE, 0xf);
+
+       /* this will push the data to the host */
+       return 1;
+}
+#undef GET_ARRAY
+
+#ifdef CONFIG_CARL9170FW_USB_STANDARD_CMDS
+static int usb_get_status(const struct usb_ctrlrequest *ctrl)
+{
+       __le16 status = cpu_to_le16(0);
+
+       if ((ctrl->bRequestType & USB_DIR_MASK) != USB_DIR_IN)
+               return -1;
+
+       switch (ctrl->bRequestType & USB_RECIP_MASK) {
+       case USB_RECIP_DEVICE:
+               status &= cpu_to_le16(~USB_DEVICE_SELF_POWERED);
+               status &= cpu_to_le16(~USB_DEVICE_REMOTE_WAKEUP);
+               break;
+
+       case USB_RECIP_INTERFACE:
+               /* USB spec: This is reserved for future use. */
+               status = cpu_to_le16(0);
+               break;
+
+       case USB_RECIP_ENDPOINT:
+       case USB_RECIP_OTHER:
+       default:
+               break;
+       }
+
+       return usb_ep0tx_data((const void *) &status, sizeof(status));
+}
+
+static int usb_get_string_desc(const struct usb_ctrlrequest *ctrl)
+{
+       const struct usb_string_descriptor *string_desc = NULL;
+
+       switch (le16_to_cpu(ctrl->wValue) & 0xff) {
+       case 0x00:
+               string_desc = (const struct usb_string_descriptor *)
+                       rom.hw.usb.string0_desc;
+               break;
+
+       case 0x10:
+               string_desc = (const struct usb_string_descriptor *)
+                       rom.hw.usb.string1_desc;
+               break;
+
+       case 0x20:
+               string_desc = (const struct usb_string_descriptor *)
+                       rom.hw.usb.string2_desc;
+               break;
+
+       case 0x30:
+               string_desc = (const struct usb_string_descriptor *)
+                       rom.hw.usb.string3_desc;
+               break;
+
+       default:
+               break;
+       }
+
+       if (string_desc)
+               return usb_ep0tx_data(string_desc, string_desc->bLength);
+
+       return -1;
+}
+
+static int usb_get_device_desc(const struct usb_ctrlrequest *ctrl __unused)
+{
+       return usb_ep0tx_data(&rom.hw.usb.device_desc,
+                             rom.hw.usb.device_desc.bLength);
+}
+
+static int usb_get_config_desc(const struct usb_ctrlrequest *ctrl __unused)
+{
+       fw.usb.cfg_desc->cfg.bDescriptorType = USB_DT_CONFIG;
+
+       return usb_ep0tx_data(fw.usb.cfg_desc,
+               le16_to_cpu(fw.usb.cfg_desc->cfg.wTotalLength));
+}
+
+#ifdef CONFIG_CARL9170FW_USB_MODESWITCH
+static int usb_get_otherspeed_desc(const struct usb_ctrlrequest *ctrl __unused)
+{
+
+       fw.usb.os_cfg_desc->cfg.bDescriptorType = USB_DT_OTHER_SPEED_CONFIG;
+
+       return usb_ep0tx_data(fw.usb.os_cfg_desc,
+               le16_to_cpu(fw.usb.os_cfg_desc->cfg.wTotalLength));
+}
+#endif /* CONFIG_CARL9170FW_USB_MODESWITCH */
+
+static int usb_get_qualifier_desc(const struct usb_ctrlrequest *ctrl __unused)
+{
+       struct usb_qualifier_descriptor qual;
+
+       /*
+        * The qualifier descriptor shares some structural details
+        * with the main device descriptor.
+        */
+
+       memcpy(&qual, &rom.hw.usb.device_desc, sizeof(qual));
+
+       /* (Re)-Initialize fields */
+       qual.bDescriptorType = USB_DT_DEVICE_QUALIFIER;
+       qual.bLength = sizeof(qual);
+       qual.bNumConfigurations = rom.hw.usb.device_desc.bNumConfigurations;
+       qual.bRESERVED = 0;
+
+       return usb_ep0tx_data(&qual, qual.bLength);
+}
+
+#define USB_CHECK_REQTYPE(ctrl, recip, dir)                    \
+       (((ctrl->bRequestType & USB_RECIP_MASK) != recip) ||    \
+        ((ctrl->bRequestType & USB_DIR_MASK) != dir))
+
+static int usb_get_descriptor(const struct usb_ctrlrequest *ctrl)
+{
+       int status = -1;
+
+       if (USB_CHECK_REQTYPE(ctrl, USB_RECIP_DEVICE, USB_DIR_IN))
+               return status;
+
+       switch (le16_to_cpu(ctrl->wValue) >> 8) {
+       case USB_DT_DEVICE:
+               status = usb_get_device_desc(ctrl);
+               break;
+
+       case USB_DT_CONFIG:
+               status = usb_get_config_desc(ctrl);
+               break;
+
+       case USB_DT_STRING:
+               status = usb_get_string_desc(ctrl);
+               break;
+
+       case USB_DT_INTERFACE:
+               break;
+
+       case USB_DT_ENDPOINT:
+               break;
+
+       case USB_DT_DEVICE_QUALIFIER:
+               status = usb_get_qualifier_desc(ctrl);
+               break;
+
+#ifdef CONFIG_CARL9170FW_USB_MODESWITCH
+       case USB_DT_OTHER_SPEED_CONFIG:
+               status = usb_get_otherspeed_desc(ctrl);
+               break;
+#endif /* CONFIG_CARL9170FW_USB_MODESWITCH */
+       default:
+               break;
+
+       }
+
+       return status;
+}
+
+static int usb_get_configuration(const struct usb_ctrlrequest *ctrl)
+{
+       if (USB_CHECK_REQTYPE(ctrl, USB_RECIP_DEVICE, USB_DIR_IN))
+               return -1;
+
+       return usb_ep0tx_data(&fw.usb.config, 1);
+}
+
+static int usb_set_configuration(const struct usb_ctrlrequest *ctrl)
+{
+       unsigned int config;
+
+       if (USB_CHECK_REQTYPE(ctrl, USB_RECIP_DEVICE, USB_DIR_OUT))
+               return -1;
+
+       config = le16_to_cpu(ctrl->wValue);
+       switch (config) {
+       case 0:
+               /* Disable Device */
+               andb(AR9170_USB_REG_DEVICE_ADDRESS,
+                     (uint8_t) ~(AR9170_USB_DEVICE_ADDRESS_CONFIGURE));
+#ifdef CONFIG_CARL9170FW_USB_MODESWITCH
+       case 1:
+               fw.usb.config = config;
+
+               if (usb_detect_highspeed()) {
+                       /* High Speed Configuration */
+                       usb_init_highspeed_fifo_cfg();
+               } else {
+                       /* Full Speed Configuration */
+                       usb_init_fullspeed_fifo_cfg();
+               }
+               break;
+
+       default:
+               return -1;
+       }
+       /* usb_pta_init() ? */
+
+       usb_reset_eps();
+       orb(AR9170_USB_REG_DEVICE_ADDRESS,
+           (AR9170_USB_DEVICE_ADDRESS_CONFIGURE));
+
+       usb_enable_global_int();
+       usb_trigger_out();
+       return 1;
+#else
+       default:
+               return -1;
+       }
+#endif /* CONFIG_CARL9170FW_USB_MODESWITCH */
+}
+
+static int usb_set_address(const struct usb_ctrlrequest *ctrl)
+{
+       unsigned int address;
+
+       if (USB_CHECK_REQTYPE(ctrl, USB_RECIP_DEVICE, USB_DIR_OUT))
+               return -1;
+
+       address = le16_to_cpu(ctrl->wValue);
+
+       /*
+        * The original firmware used 0x100 (which is, of course,
+        * too big to fit into uint8_t).
+        * However based on the available information (hw.h), BIT(7)
+        * is used as some sort of flag and should not be
+        * part of the device address.
+        */
+       if (address >= BIT(7))
+               return -1;
+
+       setb(AR9170_USB_REG_DEVICE_ADDRESS, (uint8_t) address);
+       return 1;
+}
+
+static int usb_get_interface(const struct usb_ctrlrequest *ctrl)
+{
+       if (USB_CHECK_REQTYPE(ctrl, USB_RECIP_INTERFACE, USB_DIR_IN))
+               return -1;
+
+       if (usb_configured() == false)
+               return -1;
+
+       switch (fw.usb.config) {
+       case 1:
+               break;
+
+       default:
+               return -1;
+       }
+
+       return usb_ep0tx_data(&fw.usb.alternate_interface_setting, 1);
+}
+
+#ifdef CONFIG_CARL9170FW_USB_MODESWITCH
+static int usb_set_interface(const struct usb_ctrlrequest *ctrl)
+{
+       unsigned int intf, alt_intf;
+       if (USB_CHECK_REQTYPE(ctrl, USB_RECIP_INTERFACE, USB_DIR_OUT))
+               return -1;
+
+       if (usb_configured() == false)
+               return -1;
+
+       intf = le16_to_cpu(ctrl->wIndex);
+       alt_intf = le16_to_cpu(ctrl->wValue);
+
+       switch (intf) {
+       case 0:
+               if (alt_intf != fw.usb.cfg_desc->intf.bAlternateSetting)
+                       return -1;
+
+               fw.usb.interface_setting = (uint8_t) intf;
+               fw.usb.alternate_interface_setting = (uint8_t) alt_intf;
+               if (usb_detect_highspeed())
+                       usb_init_highspeed_fifo_cfg();
+               else
+                       usb_init_fullspeed_fifo_cfg();
+
+               usb_reset_eps();
+               usb_enable_global_int();
+               usb_trigger_out();
+               return 1;
+
+       default:
+               return -1;
+       }
+}
+#endif /* CONFIG_CARL9170FW_USB_MODESWITCH */
+#endif /* CONFIG_CARL9170FW_USB_STANDARD_CMDS */
+
+static int usb_standard_command(const struct usb_ctrlrequest *ctrl __unused)
+{
+       int status = -1;
+
+#ifdef CONFIG_CARL9170FW_USB_STANDARD_CMDS
+       switch (ctrl->bRequest) {
+       case USB_REQ_GET_STATUS:
+               status = usb_get_status(ctrl);
+               break;
+
+       case USB_REQ_CLEAR_FEATURE:
+               break;
+
+       case USB_REQ_SET_FEATURE:
+               break;
+
+       case USB_REQ_SET_ADDRESS:
+               status = usb_set_address(ctrl);
+               break;
+
+       case USB_REQ_GET_DESCRIPTOR:
+               status = usb_get_descriptor(ctrl);
+               break;
+
+       case USB_REQ_SET_DESCRIPTOR:
+               break;
+
+       case USB_REQ_GET_CONFIGURATION:
+               status = usb_get_configuration(ctrl);
+               break;
+
+       case USB_REQ_SET_CONFIGURATION:
+               status = usb_set_configuration(ctrl);
+               break;
+
+       case USB_REQ_GET_INTERFACE:
+               status = usb_get_interface(ctrl);
+               break;
+
+       case USB_REQ_SET_INTERFACE:
+#ifdef CONFIG_CARL9170FW_USB_MODESWITCH
+               status = usb_set_interface(ctrl);
+#endif /* CONFIG_CARL9170FW_USB_MODESWITCH */
+               break;
+
+       case USB_REQ_SYNCH_FRAME:
+               break;
+
+       default:
+               break;
+
+       }
+#endif /* CONFIG_CARL9170FW_USB_STANDARD_CMDS */
+
+       return status;
+}
+
+static int usb_class_command(const struct usb_ctrlrequest *ctrl __unused)
+{
+       return -1;
+}
+
+static int usb_vendor_command(const struct usb_ctrlrequest *ctrl __unused)
+{
+       /*
+        * Note: Firmware upload/boot is not implemented.
+        * It's impossible to replace the current image
+        * in place.
+        */
+
+       return -1;
+}
+
+#undef USB_CHECK_TYPE
+
+void usb_ep0setup(void)
+{
+       struct usb_ctrlrequest ctrl;
+       int status = -1;
+       usb_ep0rx_data(&ctrl, sizeof(ctrl));
+
+       switch (ctrl.bRequestType & USB_TYPE_MASK) {
+       case USB_TYPE_STANDARD:
+               status = usb_standard_command(&ctrl);
+               break;
+
+       case USB_TYPE_CLASS:
+               status = usb_class_command(&ctrl);
+               break;
+
+       case USB_TYPE_VENDOR:
+               status = usb_vendor_command(&ctrl);
+               break;
+
+       default:
+               break;
+
+       }
+
+       if (status < 0)
+               fw.usb.ep0_action |= CARL9170_EP0_STALL;
+       if (status > 0)
+               fw.usb.ep0_action |= CARL9170_EP0_TRIGGER;
+}
+
+void usb_ep0rx(void)
+{
+       if (BUG_ON(!fw.usb.ep0_txrx_buffer || !fw.usb.ep0_txrx_len))
+               return ;
+
+       usb_ep0rx_data(fw.usb.ep0_txrx_buffer, fw.usb.ep0_txrx_len);
+       fw.usb.ep0_txrx_pos = fw.usb.ep0_txrx_len;
+}
+
+void usb_ep0tx(void)
+{
+       if (BUG_ON(!fw.usb.ep0_txrx_buffer || !fw.usb.ep0_txrx_len))
+               return ;
+
+       usb_ep0tx_data(fw.usb.ep0_txrx_buffer, fw.usb.ep0_txrx_len);
+       fw.usb.ep0_txrx_pos = fw.usb.ep0_txrx_len;
+}
diff --git a/config/.gitignore b/config/.gitignore
new file mode 100644 (file)
index 0000000..a7ecde7
--- /dev/null
@@ -0,0 +1,5 @@
+lex.zconf.c
+zconf.hash.c
+zconf.tab.c
+conf
+lex.backup
diff --git a/config/Makefile b/config/Makefile
new file mode 100644 (file)
index 0000000..c76a975
--- /dev/null
@@ -0,0 +1,35 @@
+# ===========================================================================
+# carl9170 configuration targets
+# These targets are used from top-level makefile
+
+PHONY += config
+
+config: conf
+
+# ===========================================================================
+# Shared Makefile for the various kconfig executables:
+
+clean:
+       @rm -f *.o zconf.tab.c lex.zconf.c zconf.hash.c lex.backup conf
+
+# generated files seem to need this to find local include files
+
+zconf.tab.o: lex.zconf.c zconf.hash.c
+
+zconf.tab.c: zconf.y
+lex.zconf.c: zconf.l
+zconf.hash.c: zconf.gperf
+
+%.tab.c: %.y
+       bison -l -b $* -p $(notdir $*) $<
+
+lex.%.c: %.l
+       flex -L -P$(notdir $*) -o$@ $<
+
+%.hash.c: %.gperf
+       gperf < $< > $@
+
+%.o: %.c
+       gcc $(CFLAGS) -I./ -c $< -o $@
+
+conf: zconf.tab.o conf.o
diff --git a/config/conf.c b/config/conf.c
new file mode 100644 (file)
index 0000000..38a610b
--- /dev/null
@@ -0,0 +1,595 @@
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#include <locale.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#define LKC_DIRECT_LINK
+#include "lkc.h"
+
+static void conf(struct menu *menu);
+static void check_conf(struct menu *menu);
+
+enum {
+       ask_all,
+       ask_new,
+       ask_silent,
+       set_default,
+       set_yes,
+       set_mod,
+       set_no,
+       set_random
+} input_mode = ask_all;
+char *defconfig_file;
+
+static int indent = 1;
+static int valid_stdin = 1;
+static int conf_cnt;
+static char line[128];
+static struct menu *rootEntry;
+
+static void print_help(struct menu *menu)
+{
+       struct gstr help = str_new();
+
+       menu_get_ext_help(menu, &help);
+
+       printf("\n%s\n", str_get(&help));
+       str_free(&help);
+}
+
+static void strip(char *str)
+{
+       char *p = str;
+       int l;
+
+       while ((isspace(*p)))
+               p++;
+       l = strlen(p);
+       if (p != str)
+               memmove(str, p, l + 1);
+       if (!l)
+               return;
+       p = str + l - 1;
+       while ((isspace(*p)))
+               *p-- = 0;
+}
+
+static void check_stdin(void)
+{
+       if (!valid_stdin) {
+               printf(_("aborted!\n\n"));
+               printf(_("Console input/output is redirected. "));
+               printf(_("Run 'make config' to update configuration.\n\n"));
+               exit(1);
+       }
+}
+
+static int conf_askvalue(struct symbol *sym, const char *def)
+{
+       enum symbol_type type = sym_get_type(sym);
+
+       if (!sym_has_value(sym))
+               printf(_("(NEW) "));
+
+       line[0] = '\n';
+       line[1] = 0;
+
+       if (!sym_is_changable(sym)) {
+               printf("%s\n", def);
+               line[0] = '\n';
+               line[1] = 0;
+               return 0;
+       }
+
+       switch (input_mode) {
+       case ask_new:
+       case ask_silent:
+               if (sym_has_value(sym)) {
+                       printf("%s\n", def);
+                       return 0;
+               }
+               check_stdin();
+       case ask_all:
+               fflush(stdout);
+               fgets(line, 128, stdin);
+               return 1;
+       default:
+               break;
+       }
+
+       switch (type) {
+       case S_INT:
+       case S_HEX:
+       case S_STRING:
+               printf("%s\n", def);
+               return 1;
+       default:
+               ;
+       }
+       printf("%s", line);
+       return 1;
+}
+
+static int conf_string(struct menu *menu)
+{
+       struct symbol *sym = menu->sym;
+       const char *def;
+
+       while (1) {
+               printf("%*s%s ", indent - 1, "", _(menu->prompt->text));
+               printf("(%s) ", sym->name);
+               def = sym_get_string_value(sym);
+               if (sym_get_string_value(sym))
+                       printf("[%s] ", def);
+               if (!conf_askvalue(sym, def))
+                       return 0;
+               switch (line[0]) {
+               case '\n':
+                       break;
+               case '?':
+                       /* print help */
+                       if (line[1] == '\n') {
+                               print_help(menu);
+                               def = NULL;
+                               break;
+                       }
+               default:
+                       line[strlen(line)-1] = 0;
+                       def = line;
+               }
+               if (def && sym_set_string_value(sym, def))
+                       return 0;
+       }
+}
+
+static int conf_sym(struct menu *menu)
+{
+       struct symbol *sym = menu->sym;
+       int type;
+       tristate oldval, newval;
+
+       while (1) {
+               printf("%*s%s ", indent - 1, "", _(menu->prompt->text));
+               if (sym->name)
+                       printf("(%s) ", sym->name);
+               type = sym_get_type(sym);
+               putchar('[');
+               oldval = sym_get_tristate_value(sym);
+               switch (oldval) {
+               case no:
+                       putchar('N');
+                       break;
+               case mod:
+                       putchar('M');
+                       break;
+               case yes:
+                       putchar('Y');
+                       break;
+               }
+               if (oldval != no && sym_tristate_within_range(sym, no))
+                       printf("/n");
+               if (oldval != mod && sym_tristate_within_range(sym, mod))
+                       printf("/m");
+               if (oldval != yes && sym_tristate_within_range(sym, yes))
+                       printf("/y");
+               if (menu_has_help(menu))
+                       printf("/?");
+               printf("] ");
+               if (!conf_askvalue(sym, sym_get_string_value(sym)))
+                       return 0;
+               strip(line);
+
+               switch (line[0]) {
+               case 'n':
+               case 'N':
+                       newval = no;
+                       if (!line[1] || !strcmp(&line[1], "o"))
+                               break;
+                       continue;
+               case 'm':
+               case 'M':
+                       newval = mod;
+                       if (!line[1])
+                               break;
+                       continue;
+               case 'y':
+               case 'Y':
+                       newval = yes;
+                       if (!line[1] || !strcmp(&line[1], "es"))
+                               break;
+                       continue;
+               case 0:
+                       newval = oldval;
+                       break;
+               case '?':
+                       goto help;
+               default:
+                       continue;
+               }
+               if (sym_set_tristate_value(sym, newval))
+                       return 0;
+help:
+               print_help(menu);
+       }
+}
+
+static int conf_choice(struct menu *menu)
+{
+       struct symbol *sym, *def_sym;
+       struct menu *child;
+       int type;
+       bool is_new;
+
+       sym = menu->sym;
+       type = sym_get_type(sym);
+       is_new = !sym_has_value(sym);
+       if (sym_is_changable(sym)) {
+               conf_sym(menu);
+               sym_calc_value(sym);
+               switch (sym_get_tristate_value(sym)) {
+               case no:
+                       return 1;
+               case mod:
+                       return 0;
+               case yes:
+                       break;
+               }
+       } else {
+               switch (sym_get_tristate_value(sym)) {
+               case no:
+                       return 1;
+               case mod:
+                       printf("%*s%s\n", indent - 1, "", _(menu_get_prompt(menu)));
+                       return 0;
+               case yes:
+                       break;
+               }
+       }
+
+       while (1) {
+               int cnt, def;
+
+               printf("%*s%s\n", indent - 1, "", _(menu_get_prompt(menu)));
+               def_sym = sym_get_choice_value(sym);
+               cnt = def = 0;
+               line[0] = 0;
+               for (child = menu->list; child; child = child->next) {
+                       if (!menu_is_visible(child))
+                               continue;
+                       if (!child->sym) {
+                               printf("%*c %s\n", indent, '*', _(menu_get_prompt(child)));
+                               continue;
+                       }
+                       cnt++;
+                       if (child->sym == def_sym) {
+                               def = cnt;
+                               printf("%*c", indent, '>');
+                       } else
+                               printf("%*c", indent, ' ');
+                       printf(" %d. %s", cnt, _(menu_get_prompt(child)));
+                       if (child->sym->name)
+                               printf(" (%s)", child->sym->name);
+                       if (!sym_has_value(child->sym))
+                               printf(_(" (NEW)"));
+                       printf("\n");
+               }
+               printf(_("%*schoice"), indent - 1, "");
+               if (cnt == 1) {
+                       printf("[1]: 1\n");
+                       goto conf_childs;
+               }
+               printf("[1-%d", cnt);
+               if (menu_has_help(menu))
+                       printf("?");
+               printf("]: ");
+               switch (input_mode) {
+               case ask_new:
+               case ask_silent:
+                       if (!is_new) {
+                               cnt = def;
+                               printf("%d\n", cnt);
+                               break;
+                       }
+                       check_stdin();
+               case ask_all:
+                       fflush(stdout);
+                       fgets(line, 128, stdin);
+                       strip(line);
+                       if (line[0] == '?') {
+                               print_help(menu);
+                               continue;
+                       }
+                       if (!line[0])
+                               cnt = def;
+                       else if (isdigit(line[0]))
+                               cnt = atoi(line);
+                       else
+                               continue;
+                       break;
+               default:
+                       break;
+               }
+
+conf_childs:
+               for (child = menu->list; child; child = child->next) {
+                       if (!child->sym || !menu_is_visible(child))
+                               continue;
+                       if (!--cnt)
+                               break;
+               }
+               if (!child)
+                       continue;
+               if (line[strlen(line) - 1] == '?') {
+                       print_help(child);
+                       continue;
+               }
+               sym_set_choice_value(sym, child->sym);
+               for (child = child->list; child; child = child->next) {
+                       indent += 2;
+                       conf(child);
+                       indent -= 2;
+               }
+               return 1;
+       }
+}
+
+static void conf(struct menu *menu)
+{
+       struct symbol *sym;
+       struct property *prop;
+       struct menu *child;
+
+       if (!menu_is_visible(menu))
+               return;
+
+       sym = menu->sym;
+       prop = menu->prompt;
+       if (prop) {
+               const char *prompt;
+
+               switch (prop->type) {
+               case P_MENU:
+                       if (input_mode == ask_silent && rootEntry != menu) {
+                               check_conf(menu);
+                               return;
+                       }
+               case P_COMMENT:
+                       prompt = menu_get_prompt(menu);
+                       if (prompt)
+                               printf("%*c\n%*c %s\n%*c\n",
+                                       indent, '*',
+                                       indent, '*', _(prompt),
+                                       indent, '*');
+               default:
+                       ;
+               }
+       }
+
+       if (!sym)
+               goto conf_childs;
+
+       if (sym_is_choice(sym)) {
+               conf_choice(menu);
+               if (sym->curr.tri != mod)
+                       return;
+               goto conf_childs;
+       }
+
+       switch (sym->type) {
+       case S_INT:
+       case S_HEX:
+       case S_STRING:
+               conf_string(menu);
+               break;
+       default:
+               conf_sym(menu);
+               break;
+       }
+
+conf_childs:
+       if (sym)
+               indent += 2;
+       for (child = menu->list; child; child = child->next)
+               conf(child);
+       if (sym)
+               indent -= 2;
+}
+
+static void check_conf(struct menu *menu)
+{
+       struct symbol *sym;
+       struct menu *child;
+
+       if (!menu_is_visible(menu))
+               return;
+
+       sym = menu->sym;
+       if (sym && !sym_has_value(sym)) {
+               if (sym_is_changable(sym) ||
+                   (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes)) {
+                       if (!conf_cnt++)
+                               printf(_("*\n* Restart config...\n*\n"));
+                       rootEntry = menu_get_parent_menu(menu);
+                       conf(rootEntry);
+               }
+       }
+
+       for (child = menu->list; child; child = child->next)
+               check_conf(child);
+}
+
+int main(int ac, char **av)
+{
+       int opt;
+       const char *name;
+       struct stat tmpstat;
+
+       setlocale(LC_ALL, "");
+       bindtextdomain(PACKAGE, LOCALEDIR);
+       textdomain(PACKAGE);
+
+       while ((opt = getopt(ac, av, "sdD:nmyrh")) != -1) {
+               switch (opt) {
+               case 's':
+                       input_mode = ask_silent;
+                       break;
+               case 'd':
+                       input_mode = set_default;
+                       break;
+               case 'D':
+                       input_mode = set_default;
+                       defconfig_file = optarg;
+                       break;
+               case 'n':
+                       input_mode = set_no;
+                       break;
+               case 'm':
+                       input_mode = set_mod;
+                       break;
+               case 'y':
+                       input_mode = set_yes;
+                       break;
+               case 'r':
+               {
+                       struct timeval now;
+                       unsigned int seed;
+
+                       /*
+                        * Use microseconds derived seed,
+                        * compensate for systems where it may be zero
+                        */
+                       gettimeofday(&now, NULL);
+
+                       seed = (unsigned int)((now.tv_sec + 1) * (now.tv_usec + 1));
+                       srand(seed);
+
+                       input_mode = set_random;
+                       break;
+               }
+               case 'h':
+                       printf(_("Usage: %s [sdD] [nmyrh] Kconfig-file\n"), av[0]);
+                       exit(0);
+                       break;
+               default:
+                       fprintf(stderr, _("Usage: %s [sdD] [nmyrh] Kconfig-file\n"), av[0]);
+                       exit(1);
+               }
+       }
+       if (ac == optind) {
+               printf(_("%s: Kconfig file missing\n"), av[0]);
+               exit(1);
+       }
+       name = av[optind];
+       conf_parse(name);
+
+       switch (input_mode) {
+       case set_default:
+               if (!defconfig_file)
+                       defconfig_file = conf_get_default_confname();
+               if (conf_read(defconfig_file)) {
+                       printf(_("***\n"
+                               "*** Can't find default configuration \"%s\"!\n"
+                               "***\n"), defconfig_file);
+                       exit(1);
+               }
+               break;
+       case ask_silent:
+       case ask_all:
+       case ask_new:
+               conf_read(NULL);
+               break;
+       case set_no:
+       case set_mod:
+       case set_yes:
+       case set_random:
+               name = getenv("KCONFIG_ALLCONFIG");
+               if (name && !stat(name, &tmpstat)) {
+                       conf_read_simple(name, S_DEF_USER);
+                       break;
+               }
+               switch (input_mode) {
+               case set_no:
+                       name = "allno.config";
+                       break;
+               case set_mod:
+                       name = "allmod.config";
+                       break;
+               case set_yes:
+                       name = "allyes.config";
+                       break;
+               case set_random:
+                       name = "allrandom.config";
+                       break;
+               default:
+                       break;
+               }
+               if (!stat(name, &tmpstat))
+                       conf_read_simple(name, S_DEF_USER);
+               else if (!stat("all.config", &tmpstat))
+                       conf_read_simple("all.config", S_DEF_USER);
+               break;
+       default:
+               break;
+       }
+
+       if (conf_get_changed()) {
+               name = getenv("KCONFIG_NOSILENTUPDATE");
+               if (name && *name) {
+                       fprintf(stderr,
+                               _("\n*** firmware configuration requires explicit update.\n\n"));
+                       return 1;
+               }
+       }
+       valid_stdin = isatty(0) && isatty(1) && isatty(2);
+
+       switch (input_mode) {
+       case set_no:
+               conf_set_all_new_symbols(def_no);
+               break;
+       case set_yes:
+               conf_set_all_new_symbols(def_yes);
+               break;
+       case set_mod:
+               conf_set_all_new_symbols(def_mod);
+               break;
+       case set_random:
+               conf_set_all_new_symbols(def_random);
+               break;
+       case set_default:
+               conf_set_all_new_symbols(def_default);
+               break;
+       case ask_new:
+       case ask_all:
+               rootEntry = &rootmenu;
+               conf(&rootmenu);
+               input_mode = ask_silent;
+               /* fall through */
+       case ask_silent:
+               /* Update until a loop caused no more changes */
+               do {
+                       conf_cnt = 0;
+                       check_conf(&rootmenu);
+               } while (conf_cnt);
+               break;
+       }
+
+       if (conf_get_changed() && conf_write(NULL)) {
+               fprintf(stderr, _("\n*** Error during writing of the firmware configuration.\n\n"));
+               exit(1);
+       }
+       if (conf_write_autoconf()) {
+               fprintf(stderr, _("\n*** Error during update of the firmware configuration.\n\n"));
+               return 1;
+       }
+       return 0;
+}
diff --git a/config/confdata.c b/config/confdata.c
new file mode 100644 (file)
index 0000000..8c64399
--- /dev/null
@@ -0,0 +1,942 @@
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#include <sys/stat.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#define LKC_DIRECT_LINK
+#include "lkc.h"
+
+static void conf_warning(const char *fmt, ...)
+       __attribute__ ((format (printf, 1, 2)));
+
+static const char *conf_filename;
+static int conf_lineno, conf_warnings, conf_unsaved;
+
+const char conf_defname[] = "include/generated/defconfig";
+
+static void conf_warning(const char *fmt, ...)
+{
+       va_list ap;
+       va_start(ap, fmt);
+       fprintf(stderr, "%s:%d:warning: ", conf_filename, conf_lineno);
+       vfprintf(stderr, fmt, ap);
+       fprintf(stderr, "\n");
+       va_end(ap);
+       conf_warnings++;
+}
+
+const char *conf_get_configname(void)
+{
+       char *name = getenv("KCONFIG_CONFIG");
+
+       return name ? name : ".config";
+}
+
+const char *conf_get_autoconfig_name(void)
+{
+       char *name = getenv("KCONFIG_AUTOCONFIG");
+
+       return name ? name : "include/generated/auto.conf";
+}
+
+static char *conf_expand_value(const char *in)
+{
+       struct symbol *sym;
+       const char *src;
+       static char res_value[SYMBOL_MAXLENGTH];
+       char *dst, name[SYMBOL_MAXLENGTH];
+
+       res_value[0] = 0;
+       dst = name;
+       while ((src = strchr(in, '$'))) {
+               strncat(res_value, in, src - in);
+               src++;
+               dst = name;
+               while (isalnum(*src) || *src == '_')
+                       *dst++ = *src++;
+               *dst = 0;
+               sym = sym_lookup(name, 0);
+               sym_calc_value(sym);
+               strcat(res_value, sym_get_string_value(sym));
+               in = src;
+       }
+       strcat(res_value, in);
+
+       return res_value;
+}
+
+char *conf_get_default_confname(void)
+{
+       struct stat buf;
+       static char fullname[PATH_MAX+1];
+       char *env, *name;
+
+       name = conf_expand_value(conf_defname);
+       env = getenv(SRCTREE);
+       if (env) {
+               sprintf(fullname, "%s/%s", env, name);
+               if (!stat(fullname, &buf))
+                       return fullname;
+       }
+       return name;
+}
+
+static int conf_set_sym_val(struct symbol *sym, int def, int def_flags, char *p)
+{
+       char *p2;
+
+       switch (sym->type) {
+       case S_TRISTATE:
+               if (p[0] == 'm') {
+                       sym->def[def].tri = mod;
+                       sym->flags |= def_flags;
+                       break;
+               }
+       case S_BOOLEAN:
+               if (p[0] == 'y') {
+                       sym->def[def].tri = yes;
+                       sym->flags |= def_flags;
+                       break;
+               }
+               if (p[0] == 'n') {
+                       sym->def[def].tri = no;
+                       sym->flags |= def_flags;
+                       break;
+               }
+               conf_warning("symbol value '%s' invalid for %s", p, sym->name);
+               break;
+       case S_OTHER:
+               if (*p != '"') {
+                       for (p2 = p; *p2 && !isspace(*p2); p2++)
+                               ;
+                       sym->type = S_STRING;
+                       goto done;
+               }
+       case S_STRING:
+               if (*p++ != '"')
+                       break;
+               for (p2 = p; (p2 = strpbrk(p2, "\"\\")); p2++) {
+                       if (*p2 == '"') {
+                               *p2 = 0;
+                               break;
+                       }
+                       memmove(p2, p2 + 1, strlen(p2));
+               }
+               if (!p2) {
+                       conf_warning("invalid string found");
+                       return 1;
+               }
+       case S_INT:
+       case S_HEX:
+done:
+               if (sym_string_valid(sym, p)) {
+                       sym->def[def].val = strdup(p);
+                       sym->flags |= def_flags;
+               } else {
+                       conf_warning("symbol value '%s' invalid for %s", p, sym->name);
+                       return 1;
+               }
+               break;
+       default:
+               ;
+       }
+       return 0;
+}
+
+int conf_read_simple(const char *name, int def)
+{
+       FILE *in = NULL;
+       char line[1024];
+       char *p, *p2;
+       struct symbol *sym;
+       int i, def_flags;
+
+       if (name) {
+               in = zconf_fopen(name);
+       } else {
+               struct property *prop;
+
+               name = conf_get_configname();
+               in = zconf_fopen(name);
+               if (in)
+                       goto load;
+               sym_add_change_count(1);
+               if (!sym_defconfig_list)
+                       return 1;
+
+               for_all_defaults(sym_defconfig_list, prop) {
+                       if (expr_calc_value(prop->visible.expr) == no ||
+                           prop->expr->type != E_SYMBOL)
+                               continue;
+                       name = conf_expand_value(prop->expr->left.sym->name);
+                       in = zconf_fopen(name);
+                       if (in) {
+                               printf(_("#\n"
+                                        "# using defaults found in %s\n"
+                                        "#\n"), name);
+                               goto load;
+                       }
+               }
+       }
+       if (!in)
+               return 1;
+
+load:
+       conf_filename = name;
+       conf_lineno = 0;
+       conf_warnings = 0;
+       conf_unsaved = 0;
+
+       def_flags = SYMBOL_DEF << def;
+       for_all_symbols(i, sym) {
+               sym->flags |= SYMBOL_CHANGED;
+               sym->flags &= ~(def_flags|SYMBOL_VALID);
+               if (sym_is_choice(sym))
+                       sym->flags |= def_flags;
+               switch (sym->type) {
+               case S_INT:
+               case S_HEX:
+               case S_STRING:
+                       if (sym->def[def].val)
+                               free(sym->def[def].val);
+               default:
+                       sym->def[def].val = NULL;
+                       sym->def[def].tri = no;
+               }
+       }
+
+       while (fgets(line, sizeof(line), in)) {
+               conf_lineno++;
+               sym = NULL;
+               switch (line[0]) {
+               case '#':
+                       if (memcmp(line + 2, "CONFIG_", 7))
+                               continue;
+                       p = strchr(line + 9, ' ');
+                       if (!p)
+                               continue;
+                       *p++ = 0;
+                       if (strncmp(p, "is not set", 10))
+                               continue;
+                       if (def == S_DEF_USER) {
+                               sym = sym_find(line + 9);
+                               if (!sym) {
+                                       sym_add_change_count(1);
+                                       break;
+                               }
+                       } else {
+                               sym = sym_lookup(line + 9, 0);
+                               if (sym->type == S_UNKNOWN)
+                                       sym->type = S_BOOLEAN;
+                       }
+                       if (sym->flags & def_flags)
+                               conf_warning("override: reassigning to symbol %s", sym->name);
+
+                       switch (sym->type) {
+                       case S_BOOLEAN:
+                       case S_TRISTATE:
+                               sym->def[def].tri = no;
+                               sym->flags |= def_flags;
+                               break;
+                       default:
+                               ;
+                       }
+                       break;
+               case 'C':
+                       if (memcmp(line, "CONFIG_", 7)) {
+                               conf_warning("unexpected data");
+                               continue;
+                       }
+                       p = strchr(line + 7, '=');
+                       if (!p)
+                               continue;
+                       *p++ = 0;
+                       p2 = strchr(p, '\n');
+                       if (p2) {
+                               *p2-- = 0;
+                               if (*p2 == '\r')
+                                       *p2 = 0;
+                       }
+                       if (def == S_DEF_USER) {
+                               sym = sym_find(line + 7);
+                               if (!sym) {
+                                       sym_add_change_count(1);
+                                       break;
+                               }
+                       } else {
+                               sym = sym_lookup(line + 7, 0);
+                               if (sym->type == S_UNKNOWN)
+                                       sym->type = S_OTHER;
+                       }
+                       if (sym->flags & def_flags)
+                               conf_warning("override: reassigning to symbol %s", sym->name);
+
+                       if (conf_set_sym_val(sym, def, def_flags, p))
+                               continue;
+             &