build/patch/kernel/archive/rk322x-6.2/wifi-4004-esp8089-kernel-driver.patch

10917 lines
297 KiB
Diff
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

From 2d6165af6e9d5ed5026cdf250536c0a00d84fd75 Mon Sep 17 00:00:00 2001
From: Paolo Sabatino <paolo.sabatino@gmail.com>
Date: Sat, 1 Oct 2022 12:43:53 +0000
Subject: [PATCH] add esp8089 kernel driver
---
drivers/net/wireless/Kconfig | 1 +
drivers/net/wireless/Makefile | 1 +
drivers/net/wireless/esp8089/.gitignore | 7 +
drivers/net/wireless/esp8089/Kconfig | 13 +
drivers/net/wireless/esp8089/LICENSE | 340 +++
drivers/net/wireless/esp8089/Makefile | 7 +
drivers/net/wireless/esp8089/Makefile.old | 99 +
drivers/net/wireless/esp8089/README.md | 31 +
drivers/net/wireless/esp8089/esp_ctrl.c | 801 ++++++
drivers/net/wireless/esp8089/esp_ctrl.h | 58 +
drivers/net/wireless/esp8089/esp_debug.c | 297 ++
drivers/net/wireless/esp8089/esp_debug.h | 101 +
drivers/net/wireless/esp8089/esp_ext.c | 542 ++++
drivers/net/wireless/esp8089/esp_ext.h | 100 +
drivers/net/wireless/esp8089/esp_file.c | 258 ++
drivers/net/wireless/esp8089/esp_file.h | 43 +
drivers/net/wireless/esp8089/esp_init_data.h | 7 +
drivers/net/wireless/esp8089/esp_io.c | 639 +++++
drivers/net/wireless/esp8089/esp_mac80211.c | 1727 ++++++++++++
drivers/net/wireless/esp8089/esp_mac80211.h | 38 +
drivers/net/wireless/esp8089/esp_main.c | 263 ++
drivers/net/wireless/esp8089/esp_path.h | 6 +
drivers/net/wireless/esp8089/esp_pub.h | 222 ++
drivers/net/wireless/esp8089/esp_sif.h | 207 ++
drivers/net/wireless/esp8089/esp_sip.c | 2418 +++++++++++++++++
drivers/net/wireless/esp8089/esp_sip.h | 171 ++
drivers/net/wireless/esp8089/esp_utils.c | 262 ++
drivers/net/wireless/esp8089/esp_utils.h | 41 +
drivers/net/wireless/esp8089/esp_version.h | 1 +
drivers/net/wireless/esp8089/esp_wl.h | 63 +
drivers/net/wireless/esp8089/esp_wmac.h | 92 +
.../wireless/esp8089/firmware/LICENSE-2.0.txt | 203 ++
drivers/net/wireless/esp8089/sdio_sif_esp.c | 811 ++++++
drivers/net/wireless/esp8089/sip2_common.h | 475 ++++
.../net/wireless/esp8089/slc_host_register.h | 271 ++
35 files changed, 10616 insertions(+)
create mode 100644 drivers/net/wireless/esp8089/.gitignore
create mode 100644 drivers/net/wireless/esp8089/Kconfig
create mode 100644 drivers/net/wireless/esp8089/LICENSE
create mode 100644 drivers/net/wireless/esp8089/Makefile
create mode 100644 drivers/net/wireless/esp8089/Makefile.old
create mode 100644 drivers/net/wireless/esp8089/README.md
create mode 100644 drivers/net/wireless/esp8089/esp_ctrl.c
create mode 100644 drivers/net/wireless/esp8089/esp_ctrl.h
create mode 100644 drivers/net/wireless/esp8089/esp_debug.c
create mode 100644 drivers/net/wireless/esp8089/esp_debug.h
create mode 100644 drivers/net/wireless/esp8089/esp_ext.c
create mode 100644 drivers/net/wireless/esp8089/esp_ext.h
create mode 100644 drivers/net/wireless/esp8089/esp_file.c
create mode 100644 drivers/net/wireless/esp8089/esp_file.h
create mode 100644 drivers/net/wireless/esp8089/esp_init_data.h
create mode 100644 drivers/net/wireless/esp8089/esp_io.c
create mode 100644 drivers/net/wireless/esp8089/esp_mac80211.c
create mode 100644 drivers/net/wireless/esp8089/esp_mac80211.h
create mode 100644 drivers/net/wireless/esp8089/esp_main.c
create mode 100644 drivers/net/wireless/esp8089/esp_path.h
create mode 100644 drivers/net/wireless/esp8089/esp_pub.h
create mode 100644 drivers/net/wireless/esp8089/esp_sif.h
create mode 100644 drivers/net/wireless/esp8089/esp_sip.c
create mode 100644 drivers/net/wireless/esp8089/esp_sip.h
create mode 100644 drivers/net/wireless/esp8089/esp_utils.c
create mode 100644 drivers/net/wireless/esp8089/esp_utils.h
create mode 100644 drivers/net/wireless/esp8089/esp_version.h
create mode 100644 drivers/net/wireless/esp8089/esp_wl.h
create mode 100644 drivers/net/wireless/esp8089/esp_wmac.h
create mode 100644 drivers/net/wireless/esp8089/firmware/LICENSE-2.0.txt
create mode 100644 drivers/net/wireless/esp8089/sdio_sif_esp.c
create mode 100644 drivers/net/wireless/esp8089/sip2_common.h
create mode 100644 drivers/net/wireless/esp8089/slc_host_register.h
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index cb1c15012dd0..de5e37846397 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -37,6 +37,7 @@ source "drivers/net/wireless/st/Kconfig"
source "drivers/net/wireless/ti/Kconfig"
source "drivers/net/wireless/zydas/Kconfig"
source "drivers/net/wireless/quantenna/Kconfig"
+source "drivers/net/wireless/esp8089/Kconfig"
config PCMCIA_RAYCS
tristate "Aviator/Raytheon 2.4GHz wireless support"
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index a61cf6c90343..92ffd2cef51c 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -22,6 +22,7 @@ obj-$(CONFIG_WLAN_VENDOR_SILABS) += silabs/
obj-$(CONFIG_WLAN_VENDOR_ST) += st/
obj-$(CONFIG_WLAN_VENDOR_TI) += ti/
obj-$(CONFIG_WLAN_VENDOR_ZYDAS) += zydas/
+obj-$(CONFIG_ESP8089) += esp8089/
# 16-bit wireless PCMCIA client drivers
obj-$(CONFIG_PCMCIA_RAYCS) += ray_cs.o
diff --git a/drivers/net/wireless/esp8089/.gitignore b/drivers/net/wireless/esp8089/.gitignore
new file mode 100644
index 000000000000..eae6529085d0
--- /dev/null
+++ b/drivers/net/wireless/esp8089/.gitignore
@@ -0,0 +1,7 @@
+*.cmd
+*.o
+Module.symvers
+modules.order
+.tmp_versions
+*.ko
+*.mod.c
diff --git a/drivers/net/wireless/esp8089/Kconfig b/drivers/net/wireless/esp8089/Kconfig
new file mode 100644
index 000000000000..8db1fc54712d
--- /dev/null
+++ b/drivers/net/wireless/esp8089/Kconfig
@@ -0,0 +1,13 @@
+config ESP8089
+ tristate "Espressif ESP8089 SDIO WiFi"
+ depends on MAC80211
+ help
+ ESP8089 is a low-budget 2.4GHz WiFi chip by Espressif, used in many
+ cheap tablets with Allwinner or Rockchip SoC
+
+config ESP8089_DEBUG_FS
+ bool "Enable DebugFS support for ESP8089"
+ depends on ESP8089
+ default y
+ help
+ DebugFS support for ESP8089
diff --git a/drivers/net/wireless/esp8089/LICENSE b/drivers/net/wireless/esp8089/LICENSE
new file mode 100644
index 000000000000..d6a93266f748
--- /dev/null
+++ b/drivers/net/wireless/esp8089/LICENSE
@@ -0,0 +1,340 @@
+GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc., <http://fsf.org/>
+ 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.
+
+ {description}
+ Copyright (C) {year} {fullname}
+
+ 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/drivers/net/wireless/esp8089/Makefile b/drivers/net/wireless/esp8089/Makefile
new file mode 100644
index 000000000000..36decfd20ecd
--- /dev/null
+++ b/drivers/net/wireless/esp8089/Makefile
@@ -0,0 +1,7 @@
+MODULE_NAME = esp8089
+
+$(MODULE_NAME)-y := esp_debug.o sdio_sif_esp.o esp_io.o \
+ esp_file.o esp_main.o esp_sip.o esp_ext.o esp_ctrl.o \
+ esp_mac80211.o esp_debug.o esp_utils.o
+
+obj-$(CONFIG_ESP8089) := esp8089.o
diff --git a/drivers/net/wireless/esp8089/Makefile.old b/drivers/net/wireless/esp8089/Makefile.old
new file mode 100644
index 000000000000..b7b1a47b159c
--- /dev/null
+++ b/drivers/net/wireless/esp8089/Makefile.old
@@ -0,0 +1,99 @@
+MODNAME = esp8089
+
+# By default, we try to compile the modules for the currently running
+# kernel. But it's the first approximation, as we will re-read the
+# version from the kernel sources.
+KVERS_UNAME ?= $(shell uname -r)
+
+# KBUILD is the path to the Linux kernel build tree. It is usually the
+# same as the kernel source tree, except when the kernel was compiled in
+# a separate directory.
+KBUILD ?= $(shell readlink -f /lib/modules/$(KVERS_UNAME)/build)
+
+ifeq (,$(KBUILD))
+$(error Kernel build tree not found - please set KBUILD to configured kernel)
+endif
+
+KCONFIG := $(KBUILD)/.config
+ifeq (,$(wildcard $(KCONFIG)))
+$(error No .config found in $(KBUILD), please set KBUILD to configured kernel)
+endif
+
+ifneq (,$(wildcard $(KBUILD)/include/linux/version.h))
+ifneq (,$(wildcard $(KBUILD)/include/generated/uapi/linux/version.h))
+$(error Multiple copies of version.h found, please clean your build tree)
+endif
+endif
+
+# Kernel Makefile doesn't always know the exact kernel version, so we
+# get it from the kernel headers instead and pass it to make.
+VERSION_H := $(KBUILD)/include/generated/utsrelease.h
+ifeq (,$(wildcard $(VERSION_H)))
+VERSION_H := $(KBUILD)/include/linux/utsrelease.h
+endif
+ifeq (,$(wildcard $(VERSION_H)))
+VERSION_H := $(KBUILD)/include/linux/version.h
+endif
+ifeq (,$(wildcard $(VERSION_H)))
+$(error Please run 'make modules_prepare' in $(KBUILD))
+endif
+
+KVERS := $(shell sed -ne 's/"//g;s/^\#define UTS_RELEASE //p' $(VERSION_H))
+
+ifeq (,$(KVERS))
+$(error Cannot find UTS_RELEASE in $(VERSION_H), please report)
+endif
+
+INST_DIR = /lib/modules/$(KVERS)/misc
+
+SRC_DIR=$(shell pwd)
+
+include $(KCONFIG)
+
+EXTRA_CFLAGS += -DCONFIG_ESP8089_DEBUG_FS
+
+OBJS = esp_debug.o sdio_sif_esp.o esp_io.o \
+ esp_file.o esp_main.o esp_sip.o esp_ext.o esp_ctrl.o \
+ esp_mac80211.o esp_debug.o esp_utils.o esp_pm.o
+
+all: config_check modules
+
+MODULE := $(MODNAME).ko
+obj-m := $(MODNAME).o
+
+$(MODNAME)-objs := $(OBJS)
+
+config_check:
+ @if [ -z "$(CONFIG_WIRELESS_EXT)$(CONFIG_NET_RADIO)" ]; then \
+ echo; echo; \
+ echo "*** WARNING: This kernel lacks wireless extensions."; \
+ echo "Wireless drivers will not work properly."; \
+ echo; echo; \
+ fi
+
+modules:
+ $(MAKE) -C $(KBUILD) M=$(SRC_DIR)
+
+$(MODULE):
+ $(MAKE) modules
+
+clean:
+ rm -f *.o *.ko .*.cmd *.mod.c *.symvers modules.order
+ rm -rf .tmp_versions
+
+install: config_check $(MODULE)
+ @/sbin/modinfo $(MODULE) | grep -q "^vermagic: *$(KVERS) " || \
+ { echo "$(MODULE)" is not for Linux $(KVERS); exit 1; }
+ mkdir -p -m 755 $(DESTDIR)$(INST_DIR)
+ install -m 0644 $(MODULE) $(DESTDIR)$(INST_DIR)
+ifndef DESTDIR
+ -/sbin/depmod -a $(KVERS)
+endif
+
+uninstall:
+ rm -f $(DESTDIR)$(INST_DIR)/$(MODULE)
+ifndef DESTDIR
+ -/sbin/depmod -a $(KVERS)
+endif
+
+.PHONY: all modules clean install config_check
diff --git a/drivers/net/wireless/esp8089/README.md b/drivers/net/wireless/esp8089/README.md
new file mode 100644
index 000000000000..56b40db272f3
--- /dev/null
+++ b/drivers/net/wireless/esp8089/README.md
@@ -0,0 +1,31 @@
+esp8089
+======
+
+ESP8089 Linux driver
+
+v1.9 imported from the Rockchip Linux kernel github repo
+
+Modified to build as a standalone module for SDIO devices.
+
+
+
+
+Building:
+
+ make
+
+Using:
+
+Must load mac80211.ko first if not baked in.
+
+ sudo modprobe esp8089.ko
+
+If you get a wlan interface, but scanning shows no networks try using:
+
+ sudo modprobe esp8089.ko config=crystal_26M_en=1
+
+or:
+
+ sudo modprobe esp8089.ko config=crystal_26M_en=2
+
+To load the module.
diff --git a/drivers/net/wireless/esp8089/esp_ctrl.c b/drivers/net/wireless/esp8089/esp_ctrl.c
new file mode 100644
index 000000000000..ee64fab67a3b
--- /dev/null
+++ b/drivers/net/wireless/esp8089/esp_ctrl.c
@@ -0,0 +1,801 @@
+/*
+ * Copyright (c) 2009 - 2014 Espressif System.
+ *
+ * SIP ctrl packet parse and pack
+ *
+ * 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.
+ */
+
+#include <net/mac80211.h>
+#include <net/cfg80211.h>
+#include <linux/skbuff.h>
+#include <linux/bitops.h>
+#include <linux/firmware.h>
+
+#include "esp_pub.h"
+#include "esp_sip.h"
+#include "esp_ctrl.h"
+#include "esp_sif.h"
+#include "esp_debug.h"
+#include "esp_wmac.h"
+#include "esp_utils.h"
+#include "esp_wl.h"
+#include "esp_file.h"
+#include "esp_path.h"
+#ifdef TEST_MODE
+#include "testmode.h"
+#endif /* TEST_MODE */
+#include "esp_version.h"
+
+extern struct completion *gl_bootup_cplx;
+
+static void esp_tx_ba_session_op(struct esp_sip *sip,
+ struct esp_node *node,
+ trc_ampdu_state_t state, u8 tid)
+{
+ struct esp_tx_tid *txtid;
+
+ txtid = &node->tid[tid];
+ if (state == TRC_TX_AMPDU_STOPPED) {
+ if (txtid->state == ESP_TID_STATE_OPERATIONAL) {
+ esp_dbg(ESP_DBG_TXAMPDU,
+ "%s tid %d TXAMPDU GOT STOP EVT\n",
+ __func__, tid);
+
+ spin_lock_bh(&sip->epub->tx_ampdu_lock);
+ txtid->state = ESP_TID_STATE_WAIT_STOP;
+ spin_unlock_bh(&sip->epub->tx_ampdu_lock);
+ ieee80211_stop_tx_ba_session(node->sta, (u16) tid);
+ } else {
+ esp_dbg(ESP_DBG_TXAMPDU,
+ "%s tid %d TXAMPDU GOT STOP EVT IN WRONG STATE %d\n",
+ __func__, tid, txtid->state);
+ }
+ } else if (state == TRC_TX_AMPDU_OPERATIONAL) {
+ if (txtid->state == ESP_TID_STATE_STOP) {
+ esp_dbg(ESP_DBG_TXAMPDU,
+ "%s tid %d TXAMPDU GOT OPERATIONAL\n",
+ __func__, tid);
+
+ spin_lock_bh(&sip->epub->tx_ampdu_lock);
+ txtid->state = ESP_TID_STATE_TRIGGER;
+ spin_unlock_bh(&sip->epub->tx_ampdu_lock);
+ ieee80211_start_tx_ba_session(node->sta, (u16) tid,
+ 0);
+
+ } else if (txtid->state == ESP_TID_STATE_OPERATIONAL) {
+ sip_send_ampdu_action(sip->epub,
+ SIP_AMPDU_TX_OPERATIONAL,
+ node->sta->addr, tid,
+ node->ifidx, 0);
+ } else {
+ esp_dbg(ESP_DBG_TXAMPDU,
+ "%s tid %d TXAMPDU GOT OPERATIONAL EVT IN WRONG STATE %d\n",
+ __func__, tid, txtid->state);
+ }
+ }
+}
+
+int sip_parse_events(struct esp_sip *sip, u8 * buf)
+{
+ struct sip_hdr *hdr = (struct sip_hdr *) buf;
+
+ switch (hdr->c_evtid) {
+ case SIP_EVT_TARGET_ON:{
+ /* use rx work queue to send... */
+ if (atomic_read(&sip->state) == SIP_PREPARE_BOOT
+ || atomic_read(&sip->state) == SIP_BOOT) {
+ atomic_set(&sip->state, SIP_SEND_INIT);
+ queue_work(sip->epub->esp_wkq,
+ &sip->rx_process_work);
+ } else {
+ esp_dbg(ESP_DBG_ERROR,
+ "%s boot during wrong state %d\n",
+ __func__,
+ atomic_read(&sip->state));
+ }
+ break;
+ }
+
+ case SIP_EVT_BOOTUP:{
+ struct sip_evt_bootup2 *bootup_evt =
+ (struct sip_evt_bootup2 *) (buf +
+ SIP_CTRL_HDR_LEN);
+ if (sip->rawbuf)
+ kfree(sip->rawbuf);
+
+ sip_post_init(sip, bootup_evt);
+
+ if (gl_bootup_cplx)
+ complete(gl_bootup_cplx);
+
+ break;
+ }
+ case SIP_EVT_RESETTING:{
+ sip->epub->wait_reset = 1;
+ if (gl_bootup_cplx)
+ complete(gl_bootup_cplx);
+ break;
+ }
+ case SIP_EVT_SLEEP:{
+ //atomic_set(&sip->epub->ps.state, ESP_PM_ON);
+ break;
+ }
+ case SIP_EVT_TXIDLE:{
+ //struct sip_evt_txidle *txidle = (struct sip_evt_txidle *)(buf + SIP_CTRL_HDR_LEN);
+ //sip_txdone_clear(sip, txidle->last_seq);
+ break;
+ }
+
+ case SIP_EVT_SCAN_RESULT:{
+ struct sip_evt_scan_report *report =
+ (struct sip_evt_scan_report *) (buf +
+ SIP_CTRL_HDR_LEN);
+ if (atomic_read(&sip->epub->wl.off)) {
+ esp_dbg(ESP_DBG_ERROR,
+ "%s scan result while wlan off\n",
+ __func__);
+ return 0;
+ }
+ sip_scandone_process(sip, report);
+
+ break;
+ }
+
+ case SIP_EVT_ROC:{
+ struct sip_evt_roc *report =
+ (struct sip_evt_roc *) (buf +
+ SIP_CTRL_HDR_LEN);
+ esp_rocdone_process(sip->epub->hw, report);
+ break;
+ }
+
+
+#ifdef ESP_RX_COPYBACK_TEST
+
+ case SIP_EVT_COPYBACK:{
+ u32 len = hdr->len - SIP_CTRL_HDR_LEN;
+
+ esp_dbg(ESP_DBG_TRACE,
+ "%s copyback len %d seq %u\n", __func__,
+ len, hdr->seq);
+
+ memcpy(copyback_buf + copyback_offset,
+ pkt->buf + SIP_CTRL_HDR_LEN, len);
+ copyback_offset += len;
+
+ //show_buf(pkt->buf, 256);
+
+ //how about totlen % 256 == 0??
+ if (hdr->hdr.len < 256) {
+ kfree(copyback_buf);
+ }
+ }
+ break;
+#endif /* ESP_RX_COPYBACK_TEST */
+ case SIP_EVT_CREDIT_RPT:
+ break;
+
+#ifdef TEST_MODE
+ case SIP_EVT_WAKEUP:{
+ u8 check_str[12];
+ struct sip_evt_wakeup *wakeup_evt =
+ (struct sip_evt_wakeup *) (buf +
+ SIP_CTRL_HDR_LEN);
+ sprintf((char *) &check_str, "%d",
+ wakeup_evt->check_data);
+ esp_test_cmd_event(TEST_CMD_WAKEUP,
+ (char *) &check_str);
+ break;
+ }
+
+ case SIP_EVT_DEBUG:{
+ u8 check_str[640];
+ sip_parse_event_debug(sip->epub, buf, check_str);
+ esp_dbg(ESP_DBG_TRACE, "%s", check_str);
+ esp_test_cmd_event(TEST_CMD_DEBUG,
+ (char *) &check_str);
+ break;
+ }
+
+ case SIP_EVT_LOOPBACK:{
+ u8 check_str[12];
+ struct sip_evt_loopback *loopback_evt =
+ (struct sip_evt_loopback *) (buf +
+ SIP_CTRL_HDR_LEN);
+ esp_dbg(ESP_DBG_LOG, "%s loopback len %d seq %u\n",
+ __func__, hdr->len, hdr->seq);
+
+ if (loopback_evt->pack_id != get_loopback_id()) {
+ sprintf((char *) &check_str,
+ "seq id error %d, expect %d",
+ loopback_evt->pack_id,
+ get_loopback_id());
+ esp_test_cmd_event(TEST_CMD_LOOPBACK,
+ (char *) &check_str);
+ }
+
+ if ((loopback_evt->pack_id + 1) <
+ get_loopback_num()) {
+ inc_loopback_id();
+ sip_send_loopback_mblk(sip,
+ loopback_evt->txlen,
+ loopback_evt->rxlen,
+ get_loopback_id());
+ } else {
+ sprintf((char *) &check_str, "test over!");
+ esp_test_cmd_event(TEST_CMD_LOOPBACK,
+ (char *) &check_str);
+ }
+ break;
+ }
+#endif /*TEST_MODE */
+
+ case SIP_EVT_SNPRINTF_TO_HOST:{
+ u8 *p =
+ (buf + sizeof(struct sip_hdr) + sizeof(u16));
+ u16 *len = (u16 *) (buf + sizeof(struct sip_hdr));
+ char test_res_str[560];
+ sprintf(test_res_str,
+ "esp_host:%llx\nesp_target: %.*s",
+ DRIVER_VER, *len, p);
+
+ esp_dbg(ESP_DBG_TRACE, "%s\n", test_res_str);
+ if (*len
+ && sip->epub->sdio_state ==
+ ESP_SDIO_STATE_FIRST_INIT) {
+ char filename[256];
+ if (mod_eagle_path_get() == NULL)
+ sprintf(filename, "%s/%s", FWPATH,
+ "test_results");
+ else
+ sprintf(filename, "%s/%s",
+ mod_eagle_path_get(),
+ "test_results");
+ esp_dbg(ESP_DBG_TRACE,
+ "SNPRINTF TO HOST: %s\n",
+ test_res_str);
+ }
+ break;
+ }
+ case SIP_EVT_TRC_AMPDU:{
+ struct sip_evt_trc_ampdu *ep =
+ (struct sip_evt_trc_ampdu *) (buf +
+ SIP_CTRL_HDR_LEN);
+ struct esp_node *node = NULL;
+ int i = 0;
+
+ if (atomic_read(&sip->epub->wl.off)) {
+ esp_dbg(ESP_DBG_ERROR,
+ "%s scan result while wlan off\n",
+ __func__);
+ return 0;
+ }
+
+ node = esp_get_node_by_addr(sip->epub, ep->addr);
+ if (node == NULL)
+ break;
+ for (i = 0; i < 8; i++) {
+ if (ep->tid & (1 << i)) {
+ esp_tx_ba_session_op(sip, node,
+ ep->state, i);
+ }
+ }
+ break;
+ }
+
+#ifdef TEST_MODE
+ case SIP_EVT_EP:{
+ char *ep = (char *) (buf + SIP_CTRL_HDR_LEN);
+ static int counter = 0;
+
+ esp_dbg(ESP_ATE, "%s EVT_EP \n\n", __func__);
+ if (counter++ < 2) {
+ esp_dbg(ESP_ATE, "ATE: %s \n", ep);
+ }
+
+ esp_test_ate_done_cb(ep);
+
+ break;
+ }
+#endif /*TEST_MODE */
+
+ case SIP_EVT_INIT_EP:{
+ char *ep = (char *) (buf + SIP_CTRL_HDR_LEN);
+ esp_dbg(ESP_ATE, "Phy Init: %s \n", ep);
+ break;
+ }
+
+ case SIP_EVT_NOISEFLOOR:{
+ struct sip_evt_noisefloor *ep =
+ (struct sip_evt_noisefloor *) (buf +
+ SIP_CTRL_HDR_LEN);
+ atomic_set(&sip->noise_floor, ep->noise_floor);
+ break;
+ }
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+#include "esp_init_data.h"
+
+void sip_send_chip_init(struct esp_sip *sip)
+{
+ size_t size = 0;
+ size = sizeof(esp_init_data);
+
+ esp_conf_upload_second(esp_init_data, size);
+
+ atomic_sub(1, &sip->tx_credits);
+
+ sip_send_cmd(sip, SIP_CMD_INIT, size, (void *) esp_init_data);
+
+}
+
+int sip_send_config(struct esp_pub *epub, struct ieee80211_conf *conf)
+{
+ struct sk_buff *skb = NULL;
+ struct sip_cmd_config *configcmd;
+
+ skb =
+ sip_alloc_ctrl_skbuf(epub->sip,
+ sizeof(struct sip_cmd_config) +
+ sizeof(struct sip_hdr), SIP_CMD_CONFIG);
+ if (!skb)
+ return -EINVAL;
+ esp_dbg(ESP_DBG_TRACE, "%s config center freq %d\n", __func__,
+ conf->chandef.chan->center_freq);
+ configcmd =
+ (struct sip_cmd_config *) (skb->data + sizeof(struct sip_hdr));
+ configcmd->center_freq = conf->chandef.chan->center_freq;
+ configcmd->duration = 0;
+ return sip_cmd_enqueue(epub->sip, skb, ENQUEUE_PRIOR_TAIL);
+}
+
+int sip_send_bss_info_update(struct esp_pub *epub, struct esp_vif *evif,
+ u8 * bssid, int assoc)
+{
+ struct sk_buff *skb = NULL;
+ struct sip_cmd_bss_info_update *bsscmd;
+
+ skb =
+ sip_alloc_ctrl_skbuf(epub->sip,
+ sizeof(struct sip_cmd_bss_info_update) +
+ sizeof(struct sip_hdr),
+ SIP_CMD_BSS_INFO_UPDATE);
+ if (!skb)
+ return -EINVAL;
+
+ bsscmd =
+ (struct sip_cmd_bss_info_update *) (skb->data +
+ sizeof(struct sip_hdr));
+ if (assoc == 2) { //hack for softAP mode
+ bsscmd->beacon_int = evif->beacon_interval;
+ } else if (assoc == 1) {
+ set_bit(ESP_WL_FLAG_CONNECT, &epub->wl.flags);
+ } else {
+ clear_bit(ESP_WL_FLAG_CONNECT, &epub->wl.flags);
+ }
+ bsscmd->bssid_no = evif->index;
+ bsscmd->isassoc = assoc;
+ bsscmd->beacon_int = evif->beacon_interval;
+ memcpy(bsscmd->bssid, bssid, ETH_ALEN);
+ return sip_cmd_enqueue(epub->sip, skb, ENQUEUE_PRIOR_TAIL);
+}
+
+int sip_send_wmm_params(struct esp_pub *epub, u8 aci,
+ const struct ieee80211_tx_queue_params *params)
+{
+ struct sk_buff *skb = NULL;
+ struct sip_cmd_set_wmm_params *bsscmd;
+ skb =
+ sip_alloc_ctrl_skbuf(epub->sip,
+ sizeof(struct sip_cmd_set_wmm_params) +
+ sizeof(struct sip_hdr),
+ SIP_CMD_SET_WMM_PARAM);
+ if (!skb)
+ return -EINVAL;
+
+ bsscmd =
+ (struct sip_cmd_set_wmm_params *) (skb->data +
+ sizeof(struct sip_hdr));
+ bsscmd->aci = aci;
+ bsscmd->aifs = params->aifs;
+ bsscmd->txop_us = params->txop * 32;
+
+ bsscmd->ecw_min = 32 - __builtin_clz(params->cw_min);
+ bsscmd->ecw_max = 32 - __builtin_clz(params->cw_max);
+
+ return sip_cmd_enqueue(epub->sip, skb, ENQUEUE_PRIOR_TAIL);
+}
+
+int sip_send_ampdu_action(struct esp_pub *epub, u8 action_num,
+ const u8 * addr, u16 tid, u16 ssn, u8 buf_size)
+{
+ int index = 0;
+ struct sk_buff *skb = NULL;
+ struct sip_cmd_ampdu_action *action;
+ if (action_num == SIP_AMPDU_RX_START) {
+ index = esp_get_empty_rxampdu(epub, addr, tid);
+ } else if (action_num == SIP_AMPDU_RX_STOP) {
+ index = esp_get_exist_rxampdu(epub, addr, tid);
+ }
+ if (index < 0)
+ return -EACCES;
+ skb =
+ sip_alloc_ctrl_skbuf(epub->sip,
+ sizeof(struct sip_cmd_ampdu_action) +
+ sizeof(struct sip_hdr),
+ SIP_CMD_AMPDU_ACTION);
+ if (!skb)
+ return -EINVAL;
+
+ action =
+ (struct sip_cmd_ampdu_action *) (skb->data +
+ sizeof(struct sip_hdr));
+ action->action = action_num;
+ //for TX, it means interface index
+ action->index = ssn;
+
+ switch (action_num) {
+ case SIP_AMPDU_RX_START:
+ action->ssn = ssn;
+ // fall through
+ case SIP_AMPDU_RX_STOP:
+ action->index = index;
+ // fall through
+ case SIP_AMPDU_TX_OPERATIONAL:
+ case SIP_AMPDU_TX_STOP:
+ action->win_size = buf_size;
+ action->tid = tid;
+ memcpy(action->addr, addr, ETH_ALEN);
+ break;
+ }
+
+ return sip_cmd_enqueue(epub->sip, skb, ENQUEUE_PRIOR_TAIL);
+}
+
+#ifdef HW_SCAN
+/*send cmd to target, if aborted is true, inform target stop scan, report scan complete imediately
+ return 1: complete over, 0: success, still have next scan, -1: hardware failure
+ */
+int sip_send_scan(struct esp_pub *epub)
+{
+ struct cfg80211_scan_request *scan_req = epub->wl.scan_req;
+ struct sk_buff *skb = NULL;
+ struct sip_cmd_scan *scancmd;
+ u8 *ptr = NULL;
+ int i;
+ u8 append_len, ssid_len;
+
+ ESSERT(scan_req != NULL);
+ ssid_len = scan_req->n_ssids == 0 ? 0 :
+ (scan_req->n_ssids ==
+ 1 ? scan_req->ssids->ssid_len : scan_req->ssids->ssid_len +
+ (scan_req->ssids + 1)->ssid_len);
+ append_len = ssid_len + scan_req->n_channels + scan_req->ie_len;
+
+ skb =
+ sip_alloc_ctrl_skbuf(epub->sip,
+ sizeof(struct sip_cmd_scan) +
+ sizeof(struct sip_hdr) + append_len,
+ SIP_CMD_SCAN);
+
+ if (!skb)
+ return -EINVAL;
+
+ ptr = skb->data;
+ scancmd = (struct sip_cmd_scan *) (ptr + sizeof(struct sip_hdr));
+ ptr += sizeof(struct sip_hdr);
+
+ scancmd->aborted = false;
+
+ if (scancmd->aborted == false) {
+ ptr += sizeof(struct sip_cmd_scan);
+ if (scan_req->n_ssids <= 0
+ || (scan_req->n_ssids == 1 && ssid_len == 0)) {
+ scancmd->ssid_len = 0;
+ } else {
+ scancmd->ssid_len = ssid_len;
+ if (scan_req->ssids->ssid_len == ssid_len)
+ memcpy(ptr, scan_req->ssids->ssid,
+ scancmd->ssid_len);
+ else
+ memcpy(ptr, (scan_req->ssids + 1)->ssid,
+ scancmd->ssid_len);
+ }
+
+ ptr += scancmd->ssid_len;
+ scancmd->n_channels = scan_req->n_channels;
+ for (i = 0; i < scan_req->n_channels; i++)
+ ptr[i] = scan_req->channels[i]->hw_value;
+
+ ptr += scancmd->n_channels;
+ if (scan_req->ie_len && scan_req->ie != NULL) {
+ scancmd->ie_len = scan_req->ie_len;
+ memcpy(ptr, scan_req->ie, scan_req->ie_len);
+ } else {
+ scancmd->ie_len = 0;
+ }
+ //add a flag that support two ssids,
+ if (scan_req->n_ssids > 1)
+ scancmd->ssid_len |= 0x80;
+
+ }
+
+ return sip_cmd_enqueue(epub->sip, skb, ENQUEUE_PRIOR_TAIL);
+}
+#endif
+
+int sip_send_suspend_config(struct esp_pub *epub, u8 suspend)
+{
+ struct sip_cmd_suspend *cmd = NULL;
+ struct sk_buff *skb = NULL;
+
+ skb =
+ sip_alloc_ctrl_skbuf(epub->sip,
+ sizeof(struct sip_cmd_suspend) +
+ sizeof(struct sip_hdr), SIP_CMD_SUSPEND);
+
+ if (!skb)
+ return -EINVAL;
+
+ cmd =
+ (struct sip_cmd_suspend *) (skb->data +
+ sizeof(struct sip_hdr));
+ cmd->suspend = suspend;
+ return sip_cmd_enqueue(epub->sip, skb, ENQUEUE_PRIOR_TAIL);
+}
+
+int sip_send_ps_config(struct esp_pub *epub, struct esp_ps *ps)
+{
+ struct sip_cmd_ps *pscmd = NULL;
+ struct sk_buff *skb = NULL;
+ struct sip_hdr *shdr = NULL;
+
+ skb =
+ sip_alloc_ctrl_skbuf(epub->sip,
+ sizeof(struct sip_cmd_ps) +
+ sizeof(struct sip_hdr), SIP_CMD_PS);
+
+ if (!skb)
+ return -EINVAL;
+
+
+ shdr = (struct sip_hdr *) skb->data;
+ pscmd = (struct sip_cmd_ps *) (skb->data + sizeof(struct sip_hdr));
+
+ pscmd->dtim_period = ps->dtim_period;
+ pscmd->max_sleep_period = ps->max_sleep_period;
+
+ return sip_cmd_enqueue(epub->sip, skb, ENQUEUE_PRIOR_TAIL);
+}
+
+void sip_scandone_process(struct esp_sip *sip,
+ struct sip_evt_scan_report *scan_report)
+{
+ struct esp_pub *epub = sip->epub;
+
+ esp_dbg(ESP_DBG_TRACE, "eagle hw scan report\n");
+
+ if (epub->wl.scan_req) {
+ hw_scan_done(epub, scan_report->aborted);
+ epub->wl.scan_req = NULL;
+ }
+}
+
+int sip_send_setkey(struct esp_pub *epub, u8 bssid_no, u8 * peer_addr,
+ struct ieee80211_key_conf *key, u8 isvalid)
+{
+ struct sip_cmd_setkey *setkeycmd;
+ struct sk_buff *skb = NULL;
+
+ skb =
+ sip_alloc_ctrl_skbuf(epub->sip,
+ sizeof(struct sip_cmd_setkey) +
+ sizeof(struct sip_hdr), SIP_CMD_SETKEY);
+
+ if (!skb)
+ return -EINVAL;
+
+ setkeycmd =
+ (struct sip_cmd_setkey *) (skb->data + sizeof(struct sip_hdr));
+
+ if (peer_addr) {
+ memcpy(setkeycmd->addr, peer_addr, ETH_ALEN);
+ } else {
+ memset(setkeycmd->addr, 0, ETH_ALEN);
+ }
+
+ setkeycmd->bssid_no = bssid_no;
+ setkeycmd->hw_key_idx = key->hw_key_idx;
+
+ if (isvalid) {
+ setkeycmd->alg = esp_cipher2alg(key->cipher);
+ setkeycmd->keyidx = key->keyidx;
+ setkeycmd->keylen = key->keylen;
+ if (key->cipher == WLAN_CIPHER_SUITE_TKIP) {
+ memcpy(setkeycmd->key, key->key, 16);
+ memcpy(setkeycmd->key + 16, key->key + 24, 8);
+ memcpy(setkeycmd->key + 24, key->key + 16, 8);
+ } else {
+ memcpy(setkeycmd->key, key->key, key->keylen);
+ }
+
+ setkeycmd->flags = 1;
+ } else {
+ setkeycmd->flags = 0;
+ }
+ return sip_cmd_enqueue(epub->sip, skb, ENQUEUE_PRIOR_TAIL);
+}
+
+#ifdef FPGA_LOOPBACK
+#define LOOPBACK_PKT_LEN 200
+int sip_send_loopback_cmd_mblk(struct esp_sip *sip)
+{
+ int cnt, ret;
+
+ for (cnt = 0; cnt < 4; cnt++) {
+ if (0 !=
+ (ret =
+ sip_send_loopback_mblk(sip, LOOPBACK_PKT_LEN,
+ LOOPBACK_PKT_LEN, 0)))
+ return ret;
+ }
+ return 0;
+}
+#endif /* FPGA_LOOPBACK */
+
+int sip_send_loopback_mblk(struct esp_sip *sip, int txpacket_len,
+ int rxpacket_len, int packet_id)
+{
+ struct sk_buff *skb = NULL;
+ struct sip_cmd_loopback *cmd;
+ u8 *ptr = NULL;
+ int i, ret;
+
+ //send 100 loopback pkt
+ if (txpacket_len)
+ skb =
+ sip_alloc_ctrl_skbuf(sip,
+ sizeof(struct sip_cmd_loopback) +
+ sizeof(struct sip_hdr) +
+ txpacket_len, SIP_CMD_LOOPBACK);
+ else
+ skb =
+ sip_alloc_ctrl_skbuf(sip,
+ sizeof(struct sip_cmd_loopback) +
+ sizeof(struct sip_hdr),
+ SIP_CMD_LOOPBACK);
+
+ if (!skb)
+ return -ENOMEM;
+
+ ptr = skb->data;
+ cmd = (struct sip_cmd_loopback *) (ptr + sizeof(struct sip_hdr));
+ ptr += sizeof(struct sip_hdr);
+ cmd->txlen = txpacket_len;
+ cmd->rxlen = rxpacket_len;
+ cmd->pack_id = packet_id;
+
+ if (txpacket_len) {
+ ptr += sizeof(struct sip_cmd_loopback);
+ /* fill up pkt payload */
+ for (i = 0; i < txpacket_len; i++) {
+ ptr[i] = i;
+ }
+ }
+
+ ret = sip_cmd_enqueue(sip, skb, ENQUEUE_PRIOR_TAIL);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+//remain_on_channel
+int sip_send_roc(struct esp_pub *epub, u16 center_freq, u16 duration)
+{
+ struct sk_buff *skb = NULL;
+ struct sip_cmd_config *configcmd;
+
+ skb =
+ sip_alloc_ctrl_skbuf(epub->sip,
+ sizeof(struct sip_cmd_config) +
+ sizeof(struct sip_hdr), SIP_CMD_CONFIG);
+ if (!skb)
+ return -EINVAL;
+
+ configcmd =
+ (struct sip_cmd_config *) (skb->data + sizeof(struct sip_hdr));
+ configcmd->center_freq = center_freq;
+ configcmd->duration = duration;
+ return sip_cmd_enqueue(epub->sip, skb, ENQUEUE_PRIOR_TAIL);
+}
+
+int sip_send_set_sta(struct esp_pub *epub, u8 ifidx, u8 set,
+ struct ieee80211_sta *sta, struct ieee80211_vif *vif,
+ u8 index)
+{
+ struct sk_buff *skb = NULL;
+ struct sip_cmd_setsta *setstacmd;
+ skb =
+ sip_alloc_ctrl_skbuf(epub->sip,
+ sizeof(struct sip_cmd_setsta) +
+ sizeof(struct sip_hdr), SIP_CMD_SETSTA);
+ if (!skb)
+ return -EINVAL;
+
+ setstacmd =
+ (struct sip_cmd_setsta *) (skb->data + sizeof(struct sip_hdr));
+ setstacmd->ifidx = ifidx;
+ setstacmd->index = index;
+ setstacmd->set = set;
+ if (sta->aid == 0)
+ setstacmd->aid = vif->cfg.aid;
+ else
+ setstacmd->aid = sta->aid;
+ memcpy(setstacmd->mac, sta->addr, ETH_ALEN);
+ if (set) {
+ if (sta->deflink.ht_cap.ht_supported) {
+ if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_SGI_20)
+ setstacmd->phymode =
+ ESP_IEEE80211_T_HT20_S;
+ else
+ setstacmd->phymode =
+ ESP_IEEE80211_T_HT20_L;
+ setstacmd->ampdu_factor = sta->deflink.ht_cap.ampdu_factor;
+ setstacmd->ampdu_density =
+ sta->deflink.ht_cap.ampdu_density;
+ } else {
+ if (sta->deflink.supp_rates[NL80211_BAND_2GHZ] & (~(u32)
+ CONF_HW_BIT_RATE_11B_MASK))
+ {
+ setstacmd->phymode = ESP_IEEE80211_T_OFDM;
+ } else {
+ setstacmd->phymode = ESP_IEEE80211_T_CCK;
+ }
+ }
+ }
+ return sip_cmd_enqueue(epub->sip, skb, ENQUEUE_PRIOR_TAIL);
+}
+
+int sip_send_recalc_credit(struct esp_pub *epub)
+{
+ struct sk_buff *skb = NULL;
+
+ skb =
+ sip_alloc_ctrl_skbuf(epub->sip, 0 + sizeof(struct sip_hdr),
+ SIP_CMD_RECALC_CREDIT);
+ if (!skb)
+ return -ENOMEM;
+
+ return sip_cmd_enqueue(epub->sip, skb, ENQUEUE_PRIOR_HEAD);
+}
+
+int sip_cmd(struct esp_pub *epub, enum sip_cmd_id cmd_id, u8 * cmd_buf,
+ u8 cmd_len)
+{
+ struct sk_buff *skb = NULL;
+
+ skb =
+ sip_alloc_ctrl_skbuf(epub->sip,
+ cmd_len + sizeof(struct sip_hdr), cmd_id);
+ if (!skb)
+ return -ENOMEM;
+
+ memcpy(skb->data + sizeof(struct sip_hdr), cmd_buf, cmd_len);
+
+ return sip_cmd_enqueue(epub->sip, skb, ENQUEUE_PRIOR_TAIL);
+}
diff --git a/drivers/net/wireless/esp8089/esp_ctrl.h b/drivers/net/wireless/esp8089/esp_ctrl.h
new file mode 100644
index 000000000000..29c18caa9ede
--- /dev/null
+++ b/drivers/net/wireless/esp8089/esp_ctrl.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2009- 2014 Espressif System.
+ *
+ * SIP ctrl packet parse and pack
+ *
+ * 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.
+ */
+#ifndef _ESP_CTRL_H_
+#define _ESP_CTRL_H_
+
+int sip_send_loopback_mblk(struct esp_sip *sip, int txpacket_len,
+ int rxpacket_len, int packet_id);
+
+int sip_send_config(struct esp_pub *epub, struct ieee80211_conf *conf);
+
+int sip_send_setkey(struct esp_pub *epub, u8 bssid_no, u8 * peer_addr,
+ struct ieee80211_key_conf *key, u8 isvalid);
+
+int sip_send_scan(struct esp_pub *epub);
+
+void sip_scandone_process(struct esp_sip *sip,
+ struct sip_evt_scan_report *scan_report);
+
+int sip_send_bss_info_update(struct esp_pub *epub, struct esp_vif *evif,
+ u8 * bssid, int assoc);
+
+int sip_send_wmm_params(struct esp_pub *epub, u8 aci,
+ const struct ieee80211_tx_queue_params *params);
+
+int sip_send_ampdu_action(struct esp_pub *epub, u8 action_num,
+ const u8 * addr, u16 tid, u16 ssn, u8 buf_size);
+
+int sip_send_roc(struct esp_pub *epub, u16 center_freq, u16 duration);
+
+int sip_send_set_sta(struct esp_pub *epub, u8 ifidx, u8 set,
+ struct ieee80211_sta *sta, struct ieee80211_vif *vif,
+ u8 index);
+
+int sip_send_suspend_config(struct esp_pub *epub, u8 suspend);
+
+int sip_send_ps_config(struct esp_pub *epub, struct esp_ps *ps);
+
+int sip_parse_events(struct esp_sip *sip, u8 * buf);
+
+int sip_send_recalc_credit(struct esp_pub *epub);
+
+int sip_cmd(struct esp_pub *epub, enum sip_cmd_id cmd_id, u8 * cmd_buf,
+ u8 cmd_len);
+
+#endif /* _ESP_CTRL_H_ */
diff --git a/drivers/net/wireless/esp8089/esp_debug.c b/drivers/net/wireless/esp8089/esp_debug.c
new file mode 100644
index 000000000000..5ce8fd2ebd6b
--- /dev/null
+++ b/drivers/net/wireless/esp8089/esp_debug.c
@@ -0,0 +1,297 @@
+/*
+ * Copyright (c) 2011-2014 Espressif System.
+ *
+ * esp debug interface
+ * - debugfs
+ * - debug level control
+ *
+ * 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.
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+
+#include <net/mac80211.h>
+#include "sip2_common.h"
+
+#include "esp_debug.h"
+
+#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_ESP8089_DEBUG_FS)
+
+static struct dentry *esp_debugfs_root = NULL;
+
+static int esp_debugfs_open(struct inode *inode, struct file *filp)
+{
+ filp->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t esp_debugfs_read(struct file *filp, char __user * buffer,
+ size_t count, loff_t * ppos)
+{
+ if (*ppos >= 32)
+ return 0;
+ if (*ppos + count > 32)
+ count = 32 - *ppos;
+
+ if (copy_to_user(buffer, filp->private_data + *ppos, count))
+ return -EFAULT;
+
+ *ppos += count;
+
+ return count;
+}
+
+static ssize_t esp_debugfs_write(struct file *filp,
+ const char __user * buffer, size_t count,
+ loff_t * ppos)
+{
+ if (*ppos >= 32)
+ return 0;
+ if (*ppos + count > 32)
+ count = 32 - *ppos;
+
+ if (copy_from_user(filp->private_data + *ppos, buffer, count))
+ return -EFAULT;
+
+ *ppos += count;
+
+ return count;
+}
+
+struct file_operations esp_debugfs_fops = {
+ .owner = THIS_MODULE,
+ .open = esp_debugfs_open,
+ .read = esp_debugfs_read,
+ .write = esp_debugfs_write,
+};
+
+
+void esp_dump_var(const char *name, struct dentry *parent,
+ void *value, esp_type type)
+{
+ umode_t mode = 0644;
+
+ if (!esp_debugfs_root)
+ return;
+
+ if (!parent)
+ parent = esp_debugfs_root;
+
+ switch (type) {
+ case ESP_U8:
+ debugfs_create_u8(name, mode, parent, (u8 *) value);
+ break;
+ case ESP_U16:
+ debugfs_create_u16(name, mode, parent, (u16 *) value);
+ break;
+ case ESP_U32:
+ debugfs_create_u32(name, mode, parent, (u32 *) value);
+ break;
+ case ESP_U64:
+ debugfs_create_u64(name, mode, parent, (u64 *) value);
+ break;
+ case ESP_BOOL:
+ debugfs_create_bool(name, mode, parent,
+ (bool *) value);
+ break;
+ default: //32
+ debugfs_create_u32(name, mode, parent, (u32 *) value);
+ }
+
+ return;
+
+}
+
+void esp_dump_array(const char *name, struct dentry *parent,
+ struct debugfs_blob_wrapper *blob)
+{
+ umode_t mode = 0644;
+
+ if (!esp_debugfs_root)
+ return;
+
+ if (!parent)
+ parent = esp_debugfs_root;
+
+ debugfs_create_blob(name, mode, parent, blob);
+
+}
+
+void esp_dump(const char *name, struct dentry *parent,
+ void *data, int size)
+{
+ umode_t mode = 0644;
+
+ if (!esp_debugfs_root)
+ return;
+
+ if (!parent)
+ parent = esp_debugfs_root;
+
+ debugfs_create_file(name, mode, parent, data,
+ &esp_debugfs_fops);
+
+}
+
+struct dentry *esp_debugfs_add_sub_dir(const char *name)
+{
+ struct dentry *sub_dir = NULL;
+
+ sub_dir = debugfs_create_dir(name, esp_debugfs_root);
+
+ if (!sub_dir)
+ goto Fail;
+
+ return sub_dir;
+
+ Fail:
+ debugfs_remove_recursive(esp_debugfs_root);
+ esp_debugfs_root = NULL;
+ esp_dbg(ESP_DBG_ERROR,
+ "%s failed, debugfs root removed; dir name: %s\n",
+ __FUNCTION__, name);
+ return NULL;
+
+}
+
+int esp_debugfs_init(void)
+{
+ esp_dbg(ESP_DBG, "esp debugfs init\n");
+ esp_debugfs_root = debugfs_create_dir("esp_debug", NULL);
+
+ if (!esp_debugfs_root || IS_ERR_OR_NULL(esp_debugfs_root)) {
+ return -ENOENT;
+ }
+
+ return 0;
+}
+
+void esp_debugfs_exit(void)
+{
+ esp_dbg(ESP_DBG, "esp debugfs exit");
+
+ debugfs_remove_recursive(esp_debugfs_root);
+
+ return;
+}
+
+#else
+
+inline struct dentry *esp_dump_var(const char *name, struct dentry *parent,
+ void *value, esp_type type)
+{
+ return NULL;
+}
+
+inline struct dentry *esp_dump_array(const char *name,
+ struct dentry *parent,
+ struct debugfs_blob_wrapper *blob)
+{
+ return NULL;
+}
+
+inline struct dentry *esp_dump(const char *name, struct dentry *parent,
+ void *data, int size)
+{
+ return NULL;
+}
+
+struct dentry *esp_debugfs_add_sub_dir(const char *name)
+{
+ return NULL;
+}
+
+inline int esp_debugfs_init(void)
+{
+ return -EPERM;
+}
+
+inline void esp_debugfs_exit(void)
+{
+
+}
+
+#endif
+
+
+void show_buf(u8 * buf, u32 len)
+{
+// print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 16, 1, buf, len, true);
+#if 1
+ int i = 0, j;
+
+ printk(KERN_INFO "\n++++++++++++++++show rbuf+++++++++++++++\n");
+ for (i = 0; i < (len / 16); i++) {
+ j = i * 16;
+ printk(KERN_INFO
+ "0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x \n",
+ buf[j], buf[j + 1], buf[j + 2], buf[j + 3],
+ buf[j + 4], buf[j + 5], buf[j + 6], buf[j + 7],
+ buf[j + 8], buf[j + 9], buf[j + 10], buf[j + 11],
+ buf[j + 12], buf[j + 13], buf[j + 14], buf[j + 15]);
+ }
+ printk(KERN_INFO "\n++++++++++++++++++++++++++++++++++++++++\n");
+#endif //0000
+}
+
+#ifdef HOST_RC
+static u8 get_cnt(u32 cnt_store, int idx)
+{
+ int shift = idx << 2;
+
+ return (u8) ((cnt_store >> shift) & 0xf);
+}
+
+void esp_show_rcstatus(struct sip_rc_status *rcstatus)
+{
+ int i;
+ char msg[82];
+ char rcstr[16];
+ u32 cnt_store = rcstatus->rc_cnt_store;
+
+ memset(msg, 0, sizeof(msg));
+ memset(rcstr, 0, sizeof(rcstr));
+
+ printk(KERN_INFO "rcstatus map 0x%08x cntStore 0x%08x\n",
+ rcstatus->rc_map, rcstatus->rc_cnt_store);
+
+ for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
+ if (rcstatus->rc_map & BIT(i)) {
+ sprintf(rcstr, "rcIdx %d, cnt %d ", i,
+ get_cnt(cnt_store, i));
+ strcat(msg, rcstr);
+ }
+ }
+ printk(KERN_INFO "%s \n", msg);
+}
+
+void esp_show_tx_rates(struct ieee80211_tx_rate *rates)
+{
+ int i;
+ char msg[128];
+ char rcstr[32];
+
+ memset(msg, 0, sizeof(msg));
+ memset(rcstr, 0, sizeof(rcstr));
+
+ for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
+ if (rates->idx != -1) {
+ sprintf(rcstr, "Idx %d, cnt %d, flag %02x ",
+ rates->idx, rates->count, rates->flags);
+ strcat(msg, rcstr);
+ }
+ rates++;
+ }
+ strcat(msg, "\n");
+ printk(KERN_INFO "%s \n", msg);
+}
+#endif /* HOST_RC */
diff --git a/drivers/net/wireless/esp8089/esp_debug.h b/drivers/net/wireless/esp8089/esp_debug.h
new file mode 100644
index 000000000000..bab695d34bfb
--- /dev/null
+++ b/drivers/net/wireless/esp8089/esp_debug.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2011-2014 Espressif System.
+ *
+ * esp debug
+ *
+ * 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.
+ */
+
+#ifndef _DEBUG_H_
+
+#ifdef ASSERT_PANIC
+#define ESSERT(v) BUG_ON(!(v))
+#else
+#define ESSERT(v) if(!(v)) printk("ESSERT:%s %d\n", __FILE__, __LINE__)
+#endif
+
+
+#include <linux/slab.h>
+#include <linux/debugfs.h>
+#include <asm/uaccess.h>
+
+typedef enum esp_type {
+ ESP_BOOL,
+ ESP_U8,
+ ESP_U16,
+ ESP_U32,
+ ESP_U64
+} esp_type;
+
+void esp_dump_var(const char *name, struct dentry *parent,
+ void *value, esp_type type);
+
+void esp_dump_array(const char *name, struct dentry *parent,
+ struct debugfs_blob_wrapper *blob);
+
+void esp_dump(const char *name, struct dentry *parent,
+ void *data, int size);
+
+struct dentry *esp_debugfs_add_sub_dir(const char *name);
+
+int esp_debugfs_init(void);
+
+void esp_debugfs_exit(void);
+
+enum {
+ ESP_DBG_ERROR = BIT(0),
+ ESP_DBG_TRACE = BIT(1),
+ ESP_DBG_LOG = BIT(2),
+ ESP_DBG = BIT(3),
+ ESP_SHOW = BIT(4),
+ ESP_DBG_TXAMPDU = BIT(5),
+ ESP_DBG_OP = BIT(6),
+ ESP_DBG_PS = BIT(7),
+ ESP_ATE = BIT(8),
+ ESP_DBG_ALL = 0xffffffff
+};
+
+extern unsigned int esp_msg_level;
+
+#ifdef ESP_ANDROID_LOGGER
+extern bool log_off;
+#endif /* ESP_ANDROID_LOGGER */
+
+#ifdef ESP_ANDROID_LOGGER
+#include "esp_file.h"
+#define esp_dbg(mask, fmt, args...) do { \
+ if (esp_msg_level & mask) \
+ { \
+ if (log_off) \
+ printk(fmt, ##args); \
+ else \
+ logger_write(4, "esp_wifi", fmt, ##args); \
+ } \
+ } while (0)
+#else
+#define esp_dbg(mask, fmt, args...) do { \
+ if (esp_msg_level & mask) \
+ printk("esp8089: " fmt, ##args); \
+ } while (0)
+#endif /* ESP_ANDROID_LOGGER */
+
+void show_buf(u8 * buf, u32 len);
+
+#ifdef HOST_RC
+struct sip_rc_status;
+struct ieee80211_tx_rate;
+
+void esp_show_rcstatus(struct sip_rc_status *rcstatus);
+
+void esp_show_tx_rates(struct ieee80211_tx_rate *rates);
+#endif /* HOST_RC */
+
+#endif /* _DEBUG_H_ */
diff --git a/drivers/net/wireless/esp8089/esp_ext.c b/drivers/net/wireless/esp8089/esp_ext.c
new file mode 100644
index 000000000000..541f27a6853f
--- /dev/null
+++ b/drivers/net/wireless/esp8089/esp_ext.c
@@ -0,0 +1,542 @@
+/*
+ * Copyright (c) 2010 -2013 Espressif System.
+ *
+ * extended gpio
+ * - interface for other driver or kernel
+ * - gpio control
+ *
+ * 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.
+ */
+
+#ifdef USE_EXT_GPIO
+
+#include <net/cfg80211.h>
+#include <linux/skbuff.h>
+#include <linux/bitops.h>
+#include <linux/version.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/sdio_ids.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/sd.h>
+#include <linux/completion.h>
+
+#include "esp_ext.h"
+#include "esp_debug.h"
+#include "esp_sip.h"
+#include "esp_sif.h"
+
+#ifdef EXT_GPIO_OPS
+extern void register_ext_gpio_ops(struct esp_ext_gpio_ops *ops);
+extern void unregister_ext_gpio_ops(void);
+
+static struct esp_ext_gpio_ops ext_gpio_ops = {
+ .gpio_request = ext_gpio_request, /* gpio_request gpio_no from 0x0 to 0xf */
+ .gpio_release = ext_gpio_release, /* gpio_release */
+ .gpio_set_mode = ext_gpio_set_mode, /* gpio_set_mode, data is irq_func of irq_mode , default level of output_mode */
+ .gpio_get_mode = ext_gpio_get_mode, /* gpio_get_mode, current mode */
+ .gpio_set_state = ext_gpio_set_output_state, /* only output state, high level or low level */
+ .gpio_get_state = ext_gpio_get_state, /* current state */
+ .irq_ack = ext_irq_ack, /* ack interrupt */
+};
+
+
+#endif
+
+static struct esp_pub *ext_epub = NULL;
+
+static u16 intr_mask_reg = 0x0000;
+struct workqueue_struct *ext_irq_wkq = NULL;
+struct work_struct ext_irq_work;
+static struct mutex ext_mutex_lock;
+
+static struct ext_gpio_info gpio_list[EXT_GPIO_MAX_NUM] = {
+ {0, EXT_GPIO_MODE_DISABLE, EXT_GPIO_STATE_IDLE, NULL},
+ {1, EXT_GPIO_MODE_DISABLE, EXT_GPIO_STATE_IDLE, NULL},
+ {2, EXT_GPIO_MODE_DISABLE, EXT_GPIO_STATE_IDLE, NULL},
+ {3, EXT_GPIO_MODE_DISABLE, EXT_GPIO_STATE_IDLE, NULL},
+ {4, EXT_GPIO_MODE_DISABLE, EXT_GPIO_STATE_IDLE, NULL},
+ {5, EXT_GPIO_MODE_DISABLE, EXT_GPIO_STATE_IDLE, NULL},
+ {6, EXT_GPIO_MODE_DISABLE, EXT_GPIO_STATE_IDLE, NULL},
+ {7, EXT_GPIO_MODE_DISABLE, EXT_GPIO_STATE_IDLE, NULL},
+ {8, EXT_GPIO_MODE_DISABLE, EXT_GPIO_STATE_IDLE, NULL},
+ {9, EXT_GPIO_MODE_DISABLE, EXT_GPIO_STATE_IDLE, NULL},
+ {10, EXT_GPIO_MODE_DISABLE, EXT_GPIO_STATE_IDLE, NULL},
+ {11, EXT_GPIO_MODE_DISABLE, EXT_GPIO_STATE_IDLE, NULL},
+ {12, EXT_GPIO_MODE_DISABLE, EXT_GPIO_STATE_IDLE, NULL},
+ {13, EXT_GPIO_MODE_DISABLE, EXT_GPIO_STATE_IDLE, NULL},
+ {14, EXT_GPIO_MODE_DISABLE, EXT_GPIO_STATE_IDLE, NULL},
+ {15, EXT_GPIO_MODE_DISABLE, EXT_GPIO_STATE_IDLE, NULL},
+};
+
+static struct pending_intr_list_info esp_pending_intr_list = {
+ .start_pos = 0,
+ .end_pos = 0,
+ .curr_num = 0,
+};
+
+u16 ext_gpio_get_int_mask_reg(void)
+{
+ return intr_mask_reg;
+}
+
+int ext_gpio_request(int gpio_no)
+{
+ if (ext_epub == NULL || ext_epub->sip == NULL ||
+ atomic_read(&ext_epub->sip->state) != SIP_RUN) {
+ esp_dbg(ESP_DBG_ERROR, "%s esp state is not ok\n",
+ __func__);
+ return -ENOTRECOVERABLE;
+ }
+
+ mutex_lock(&ext_mutex_lock);
+
+ if (gpio_no >= EXT_GPIO_MAX_NUM || gpio_no < 0) {
+ mutex_unlock(&ext_mutex_lock);
+ esp_dbg(ESP_DBG_ERROR, "%s unkown gpio num\n", __func__);
+ return -ERANGE;
+ }
+
+ if (gpio_list[gpio_no].gpio_mode != EXT_GPIO_MODE_DISABLE) {
+ mutex_unlock(&ext_mutex_lock);
+ esp_dbg(ESP_DBG_ERROR,
+ "%s gpio is already in used by other\n", __func__);
+ return -EPERM;
+ } else {
+ gpio_list[gpio_no].gpio_mode = EXT_GPIO_MODE_MAX;
+ mutex_unlock(&ext_mutex_lock);
+ return 0;
+ }
+}
+
+EXPORT_SYMBOL(ext_gpio_request);
+
+int ext_gpio_release(int gpio_no)
+{
+ int ret;
+
+ if (ext_epub == NULL || ext_epub->sip == NULL ||
+ atomic_read(&ext_epub->sip->state) != SIP_RUN) {
+ esp_dbg(ESP_DBG_ERROR, "%s esp state is not ok\n",
+ __func__);
+ return -ENOTRECOVERABLE;
+ }
+
+ mutex_lock(&ext_mutex_lock);
+
+ if (gpio_no >= EXT_GPIO_MAX_NUM || gpio_no < 0) {
+ mutex_unlock(&ext_mutex_lock);
+ esp_dbg(ESP_DBG_ERROR, "%s unkown gpio num\n", __func__);
+ return -ERANGE;
+ }
+ sif_lock_bus(ext_epub);
+ ret =
+ sif_config_gpio_mode(ext_epub, (u8) gpio_no,
+ EXT_GPIO_MODE_DISABLE);
+ sif_unlock_bus(ext_epub);
+ if (ret) {
+ esp_dbg(ESP_DBG_ERROR, "%s gpio release error\n",
+ __func__);
+ mutex_unlock(&ext_mutex_lock);
+ return ret;
+ }
+
+ gpio_list[gpio_no].gpio_mode = EXT_GPIO_MODE_DISABLE;
+ gpio_list[gpio_no].gpio_state = EXT_GPIO_STATE_IDLE;
+ gpio_list[gpio_no].irq_handler = NULL;
+ intr_mask_reg &= ~(1 << gpio_no);
+
+ mutex_unlock(&ext_mutex_lock);
+
+ return 0;
+}
+
+EXPORT_SYMBOL(ext_gpio_release);
+
+int ext_gpio_set_mode(int gpio_no, int mode, void *data)
+{
+ u8 gpio_mode;
+ int ret;
+ struct ext_gpio_info backup_info;
+
+ if (ext_epub == NULL || ext_epub->sip == NULL ||
+ atomic_read(&ext_epub->sip->state) != SIP_RUN) {
+ esp_dbg(ESP_DBG_LOG, "%s esp state is not ok\n", __func__);
+ return -ENOTRECOVERABLE;
+ }
+
+ mutex_lock(&ext_mutex_lock);
+
+ if (gpio_no >= EXT_GPIO_MAX_NUM || gpio_no < 0) {
+ mutex_unlock(&ext_mutex_lock);
+ esp_dbg(ESP_DBG_ERROR, "%s unkown gpio num\n", __func__);
+ return -ERANGE;
+ }
+
+ if (gpio_list[gpio_no].gpio_mode == EXT_GPIO_MODE_DISABLE) {
+ mutex_unlock(&ext_mutex_lock);
+ esp_dbg(ESP_DBG_ERROR,
+ "%s gpio is not in occupy, please request gpio\n",
+ __func__);
+ return -ENOTRECOVERABLE;
+ }
+
+ if (mode <= EXT_GPIO_MODE_OOB || mode >= EXT_GPIO_MODE_MAX) {
+ mutex_unlock(&ext_mutex_lock);
+ esp_dbg(ESP_DBG_ERROR, "%s gpio mode unknown\n", __func__);
+ return -EOPNOTSUPP;
+ }
+
+ memcpy(&backup_info, &gpio_list[gpio_no],
+ sizeof(struct ext_gpio_info));
+
+ gpio_list[gpio_no].gpio_mode = mode;
+ gpio_mode = (u8) mode;
+
+ switch (mode) {
+ case EXT_GPIO_MODE_INTR_POSEDGE:
+ case EXT_GPIO_MODE_INTR_NEGEDGE:
+ case EXT_GPIO_MODE_INTR_LOLEVEL:
+ case EXT_GPIO_MODE_INTR_HILEVEL:
+ if (!data) {
+ memcpy(&gpio_list[gpio_no], &backup_info,
+ sizeof(struct ext_gpio_info));
+ esp_dbg(ESP_DBG_ERROR, "%s irq_handler is NULL\n",
+ __func__);
+ mutex_unlock(&ext_mutex_lock);
+ return -EINVAL;
+ }
+ gpio_list[gpio_no].irq_handler = (ext_irq_handler_t) data;
+ intr_mask_reg |= (1 << gpio_no);
+ break;
+ case EXT_GPIO_MODE_OUTPUT:
+ if (!data) {
+ memcpy(&gpio_list[gpio_no], &backup_info,
+ sizeof(struct ext_gpio_info));
+ esp_dbg(ESP_DBG_ERROR,
+ "%s output default value is NULL\n",
+ __func__);
+ mutex_unlock(&ext_mutex_lock);
+ return -EINVAL;
+ }
+ *(int *) data = (*(int *) data == 0 ? 0 : 1);
+ gpio_mode = (u8) (((*(int *) data) << 4) | gpio_mode);
+ default:
+ gpio_list[gpio_no].irq_handler = NULL;
+ intr_mask_reg &= ~(1 << gpio_no);
+ break;
+ }
+
+ sif_lock_bus(ext_epub);
+ ret = sif_config_gpio_mode(ext_epub, (u8) gpio_no, gpio_mode);
+ sif_unlock_bus(ext_epub);
+ if (ret) {
+ memcpy(&gpio_list[gpio_no], &backup_info,
+ sizeof(struct ext_gpio_info));
+ esp_dbg(ESP_DBG_ERROR, "%s gpio set error\n", __func__);
+ mutex_unlock(&ext_mutex_lock);
+ return ret;
+ }
+
+ mutex_unlock(&ext_mutex_lock);
+ return 0;
+}
+
+EXPORT_SYMBOL(ext_gpio_set_mode);
+
+int ext_gpio_get_mode(int gpio_no)
+{
+ int gpio_mode;
+
+ if (ext_epub == NULL || ext_epub->sip == NULL ||
+ atomic_read(&ext_epub->sip->state) != SIP_RUN) {
+ esp_dbg(ESP_DBG_LOG, "%s esp state is not ok\n", __func__);
+ return -ENOTRECOVERABLE;
+ }
+
+ mutex_lock(&ext_mutex_lock);
+
+ if (gpio_no >= EXT_GPIO_MAX_NUM || gpio_no < 0) {
+ esp_dbg(ESP_DBG_ERROR, "%s unkown gpio num\n", __func__);
+ mutex_unlock(&ext_mutex_lock);
+ return -ERANGE;
+ }
+
+ gpio_mode = gpio_list[gpio_no].gpio_mode;
+
+ mutex_unlock(&ext_mutex_lock);
+
+ return gpio_mode;
+}
+
+EXPORT_SYMBOL(ext_gpio_get_mode);
+
+
+int ext_gpio_set_output_state(int gpio_no, int state)
+{
+ int ret;
+
+ if (ext_epub == NULL || ext_epub->sip == NULL ||
+ atomic_read(&ext_epub->sip->state) != SIP_RUN) {
+ esp_dbg(ESP_DBG_LOG, "%s esp state is not ok\n", __func__);
+ return -ENOTRECOVERABLE;
+ }
+
+ mutex_lock(&ext_mutex_lock);
+
+ if (gpio_no >= EXT_GPIO_MAX_NUM || gpio_no < 0) {
+ mutex_unlock(&ext_mutex_lock);
+ esp_dbg(ESP_DBG_ERROR, "%s unkown gpio num\n", __func__);
+ return -ERANGE;
+ }
+
+ if (gpio_list[gpio_no].gpio_mode != EXT_GPIO_MODE_OUTPUT) {
+ mutex_unlock(&ext_mutex_lock);
+ esp_dbg(ESP_DBG_ERROR,
+ "%s gpio is not in output state, please request gpio or set output state\n",
+ __func__);
+ return -EOPNOTSUPP;
+ }
+
+ if (state != EXT_GPIO_STATE_LOW && state != EXT_GPIO_STATE_HIGH) {
+ mutex_unlock(&ext_mutex_lock);
+ esp_dbg(ESP_DBG_ERROR, "%s gpio state unknown\n",
+ __func__);
+ return -ENOTRECOVERABLE;
+ }
+
+ sif_lock_bus(ext_epub);
+ ret =
+ sif_set_gpio_output(ext_epub, 1 << gpio_no, state << gpio_no);
+ sif_unlock_bus(ext_epub);
+ if (ret) {
+ esp_dbg(ESP_DBG_ERROR, "%s gpio state set error\n",
+ __func__);
+ mutex_unlock(&ext_mutex_lock);
+ return ret;
+ }
+ gpio_list[gpio_no].gpio_state = state;
+
+ mutex_unlock(&ext_mutex_lock);
+
+ return 0;
+}
+
+EXPORT_SYMBOL(ext_gpio_set_output_state);
+
+int ext_gpio_get_state(int gpio_no)
+{
+ int ret;
+ u16 state;
+ u16 mask;
+
+ if (ext_epub == NULL || ext_epub->sip == NULL ||
+ atomic_read(&ext_epub->sip->state) != SIP_RUN) {
+ esp_dbg(ESP_DBG_LOG, "%s esp state is not ok\n", __func__);
+ return -ENOTRECOVERABLE;
+ }
+
+ mutex_lock(&ext_mutex_lock);
+
+ if (gpio_no >= EXT_GPIO_MAX_NUM || gpio_no < 0) {
+ esp_dbg(ESP_DBG_ERROR, "%s unkown gpio num\n", __func__);
+ mutex_unlock(&ext_mutex_lock);
+ return -ERANGE;
+ }
+
+ if (gpio_list[gpio_no].gpio_mode == EXT_GPIO_MODE_OUTPUT) {
+ state = gpio_list[gpio_no].gpio_state;
+ } else if (gpio_list[gpio_no].gpio_mode == EXT_GPIO_MODE_INPUT) {
+ sif_lock_bus(ext_epub);
+ ret = sif_get_gpio_input(ext_epub, &mask, &state);
+ sif_unlock_bus(ext_epub);
+ if (ret) {
+ esp_dbg(ESP_DBG_ERROR,
+ "%s get gpio_input state error\n",
+ __func__);
+ mutex_unlock(&ext_mutex_lock);
+ return ret;
+ }
+ } else {
+ esp_dbg(ESP_DBG_ERROR,
+ "%s gpio_state is not input or output\n",
+ __func__);
+ mutex_unlock(&ext_mutex_lock);
+ return -EOPNOTSUPP;
+ }
+ mutex_unlock(&ext_mutex_lock);
+
+ return (state & (1 << gpio_no)) ? 1 : 0;
+}
+
+EXPORT_SYMBOL(ext_gpio_get_state);
+
+int ext_irq_ack(int gpio_no)
+{
+ int ret;
+
+ if (ext_epub == NULL || ext_epub->sip == NULL ||
+ atomic_read(&ext_epub->sip->state) != SIP_RUN) {
+ esp_dbg(ESP_DBG_LOG, "%s esp state is not ok\n", __func__);
+ return -ENOTRECOVERABLE;
+ }
+
+ mutex_lock(&ext_mutex_lock);
+ if (gpio_no >= EXT_GPIO_MAX_NUM || gpio_no < 0) {
+ esp_dbg(ESP_DBG_ERROR, "%s unkown gpio num\n", __func__);
+ mutex_unlock(&ext_mutex_lock);
+ return -ERANGE;
+ }
+
+ if (gpio_list[gpio_no].gpio_mode != EXT_GPIO_MODE_INTR_POSEDGE
+ && gpio_list[gpio_no].gpio_mode != EXT_GPIO_MODE_INTR_NEGEDGE
+ && gpio_list[gpio_no].gpio_mode != EXT_GPIO_MODE_INTR_LOLEVEL
+ && gpio_list[gpio_no].gpio_mode !=
+ EXT_GPIO_MODE_INTR_HILEVEL) {
+ esp_dbg(ESP_DBG_ERROR, "%s gpio mode is not intr mode\n",
+ __func__);
+ mutex_unlock(&ext_mutex_lock);
+ return -ENOTRECOVERABLE;
+ }
+
+ sif_lock_bus(ext_epub);
+ ret = sif_set_gpio_output(ext_epub, 0x00, 1 << gpio_no);
+ sif_unlock_bus(ext_epub);
+ if (ret) {
+ esp_dbg(ESP_DBG_ERROR, "%s gpio intr ack error\n",
+ __func__);
+ mutex_unlock(&ext_mutex_lock);
+ return ret;
+ }
+
+ mutex_unlock(&ext_mutex_lock);
+ return 0;
+}
+
+EXPORT_SYMBOL(ext_irq_ack);
+
+void show_status(void)
+{
+ int i = 0;
+ for (i = 0; i < MAX_PENDING_INTR_LIST; i++)
+ esp_dbg(ESP_DBG_ERROR, "status[%d] = [0x%04x]\n", i,
+ esp_pending_intr_list.pending_intr_list[i]);
+
+ esp_dbg(ESP_DBG_ERROR, "start_pos[%d]\n",
+ esp_pending_intr_list.start_pos);
+ esp_dbg(ESP_DBG_ERROR, "end_pos[%d]\n",
+ esp_pending_intr_list.end_pos);
+ esp_dbg(ESP_DBG_ERROR, "curr_num[%d]\n",
+ esp_pending_intr_list.curr_num);
+
+}
+void esp_tx_work(struct work_struct *work)
+{
+ int i;
+ u16 tmp_intr_status_reg;
+
+ esp_dbg(ESP_DBG_TRACE, "%s enter\n", __func__);
+
+ spin_lock(&esp_pending_intr_list.spin_lock);
+
+ tmp_intr_status_reg =
+ esp_pending_intr_list.pending_intr_list[esp_pending_intr_list.
+ start_pos];
+
+ esp_pending_intr_list.pending_intr_list[esp_pending_intr_list.
+ start_pos] = 0x0000;
+ esp_pending_intr_list.start_pos =
+ (esp_pending_intr_list.start_pos + 1) % MAX_PENDING_INTR_LIST;
+ esp_pending_intr_list.curr_num--;
+
+ spin_unlock(&esp_pending_intr_list.spin_lock);
+
+ for (i = 0; i < EXT_GPIO_MAX_NUM; i++) {
+ if (tmp_intr_status_reg & (1 << i)
+ && (gpio_list[i].irq_handler))
+ gpio_list[i].irq_handler();
+ }
+
+ spin_lock(&esp_pending_intr_list.spin_lock);
+ if (esp_pending_intr_list.curr_num > 0)
+ queue_work(ext_irq_wkq, &ext_irq_work);
+ spin_unlock(&esp_pending_intr_list.spin_lock);
+}
+
+void ext_gpio_int_process(u16 value)
+{
+ if (value == 0x00)
+ return;
+
+ esp_dbg(ESP_DBG_TRACE, "%s enter\n", __func__);
+
+ /* intr cycle queue is full, wait */
+ while (esp_pending_intr_list.curr_num >= MAX_PENDING_INTR_LIST) {
+ udelay(1);
+ }
+
+ spin_lock(&esp_pending_intr_list.spin_lock);
+
+ esp_pending_intr_list.pending_intr_list[esp_pending_intr_list.
+ end_pos] = value;
+ esp_pending_intr_list.end_pos =
+ (esp_pending_intr_list.end_pos + 1) % MAX_PENDING_INTR_LIST;
+ esp_pending_intr_list.curr_num++;
+
+ queue_work(ext_irq_wkq, &ext_irq_work);
+
+ spin_unlock(&esp_pending_intr_list.spin_lock);
+}
+
+int ext_gpio_init(struct esp_pub *epub)
+{
+ esp_dbg(ESP_DBG_ERROR, "%s enter\n", __func__);
+
+ ext_irq_wkq = create_singlethread_workqueue("esp_ext_irq_wkq");
+ if (ext_irq_wkq == NULL) {
+ esp_dbg(ESP_DBG_ERROR, "%s create workqueue error\n",
+ __func__);
+ return -EACCES;
+ }
+
+ INIT_WORK(&ext_irq_work, esp_tx_work);
+ mutex_init(&ext_mutex_lock);
+
+ ext_epub = epub;
+
+ if (ext_epub == NULL)
+ return -EINVAL;
+
+#ifdef EXT_GPIO_OPS
+ register_ext_gpio_ops(&ext_gpio_ops);
+#endif
+
+ return 0;
+}
+
+void ext_gpio_deinit(void)
+{
+ esp_dbg(ESP_DBG_ERROR, "%s enter\n", __func__);
+
+#ifdef EXT_GPIO_OPS
+ unregister_ext_gpio_ops();
+#endif
+ ext_epub = NULL;
+ cancel_work_sync(&ext_irq_work);
+
+ if (ext_irq_wkq)
+ destroy_workqueue(ext_irq_wkq);
+
+}
+
+#endif /* USE_EXT_GPIO */
diff --git a/drivers/net/wireless/esp8089/esp_ext.h b/drivers/net/wireless/esp8089/esp_ext.h
new file mode 100644
index 000000000000..0eeba4d22111
--- /dev/null
+++ b/drivers/net/wireless/esp8089/esp_ext.h
@@ -0,0 +1,100 @@
+#ifdef USE_EXT_GPIO
+
+#ifndef _ESP_EXT_H_
+#define _ESP_EXT_H_
+
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include "esp_sip.h"
+
+#define MAX_PENDING_INTR_LIST 16
+
+#ifdef EXT_GPIO_OPS
+typedef struct esp_ext_gpio_ops {
+ int (*gpio_request) (int gpio_no); /* gpio_request gpio_no from 0x0 to 0xf */
+ int (*gpio_release) (int gpio_no); /* gpio_release */
+ int (*gpio_set_mode) (int gpio_no, int mode, void *data); /* gpio_set_mode, data is irq_func of irq_mode , default level of output_mode */
+ int (*gpio_get_mode) (int gpio_no); /* gpio_get_mode, current mode */
+ int (*gpio_set_state) (int gpio_no, int state); /* only output state, high level or low level */
+ int (*gpio_get_state) (int gpio_no); /* current state */
+ int (*irq_ack) (int gpio_no); /* ack interrupt */
+} esp_ext_gpio_ops_t;
+#endif
+
+typedef enum EXT_GPIO_NO {
+ EXT_GPIO_GPIO0 = 0,
+ EXT_GPIO_U0TXD,
+ EXT_GPIO_GPIO2,
+ EXT_GPIO_U0RXD,
+ EXT_GPIO_GPIO4,
+ EXT_GPIO_GPIO5,
+ EXT_GPIO_SD_CLK,
+ EXT_GPIO_SD_DATA0,
+ EXT_GPIO_SD_DATA1,
+ EXT_GPIO_SD_DATA2,
+ EXT_GPIO_SD_DATA3,
+ EXT_GPIO_SD_CMD,
+ EXT_GPIO_MTDI,
+ EXT_GPIO_MTCK,
+ EXT_GPIO_MTMS,
+ EXT_GPIO_MTDO,
+ EXT_GPIO_MAX_NUM
+} EXT_GPIO_NO_T;
+
+typedef enum EXT_GPIO_MODE { //dir def pullup mode wake
+ EXT_GPIO_MODE_OOB = 0, //output 1 0 n/a n/a
+ EXT_GPIO_MODE_OUTPUT, //output / 0 n/a n/a
+ EXT_GPIO_MODE_DISABLE, //input n/a 0 DIS n/a
+ EXT_GPIO_MODE_INTR_POSEDGE, //input n/a 0 POS 1
+ EXT_GPIO_MODE_INTR_NEGEDGE, //input n/a 1 NEG 1
+ EXT_GPIO_MODE_INPUT, //input n/a 0 ANY 1
+ EXT_GPIO_MODE_INTR_LOLEVEL, //input n/a 1 LOW 1
+ EXT_GPIO_MODE_INTR_HILEVEL, //input n/a 0 HIGH 1
+ EXT_GPIO_MODE_MAX,
+} EXT_GPIO_MODE_T;
+
+typedef enum EXT_GPIO_STATE {
+ EXT_GPIO_STATE_LOW,
+ EXT_GPIO_STATE_HIGH,
+ EXT_GPIO_STATE_IDLE
+} EXT_GPIO_STATE_T;
+
+typedef irqreturn_t(*ext_irq_handler_t) (void);
+
+struct ext_gpio_info {
+ int gpio_no;
+ int gpio_mode;
+ int gpio_state;
+ ext_irq_handler_t irq_handler;
+};
+
+struct pending_intr_list_info {
+ u16 pending_intr_list[MAX_PENDING_INTR_LIST];
+ int start_pos;
+ int end_pos;
+ int curr_num;
+ spinlock_t spin_lock;
+};
+
+u16 ext_gpio_get_int_mask_reg(void);
+
+/* for extern user start */
+int ext_gpio_request(int gpio_no);
+int ext_gpio_release(int gpio_no);
+
+int ext_gpio_set_mode(int gpio_no, int mode, void *data);
+int ext_gpio_get_mode(int gpio_no);
+
+int ext_gpio_set_output_state(int gpio_no, int state);
+int ext_gpio_get_state(int gpio_no);
+
+int ext_irq_ack(int gpio_no);
+/* for extern user end */
+
+void ext_gpio_int_process(u16 value);
+
+int ext_gpio_init(struct esp_pub *epub);
+void ext_gpio_deinit(void);
+#endif /* _ESP_EXT_H_ */
+
+#endif /* USE_EXT_GPIO */
diff --git a/drivers/net/wireless/esp8089/esp_file.c b/drivers/net/wireless/esp8089/esp_file.c
new file mode 100644
index 000000000000..ea702f010eec
--- /dev/null
+++ b/drivers/net/wireless/esp8089/esp_file.c
@@ -0,0 +1,258 @@
+/*
+ * Copyright (c) 2010 -2014 Espressif System.
+ *
+ * file operation in kernel space
+ *
+ * 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.
+ */
+
+#include <linux/fs.h>
+#include <linux/vmalloc.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/moduleparam.h>
+#include <linux/firmware.h>
+#include <linux/netdevice.h>
+#include <linux/aio.h>
+#include <linux/property.h>
+
+#include "esp_file.h"
+#include "esp_debug.h"
+#include "esp_sif.h"
+
+static int mod_parm_crystal = -1;
+module_param_named(crystal, mod_parm_crystal, int, 0444);
+MODULE_PARM_DESC(crystal, "crystal frequency: 0=40MHz, 1=26MHz, 2=24MHz");
+
+struct esp_init_table_elem esp_init_table[MAX_ATTR_NUM] = {
+ /*
+ * Crystal type:
+ * 0: 40MHz (default)
+ * 1: 26MHz (ESP8266 ESP-12F)
+ * 2: 24MHz
+ */
+ {"crystal_26M_en", 48, 0},
+ /*
+ * Output crystal clock to pin:
+ * 0: None
+ * 1: GPIO1
+ * 2: URXD0
+ */
+ {"test_xtal", 49, 0},
+ /*
+ * Host SDIO mode:
+ * 0: Auto by pin strapping
+ * 1: SDIO data output on negative edges (SDIO v1.1)
+ * 2: SDIO data output on positive edges (SDIO v2.0)
+ */
+ {"sdio_configure", 50, 2},
+ /*
+ * WiFi/Bluetooth co-existence with BK3515A BT chip
+ * 0: None
+ * 1: GPIO0->WLAN_ACTIVE, MTMS->BT_ACTIVE, MTDI->BT_PRIORITY,
+ * U0TXD->ANT_SEL_BT, U0RXD->ANT_SEL_WIFI
+ */
+ {"bt_configure", 51, 0},
+ /*
+ * Antenna selection:
+ * 0: Antenna is for WiFi
+ * 1: Antenna is for Bluetooth
+ */
+ {"bt_protocol", 52, 0},
+ /*
+ * Dual antenna configuration mode:
+ * 0: None
+ * 1: U0RXD + XPD_DCDC
+ * 2: U0RXD + GPIO0
+ * 3: U0RXD + U0TXD
+ */
+ {"dual_ant_configure", 53, 0},
+ /*
+ * Firmware debugging output pin:
+ * 0: None
+ * 1: UART TX on GPIO2
+ * 2: UART TX on U0TXD
+ */
+ {"test_uart_configure", 54, 2},
+ /*
+ * Whether to share crystal clock with BT (in sleep mode):
+ * 0: no
+ * 1: always on
+ * 2: automatically on according to XPD_DCDC
+ */
+ {"share_xtal", 55, 0},
+ /*
+ * Allow chip to be woken up during sleep on pin:
+ * 0: None
+ * 1: XPD_DCDC
+ * 2: GPIO0
+ * 3: Both XPD_DCDC and GPIO0
+ */
+ {"gpio_wake", 56, 0},
+ {"no_auto_sleep", 57, 0},
+ {"speed_suspend", 58, 0},
+ {"attr11", -1, -1},
+ {"attr12", -1, -1},
+ {"attr13", -1, -1},
+ {"attr14", -1, -1},
+ {"attr15", -1, -1},
+ //attr that is not send to target
+ /*
+ * Allow chip to be reset by GPIO pin:
+ * 0: no
+ * 1: yes
+ */
+ {"ext_rst", -1, 0},
+ {"wakeup_gpio", -1, 12},
+ {"ate_test", -1, 0},
+ {"attr19", -1, -1},
+ {"attr20", -1, -1},
+ {"attr21", -1, -1},
+ {"attr22", -1, -1},
+ {"attr23", -1, -1},
+};
+
+/*
+ * Export part of the configuration related to first initiliazition to the esp8089
+ */
+void esp_conf_upload_first(void)
+{
+ int i;
+
+ for (i = 0; i < MAX_ATTR_NUM; i++) {
+ if (esp_init_table[i].value < 0)
+ continue;
+
+ if (!strcmp(esp_init_table[i].attr, "share_xtal"))
+ sif_record_bt_config(esp_init_table[i].value);
+ else if (!strcmp(esp_init_table[i].attr, "ext_rst"))
+ sif_record_rst_config(esp_init_table[i].value);
+ else if (!strcmp(esp_init_table[i].attr, "wakeup_gpio"))
+ sif_record_wakeup_gpio_config(esp_init_table[i].value);
+ else if (!strcmp(esp_init_table[i].attr, "ate_test"))
+ sif_record_ate_config(esp_init_table[i].value);
+ }
+}
+
+/*
+ * Export part of the configuration related to second initiliazition
+ */
+void esp_conf_upload_second(u8 * init_data_buf, int buf_size)
+{
+ int i;
+
+ for (i = 0; i < MAX_FIX_ATTR_NUM; i++) {
+ if (esp_init_table[i].offset > -1
+ && esp_init_table[i].offset < buf_size
+ && esp_init_table[i].value > -1) {
+ *(u8 *) (init_data_buf +
+ esp_init_table[i].offset) =
+ esp_init_table[i].value;
+ } else if (esp_init_table[i].offset > buf_size) {
+ esp_dbg(ESP_DBG_ERROR,
+ "%s: offset[%d] longer than init_data_buf len[%d] Ignore\n",
+ __FUNCTION__, esp_init_table[i].offset,
+ buf_size);
+ }
+ }
+
+}
+
+
+void esp_conf_init(struct device *dev)
+{
+
+ struct device_node *np = dev->of_node;
+
+ if (np) {
+
+ u32 value;
+
+ if (!of_property_read_u32(np, "esp,crystal-26M-en", &value))
+ esp_conf_set_attr("crystal_26M_en", value);
+
+ if (!of_property_read_u32(np, "esp,sdio-configure", &value))
+ esp_conf_set_attr("sdio_configure", value);
+
+ if (of_property_read_bool(np, "esp,shared-xtal"))
+ esp_conf_set_attr("share_xtal", 1);
+
+ if (!of_property_read_u32(np, "esp,gpio-wake", &value))
+ esp_conf_set_attr("gpio_wake", value);
+
+ if (!of_property_read_u32(np, "esp,wakeup-gpio", &value))
+ esp_conf_set_attr("wakeup_gpio", value);
+
+ if (of_property_read_bool(np, "esp,configure-dual-antenna"))
+ esp_conf_set_attr("dual_ant_configure", 1);
+
+ if (of_property_read_bool(np, "esp,no-auto-sleep"))
+ esp_conf_set_attr("no_auto_sleep", 1);
+
+ if (of_property_read_bool(np, "esp,test-xtal"))
+ esp_conf_set_attr("test_xtal", 1);
+
+ if (of_property_read_bool(np, "esp,bt-configure"))
+ esp_conf_set_attr("bt_configure", 1);
+
+ if (!of_property_read_u32(np, "esp,bt-protocol", &value))
+ esp_conf_set_attr("bt_protocol", value);
+
+ if (of_property_read_bool(np, "esp,test-uart-configure"))
+ esp_conf_set_attr("test_uart_configure", 1);
+
+ if (of_property_read_bool(np, "esp,speed-suspend"))
+ esp_conf_set_attr("speed_suspend", 1);
+
+ if (of_property_read_bool(np, "esp,ate-test"))
+ esp_conf_set_attr("ate_test", 1);
+
+ if (!of_property_read_u32(np, "esp,ext-rst", &value))
+ esp_conf_set_attr("ext_rst", value);
+
+ }
+
+ if (mod_parm_crystal >= 0 && mod_parm_crystal <= 2)
+ esp_conf_set_attr("crystal_26M_en", mod_parm_crystal);
+
+
+ esp_conf_show_attrs();
+
+}
+
+int esp_conf_set_attr(char *name, u8 value) {
+
+ int i;
+
+ for (i = 0; i < MAX_ATTR_NUM; i++) {
+
+ if (strcmp(esp_init_table[i].attr, name) == 0) {
+ esp_dbg(ESP_DBG, "set config: %s value: %d", name, value);
+ esp_init_table[i].value = value;
+ return 0;
+ }
+
+ }
+
+ return -1;
+
+}
+
+void esp_conf_show_attrs(void)
+{
+ int i;
+ for (i = 0; i < MAX_ATTR_NUM; i++)
+ if (esp_init_table[i].offset > -1)
+ esp_dbg(ESP_SHOW, "config parm:%s (id:%d), value: %d\n",
+ esp_init_table[i].attr,
+ esp_init_table[i].offset,
+ esp_init_table[i].value);
+}
diff --git a/drivers/net/wireless/esp8089/esp_file.h b/drivers/net/wireless/esp8089/esp_file.h
new file mode 100644
index 000000000000..5ba39c626baa
--- /dev/null
+++ b/drivers/net/wireless/esp8089/esp_file.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2010 -2014 Espressif System.
+ *
+ * file operation in kernel space
+ *
+ * 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.
+ */
+
+#ifndef _ESP_FILE_H_
+#define _ESP_FILE_H_
+
+#include <linux/version.h>
+#include <linux/firmware.h>
+
+#define E_ROUND_UP(x, y) ((((x) + ((y) - 1)) / (y)) * (y))
+
+#define CONF_ATTR_LEN 24
+#define CONF_VAL_LEN 3
+#define MAX_ATTR_NUM 24
+#define MAX_FIX_ATTR_NUM 16
+#define MAX_BUF_LEN ((CONF_ATTR_LEN + CONF_VAL_LEN + 2) * MAX_ATTR_NUM + 2)
+
+struct esp_init_table_elem {
+ char attr[CONF_ATTR_LEN];
+ int offset;
+ short value;
+};
+
+void esp_conf_init(struct device *dev);
+void esp_conf_upload_first(void);
+void esp_conf_upload_second(u8 * init_data_buf, int buf_size);
+int esp_conf_set_attr(char *name, u8 value);
+void esp_conf_show_attrs(void);
+
+#endif /* _ESP_FILE_H_ */
diff --git a/drivers/net/wireless/esp8089/esp_init_data.h b/drivers/net/wireless/esp8089/esp_init_data.h
new file mode 100644
index 000000000000..16f451affd1e
--- /dev/null
+++ b/drivers/net/wireless/esp8089/esp_init_data.h
@@ -0,0 +1,7 @@
+static char esp_init_data[] =
+ { 0x5, 0x0, 4, 2, 5, 5, 5, 2, 5, 0, 4, 5, 5, 4, 5, 5, 4, -2, -3, -1,
+-16, -16, -16, -32, -32, -32, 204, 1, 0xff, 0xff, 0, 0, 0, 0, 82, 78, 74, 68, 64, 56, 0,
+0, 1, 1, 2, 3, 4, 5, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 10, 0x0, 0x0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0 };
diff --git a/drivers/net/wireless/esp8089/esp_io.c b/drivers/net/wireless/esp8089/esp_io.c
new file mode 100644
index 000000000000..6c5c01aad4e5
--- /dev/null
+++ b/drivers/net/wireless/esp8089/esp_io.c
@@ -0,0 +1,639 @@
+/*
+ * Copyright (c) 2009 - 2014 Espressif System.
+ * IO interface
+ * - sdio/spi common i/f driver
+ * - target sdio hal
+ *
+ * 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.
+ */
+
+#include <linux/mmc/sdio_func.h>
+#include "esp_sif.h"
+#include "slc_host_register.h"
+#include "esp_debug.h"
+
+#ifdef SIF_DEBUG_DSR_DUMP_REG
+static void dump_slc_regs(struct slc_host_regs *regs);
+#endif /* SIF_DEBUG_DSR_DUMP_REG */
+
+int esp_common_read(struct esp_pub *epub, u8 * buf, u32 len, int sync,
+ bool noround)
+{
+ if (sync) {
+ return sif_lldesc_read_sync(epub, buf, len);
+ } else {
+ return sif_lldesc_read_raw(epub, buf, len, noround);
+ }
+}
+
+
+int esp_common_write(struct esp_pub *epub, u8 * buf, u32 len, int sync)
+{
+ if (sync) {
+ return sif_lldesc_write_sync(epub, buf, len);
+ } else {
+ return sif_lldesc_write_raw(epub, buf, len);
+ }
+}
+
+
+int esp_common_read_with_addr(struct esp_pub *epub, u32 addr, u8 * buf,
+ u32 len, int sync)
+{
+ if (sync) {
+ return sif_io_sync(epub, addr, buf, len,
+ SIF_FROM_DEVICE | SIF_SYNC |
+ SIF_BYTE_BASIS | SIF_INC_ADDR);
+ } else {
+ return sif_io_raw(epub, addr, buf, len,
+ SIF_FROM_DEVICE | SIF_BYTE_BASIS |
+ SIF_INC_ADDR);
+ }
+
+}
+
+
+int esp_common_write_with_addr(struct esp_pub *epub, u32 addr, u8 * buf,
+ u32 len, int sync)
+{
+ if (sync) {
+ return sif_io_sync(epub, addr, buf, len,
+ SIF_TO_DEVICE | SIF_SYNC |
+ SIF_BYTE_BASIS | SIF_INC_ADDR);
+ } else {
+ return sif_io_raw(epub, addr, buf, len,
+ SIF_TO_DEVICE | SIF_BYTE_BASIS |
+ SIF_INC_ADDR);
+ }
+}
+
+int esp_common_readbyte_with_addr(struct esp_pub *epub, u32 addr, u8 * buf,
+ int sync)
+{
+ if (sync) {
+ int res;
+ sif_lock_bus(epub);
+ *buf = sdio_io_readb(epub, addr, &res);
+ sif_unlock_bus(epub);
+ return res;
+ } else {
+ int res;
+ *buf = sdio_io_readb(epub, addr, &res);
+ return res;
+ }
+
+}
+
+
+
+int esp_common_writebyte_with_addr(struct esp_pub *epub, u32 addr, u8 buf,
+ int sync)
+{
+ if (sync) {
+ int res;
+ sif_lock_bus(epub);
+ sdio_io_writeb(epub, buf, addr, &res);
+ sif_unlock_bus(epub);
+ return res;
+ } else {
+ int res;
+ sdio_io_writeb(epub, buf, addr, &res);
+ return res;
+ }
+}
+
+int sif_read_reg_window(struct esp_pub *epub, unsigned int reg_addr,
+ u8 * value)
+{
+ u8 *p_tbuf = NULL;
+ int ret = 0;
+ int retry = 20;
+
+ reg_addr >>= 2;
+ if (reg_addr > 0x1f)
+ return -1;
+
+ p_tbuf = kzalloc(4, GFP_KERNEL);
+ if (p_tbuf == NULL)
+ return -ENOMEM;
+
+ p_tbuf[0] = 0x80 | (reg_addr & 0x1f);
+
+ ret =
+ esp_common_write_with_addr(epub, SLC_HOST_WIN_CMD, p_tbuf, 1,
+ ESP_SIF_NOSYNC);
+
+ if (ret == 0) {
+ do {
+ if (retry < 20)
+ mdelay(10);
+ retry--;
+ ret =
+ esp_common_read_with_addr(epub,
+ SLC_HOST_STATE_W0,
+ p_tbuf, 4,
+ ESP_SIF_NOSYNC);
+ } while (retry > 0 && ret != 0);
+ }
+
+ if (ret == 0)
+ memcpy(value, p_tbuf, 4);
+
+ kfree(p_tbuf);
+ return ret;
+}
+
+int sif_write_reg_window(struct esp_pub *epub, unsigned int reg_addr,
+ u8 * value)
+{
+ u8 *p_tbuf = NULL;
+ int ret = 0;
+
+ reg_addr >>= 2;
+ if (reg_addr > 0x1f)
+ return -1;
+
+ p_tbuf = kzalloc(8, GFP_KERNEL);
+ if (p_tbuf == NULL)
+ return -ENOMEM;
+ memcpy(p_tbuf, value, 4);
+ p_tbuf[4] = 0xc0 | (reg_addr & 0x1f);
+
+ ret =
+ esp_common_write_with_addr(epub, SLC_HOST_CONF_W5, p_tbuf, 5,
+ ESP_SIF_NOSYNC);
+
+ kfree(p_tbuf);
+ return ret;
+}
+
+int sif_ack_target_read_err(struct esp_pub *epub)
+{
+ u32 value[1];
+ int ret;
+
+ ret = sif_read_reg_window(epub, SLC_RX_LINK, (u8 *) value);
+ if (ret)
+ return ret;
+ value[0] |= SLC_RXLINK_START;
+ ret = sif_write_reg_window(epub, SLC_RX_LINK, (u8 *) value);
+ return ret;
+}
+
+int sif_had_io_enable(struct esp_pub *epub)
+{
+ u32 *p_tbuf = NULL;
+ int ret;
+
+ p_tbuf = kzalloc(sizeof(u32), GFP_KERNEL);
+ if (p_tbuf == NULL)
+ return -ENOMEM;
+
+ *p_tbuf =
+ SLC_TXEOF_ENA | (0x4 << SLC_FIFO_MAP_ENA_S) | SLC_TX_DUMMY_MODE
+ | SLC_HDA_MAP_128K | (0xFE << SLC_TX_PUSH_IDLE_NUM_S);
+ ret = sif_write_reg_window(epub, SLC_BRIDGE_CONF, (u8 *) p_tbuf);
+
+ if (ret)
+ goto _err;
+
+ *p_tbuf = 0x30;
+ ret =
+ esp_common_write_with_addr((epub), SLC_HOST_CONF_W4 + 1,
+ (u8 *) p_tbuf, 1, ESP_SIF_NOSYNC);
+
+ if (ret)
+ goto _err;
+ //set w3 0
+ *p_tbuf = 0x1;
+ ret =
+ esp_common_write_with_addr((epub), SLC_HOST_CONF_W3,
+ (u8 *) p_tbuf, 1, ESP_SIF_NOSYNC);
+
+ _err:
+ kfree(p_tbuf);
+ return ret;
+}
+
+typedef enum _SDIO_INTR_MODE {
+ SDIO_INTR_IB = 0,
+ SDIO_INTR_OOB_TOGGLE,
+ SDIO_INTR_OOB_HIGH_LEVEL,
+ SDIO_INTR_OOB_LOW_LEVEL,
+} SDIO_INTR_MODE;
+
+#define GEN_GPIO_SEL(_gpio_num, _sel_func, _intr_mode, _offset) (((_offset)<< 9 ) |((_intr_mode) << 7)|((_sel_func) << 4)|(_gpio_num))
+//bit[3:0] = gpio num, 2
+//bit[6:4] = gpio sel func, 0
+//bit[8:7] = gpio intr mode, SDIO_INTR_OOB_TOGGLE
+//bit[15:9] = register offset, 0x38
+
+u16 gpio_sel_sets[17] = {
+ GEN_GPIO_SEL(0, 0, SDIO_INTR_OOB_TOGGLE, 0x34), //GPIO0
+ GEN_GPIO_SEL(1, 3, SDIO_INTR_OOB_TOGGLE, 0x18), //U0TXD
+ GEN_GPIO_SEL(2, 0, SDIO_INTR_OOB_TOGGLE, 0x38), //GPIO2
+ GEN_GPIO_SEL(3, 3, SDIO_INTR_OOB_TOGGLE, 0x14), //U0RXD
+ GEN_GPIO_SEL(4, 0, SDIO_INTR_OOB_TOGGLE, 0x3C), //GPIO4
+ GEN_GPIO_SEL(5, 0, SDIO_INTR_OOB_TOGGLE, 0x40), //GPIO5
+ GEN_GPIO_SEL(6, 3, SDIO_INTR_OOB_TOGGLE, 0x1C), //SD_CLK
+ GEN_GPIO_SEL(7, 3, SDIO_INTR_OOB_TOGGLE, 0x20), //SD_DATA0
+ GEN_GPIO_SEL(8, 3, SDIO_INTR_OOB_TOGGLE, 0x24), //SD_DATA1
+ GEN_GPIO_SEL(9, 3, SDIO_INTR_OOB_TOGGLE, 0x28), //SD_DATA2
+ GEN_GPIO_SEL(10, 3, SDIO_INTR_OOB_TOGGLE, 0x2C), //SD_DATA3
+ GEN_GPIO_SEL(11, 3, SDIO_INTR_OOB_TOGGLE, 0x30), //SD_CMD
+ GEN_GPIO_SEL(12, 3, SDIO_INTR_OOB_TOGGLE, 0x04), //MTDI
+ GEN_GPIO_SEL(13, 3, SDIO_INTR_OOB_TOGGLE, 0x08), //MTCK
+ GEN_GPIO_SEL(14, 3, SDIO_INTR_OOB_TOGGLE, 0x0C), //MTMS
+ GEN_GPIO_SEL(15, 3, SDIO_INTR_OOB_TOGGLE, 0x10), //MTDO
+ //pls do not change sel before, if you want to change intr mode,change the one blow
+ //GEN_GPIO_SEL(2, 0, SDIO_INTR_OOB_TOGGLE, 0x38)
+ GEN_GPIO_SEL(2, 0, SDIO_INTR_OOB_LOW_LEVEL, 0x38)
+};
+
+#if defined(USE_EXT_GPIO)
+u16 gpio_forbidden = 0;
+#endif
+
+int sif_interrupt_target(struct esp_pub *epub, u8 index)
+{
+ u8 low_byte = BIT(index);
+ return esp_common_writebyte_with_addr(epub, SLC_HOST_CONF_W4 + 2,
+ low_byte, ESP_SIF_NOSYNC);
+
+}
+
+#ifdef USE_EXT_GPIO
+int sif_config_gpio_mode(struct esp_pub *epub, u8 gpio_num, u8 gpio_mode)
+{
+ u32 *p_tbuf = NULL;
+ int err;
+
+ if ((BIT(gpio_num) & gpio_forbidden) || gpio_num > 15)
+ return -EINVAL;
+
+ p_tbuf = kzalloc(sizeof(u32), GFP_KERNEL);
+ if (p_tbuf == NULL)
+ return -ENOMEM;
+ *p_tbuf = (gpio_mode << 16) | gpio_sel_sets[gpio_num];
+ err =
+ esp_common_write_with_addr(epub, SLC_HOST_CONF_W1,
+ (u8 *) p_tbuf, sizeof(u32),
+ ESP_SIF_NOSYNC);
+ kfree(p_tbuf);
+ if (err)
+ return err;
+
+ return sif_interrupt_target(epub, 4);
+}
+
+int sif_set_gpio_output(struct esp_pub *epub, u16 mask, u16 value)
+{
+ u32 *p_tbuf = NULL;
+ int err;
+
+ mask &= ~gpio_forbidden;
+ p_tbuf = kzalloc(sizeof(u32), GFP_KERNEL);
+ if (p_tbuf == NULL)
+ return -ENOMEM;
+ *p_tbuf = (mask << 16) | value;
+ err =
+ esp_common_write_with_addr(epub, SLC_HOST_CONF_W2,
+ (u8 *) p_tbuf, sizeof(u32),
+ ESP_SIF_NOSYNC);
+ kfree(p_tbuf);
+ if (err)
+ return err;
+
+ return sif_interrupt_target(epub, 5);
+}
+
+int sif_get_gpio_intr(struct esp_pub *epub, u16 intr_mask, u16 * value)
+{
+ u32 *p_tbuf = NULL;
+ int err;
+
+ p_tbuf = kzalloc(sizeof(u32), GFP_KERNEL);
+ if (p_tbuf == NULL)
+ return -ENOMEM;
+ *p_tbuf = 0;
+ err =
+ esp_common_read_with_addr(epub, SLC_HOST_CONF_W3,
+ (u8 *) p_tbuf, sizeof(u32),
+ ESP_SIF_NOSYNC);
+ if (err) {
+ kfree(p_tbuf);
+ return err;
+ }
+
+ *value = *p_tbuf & intr_mask;
+ kfree(p_tbuf);
+ if (*value == 0)
+ return 0;
+ return sif_interrupt_target(epub, 6);
+}
+
+int sif_get_gpio_input(struct esp_pub *epub, u16 * mask, u16 * value)
+{
+ u32 *p_tbuf = NULL;
+ int err;
+
+ err = sif_interrupt_target(epub, 3);
+ if (err)
+ return err;
+
+ udelay(20);
+ p_tbuf = kzalloc(sizeof(u32), GFP_KERNEL);
+ if (p_tbuf == NULL)
+ return -ENOMEM;
+ *p_tbuf = 0;
+ err =
+ esp_common_read_with_addr(epub, SLC_HOST_CONF_W3,
+ (u8 *) p_tbuf, sizeof(u32),
+ ESP_SIF_NOSYNC);
+ if (err) {
+ kfree(p_tbuf);
+ return err;
+ }
+
+ *mask = *p_tbuf >> 16;
+ *value = *p_tbuf & *mask;
+ kfree(p_tbuf);
+
+ return 0;
+}
+#endif
+
+void check_target_id(struct esp_pub *epub)
+{
+ u32 date;
+ int err = 0;
+ int i;
+
+ EPUB_CTRL_CHECK(epub, _err);
+
+ sif_lock_bus(epub);
+
+ for (i = 0; i < 4; i++) {
+ err =
+ esp_common_readbyte_with_addr(epub, SLC_HOST_DATE + i,
+ (u8 *) & date + i,
+ ESP_SIF_NOSYNC);
+ err =
+ esp_common_readbyte_with_addr(epub, SLC_HOST_ID + i,
+ (u8 *) &
+ EPUB_TO_CTRL(epub)->
+ target_id + i,
+ ESP_SIF_NOSYNC);
+ }
+
+ sif_unlock_bus(epub);
+
+ esp_dbg(ESP_DBG_LOG, "\n\n \t\t SLC data 0x%08x, ID 0x%08x\n\n",
+ date, EPUB_TO_CTRL(epub)->target_id);
+
+ switch (EPUB_TO_CTRL(epub)->target_id) {
+ case 0x100:
+ EPUB_TO_CTRL(epub)->slc_window_end_addr = 0x20000;
+ break;
+ case 0x600:
+ EPUB_TO_CTRL(epub)->slc_window_end_addr = 0x20000 - 0x800;
+
+ do {
+ u16 gpio_sel;
+ u8 low_byte = 0;
+ u8 high_byte = 0;
+ u8 byte2 = 0;
+ u8 byte3 = 0;
+#ifdef USE_OOB_INTR
+ gpio_sel = gpio_sel_sets[16];
+ low_byte = gpio_sel;
+ high_byte = gpio_sel >> 8;
+#ifdef USE_EXT_GPIO
+ gpio_forbidden |= BIT(gpio_sel & 0xf);
+#endif /* USE_EXT_GPIO */
+#endif /* USE_OOB_INTR */
+
+ if (sif_get_bt_config() == 1
+ && sif_get_rst_config() != 1) {
+ u8 gpio_num = sif_get_wakeup_gpio_config();
+ gpio_sel = gpio_sel_sets[gpio_num];
+ byte2 = gpio_sel;
+ byte3 = gpio_sel >> 8;
+#ifdef USE_EXT_GPIO
+ gpio_forbidden |= BIT(gpio_num);
+#endif
+ }
+ sif_lock_bus(epub);
+ err =
+ esp_common_writebyte_with_addr(epub,
+ SLC_HOST_CONF_W1,
+ low_byte,
+ ESP_SIF_NOSYNC);
+ err =
+ esp_common_writebyte_with_addr(epub,
+ SLC_HOST_CONF_W1
+ + 1, high_byte,
+ ESP_SIF_NOSYNC);
+ err =
+ esp_common_writebyte_with_addr(epub,
+ SLC_HOST_CONF_W1
+ + 2, byte2,
+ ESP_SIF_NOSYNC);
+ err =
+ esp_common_writebyte_with_addr(epub,
+ SLC_HOST_CONF_W1
+ + 3, byte3,
+ ESP_SIF_NOSYNC);
+ sif_unlock_bus(epub);
+ } while (0);
+ break;
+ default:
+ EPUB_TO_CTRL(epub)->slc_window_end_addr = 0x20000;
+ break;
+ }
+ _err:
+ return;
+}
+
+u32 sif_get_blksz(struct esp_pub * epub)
+{
+ EPUB_CTRL_CHECK(epub, _err);
+
+ return EPUB_TO_CTRL(epub)->slc_blk_sz;
+ _err:
+ return 512;
+}
+
+u32 sif_get_target_id(struct esp_pub * epub)
+{
+ EPUB_CTRL_CHECK(epub, _err);
+
+ return EPUB_TO_CTRL(epub)->target_id;
+ _err:
+ return 0x600;
+}
+
+void sif_dsr(struct sdio_func *func)
+{
+ struct esp_sdio_ctrl *sctrl = sdio_get_drvdata(func);
+ static int dsr_cnt = 0, real_intr_cnt = 0, bogus_intr_cnt = 0;
+ struct slc_host_regs *regs = &(sctrl->slc_regs);
+ esp_dbg(ESP_DBG_TRACE, " %s enter %d \n", __func__, dsr_cnt++);
+
+ sdio_release_host(sctrl->func);
+
+
+ sif_lock_bus(sctrl->epub);
+
+
+ do {
+ int ret = 0;
+
+ memset(regs, 0x0, sizeof(struct slc_host_regs));
+
+ ret =
+ esp_common_read_with_addr(sctrl->epub,
+ REG_SLC_HOST_BASE + 8,
+ (u8 *) regs,
+ sizeof(struct slc_host_regs),
+ ESP_SIF_NOSYNC);
+
+ if ((regs->intr_raw & SLC_HOST_RX_ST) && (ret == 0)) {
+ esp_dbg(ESP_DBG_TRACE, "%s eal intr cnt: %d",
+ __func__, ++real_intr_cnt);
+
+ esp_dsr(sctrl->epub);
+
+ } else {
+ sif_unlock_bus(sctrl->epub);
+
+ esp_dbg(ESP_DBG_TRACE, "%s bogus_intr_cnt %d\n",
+ __func__, ++bogus_intr_cnt);
+ }
+
+#ifdef SIF_DEBUG_DSR_DUMP_REG
+ dump_slc_regs(regs);
+#endif /* SIF_DEBUG_DUMP_DSR */
+
+ } while (0);
+
+ sdio_claim_host(func);
+
+ atomic_set(&sctrl->irq_handling, 0);
+}
+
+
+struct slc_host_regs *sif_get_regs(struct esp_pub *epub)
+{
+ EPUB_CTRL_CHECK(epub, _err);
+
+ return &EPUB_TO_CTRL(epub)->slc_regs;
+ _err:
+ return NULL;
+}
+
+void sif_disable_target_interrupt(struct esp_pub *epub)
+{
+ EPUB_FUNC_CHECK(epub, _exit);
+ sif_lock_bus(epub);
+#ifdef HOST_RESET_BUG
+ mdelay(10);
+#endif
+ memset(EPUB_TO_CTRL(epub)->dma_buffer, 0x00, sizeof(u32));
+ esp_common_write_with_addr(epub, SLC_HOST_INT_ENA,
+ EPUB_TO_CTRL(epub)->dma_buffer,
+ sizeof(u32), ESP_SIF_NOSYNC);
+#ifdef HOST_RESET_BUG
+ mdelay(10);
+#endif
+
+ sif_unlock_bus(epub);
+
+ mdelay(1);
+
+ sif_lock_bus(epub);
+ sif_interrupt_target(epub, 7);
+ sif_unlock_bus(epub);
+ _exit:
+ return;
+}
+
+#ifdef SIF_DEBUG_DSR_DUMP_REG
+static void dump_slc_regs(struct slc_host_regs *regs)
+{
+ esp_dbg(ESP_DBG_TRACE, "\n\n ------- %s --------------\n",
+ __func__);
+
+ esp_dbg(ESP_DBG_TRACE, " \
+ intr_raw 0x%08X \t \n \
+ state_w0 0x%08X \t state_w1 0x%08X \n \
+ config_w0 0x%08X \t config_w1 0x%08X \n \
+ intr_status 0x%08X \t config_w2 0x%08X \n \
+ config_w3 0x%08X \t config_w4 0x%08X \n \
+ token_wdata 0x%08X \t intr_clear 0x%08X \n \
+ intr_enable 0x%08X \n\n", regs->intr_raw, regs->state_w0, regs->state_w1, regs->config_w0, regs->config_w1, regs->intr_status, regs->config_w2, regs->config_w3, regs->config_w4, regs->token_wdata, regs->intr_clear, regs->intr_enable);
+}
+#endif /* SIF_DEBUG_DSR_DUMP_REG */
+
+static int bt_config = 0;
+void sif_record_bt_config(int value)
+{
+ bt_config = value;
+}
+
+int sif_get_bt_config(void)
+{
+ return bt_config;
+}
+
+static int rst_config = 0;
+void sif_record_rst_config(int value)
+{
+ rst_config = value;
+}
+
+int sif_get_rst_config(void)
+{
+ return rst_config;
+}
+
+static int ate_test = 0;
+void sif_record_ate_config(int value)
+{
+ ate_test = value;
+}
+
+int sif_get_ate_config(void)
+{
+ return ate_test;
+}
+
+static int retry_reset = 0;
+void sif_record_retry_config(void)
+{
+ retry_reset = 1;
+}
+
+int sif_get_retry_config(void)
+{
+ return retry_reset;
+}
+
+static int wakeup_gpio = 12;
+void sif_record_wakeup_gpio_config(int value)
+{
+ wakeup_gpio = value;
+}
+
+int sif_get_wakeup_gpio_config(void)
+{
+ return wakeup_gpio;
+}
diff --git a/drivers/net/wireless/esp8089/esp_mac80211.c b/drivers/net/wireless/esp8089/esp_mac80211.c
new file mode 100644
index 000000000000..3c8a5ab9444f
--- /dev/null
+++ b/drivers/net/wireless/esp8089/esp_mac80211.c
@@ -0,0 +1,1728 @@
+/*
+ * Copyright (c) 2011-2014 Espressif System.
+ *
+ * MAC80211 support module
+ *
+ * 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.
+ */
+
+#include <linux/etherdevice.h>
+#include <linux/workqueue.h>
+#include <linux/nl80211.h>
+#include <linux/ieee80211.h>
+#include <linux/slab.h>
+#include <net/cfg80211.h>
+#include <net/mac80211.h>
+#include <linux/version.h>
+#include <net/regulatory.h>
+#include "esp_pub.h"
+#include "esp_sip.h"
+#include "esp_ctrl.h"
+#include "esp_sif.h"
+#include "esp_debug.h"
+#include "esp_wl.h"
+#include "esp_utils.h"
+
+#define ESP_IEEE80211_DBG esp_dbg
+
+#define GET_NEXT_SEQ(seq) (((seq) +1) & 0x0fff)
+
+static u8 esp_mac_addr[ETH_ALEN * 2];
+static u8 getaddr_index(u8 * addr, struct esp_pub *epub);
+
+static
+void
+esp_op_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control,
+ struct sk_buff *skb)
+{
+ struct esp_pub *epub = (struct esp_pub *) hw->priv;
+
+ ESP_IEEE80211_DBG(ESP_DBG_LOG, "%s enter\n", __func__);
+ if (!mod_support_no_txampdu() &&
+ cfg80211_get_chandef_type(&epub->hw->conf.chandef) !=
+ NL80211_CHAN_NO_HT) {
+ struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_hdr *wh =
+ (struct ieee80211_hdr *) skb->data;
+ if (ieee80211_is_data_qos(wh->frame_control)) {
+ if (!(tx_info->flags & IEEE80211_TX_CTL_AMPDU)) {
+ u8 tidno =
+ ieee80211_get_qos_ctl(wh)[0] &
+ IEEE80211_QOS_CTL_TID_MASK;
+ struct esp_node *node =
+ esp_get_node_by_addr(epub, wh->addr1);
+ {
+ struct esp_tx_tid *tid =
+ &node->tid[tidno];
+ //record ssn
+ spin_lock_bh(&epub->tx_ampdu_lock);
+ tid->ssn =
+ GET_NEXT_SEQ(le16_to_cpu
+ (wh->
+ seq_ctrl) >> 4);
+ ESP_IEEE80211_DBG(ESP_DBG_TRACE,
+ "tidno:%u,ssn:%u\n",
+ tidno, tid->ssn);
+ spin_unlock_bh(&epub->
+ tx_ampdu_lock);
+ }
+ } else {
+ ESP_IEEE80211_DBG(ESP_DBG_TRACE,
+ "tx ampdu pkt, sn:%u, %u\n",
+ le16_to_cpu(wh->
+ seq_ctrl) >>
+ 4, skb->len);
+ }
+ }
+ }
+#ifdef GEN_ERR_CHECKSUM
+ esp_gen_err_checksum(skb);
+#endif
+
+ sip_tx_data_pkt_enqueue(epub, skb);
+ if (epub)
+ ieee80211_queue_work(hw, &epub->tx_work);
+}
+
+static int esp_op_start(struct ieee80211_hw *hw)
+{
+ struct esp_pub *epub;
+
+ ESP_IEEE80211_DBG(ESP_DBG_OP, "%s\n", __func__);
+
+ if (!hw) {
+ ESP_IEEE80211_DBG(ESP_DBG_ERROR, "%s no hw!\n", __func__);
+ return -EINVAL;
+ }
+
+ epub = (struct esp_pub *) hw->priv;
+
+ if (!epub) {
+ ESP_IEEE80211_DBG(ESP_DBG_ERROR, "%s no epub!\n",
+ __func__);
+ return EINVAL;
+ }
+ /*add rfkill poll function */
+
+ atomic_set(&epub->wl.off, 0);
+ wiphy_rfkill_start_polling(hw->wiphy);
+ return 0;
+}
+
+static void esp_op_stop(struct ieee80211_hw *hw)
+{
+ struct esp_pub *epub;
+
+ ESP_IEEE80211_DBG(ESP_DBG_OP, "%s\n", __func__);
+
+ if (!hw) {
+ ESP_IEEE80211_DBG(ESP_DBG_ERROR, "%s no hw!\n", __func__);
+ return;
+ }
+
+ epub = (struct esp_pub *) hw->priv;
+
+ if (!epub) {
+ ESP_IEEE80211_DBG(ESP_DBG_ERROR, "%s no epub!\n",
+ __func__);
+ return;
+ }
+
+ atomic_set(&epub->wl.off, 1);
+
+#ifdef HOST_RESET_BUG
+ mdelay(200);
+#endif
+
+ if (epub->wl.scan_req) {
+ hw_scan_done(epub, true);
+ epub->wl.scan_req = NULL;
+ //msleep(2);
+ }
+}
+
+#ifdef CONFIG_PM
+static int esp_op_suspend(struct ieee80211_hw *hw,
+ struct cfg80211_wowlan *wowlan)
+{
+ esp_dbg(ESP_DBG_OP, "%s\n", __func__);
+
+ return 0;
+}
+
+static int esp_op_resume(struct ieee80211_hw *hw)
+{
+ esp_dbg(ESP_DBG_OP, "%s\n", __func__);
+
+ return 0;
+}
+#endif //CONFIG_PM
+
+static int esp_op_add_interface(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ struct esp_pub *epub = (struct esp_pub *) hw->priv;
+ struct esp_vif *evif = (struct esp_vif *) vif->drv_priv;
+ struct sip_cmd_setvif svif;
+
+ ESP_IEEE80211_DBG(ESP_DBG_OP, "%s enter: type %d, addr %pM\n",
+ __func__, vif->type, vif->addr);
+
+ memset(&svif, 0, sizeof(struct sip_cmd_setvif));
+ memcpy(svif.mac, vif->addr, ETH_ALEN);
+ evif->index = svif.index = getaddr_index(vif->addr, epub);
+ evif->epub = epub;
+ epub->vif = vif;
+ svif.set = 1;
+ if ((1 << svif.index) & epub->vif_slot) {
+ ESP_IEEE80211_DBG(ESP_DBG_ERROR,
+ "%s interface %d already used\n",
+ __func__, svif.index);
+ return -EOPNOTSUPP;
+ }
+ epub->vif_slot |= 1 << svif.index;
+
+ if (svif.index == ESP_PUB_MAX_VIF) {
+ ESP_IEEE80211_DBG(ESP_DBG_ERROR,
+ "%s only support MAX %d interface\n",
+ __func__, ESP_PUB_MAX_VIF);
+ return -EOPNOTSUPP;
+ }
+
+ switch (vif->type) {
+ case NL80211_IFTYPE_STATION:
+ //if (svif.index == 1)
+ // vif->type = NL80211_IFTYPE_UNSPECIFIED;
+ ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s STA \n", __func__);
+ svif.op_mode = 0;
+ svif.is_p2p = 0;
+ break;
+ case NL80211_IFTYPE_AP:
+ ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s AP \n", __func__);
+ svif.op_mode = 1;
+ svif.is_p2p = 0;
+ break;
+ case NL80211_IFTYPE_P2P_CLIENT:
+ ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s P2P_CLIENT \n", __func__);
+ svif.op_mode = 0;
+ svif.is_p2p = 1;
+ break;
+ case NL80211_IFTYPE_P2P_GO:
+ ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s P2P_GO \n", __func__);
+ svif.op_mode = 1;
+ svif.is_p2p = 1;
+ break;
+ case NL80211_IFTYPE_UNSPECIFIED:
+ case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_AP_VLAN:
+ case NL80211_IFTYPE_WDS:
+ case NL80211_IFTYPE_MONITOR:
+ default:
+ ESP_IEEE80211_DBG(ESP_DBG_ERROR,
+ "%s does NOT support type %d\n",
+ __func__, vif->type);
+ return -EOPNOTSUPP;
+ }
+
+ sip_cmd(epub, SIP_CMD_SETVIF, (u8 *) & svif,
+ sizeof(struct sip_cmd_setvif));
+ return 0;
+}
+
+static int esp_op_change_interface(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ enum nl80211_iftype new_type, bool p2p)
+{
+ struct esp_pub *epub = (struct esp_pub *) hw->priv;
+ struct esp_vif *evif = (struct esp_vif *) vif->drv_priv;
+ struct sip_cmd_setvif svif;
+ ESP_IEEE80211_DBG(ESP_DBG_OP, "%s enter,change to if:%d \n",
+ __func__, new_type);
+
+ if (new_type == NL80211_IFTYPE_AP) {
+ ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s enter,change to AP \n",
+ __func__);
+ }
+
+ if (vif->type != new_type) {
+ ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s type from %d to %d\n",
+ __func__, vif->type, new_type);
+ }
+
+ memset(&svif, 0, sizeof(struct sip_cmd_setvif));
+ memcpy(svif.mac, vif->addr, ETH_ALEN);
+ svif.index = evif->index;
+ svif.set = 2;
+
+ switch (new_type) {
+ case NL80211_IFTYPE_STATION:
+ svif.op_mode = 0;
+ svif.is_p2p = p2p;
+ break;
+ case NL80211_IFTYPE_AP:
+ svif.op_mode = 1;
+ svif.is_p2p = p2p;
+ break;
+ case NL80211_IFTYPE_P2P_CLIENT:
+ svif.op_mode = 0;
+ svif.is_p2p = 1;
+ break;
+ case NL80211_IFTYPE_P2P_GO:
+ svif.op_mode = 1;
+ svif.is_p2p = 1;
+ break;
+ case NL80211_IFTYPE_UNSPECIFIED:
+ case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_AP_VLAN:
+ case NL80211_IFTYPE_WDS:
+ case NL80211_IFTYPE_MONITOR:
+ default:
+ ESP_IEEE80211_DBG(ESP_DBG_ERROR,
+ "%s does NOT support type %d\n",
+ __func__, vif->type);
+ return -EOPNOTSUPP;
+ }
+ sip_cmd(epub, SIP_CMD_SETVIF, (u8 *) & svif,
+ sizeof(struct sip_cmd_setvif));
+ return 0;
+}
+
+static void esp_op_remove_interface(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ struct esp_pub *epub = (struct esp_pub *) hw->priv;
+ struct esp_vif *evif = (struct esp_vif *) vif->drv_priv;
+ struct sip_cmd_setvif svif;
+
+ ESP_IEEE80211_DBG(ESP_DBG_OP,
+ "%s enter, vif addr %pM, beacon enable %x\n",
+ __func__, vif->addr,
+ vif->bss_conf.enable_beacon);
+
+ memset(&svif, 0, sizeof(struct sip_cmd_setvif));
+ svif.index = evif->index;
+ epub->vif_slot &= ~(1 << svif.index);
+
+ if (evif->ap_up) {
+ evif->beacon_interval = 0;
+ del_timer_sync(&evif->beacon_timer);
+ evif->ap_up = false;
+ }
+ epub->vif = NULL;
+ evif->epub = NULL;
+
+ sip_cmd(epub, SIP_CMD_SETVIF, (u8 *) & svif,
+ sizeof(struct sip_cmd_setvif));
+
+ /* clean up tx/rx queue */
+
+}
+
+#define BEACON_TIM_SAVE_MAX 20
+u8 beacon_tim_saved[BEACON_TIM_SAVE_MAX];
+int beacon_tim_count;
+static void beacon_tim_init(void)
+{
+ memset(beacon_tim_saved, 0, BEACON_TIM_SAVE_MAX);
+ beacon_tim_count = 0;
+}
+
+static u8 beacon_tim_save(u8 this_tim)
+{
+ u8 all_tim = 0;
+ int i;
+ beacon_tim_saved[beacon_tim_count] = this_tim;
+ if (++beacon_tim_count >= BEACON_TIM_SAVE_MAX)
+ beacon_tim_count = 0;
+ for (i = 0; i < BEACON_TIM_SAVE_MAX; i++)
+ all_tim |= beacon_tim_saved[i];
+ return all_tim;
+}
+
+static bool beacon_tim_alter(struct sk_buff *beacon)
+{
+ u8 *p, *tim_end;
+ u8 tim_count;
+ int len;
+ int remain_len;
+ struct ieee80211_mgmt *mgmt;
+
+ if (beacon == NULL)
+ return false;
+
+ mgmt = (struct ieee80211_mgmt *) ((u8 *) beacon->data);
+
+ remain_len =
+ beacon->len - ((u8 *) mgmt->u.beacon.variable - (u8 *) mgmt +
+ 12);
+ p = mgmt->u.beacon.variable;
+
+ while (remain_len > 0) {
+ len = *(++p);
+ if (*p == WLAN_EID_TIM) { // tim field
+ tim_end = p + len;
+ tim_count = *(++p);
+ p += 2;
+ //multicast
+ if (tim_count == 0)
+ *p |= 0x1;
+ if ((*p & 0xfe) == 0 && tim_end >= p + 1) { // we only support 8 sta in this case
+ p++;
+ *p = beacon_tim_save(*p);
+ }
+ return tim_count == 0;
+ }
+ p += (len + 1);
+ remain_len -= (2 + len);
+ }
+
+ return false;
+}
+
+unsigned long init_jiffies;
+unsigned long cycle_beacon_count;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0))
+static void drv_handle_beacon(struct timer_list *t)
+#else
+static void drv_handle_beacon(unsigned long data)
+#endif
+{
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0))
+ struct esp_vif *evif = from_timer(evif, t, beacon_timer);
+ struct ieee80211_vif *vif = evif->epub->vif;
+#else
+ struct ieee80211_vif *vif = (struct ieee80211_vif *) data;
+ struct esp_vif *evif = (struct esp_vif *) vif->drv_priv;
+#endif
+ struct sk_buff *beacon;
+ struct sk_buff *skb;
+ static int dbgcnt = 0;
+ bool tim_reach = false;
+
+ if (evif->epub == NULL)
+ return;
+
+ mdelay(2400 * (cycle_beacon_count % 25) % 10000 / 1000);
+
+ beacon = ieee80211_beacon_get(evif->epub->hw, vif, 0);
+
+ tim_reach = beacon_tim_alter(beacon);
+
+ if (beacon && !(dbgcnt++ % 600)) {
+ ESP_IEEE80211_DBG(ESP_DBG_TRACE, " beacon length:%d,fc:0x%x\n",
+ beacon->len,
+ ((struct ieee80211_mgmt *) (beacon->
+ data))->
+ frame_control);
+
+ }
+
+ if (beacon)
+ sip_tx_data_pkt_enqueue(evif->epub, beacon);
+
+ if (cycle_beacon_count++ == 100) {
+ init_jiffies = jiffies;
+ cycle_beacon_count -= 100;
+ }
+ mod_timer(&evif->beacon_timer,
+ init_jiffies +
+ msecs_to_jiffies(cycle_beacon_count *
+ vif->bss_conf.beacon_int * 1024 /
+ 1000));
+ //FIXME:the packets must be sent at home channel
+ //send buffer mcast frames
+ if (tim_reach) {
+ skb = ieee80211_get_buffered_bc(evif->epub->hw, vif);
+ while (skb) {
+ sip_tx_data_pkt_enqueue(evif->epub, skb);
+ skb =
+ ieee80211_get_buffered_bc(evif->epub->hw, vif);
+ }
+ }
+}
+
+static void init_beacon_timer(struct ieee80211_vif *vif)
+{
+ struct esp_vif *evif = (struct esp_vif *) vif->drv_priv;
+
+ ESP_IEEE80211_DBG(ESP_DBG_OP, " %s enter: beacon interval %x\n",
+ __func__, evif->beacon_interval);
+
+ beacon_tim_init();
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0))
+ timer_setup(&evif->beacon_timer, drv_handle_beacon, 0);
+#else
+ init_timer(&evif->beacon_timer); //TBD, not init here...
+ evif->beacon_timer.data = (unsigned long) vif;
+ evif->beacon_timer.function = drv_handle_beacon;
+#endif
+ cycle_beacon_count = 1;
+ init_jiffies = jiffies;
+ evif->beacon_timer.expires =
+ init_jiffies +
+ msecs_to_jiffies(cycle_beacon_count *
+ vif->bss_conf.beacon_int * 1024 / 1000);
+ add_timer(&evif->beacon_timer);
+}
+
+static int esp_op_config(struct ieee80211_hw *hw, u32 changed)
+{
+ //struct ieee80211_conf *conf = &hw->conf;
+
+ struct esp_pub *epub = (struct esp_pub *) hw->priv;
+
+ ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s enter 0x%08x\n", __func__,
+ changed);
+
+ if (changed &
+ (IEEE80211_CONF_CHANGE_CHANNEL | IEEE80211_CONF_CHANGE_IDLE)) {
+ sip_send_config(epub, &hw->conf);
+ }
+
+ return 0;
+}
+
+static void esp_op_bss_info_changed(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *info,
+ u64 changed)
+{
+ struct esp_pub *epub = (struct esp_pub *) hw->priv;
+ struct esp_vif *evif = (struct esp_vif *) vif->drv_priv;
+
+ // ieee80211_bss_conf(include/net/mac80211.h) is included in ieee80211_sub_if_data(net/mac80211/ieee80211_i.h) , does bssid=ieee80211_if_ap's ssid ?
+ // in 2.6.27, ieee80211_sub_if_data has ieee80211_bss_conf while in 2.6.32 ieee80211_sub_if_data don't have ieee80211_bss_conf
+ // in 2.6.27, ieee80211_bss_conf->enable_beacon don't exist, does it mean it support beacon always?
+ // ESP_IEEE80211_DBG(ESP_DBG_OP, " %s enter: vif addr %pM, changed %x, assoc %x, bssid %pM\n", __func__, vif->addr, changed, vif->cfg.assoc, info->bssid);
+ // sdata->u.sta.bssid
+
+ ESP_IEEE80211_DBG(ESP_DBG_OP,
+ " %s enter: changed %x, assoc %x, bssid %pM\n",
+ __func__, changed, vif->cfg.assoc, info->bssid);
+
+ if (vif->type == NL80211_IFTYPE_STATION) {
+ if ((changed & BSS_CHANGED_BSSID) ||
+ ((changed & BSS_CHANGED_ASSOC) && (vif->cfg.assoc))) {
+ ESP_IEEE80211_DBG(ESP_DBG_TRACE,
+ " %s STA change bssid or assoc\n",
+ __func__);
+ evif->beacon_interval = vif->cfg.aid;
+ memcpy(epub->wl.bssid, (u8 *) info->bssid,
+ ETH_ALEN);
+ sip_send_bss_info_update(epub, evif,
+ (u8 *) info->bssid,
+ vif->cfg.assoc);
+ } else if ((changed & BSS_CHANGED_ASSOC) && (!vif->cfg.assoc)) {
+ ESP_IEEE80211_DBG(ESP_DBG_TRACE,
+ " %s STA change disassoc\n",
+ __func__);
+ evif->beacon_interval = 0;
+ memset(epub->wl.bssid, 0, ETH_ALEN);
+ sip_send_bss_info_update(epub, evif,
+ (u8 *) info->bssid,
+ vif->cfg.assoc);
+ } else {
+ ESP_IEEE80211_DBG(ESP_DBG_TRACE,
+ "%s wrong mode of STA mode\n",
+ __func__);
+ }
+ } else if (vif->type == NL80211_IFTYPE_AP) {
+ if ((changed & BSS_CHANGED_BEACON_ENABLED) ||
+ (changed & BSS_CHANGED_BEACON_INT)) {
+ ESP_IEEE80211_DBG(ESP_DBG_TRACE,
+ " %s AP change enable %d, interval is %d, bssid %pM\n",
+ __func__, info->enable_beacon,
+ info->beacon_int, info->bssid);
+ if (info->enable_beacon && evif->ap_up != true) {
+ evif->beacon_interval = info->beacon_int;
+ init_beacon_timer(vif);
+ sip_send_bss_info_update(epub, evif,
+ (u8 *) info->
+ bssid, 2);
+ evif->ap_up = true;
+ } else if (!info->enable_beacon && evif->ap_up &&
+ !(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)
+ ) {
+ ESP_IEEE80211_DBG(ESP_DBG_TRACE,
+ " %s AP disable beacon, interval is %d\n",
+ __func__,
+ info->beacon_int);
+ evif->beacon_interval = 0;
+ del_timer_sync(&evif->beacon_timer);
+ sip_send_bss_info_update(epub, evif,
+ (u8 *) info->
+ bssid, 2);
+ evif->ap_up = false;
+ }
+ }
+ } else {
+ ESP_IEEE80211_DBG(ESP_DBG_ERROR,
+ "%s op mode unspecified\n", __func__);
+ }
+}
+
+
+static u64 esp_op_prepare_multicast(struct ieee80211_hw *hw,
+ struct netdev_hw_addr_list *mc_list)
+{
+ ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s enter \n", __func__);
+
+ return 0;
+}
+
+static void esp_op_configure_filter(struct ieee80211_hw *hw,
+ unsigned int changed_flags,
+ unsigned int *total_flags,
+ u64 multicast)
+{
+ struct esp_pub *epub = (struct esp_pub *) hw->priv;
+
+ ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s enter \n", __func__);
+
+ epub->rx_filter = 0;
+
+ if (*total_flags & FIF_ALLMULTI)
+ epub->rx_filter |= FIF_ALLMULTI;
+
+ *total_flags = epub->rx_filter;
+}
+
+static int esp_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key)
+{
+ u8 i;
+ int ret;
+ struct esp_pub *epub = (struct esp_pub *) hw->priv;
+ struct esp_vif *evif = (struct esp_vif *) vif->drv_priv;
+ u8 ifidx = evif->index;
+ u8 *peer_addr, isvalid;
+
+ ESP_IEEE80211_DBG(ESP_DBG_OP,
+ "%s enter, flags = %x keyindx = %x cmd = %x mac = %pM cipher = %x\n",
+ __func__, key->flags, key->keyidx, cmd,
+ vif->addr, key->cipher);
+
+ key->flags = key->flags | IEEE80211_KEY_FLAG_GENERATE_IV;
+
+ if (sta) {
+ if (memcmp(sta->addr, epub->wl.bssid, ETH_ALEN))
+ peer_addr = sta->addr;
+ else
+ peer_addr = epub->wl.bssid;
+ } else {
+ peer_addr = epub->wl.bssid;
+ }
+ isvalid = (cmd == SET_KEY) ? 1 : 0;
+
+ if ((key->flags & IEEE80211_KEY_FLAG_PAIRWISE)
+ || (key->cipher == WLAN_CIPHER_SUITE_WEP40
+ || key->cipher == WLAN_CIPHER_SUITE_WEP104)) {
+ if (isvalid) {
+ for (i = 0; i < 19; i++) {
+ if (epub->hi_map[i].flag == 0) {
+ epub->hi_map[i].flag = 1;
+ key->hw_key_idx = i + 6;
+ memcpy(epub->hi_map[i].mac,
+ peer_addr, ETH_ALEN);
+ break;
+ }
+ }
+ } else {
+ u8 index = key->hw_key_idx - 6;
+ epub->hi_map[index].flag = 0;
+ memset(epub->hi_map[index].mac, 0, ETH_ALEN);
+ }
+ } else {
+ if (isvalid) {
+ for (i = 0; i < 2; i++)
+ if (epub->low_map[ifidx][i].flag == 0) {
+ epub->low_map[ifidx][i].flag = 1;
+ key->hw_key_idx =
+ i + ifidx * 2 + 2;
+ memcpy(epub->low_map[ifidx][i].mac,
+ peer_addr, ETH_ALEN);
+ break;
+ }
+ } else {
+ u8 index = key->hw_key_idx - 2 - ifidx * 2;
+ epub->low_map[ifidx][index].flag = 0;
+ memset(epub->low_map[ifidx][index].mac, 0,
+ ETH_ALEN);
+ }
+ //key->hw_key_idx = key->keyidx + ifidx * 2 + 1;
+ }
+
+ if (key->hw_key_idx >= 6) {
+ /*send sub_scan task to target */
+ //epub->wl.ptk = (cmd==SET_KEY) ? key : NULL;
+ if (isvalid)
+ atomic_inc(&epub->wl.ptk_cnt);
+ else
+ atomic_dec(&epub->wl.ptk_cnt);
+ if (key->cipher == WLAN_CIPHER_SUITE_WEP40
+ || key->cipher == WLAN_CIPHER_SUITE_WEP104) {
+ if (isvalid)
+ atomic_inc(&epub->wl.gtk_cnt);
+ else
+ atomic_dec(&epub->wl.gtk_cnt);
+ }
+ } else {
+ /*send sub_scan task to target */
+ if (isvalid)
+ atomic_inc(&epub->wl.gtk_cnt);
+ else
+ atomic_dec(&epub->wl.gtk_cnt);
+
+ if ((key->cipher == WLAN_CIPHER_SUITE_WEP40
+ || key->cipher == WLAN_CIPHER_SUITE_WEP104)) {
+ if (isvalid)
+ atomic_inc(&epub->wl.ptk_cnt);
+ else
+ atomic_dec(&epub->wl.ptk_cnt);
+ //epub->wl.ptk = (cmd==SET_KEY) ? key : NULL;
+ }
+ }
+
+ ret = sip_send_setkey(epub, ifidx, peer_addr, key, isvalid);
+
+ if ((key->cipher == WLAN_CIPHER_SUITE_TKIP
+ || key->cipher == WLAN_CIPHER_SUITE_TKIP)) {
+ if (ret == 0)
+ atomic_set(&epub->wl.tkip_key_set, 1);
+ }
+
+ ESP_IEEE80211_DBG(ESP_DBG_OP, "%s exit\n", __func__);
+ return ret;
+}
+
+static void esp_op_update_tkip_key(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_key_conf *conf,
+ struct ieee80211_sta *sta,
+ u32 iv32, u16 * phase1key)
+{
+ ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s enter \n", __func__);
+
+}
+
+void hw_scan_done(struct esp_pub *epub, bool aborted)
+{
+ cancel_delayed_work_sync(&epub->scan_timeout_work);
+
+ ESSERT(epub->wl.scan_req != NULL);
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0))
+ {
+ struct cfg80211_scan_info info = {
+ .aborted = aborted,
+ };
+
+ ieee80211_scan_completed(epub->hw, &info);
+ }
+#else
+ ieee80211_scan_completed(epub->hw, aborted);
+#endif
+ if (test_and_clear_bit(ESP_WL_FLAG_STOP_TXQ, &epub->wl.flags)) {
+ sip_trigger_txq_process(epub->sip);
+ }
+}
+
+static void hw_scan_timeout_report(struct work_struct *work)
+{
+ struct esp_pub *epub =
+ container_of(work, struct esp_pub, scan_timeout_work.work);
+ bool aborted;
+
+ ESP_IEEE80211_DBG(ESP_DBG_TRACE, "eagle hw scan done\n");
+
+ if (test_and_clear_bit(ESP_WL_FLAG_STOP_TXQ, &epub->wl.flags)) {
+ sip_trigger_txq_process(epub->sip);
+ }
+ /*check if normally complete or aborted like timeout/hw error */
+ aborted = (epub->wl.scan_req) ? true : false;
+
+ if (aborted == true) {
+ epub->wl.scan_req = NULL;
+ }
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0))
+ {
+ struct cfg80211_scan_info info = {
+ .aborted = aborted,
+ };
+
+ ieee80211_scan_completed(epub->hw, &info);
+ }
+#else
+ ieee80211_scan_completed(epub->hw, aborted);
+#endif
+}
+
+static int esp_op_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
+{
+ ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s enter \n", __func__);
+
+ return 0;
+}
+
+static int esp_node_attach(struct ieee80211_hw *hw, u8 ifidx,
+ struct ieee80211_sta *sta)
+{
+ struct esp_pub *epub = (struct esp_pub *) hw->priv;
+ struct esp_node *node;
+ u8 tidno;
+ struct esp_tx_tid *tid;
+ int i;
+
+ spin_lock_bh(&epub->tx_ampdu_lock);
+
+ if (hweight32(epub->enodes_maps[ifidx]) < ESP_PUB_MAX_STA
+ && (i = ffz(epub->enodes_map)) < ESP_PUB_MAX_STA + 1) {
+ epub->enodes_map |= (1 << i);
+ epub->enodes_maps[ifidx] |= (1 << i);
+ node = (struct esp_node *) sta->drv_priv;
+ epub->enodes[i] = node;
+ node->sta = sta;
+ node->ifidx = ifidx;
+ node->index = i;
+
+ for (tidno = 0, tid = &node->tid[tidno];
+ tidno < WME_NUM_TID; tidno++) {
+ tid->ssn = 0;
+ tid->cnt = 0;
+ tid->state = ESP_TID_STATE_INIT;
+ }
+
+
+ } else {
+ i = -1;
+ }
+
+ spin_unlock_bh(&epub->tx_ampdu_lock);
+ return i;
+}
+
+static int esp_node_detach(struct ieee80211_hw *hw, u8 ifidx,
+ struct ieee80211_sta *sta)
+{
+ struct esp_pub *epub = (struct esp_pub *) hw->priv;
+ u32 map;
+ int i;
+ struct esp_node *node = NULL;
+
+ spin_lock_bh(&epub->tx_ampdu_lock);
+ map = epub->enodes_maps[ifidx];
+ while (map != 0) {
+ i = ffs(map) - 1;
+ if (epub->enodes[i]->sta == sta) {
+ epub->enodes[i]->sta = NULL;
+ node = epub->enodes[i];
+ epub->enodes[i] = NULL;
+ epub->enodes_map &= ~(1 << i);
+ epub->enodes_maps[ifidx] &= ~(1 << i);
+
+ spin_unlock_bh(&epub->tx_ampdu_lock);
+ return i;
+ }
+ map &= ~(1 << i);
+ }
+
+ spin_unlock_bh(&epub->tx_ampdu_lock);
+ return -1;
+}
+
+struct esp_node *esp_get_node_by_addr(struct esp_pub *epub,
+ const u8 * addr)
+{
+ int i;
+ u32 map;
+ struct esp_node *node = NULL;
+ if (addr == NULL)
+ return NULL;
+ spin_lock_bh(&epub->tx_ampdu_lock);
+ map = epub->enodes_map;
+ while (map != 0) {
+ i = ffs(map) - 1;
+ if (i < 0) {
+ spin_unlock_bh(&epub->tx_ampdu_lock);
+ return NULL;
+ }
+ map &= ~(1 << i);
+ if (memcmp(epub->enodes[i]->sta->addr, addr, ETH_ALEN) ==
+ 0) {
+ node = epub->enodes[i];
+ break;
+ }
+ }
+
+ spin_unlock_bh(&epub->tx_ampdu_lock);
+ return node;
+}
+
+struct esp_node *esp_get_node_by_index(struct esp_pub *epub, u8 index)
+{
+ u32 map;
+ struct esp_node *node = NULL;
+
+ if (epub == NULL)
+ return NULL;
+
+ spin_lock_bh(&epub->tx_ampdu_lock);
+ map = epub->enodes_map;
+ if (map & BIT(index)) {
+ node = epub->enodes[index];
+ } else {
+ spin_unlock_bh(&epub->tx_ampdu_lock);
+ return NULL;
+ }
+
+ spin_unlock_bh(&epub->tx_ampdu_lock);
+ return node;
+}
+
+int esp_get_empty_rxampdu(struct esp_pub *epub, const u8 * addr, u8 tid)
+{
+ int index = -1;
+ if (addr == NULL)
+ return index;
+ spin_lock_bh(&epub->rx_ampdu_lock);
+ if ((index = ffz(epub->rxampdu_map)) < ESP_PUB_MAX_RXAMPDU) {
+ epub->rxampdu_map |= BIT(index);
+ epub->rxampdu_node[index] =
+ esp_get_node_by_addr(epub, addr);
+ epub->rxampdu_tid[index] = tid;
+ } else {
+ index = -1;
+ }
+ spin_unlock_bh(&epub->rx_ampdu_lock);
+ return index;
+}
+
+int esp_get_exist_rxampdu(struct esp_pub *epub, const u8 * addr, u8 tid)
+{
+ u8 map;
+ int index = -1;
+ int i;
+ if (addr == NULL)
+ return index;
+ spin_lock_bh(&epub->rx_ampdu_lock);
+ map = epub->rxampdu_map;
+ while (map != 0) {
+ i = ffs(map) - 1;
+ if (i < 0) {
+ spin_unlock_bh(&epub->rx_ampdu_lock);
+ return index;
+ }
+ map &= ~BIT(i);
+ if (epub->rxampdu_tid[i] == tid &&
+ memcmp(epub->rxampdu_node[i]->sta->addr, addr,
+ ETH_ALEN) == 0) {
+ index = i;
+ break;
+ }
+ }
+
+ epub->rxampdu_map &= ~BIT(index);
+ spin_unlock_bh(&epub->rx_ampdu_lock);
+ return index;
+
+}
+
+static int esp_op_sta_add(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct esp_pub *epub = (struct esp_pub *) hw->priv;
+ struct esp_vif *evif = (struct esp_vif *) vif->drv_priv;
+ int index;
+ ESP_IEEE80211_DBG(ESP_DBG_OP,
+ "%s enter, vif addr %pM, sta addr %pM\n",
+ __func__, vif->addr, sta->addr);
+ index = esp_node_attach(hw, evif->index, sta);
+
+ if (index < 0)
+ return -1;
+ sip_send_set_sta(epub, evif->index, 1, sta, vif, (u8) index);
+ return 0;
+}
+
+static int esp_op_sta_remove(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct esp_pub *epub = (struct esp_pub *) hw->priv;
+ struct esp_vif *evif = (struct esp_vif *) vif->drv_priv;
+ int index;
+
+ ESP_IEEE80211_DBG(ESP_DBG_OP,
+ "%s enter, vif addr %pM, sta addr %pM\n",
+ __func__, vif->addr, sta->addr);
+
+ //remove a connect in target
+ index = esp_node_detach(hw, evif->index, sta);
+ sip_send_set_sta(epub, evif->index, 0, sta, vif, (u8) index);
+
+ return 0;
+}
+
+
+static void esp_op_sta_notify(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ enum sta_notify_cmd cmd,
+ struct ieee80211_sta *sta)
+{
+
+ ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s enter \n", __func__);
+
+ switch (cmd) {
+ case STA_NOTIFY_SLEEP:
+ break;
+
+ case STA_NOTIFY_AWAKE:
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+static int esp_op_conf_tx(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ u32 link_id, u16 queue,
+ const struct ieee80211_tx_queue_params *params)
+{
+ struct esp_pub *epub = (struct esp_pub *) hw->priv;
+ ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s enter \n", __func__);
+ return sip_send_wmm_params(epub, queue, params);
+}
+
+static u64 esp_op_get_tsf(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s enter \n", __func__);
+
+ return 0;
+}
+
+static void esp_op_set_tsf(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, u64 tsf)
+{
+ ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s enter \n", __func__);
+}
+
+static void esp_op_reset_tsf(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s enter \n", __func__);
+
+}
+
+static void esp_op_rfkill_poll(struct ieee80211_hw *hw)
+{
+ struct esp_pub *epub = (struct esp_pub *) hw->priv;
+
+ ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s enter \n", __func__);
+
+ wiphy_rfkill_set_hw_state(hw->wiphy,
+ test_bit(ESP_WL_FLAG_RFKILL,
+ &epub->wl.
+ flags) ? true : false);
+}
+
+#ifdef HW_SCAN
+static int esp_op_hw_scan(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct cfg80211_scan_request *req)
+{
+ struct esp_pub *epub = (struct esp_pub *) hw->priv;
+ int i, ret;
+ bool scan_often = true;
+
+ ESP_IEEE80211_DBG(ESP_DBG_OP, "%s\n", __func__);
+
+ ESP_IEEE80211_DBG(ESP_DBG_TRACE, "scan, %d\n", req->n_ssids);
+ ESP_IEEE80211_DBG(ESP_DBG_TRACE, "scan, len 1:%d,ssid 1:%s\n",
+ req->ssids->ssid_len,
+ req->ssids->ssid_len ==
+ 0 ? "" : (char *) req->ssids->ssid);
+ if (req->n_ssids > 1)
+ ESP_IEEE80211_DBG(ESP_DBG_TRACE,
+ "scan, len 2:%d,ssid 2:%s\n",
+ (req->ssids + 1)->ssid_len,
+ (req->ssids + 1)->ssid_len ==
+ 0 ? "" : (char *) (req->ssids +
+ 1)->ssid);
+
+ /*scan_request is keep allocate untill scan_done,record it
+ to split request into multi sdio_cmd */
+ if (atomic_read(&epub->wl.off)) {
+ esp_dbg(ESP_DBG_ERROR, "%s scan but wl off \n", __func__);
+ return -EPERM;
+ }
+
+ if (req->n_ssids > 1) {
+ struct cfg80211_ssid *ssid2 = req->ssids + 1;
+ if ((req->ssids->ssid_len > 0 && ssid2->ssid_len > 0)
+ || req->n_ssids > 2) {
+ ESP_IEEE80211_DBG(ESP_DBG_ERROR,
+ "scan ssid num: %d, ssid1:%s, ssid2:%s,not support\n",
+ req->n_ssids,
+ req->ssids->ssid_len ==
+ 0 ? "" : (char *) req->ssids->
+ ssid,
+ ssid2->ssid_len ==
+ 0 ? "" : (char *) ssid2->ssid);
+ return -EINVAL;
+ }
+ }
+
+ epub->wl.scan_req = req;
+
+ for (i = 0; i < req->n_channels; i++)
+ ESP_IEEE80211_DBG(ESP_DBG_TRACE, "eagle hw_scan freq %d\n",
+ req->channels[i]->center_freq);
+#if 0
+ for (i = 0; i < req->n_ssids; i++) {
+ if (req->ssids->ssid_len > 0) {
+ req->ssids->ssid[req->ssids->ssid_len] = '\0';
+ ESP_IEEE80211_DBG(ESP_DBG_TRACE,
+ "scan_ssid %d:%s\n", i,
+ req->ssids->ssid);
+ }
+ }
+#endif
+
+ /*in connect state, suspend tx data */
+ if (epub->sip->support_bgscan &&
+ test_bit(ESP_WL_FLAG_CONNECT, &epub->wl.flags) &&
+ req->n_channels > 0) {
+
+ scan_often = epub->scan_permit_valid
+ && time_before(jiffies, epub->scan_permit);
+ epub->scan_permit_valid = true;
+
+ if (!scan_often) {
+/* epub->scan_permit = jiffies + msecs_to_jiffies(900);
+ set_bit(ESP_WL_FLAG_STOP_TXQ, &epub->wl.flags);
+ if (atomic_read(&epub->txq_stopped) == false) {
+ atomic_set(&epub->txq_stopped, true);
+ ieee80211_stop_queues(hw);
+ }
+*/
+ } else {
+ ESP_IEEE80211_DBG(ESP_DBG_LOG, "scan too often\n");
+ return -EACCES;
+ }
+ } else {
+ scan_often = false;
+ }
+
+ /*send sub_scan task to target */
+ ret = sip_send_scan(epub);
+
+ if (ret) {
+ ESP_IEEE80211_DBG(ESP_DBG_ERROR,
+ "fail to send scan_cmd\n");
+ return ret;
+ } else {
+ if (!scan_often) {
+ epub->scan_permit =
+ jiffies + msecs_to_jiffies(900);
+ set_bit(ESP_WL_FLAG_STOP_TXQ, &epub->wl.flags);
+ if (atomic_read(&epub->txq_stopped) == false) {
+ atomic_set(&epub->txq_stopped, true);
+ ieee80211_stop_queues(hw);
+ }
+ /*force scan complete in case target fail to report in time */
+ ieee80211_queue_delayed_work(hw,
+ &epub->
+ scan_timeout_work,
+ req->n_channels * HZ /
+ 4);
+ }
+ }
+
+ return 0;
+}
+
+static int esp_op_remain_on_channel(struct ieee80211_hw *hw,
+ struct ieee80211_channel *chan,
+ enum nl80211_channel_type channel_type,
+ int duration)
+{
+ struct esp_pub *epub = (struct esp_pub *) hw->priv;
+
+ ESP_IEEE80211_DBG(ESP_DBG_OP,
+ "%s enter, center_freq = %d duration = %d\n",
+ __func__, chan->center_freq, duration);
+ sip_send_roc(epub, chan->center_freq, duration);
+ return 0;
+}
+
+static int esp_op_cancel_remain_on_channel(struct ieee80211_hw *hw)
+{
+ struct esp_pub *epub = (struct esp_pub *) hw->priv;
+
+ ESP_IEEE80211_DBG(ESP_DBG_OP, "%s enter \n", __func__);
+ epub->roc_flags = 0; // to disable roc state
+ sip_send_roc(epub, 0, 0);
+ return 0;
+}
+#endif
+
+void esp_rocdone_process(struct ieee80211_hw *hw,
+ struct sip_evt_roc *report)
+{
+ struct esp_pub *epub = (struct esp_pub *) hw->priv;
+
+ ESP_IEEE80211_DBG(ESP_DBG_OP, "%s enter, state = %d is_ok = %d\n",
+ __func__, report->state, report->is_ok);
+
+ //roc process begin
+ if ((report->state == 1) && (report->is_ok == 1)) {
+ epub->roc_flags = 1; //flags in roc state, to fix channel, not change
+ ieee80211_ready_on_channel(hw);
+ } else if ((report->state == 0) && (report->is_ok == 1)) //roc process timeout
+ {
+ epub->roc_flags = 0; // to disable roc state
+ ieee80211_remain_on_channel_expired(hw);
+ }
+}
+
+static int esp_op_set_bitrate_mask(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ const struct cfg80211_bitrate_mask
+ *mask)
+{
+ ESP_IEEE80211_DBG(ESP_DBG_OP, "%s enter \n", __func__);
+ ESP_IEEE80211_DBG(ESP_DBG_OP, "%s vif->macaddr[%pM], mask[%d]\n",
+ __func__, vif->addr, mask->control[0].legacy);
+
+ return 0;
+}
+
+void esp_op_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ u32 queues, bool drop)
+{
+
+ ESP_IEEE80211_DBG(ESP_DBG_OP, "%s enter \n", __func__);
+ do {
+
+ struct esp_pub *epub = (struct esp_pub *) hw->priv;
+ unsigned long time = jiffies + msecs_to_jiffies(15);
+ while (atomic_read(&epub->sip->tx_data_pkt_queued)) {
+ if (!time_before(jiffies, time)) {
+ break;
+ }
+ if (sif_get_ate_config() == 0) {
+ ieee80211_queue_work(epub->hw,
+ &epub->tx_work);
+ } else {
+ queue_work(epub->esp_wkq, &epub->tx_work);
+ }
+ //sip_txq_process(epub);
+ }
+ mdelay(10);
+
+ } while (0);
+}
+
+static int esp_op_ampdu_action(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_ampdu_params *params)
+{
+ int ret = -EOPNOTSUPP;
+ enum ieee80211_ampdu_mlme_action action = params->action;
+ struct ieee80211_sta *sta = params->sta;
+ u16 tid = params->tid;
+ u16 *ssn = &params->ssn;
+ u8 buf_size = params->buf_size;
+ struct esp_pub *epub = (struct esp_pub *) hw->priv;
+ struct esp_node *node = (struct esp_node *) sta->drv_priv;
+ struct esp_tx_tid *tid_info = &node->tid[tid];
+
+ ESP_IEEE80211_DBG(ESP_DBG_OP, "%s enter \n", __func__);
+ switch (action) {
+ case IEEE80211_AMPDU_TX_START:
+ if (mod_support_no_txampdu() ||
+ cfg80211_get_chandef_type(&epub->hw->conf.chandef) ==
+ NL80211_CHAN_NO_HT || !sta->deflink.ht_cap.ht_supported)
+ return ret;
+
+ //if (vif->p2p || vif->type != NL80211_IFTYPE_STATION)
+ // return ret;
+
+ ESP_IEEE80211_DBG(ESP_DBG_TRACE,
+ "%s TX START, addr:%pM,tid:%u,state:%d\n",
+ __func__, sta->addr, tid,
+ tid_info->state);
+ spin_lock_bh(&epub->tx_ampdu_lock);
+ ESSERT(tid_info->state == ESP_TID_STATE_TRIGGER);
+ *ssn = tid_info->ssn;
+ tid_info->state = ESP_TID_STATE_PROGRESS;
+
+ ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+ spin_unlock_bh(&epub->tx_ampdu_lock);
+ ret = 0;
+ break;
+ case IEEE80211_AMPDU_TX_STOP_CONT:
+ ESP_IEEE80211_DBG(ESP_DBG_TRACE,
+ "%s TX STOP, addr:%pM,tid:%u,state:%d\n",
+ __func__, sta->addr, tid,
+ tid_info->state);
+ spin_lock_bh(&epub->tx_ampdu_lock);
+ if (tid_info->state == ESP_TID_STATE_WAIT_STOP)
+ tid_info->state = ESP_TID_STATE_STOP;
+ else
+ tid_info->state = ESP_TID_STATE_INIT;
+ ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+ spin_unlock_bh(&epub->tx_ampdu_lock);
+ ret =
+ sip_send_ampdu_action(epub, SIP_AMPDU_TX_STOP,
+ sta->addr, tid, node->ifidx, 0);
+ break;
+ case IEEE80211_AMPDU_TX_STOP_FLUSH:
+ case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
+ if (tid_info->state == ESP_TID_STATE_WAIT_STOP)
+ tid_info->state = ESP_TID_STATE_STOP;
+ else
+ tid_info->state = ESP_TID_STATE_INIT;
+ ret =
+ sip_send_ampdu_action(epub, SIP_AMPDU_TX_STOP,
+ sta->addr, tid, node->ifidx, 0);
+ break;
+ case IEEE80211_AMPDU_TX_OPERATIONAL:
+ ESP_IEEE80211_DBG(ESP_DBG_TRACE,
+ "%s TX OPERATION, addr:%pM,tid:%u,state:%d\n",
+ __func__, sta->addr, tid,
+ tid_info->state);
+ spin_lock_bh(&epub->tx_ampdu_lock);
+
+ if (tid_info->state != ESP_TID_STATE_PROGRESS) {
+ if (tid_info->state == ESP_TID_STATE_INIT) {
+ printk(KERN_ERR "%s WIFI RESET, IGNORE\n",
+ __func__);
+ spin_unlock_bh(&epub->tx_ampdu_lock);
+ return -ENETRESET;
+ } else {
+ ESSERT(0);
+ }
+ }
+
+ tid_info->state = ESP_TID_STATE_OPERATIONAL;
+ spin_unlock_bh(&epub->tx_ampdu_lock);
+ ret =
+ sip_send_ampdu_action(epub, SIP_AMPDU_TX_OPERATIONAL,
+ sta->addr, tid, node->ifidx,
+ buf_size);
+ break;
+ case IEEE80211_AMPDU_RX_START:
+ if (mod_support_no_rxampdu() ||
+ cfg80211_get_chandef_type(&epub->hw->conf.chandef) ==
+ NL80211_CHAN_NO_HT || !sta->deflink.ht_cap.ht_supported)
+ return ret;
+
+ if ((vif->p2p && false)
+ || (vif->type != NL80211_IFTYPE_STATION && false)
+ )
+ return ret;
+ ESP_IEEE80211_DBG(ESP_DBG_TRACE,
+ "%s RX START %pM tid %u %u\n", __func__,
+ sta->addr, tid, *ssn);
+ ret =
+ sip_send_ampdu_action(epub, SIP_AMPDU_RX_START,
+ sta->addr, tid, *ssn, 64);
+ break;
+ case IEEE80211_AMPDU_RX_STOP:
+ ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s RX STOP %pM tid %u\n",
+ __func__, sta->addr, tid);
+ ret =
+ sip_send_ampdu_action(epub, SIP_AMPDU_RX_STOP,
+ sta->addr, tid, 0, 0);
+ break;
+ default:
+ break;
+ }
+ return ret;
+}
+
+static void esp_tx_work(struct work_struct *work)
+{
+ struct esp_pub *epub = container_of(work, struct esp_pub, tx_work);
+
+ mutex_lock(&epub->tx_mtx);
+ sip_txq_process(epub);
+ mutex_unlock(&epub->tx_mtx);
+}
+
+static const struct ieee80211_ops esp_mac80211_ops = {
+ .tx = esp_op_tx,
+ .start = esp_op_start,
+ .stop = esp_op_stop,
+#ifdef CONFIG_PM
+ .suspend = esp_op_suspend,
+ .resume = esp_op_resume,
+#endif
+ .add_interface = esp_op_add_interface,
+ .remove_interface = esp_op_remove_interface,
+ .config = esp_op_config,
+
+ .bss_info_changed = esp_op_bss_info_changed,
+ .prepare_multicast = esp_op_prepare_multicast,
+ .configure_filter = esp_op_configure_filter,
+ .set_key = esp_op_set_key,
+ .update_tkip_key = esp_op_update_tkip_key,
+ //.sched_scan_start = esp_op_sched_scan_start,
+ //.sched_scan_stop = esp_op_sched_scan_stop,
+ .set_rts_threshold = esp_op_set_rts_threshold,
+ .sta_notify = esp_op_sta_notify,
+ .conf_tx = esp_op_conf_tx,
+ .change_interface = esp_op_change_interface,
+ .get_tsf = esp_op_get_tsf,
+ .set_tsf = esp_op_set_tsf,
+ .reset_tsf = esp_op_reset_tsf,
+ .rfkill_poll = esp_op_rfkill_poll,
+#ifdef HW_SCAN
+ .hw_scan = esp_op_hw_scan,
+ .remain_on_channel = esp_op_remain_on_channel,
+ .cancel_remain_on_channel = esp_op_cancel_remain_on_channel,
+#endif
+ .ampdu_action = esp_op_ampdu_action,
+ //.get_survey = esp_op_get_survey,
+ .sta_add = esp_op_sta_add,
+ .sta_remove = esp_op_sta_remove,
+#ifdef CONFIG_NL80211_TESTMODE
+ //CFG80211_TESTMODE_CMD(esp_op_tm_cmd)
+#endif
+ .set_bitrate_mask = esp_op_set_bitrate_mask,
+ .flush = esp_op_flush,
+ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+};
+
+struct esp_pub *esp_pub_alloc_mac80211(struct device *dev)
+{
+ struct ieee80211_hw *hw;
+ struct esp_pub *epub;
+ int ret = 0;
+
+ hw = ieee80211_alloc_hw(sizeof(struct esp_pub), &esp_mac80211_ops);
+
+ if (hw == NULL) {
+ esp_dbg(ESP_DBG_ERROR, "ieee80211 can't alloc hw!\n");
+ ret = -ENOMEM;
+ return ERR_PTR(ret);
+ }
+ hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
+
+ epub = hw->priv;
+ memset(epub, 0, sizeof(*epub));
+ epub->hw = hw;
+ SET_IEEE80211_DEV(hw, dev);
+ epub->dev = dev;
+
+ skb_queue_head_init(&epub->txq);
+ skb_queue_head_init(&epub->txdoneq);
+ skb_queue_head_init(&epub->rxq);
+
+ spin_lock_init(&epub->tx_ampdu_lock);
+ spin_lock_init(&epub->rx_ampdu_lock);
+ spin_lock_init(&epub->tx_lock);
+ mutex_init(&epub->tx_mtx);
+ spin_lock_init(&epub->rx_lock);
+
+ INIT_WORK(&epub->tx_work, esp_tx_work);
+
+ //epub->esp_wkq = create_freezable_workqueue("esp_wkq");
+ epub->esp_wkq = create_singlethread_workqueue("esp_wkq");
+
+ if (epub->esp_wkq == NULL) {
+ ret = -ENOMEM;
+ return ERR_PTR(ret);
+ }
+ epub->scan_permit_valid = false;
+ INIT_DELAYED_WORK(&epub->scan_timeout_work,
+ hw_scan_timeout_report);
+
+ return epub;
+}
+
+
+int esp_pub_dealloc_mac80211(struct esp_pub *epub)
+{
+ set_bit(ESP_WL_FLAG_RFKILL, &epub->wl.flags);
+
+ destroy_workqueue(epub->esp_wkq);
+ mutex_destroy(&epub->tx_mtx);
+
+#ifdef ESP_NO_MAC80211
+ free_netdev(epub->net_dev);
+ wiphy_free(epub->wdev->wiphy);
+ kfree(epub->wdev);
+#else
+ if (epub->hw) {
+ ieee80211_free_hw(epub->hw);
+ }
+#endif
+
+ return 0;
+}
+
+#if 0
+static int esp_reg_notifier(struct wiphy *wiphy,
+ struct regulatory_request *request)
+{
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_channel *ch;
+ int i;
+
+ ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s enter %d\n", __func__,
+ request->initiator);
+
+ //TBD
+}
+#endif
+
+/* 2G band channels */
+static struct ieee80211_channel esp_channels_2ghz[] = {
+ {.hw_value = 1,.center_freq = 2412,.max_power = 25},
+ {.hw_value = 2,.center_freq = 2417,.max_power = 25},
+ {.hw_value = 3,.center_freq = 2422,.max_power = 25},
+ {.hw_value = 4,.center_freq = 2427,.max_power = 25},
+ {.hw_value = 5,.center_freq = 2432,.max_power = 25},
+ {.hw_value = 6,.center_freq = 2437,.max_power = 25},
+ {.hw_value = 7,.center_freq = 2442,.max_power = 25},
+ {.hw_value = 8,.center_freq = 2447,.max_power = 25},
+ {.hw_value = 9,.center_freq = 2452,.max_power = 25},
+ {.hw_value = 10,.center_freq = 2457,.max_power = 25},
+ {.hw_value = 11,.center_freq = 2462,.max_power = 25},
+ {.hw_value = 12,.center_freq = 2467,.max_power = 25},
+ {.hw_value = 13,.center_freq = 2472,.max_power = 25},
+ //{ .hw_value = 14, .center_freq = 2484, .max_power = 25 },
+};
+
+/* 11G rate */
+static struct ieee80211_rate esp_rates_2ghz[] = {
+ {
+ .bitrate = 10,
+ .hw_value = CONF_HW_BIT_RATE_1MBPS,
+ .hw_value_short = CONF_HW_BIT_RATE_1MBPS,
+ },
+ {
+ .bitrate = 20,
+ .hw_value = CONF_HW_BIT_RATE_2MBPS,
+ .hw_value_short = CONF_HW_BIT_RATE_2MBPS,
+ .flags = IEEE80211_RATE_SHORT_PREAMBLE},
+ {
+ .bitrate = 55,
+ .hw_value = CONF_HW_BIT_RATE_5_5MBPS,
+ .hw_value_short = CONF_HW_BIT_RATE_5_5MBPS,
+ .flags = IEEE80211_RATE_SHORT_PREAMBLE},
+ {
+ .bitrate = 110,
+ .hw_value = CONF_HW_BIT_RATE_11MBPS,
+ .hw_value_short = CONF_HW_BIT_RATE_11MBPS,
+ .flags = IEEE80211_RATE_SHORT_PREAMBLE},
+ {
+ .bitrate = 60,
+ .hw_value = CONF_HW_BIT_RATE_6MBPS,
+ .hw_value_short = CONF_HW_BIT_RATE_6MBPS,
+ },
+ {
+ .bitrate = 90,
+ .hw_value = CONF_HW_BIT_RATE_9MBPS,
+ .hw_value_short = CONF_HW_BIT_RATE_9MBPS,
+ },
+ {
+ .bitrate = 120,
+ .hw_value = CONF_HW_BIT_RATE_12MBPS,
+ .hw_value_short = CONF_HW_BIT_RATE_12MBPS,
+ },
+ {
+ .bitrate = 180,
+ .hw_value = CONF_HW_BIT_RATE_18MBPS,
+ .hw_value_short = CONF_HW_BIT_RATE_18MBPS,
+ },
+ {
+ .bitrate = 240,
+ .hw_value = CONF_HW_BIT_RATE_24MBPS,
+ .hw_value_short = CONF_HW_BIT_RATE_24MBPS,
+ },
+ {
+ .bitrate = 360,
+ .hw_value = CONF_HW_BIT_RATE_36MBPS,
+ .hw_value_short = CONF_HW_BIT_RATE_36MBPS,
+ },
+ {
+ .bitrate = 480,
+ .hw_value = CONF_HW_BIT_RATE_48MBPS,
+ .hw_value_short = CONF_HW_BIT_RATE_48MBPS,
+ },
+ {
+ .bitrate = 540,
+ .hw_value = CONF_HW_BIT_RATE_54MBPS,
+ .hw_value_short = CONF_HW_BIT_RATE_54MBPS,
+ },
+};
+
+static void esp_pub_init_mac80211(struct esp_pub *epub)
+{
+ struct ieee80211_hw *hw = epub->hw;
+
+ static const u32 cipher_suites[] = {
+ WLAN_CIPHER_SUITE_WEP40,
+ WLAN_CIPHER_SUITE_WEP104,
+ WLAN_CIPHER_SUITE_TKIP,
+ WLAN_CIPHER_SUITE_CCMP,
+ };
+
+ hw->max_listen_interval = 10;
+
+ ieee80211_hw_set(hw, SIGNAL_DBM);
+ ieee80211_hw_set(hw, HAS_RATE_CONTROL);
+ ieee80211_hw_set(hw, SUPPORTS_PS);
+ ieee80211_hw_set(hw, AMPDU_AGGREGATION);
+ ieee80211_hw_set(hw, HOST_BROADCAST_PS_BUFFERING);
+ //IEEE80211_HW_PS_NULLFUNC_STACK |
+ //IEEE80211_HW_CONNECTION_MONITOR |
+ //IEEE80211_HW_BEACON_FILTER |
+ //IEEE80211_HW_AMPDU_AGGREGATION |
+ //IEEE80211_HW_REPORTS_TX_ACK_STATUS;
+ hw->max_rx_aggregation_subframes = 0x40;
+ hw->max_tx_aggregation_subframes = 0x40;
+
+ hw->wiphy->cipher_suites = cipher_suites;
+ hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
+ hw->wiphy->max_scan_ie_len =
+ epub->sip->tx_blksz - sizeof(struct sip_hdr) -
+ sizeof(struct sip_cmd_scan);
+
+ /* ONLY station for now, support P2P soon... */
+ hw->wiphy->interface_modes =
+ BIT(NL80211_IFTYPE_P2P_GO) |
+ BIT(NL80211_IFTYPE_P2P_CLIENT) |
+ BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP);
+
+ hw->wiphy->max_scan_ssids = 2;
+ //hw->wiphy->max_sched_scan_ssids = 16;
+ //hw->wiphy->max_match_sets = 16;
+
+ hw->wiphy->max_remain_on_channel_duration = 5000;
+
+ atomic_set(&epub->wl.off, 1);
+
+ epub->wl.sbands[NL80211_BAND_2GHZ].band = NL80211_BAND_2GHZ;
+ epub->wl.sbands[NL80211_BAND_2GHZ].channels = esp_channels_2ghz;
+ epub->wl.sbands[NL80211_BAND_2GHZ].bitrates = esp_rates_2ghz;
+ epub->wl.sbands[NL80211_BAND_2GHZ].n_channels =
+ ARRAY_SIZE(esp_channels_2ghz);
+ epub->wl.sbands[NL80211_BAND_2GHZ].n_bitrates =
+ ARRAY_SIZE(esp_rates_2ghz);
+ /*add to support 11n */
+ epub->wl.sbands[NL80211_BAND_2GHZ].ht_cap.ht_supported = true;
+ epub->wl.sbands[NL80211_BAND_2GHZ].ht_cap.cap = 0x116C; //IEEE80211_HT_CAP_RX_STBC; //IEEE80211_HT_CAP_SGI_20;
+ epub->wl.sbands[NL80211_BAND_2GHZ].ht_cap.ampdu_factor =
+ IEEE80211_HT_MAX_AMPDU_16K;
+ epub->wl.sbands[NL80211_BAND_2GHZ].ht_cap.ampdu_density =
+ IEEE80211_HT_MPDU_DENSITY_NONE;
+ memset(&epub->wl.sbands[NL80211_BAND_2GHZ].ht_cap.mcs, 0,
+ sizeof(epub->wl.sbands[NL80211_BAND_2GHZ].ht_cap.mcs));
+ epub->wl.sbands[NL80211_BAND_2GHZ].ht_cap.mcs.rx_mask[0] = 0xff;
+ //epub->wl.sbands[NL80211_BAND_2GHZ].ht_cap.mcs.rx_highest = 7;
+ //epub->wl.sbands[NL80211_BAND_2GHZ].ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
+
+ /* BAND_5GHZ TBD */
+
+ hw->wiphy->bands[NL80211_BAND_2GHZ] =
+ &epub->wl.sbands[NL80211_BAND_2GHZ];
+ /* BAND_5GHZ TBD */
+
+ /*no fragment */
+ hw->wiphy->frag_threshold = IEEE80211_MAX_FRAG_THRESHOLD;
+
+ /* handle AC queue in f/w */
+ hw->queues = 4;
+ hw->max_rates = 4;
+ //hw->wiphy->reg_notifier = esp_reg_notify;
+
+ hw->vif_data_size = sizeof(struct esp_vif);
+ hw->sta_data_size = sizeof(struct esp_node);
+
+ //hw->max_rx_aggregation_subframes = 8;
+}
+
+int esp_register_mac80211(struct esp_pub *epub)
+{
+ int ret = 0;
+ u8 *wlan_addr;
+ u8 *p2p_addr;
+ int idx;
+
+ esp_pub_init_mac80211(epub);
+
+ epub->hw->wiphy->addresses = (struct mac_address *) esp_mac_addr;
+ memcpy(&epub->hw->wiphy->addresses[0], epub->mac_addr, ETH_ALEN);
+ memcpy(&epub->hw->wiphy->addresses[1], epub->mac_addr, ETH_ALEN);
+ wlan_addr = (u8 *) & epub->hw->wiphy->addresses[0];
+ p2p_addr = (u8 *) & epub->hw->wiphy->addresses[1];
+
+ for (idx = 0; idx < 64; idx++) {
+ p2p_addr[0] = wlan_addr[0] | 0x02;
+ p2p_addr[0] ^= idx << 2;
+ if (strncmp(p2p_addr, wlan_addr, 6) != 0)
+ break;
+ }
+
+ epub->hw->wiphy->n_addresses = 2;
+
+ ret = ieee80211_register_hw(epub->hw);
+
+ if (ret < 0) {
+ ESP_IEEE80211_DBG(ESP_DBG_ERROR,
+ "unable to register mac80211 hw: %d\n",
+ ret);
+ return ret;
+ } else {
+#ifdef MAC80211_NO_CHANGE
+ rtnl_lock();
+ if (epub->hw->wiphy->interface_modes &
+ (BIT(NL80211_IFTYPE_P2P_GO) |
+ BIT(NL80211_IFTYPE_P2P_CLIENT))) {
+ ret =
+ ieee80211_if_add(hw_to_local(epub->hw),
+ "p2p%d", NULL,
+ NL80211_IFTYPE_STATION, NULL);
+ if (ret)
+ wiphy_warn(epub->hw->wiphy,
+ "Failed to add default virtual iface\n");
+ }
+
+ rtnl_unlock();
+#endif
+ }
+
+ set_bit(ESP_WL_FLAG_HW_REGISTERED, &epub->wl.flags);
+
+ return ret;
+}
+
+static u8 getaddr_index(u8 * addr, struct esp_pub *epub)
+{
+ int i;
+ for (i = 0; i < ESP_PUB_MAX_VIF; i++)
+ if (memcmp
+ (addr, (u8 *) & epub->hw->wiphy->addresses[i],
+ ETH_ALEN) == 0)
+ return i;
+ return ESP_PUB_MAX_VIF;
+}
diff --git a/drivers/net/wireless/esp8089/esp_mac80211.h b/drivers/net/wireless/esp8089/esp_mac80211.h
new file mode 100644
index 000000000000..699b27dcadd1
--- /dev/null
+++ b/drivers/net/wireless/esp8089/esp_mac80211.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2011-2014 Espressif System.
+ *
+ * MAC80211 support module
+ *
+ * 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.
+ */
+#ifndef _ESP_MAC80211_H_
+#define _ESP_MAC80211_H_
+
+struct esp_80211_wmm_ac_param {
+ u8 aci_aifsn; /* AIFSN, ACM, ACI */
+ u8 cw; /* ECWmin, ECWmax (CW = 2^ECW - 1) */
+ u16 txop_limit;
+};
+
+struct esp_80211_wmm_param_element {
+ /* Element ID 221 (0xdd); length: 24 */
+ /* required fields for WMM version 1 */
+ u8 oui[3]; /* 00:50:f2 */
+ u8 oui_type; /* 2 */
+ u8 oui_subtype; /* 1 */
+ u8 version; /* 1 for WMM version 1.0 */
+ u8 qos_info; /* AP/STA specif QoS info */
+ u8 reserved; /* 0 */
+ struct esp_80211_wmm_ac_param ac[4]; /* AC_BE, AC_BK, AC_VI, AC_VO */
+};
+
+
+#endif /* _ESP_MAC80211_H_ */
diff --git a/drivers/net/wireless/esp8089/esp_main.c b/drivers/net/wireless/esp8089/esp_main.c
new file mode 100644
index 000000000000..404e0d7a6f54
--- /dev/null
+++ b/drivers/net/wireless/esp8089/esp_main.c
@@ -0,0 +1,263 @@
+/*
+ * Copyright (c) 2010 - 2014 Espressif System.
+ *
+ * main routine
+ *
+ * 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.
+ */
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/rtnetlink.h>
+#include <linux/firmware.h>
+#include <linux/sched.h>
+#include <linux/module.h>
+#include <net/cfg80211.h>
+#include <net/mac80211.h>
+#include <linux/time.h>
+#include <linux/moduleparam.h>
+
+#include "esp_pub.h"
+#include "esp_sip.h"
+#include "esp_sif.h"
+#include "esp_debug.h"
+#include "esp_file.h"
+#include "esp_wl.h"
+
+struct completion *gl_bootup_cplx = NULL;
+
+#ifndef FPGA_DEBUG
+static int esp_download_fw(struct esp_pub *epub);
+#endif /* !FGPA_DEBUG */
+
+static int modparam_no_txampdu = 0;
+static int modparam_no_rxampdu = 0;
+module_param_named(no_txampdu, modparam_no_txampdu, int, 0444);
+MODULE_PARM_DESC(no_txampdu, "Disable tx ampdu.");
+module_param_named(no_rxampdu, modparam_no_rxampdu, int, 0444);
+MODULE_PARM_DESC(no_rxampdu, "Disable rx ampdu.");
+
+static char *modparam_eagle_path = "/lib/firmware";
+module_param_named(eagle_path, modparam_eagle_path, charp, 0444);
+MODULE_PARM_DESC(eagle_path, "eagle path");
+
+bool mod_support_no_txampdu()
+{
+ return modparam_no_txampdu;
+}
+
+bool mod_support_no_rxampdu()
+{
+ return modparam_no_rxampdu;
+}
+
+void mod_support_no_txampdu_set(bool value)
+{
+ modparam_no_txampdu = value;
+}
+
+char *mod_eagle_path_get(void)
+{
+ if (modparam_eagle_path[0] == '\0')
+ return NULL;
+
+ return modparam_eagle_path;
+}
+
+int esp_pub_init_all(struct esp_pub *epub)
+{
+ int ret = 0;
+
+ /* completion for bootup event poll */
+ DECLARE_COMPLETION_ONSTACK(complete);
+ atomic_set(&epub->ps.state, ESP_PM_OFF);
+ if (epub->sdio_state == ESP_SDIO_STATE_FIRST_INIT) {
+ epub->sip = sip_attach(epub);
+ if (epub->sip == NULL) {
+ printk(KERN_ERR "%s sip alloc failed\n", __func__);
+ return -ENOMEM;
+ }
+
+ esp_dump_var("esp_msg_level", NULL, &esp_msg_level,
+ ESP_U32);
+
+#ifdef ESP_ANDROID_LOGGER
+ esp_dump_var("log_off", NULL, &log_off, ESP_U32);
+#endif /* ESP_ANDROID_LOGGER */
+ } else {
+ atomic_set(&epub->sip->state, SIP_PREPARE_BOOT);
+ atomic_set(&epub->sip->tx_credits, 0);
+ }
+
+ epub->sip->to_host_seq = 0;
+
+#ifdef TEST_MODE
+ if (sif_get_ate_config() != 0 && sif_get_ate_config() != 1
+ && sif_get_ate_config() != 6) {
+ esp_test_init(epub);
+ return -1;
+ }
+#endif
+
+#ifndef FPGA_DEBUG
+ ret = esp_download_fw(epub);
+#ifdef TEST_MODE
+ if (sif_get_ate_config() == 6) {
+ sif_enable_irq(epub);
+ mdelay(500);
+ sif_disable_irq(epub);
+ mdelay(1000);
+ esp_test_init(epub);
+ return -1;
+ }
+#endif
+ if (ret) {
+ esp_dbg(ESP_DBG_ERROR, "download firmware failed\n");
+ return ret;
+ }
+
+ esp_dbg(ESP_DBG_TRACE, "download firmware OK \n");
+#else
+ sip_send_bootup(epub->sip);
+#endif /* FPGA_DEBUG */
+
+ gl_bootup_cplx = &complete;
+ epub->wait_reset = 0;
+ sif_enable_irq(epub);
+
+ if (epub->sdio_state == ESP_SDIO_STATE_SECOND_INIT
+ || sif_get_ate_config() == 1) {
+ ret = sip_poll_bootup_event(epub->sip);
+ } else {
+ ret = sip_poll_resetting_event(epub->sip);
+ if (ret == 0) {
+ sif_lock_bus(epub);
+ sif_interrupt_target(epub, 7);
+ sif_unlock_bus(epub);
+ }
+
+ }
+
+ gl_bootup_cplx = NULL;
+
+ if (sif_get_ate_config() == 1)
+ ret = -EOPNOTSUPP;
+
+ return ret;
+}
+
+void esp_dsr(struct esp_pub *epub)
+{
+ sip_rx(epub);
+}
+
+
+struct esp_fw_hdr {
+ u8 magic;
+ u8 blocks;
+ u8 pad[2];
+ u32 entry_addr;
+} __packed;
+
+struct esp_fw_blk_hdr {
+ u32 load_addr;
+ u32 data_len;
+} __packed;
+
+#define ESP_FW_NAME1 "eagle_fw_ate_config_v19.bin"
+#define ESP_FW_NAME2 "eagle_fw_first_init_v19.bin"
+#define ESP_FW_NAME3 "eagle_fw_second_init_v19.bin"
+
+#ifndef FPGA_DEBUG
+static int esp_download_fw(struct esp_pub *epub)
+{
+ const struct firmware *fw_entry;
+ u8 *fw_buf = NULL;
+ u32 offset = 0;
+ int ret = 0;
+ u8 blocks;
+ struct esp_fw_hdr *fhdr;
+ struct esp_fw_blk_hdr *bhdr = NULL;
+ struct sip_cmd_bootup bootcmd;
+ char *esp_fw_name;
+
+ if (sif_get_ate_config() == 1) {
+ esp_fw_name = ESP_FW_NAME3;
+ } else {
+ esp_fw_name =
+ epub->sdio_state ==
+ ESP_SDIO_STATE_FIRST_INIT ? ESP_FW_NAME1 :
+ ESP_FW_NAME2;
+ }
+ ret = request_firmware(&fw_entry, esp_fw_name, epub->dev);
+
+ if (ret)
+ return ret;
+
+ fw_buf = kmemdup(fw_entry->data, fw_entry->size, GFP_KERNEL);
+
+ release_firmware(fw_entry);
+
+ if (fw_buf == NULL) {
+ return -ENOMEM;
+ }
+
+ fhdr = (struct esp_fw_hdr *) fw_buf;
+
+ if (fhdr->magic != 0xE9) {
+ esp_dbg(ESP_DBG_ERROR, "%s wrong magic! \n", __func__);
+ goto _err;
+ }
+
+ blocks = fhdr->blocks;
+ offset += sizeof(struct esp_fw_hdr);
+
+ while (blocks) {
+
+ bhdr = (struct esp_fw_blk_hdr *) (&fw_buf[offset]);
+ offset += sizeof(struct esp_fw_blk_hdr);
+
+ ret =
+ sip_write_memory(epub->sip, bhdr->load_addr,
+ &fw_buf[offset], bhdr->data_len);
+
+ if (ret) {
+ esp_dbg(ESP_DBG_ERROR,
+ "%s Failed to write fw, err: %d\n",
+ __func__, ret);
+ goto _err;
+ }
+
+ blocks--;
+ offset += bhdr->data_len;
+ }
+
+ /* TODO: last byte should be the checksum and skip checksum for now */
+
+ bootcmd.boot_addr = fhdr->entry_addr;
+ ret =
+ sip_send_cmd(epub->sip, SIP_CMD_BOOTUP,
+ sizeof(struct sip_cmd_bootup), &bootcmd);
+
+ if (ret)
+ goto _err;
+
+ _err:
+ kfree(fw_buf);
+
+ return ret;
+
+}
+
+MODULE_FIRMWARE(ESP_FW_NAME1);
+MODULE_FIRMWARE(ESP_FW_NAME2);
+MODULE_FIRMWARE(ESP_FW_NAME3);
+#endif /* !FPGA_DEBUG */
diff --git a/drivers/net/wireless/esp8089/esp_path.h b/drivers/net/wireless/esp8089/esp_path.h
new file mode 100644
index 000000000000..1ceb14bc3b15
--- /dev/null
+++ b/drivers/net/wireless/esp8089/esp_path.h
@@ -0,0 +1,6 @@
+#ifndef _ESP_PATH_H_
+#define _ESP_PATH_H_
+#define FWPATH "/lib/firmware"
+//module_param_string(fwpath, fwpath, sizeof(fwpath), 0644);
+
+#endif /* _ESP_PATH_H_ */
diff --git a/drivers/net/wireless/esp8089/esp_pub.h b/drivers/net/wireless/esp8089/esp_pub.h
new file mode 100644
index 000000000000..830dcac0a89b
--- /dev/null
+++ b/drivers/net/wireless/esp8089/esp_pub.h
@@ -0,0 +1,222 @@
+/*
+ * Copyright (c) 2011-2014 Espressif System.
+ *
+ * wlan device header file
+ *
+ * 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.
+ */
+
+#ifndef _ESP_PUB_H_
+#define _ESP_PUB_H_
+
+#include <linux/etherdevice.h>
+#include <linux/rtnetlink.h>
+#include <linux/firmware.h>
+#include <linux/sched.h>
+#include <net/mac80211.h>
+#include <net/cfg80211.h>
+#include <linux/version.h>
+#include "sip2_common.h"
+
+enum esp_sdio_state {
+ ESP_SDIO_STATE_FIRST_INIT,
+ ESP_SDIO_STATE_FIRST_NORMAL_EXIT,
+ ESP_SDIO_STATE_FIRST_ERROR_EXIT,
+ ESP_SDIO_STATE_SECOND_INIT,
+ ESP_SDIO_STATE_SECOND_ERROR_EXIT,
+};
+
+enum esp_tid_state {
+ ESP_TID_STATE_INIT,
+ ESP_TID_STATE_TRIGGER,
+ ESP_TID_STATE_PROGRESS,
+ ESP_TID_STATE_OPERATIONAL,
+ ESP_TID_STATE_WAIT_STOP,
+ ESP_TID_STATE_STOP,
+};
+
+struct esp_tx_tid {
+ u8 state;
+ u8 cnt;
+ u16 ssn;
+};
+
+#define WME_NUM_TID 16
+struct esp_node {
+ struct esp_tx_tid tid[WME_NUM_TID];
+ struct ieee80211_sta *sta;
+ u8 ifidx;
+ u8 index;
+};
+
+#define WME_AC_BE 2
+#define WME_AC_BK 3
+#define WME_AC_VI 1
+#define WME_AC_VO 0
+
+struct llc_snap_hdr {
+ u8 dsap;
+ u8 ssap;
+ u8 cntl;
+ u8 org_code[3];
+ __be16 eth_type;
+} __packed;
+
+struct esp_vif {
+ struct esp_pub *epub;
+ u8 index;
+ u32 beacon_interval;
+ bool ap_up;
+ struct timer_list beacon_timer;
+};
+
+/* WLAN related, mostly... */
+/*struct hw_scan_timeout {
+ struct delayed_work w;
+ struct ieee80211_hw *hw;
+};*/
+
+typedef struct esp_wl {
+ u8 bssid[ETH_ALEN];
+ u8 req_bssid[ETH_ALEN];
+
+ //struct hw_scan_timeout *hsd;
+ struct cfg80211_scan_request *scan_req;
+ atomic_t ptk_cnt;
+ atomic_t gtk_cnt;
+ atomic_t tkip_key_set;
+
+ /* so far only 2G band */
+ struct ieee80211_supported_band sbands[NUM_NL80211_BANDS];
+
+ unsigned long flags;
+ atomic_t off;
+} esp_wl_t;
+
+typedef struct esp_hw_idx_map {
+ u8 mac[ETH_ALEN];
+ u8 flag;
+} esp_hw_idx_map_t;
+
+#define ESP_WL_FLAG_RFKILL BIT(0)
+#define ESP_WL_FLAG_HW_REGISTERED BIT(1)
+#define ESP_WL_FLAG_CONNECT BIT(2)
+#define ESP_WL_FLAG_STOP_TXQ BIT(3)
+
+#define ESP_PUB_MAX_VIF 2
+#define ESP_PUB_MAX_STA 16 //for one interface
+#define ESP_PUB_MAX_RXAMPDU 8 //for all interfaces
+
+enum {
+ ESP_PM_OFF = 0,
+ ESP_PM_TURNING_ON,
+ ESP_PM_ON,
+ ESP_PM_TURNING_OFF, /* Do NOT change the order */
+};
+
+struct esp_ps {
+ u32 dtim_period;
+ u32 max_sleep_period;
+ unsigned long last_config_time;
+ atomic_t state;
+ bool nulldata_pm_on;
+};
+
+struct esp_mac_prefix {
+ u8 mac_index;
+ u8 mac_addr_prefix[3];
+};
+
+struct esp_pub {
+ struct device *dev;
+#ifdef ESP_NO_MAC80211
+ struct net_device *net_dev;
+ struct wireless_dev *wdev;
+ struct net_device_stats *net_stats;
+#else
+ struct ieee80211_hw *hw;
+ struct ieee80211_vif *vif;
+ u8 vif_slot;
+#endif /* ESP_MAC80211 */
+
+ void *sif; /* serial interface control block, e.g. sdio */
+ enum esp_sdio_state sdio_state;
+ struct esp_sip *sip;
+ struct esp_wl wl;
+ struct esp_hw_idx_map hi_map[19];
+ struct esp_hw_idx_map low_map[ESP_PUB_MAX_VIF][2];
+ //u32 flags; //flags to represent rfkill switch,start
+ u8 roc_flags; //0: not in remain on channel state, 1: in roc state
+
+ struct work_struct tx_work; /* attach to ieee80211 workqueue */
+ /* latest mac80211 has multiple tx queue, but we stick with single queue now */
+ spinlock_t rx_lock;
+ spinlock_t tx_ampdu_lock;
+ spinlock_t rx_ampdu_lock;
+ spinlock_t tx_lock;
+ struct mutex tx_mtx;
+ struct sk_buff_head txq;
+ atomic_t txq_stopped;
+
+ struct work_struct sendup_work; /* attach to ieee80211 workqueue */
+ struct sk_buff_head txdoneq;
+ struct sk_buff_head rxq;
+
+ struct workqueue_struct *esp_wkq;
+
+ //u8 bssid[ETH_ALEN];
+ u8 mac_addr[ETH_ALEN];
+
+ u32 rx_filter;
+ unsigned long scan_permit;
+ bool scan_permit_valid;
+ struct delayed_work scan_timeout_work;
+ u32 enodes_map;
+ u8 rxampdu_map;
+ u32 enodes_maps[ESP_PUB_MAX_VIF];
+ struct esp_node *enodes[ESP_PUB_MAX_STA + 1];
+ struct esp_node *rxampdu_node[ESP_PUB_MAX_RXAMPDU];
+ u8 rxampdu_tid[ESP_PUB_MAX_RXAMPDU];
+ struct esp_ps ps;
+ int enable_int;
+ int wait_reset;
+};
+
+typedef struct esp_pub esp_pub_t;
+
+struct esp_pub *esp_pub_alloc_mac80211(struct device *dev);
+int esp_pub_dealloc_mac80211(struct esp_pub *epub);
+int esp_register_mac80211(struct esp_pub *epub);
+
+int esp_pub_init_all(struct esp_pub *epub);
+
+char *mod_eagle_path_get(void);
+
+void esp_dsr(struct esp_pub *epub);
+void hw_scan_done(struct esp_pub *epub, bool aborted);
+void esp_rocdone_process(struct ieee80211_hw *hw,
+ struct sip_evt_roc *report);
+
+void esp_ps_config(struct esp_pub *epub, struct esp_ps *ps, bool on);
+
+struct esp_node *esp_get_node_by_addr(struct esp_pub *epub,
+ const u8 * addr);
+struct esp_node *esp_get_node_by_index(struct esp_pub *epub, u8 index);
+int esp_get_empty_rxampdu(struct esp_pub *epub, const u8 * addr, u8 tid);
+int esp_get_exist_rxampdu(struct esp_pub *epub, const u8 * addr, u8 tid);
+
+#ifdef TEST_MODE
+int test_init_netlink(struct esp_sip *sip);
+void test_exit_netlink(void);
+void esp_test_cmd_event(u32 cmd_type, char *reply_info);
+void esp_test_init(struct esp_pub *epub);
+#endif
+#endif /* _ESP_PUB_H_ */
diff --git a/drivers/net/wireless/esp8089/esp_sif.h b/drivers/net/wireless/esp8089/esp_sif.h
new file mode 100644
index 000000000000..2d49f2bc8035
--- /dev/null
+++ b/drivers/net/wireless/esp8089/esp_sif.h
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2011 - 2014 Espressif System.
+ *
+ * Serial I/F wrapper layer for eagle WLAN device,
+ * abstraction of buses like SDIO/SIP, and provides
+ * flow control for tx/rx layer
+ *
+ * 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.
+ */
+
+#ifndef _ESP_SIF_H_
+#define _ESP_SIF_H_
+
+#include "esp_pub.h"
+#include <linux/mmc/host.h>
+#include <linux/spi/spi.h>
+
+/*
+ * H/W SLC module definitions
+ */
+
+#define SIF_SLC_BLOCK_SIZE 512
+
+
+/* S/W struct mapping to slc registers */
+typedef struct slc_host_regs {
+ /* do NOT read token_rdata
+ *
+ u32 pf_data;
+ u32 token_rdata;
+ */
+ u32 intr_raw;
+ u32 state_w0;
+ u32 state_w1;
+ u32 config_w0;
+ u32 config_w1;
+ u32 intr_status;
+ u32 config_w2;
+ u32 config_w3;
+ u32 config_w4;
+ u32 token_wdata;
+ u32 intr_clear;
+ u32 intr_enable;
+} sif_slc_reg_t;
+
+
+enum io_sync_type {
+ ESP_SIF_NOSYNC = 0,
+ ESP_SIF_SYNC,
+};
+
+typedef struct esp_sdio_ctrl {
+ struct sdio_func *func;
+ struct esp_pub *epub;
+
+
+ struct list_head free_req;
+
+ u8 *dma_buffer;
+
+ spinlock_t scat_lock;
+ struct list_head scat_req;
+
+ bool off;
+ atomic_t irq_handling;
+ const struct sdio_device_id *id;
+ u32 slc_blk_sz;
+ u32 target_id;
+ u32 slc_window_end_addr;
+
+ struct slc_host_regs slc_regs;
+ atomic_t irq_installed;
+
+} esp_sdio_ctrl_t;
+
+#define SIF_TO_DEVICE 0x1
+#define SIF_FROM_DEVICE 0x2
+
+#define SIF_SYNC 0x00000010
+#define SIF_ASYNC 0x00000020
+
+#define SIF_BYTE_BASIS 0x00000040
+#define SIF_BLOCK_BASIS 0x00000080
+
+#define SIF_FIXED_ADDR 0x00000100
+#define SIF_INC_ADDR 0x00000200
+
+#define EPUB_CTRL_CHECK(_epub, _go_err) do{\
+ if (_epub == NULL) {\
+ ESSERT(0);\
+ goto _go_err;\
+ }\
+ if ((_epub)->sif == NULL) {\
+ ESSERT(0);\
+ goto _go_err;\
+ }\
+}while(0)
+
+#define EPUB_FUNC_CHECK(_epub, _go_err) do{\
+ if (_epub == NULL) {\
+ ESSERT(0);\
+ goto _go_err;\
+ }\
+ if ((_epub)->sif == NULL) {\
+ ESSERT(0);\
+ goto _go_err;\
+ }\
+ if (((struct esp_sdio_ctrl *)(_epub)->sif)->func == NULL) {\
+ ESSERT(0);\
+ goto _go_err;\
+ }\
+}while(0)
+
+#define EPUB_TO_CTRL(_epub) (((struct esp_sdio_ctrl *)(_epub)->sif))
+
+#define EPUB_TO_FUNC(_epub) (((struct esp_sdio_ctrl *)(_epub)->sif)->func)
+
+void sdio_io_writeb(struct esp_pub *epub, u8 value, int addr, int *res);
+u8 sdio_io_readb(struct esp_pub *epub, int addr, int *res);
+
+
+void sif_enable_irq(struct esp_pub *epub);
+void sif_disable_irq(struct esp_pub *epub);
+void sif_disable_target_interrupt(struct esp_pub *epub);
+
+u32 sif_get_blksz(struct esp_pub *epub);
+u32 sif_get_target_id(struct esp_pub *epub);
+
+void sif_dsr(struct sdio_func *func);
+int sif_io_raw(struct esp_pub *epub, u32 addr, u8 * buf, u32 len,
+ u32 flag);
+int sif_io_sync(struct esp_pub *epub, u32 addr, u8 * buf, u32 len,
+ u32 flag);
+int sif_io_async(struct esp_pub *epub, u32 addr, u8 * buf, u32 len,
+ u32 flag, void *context);
+int sif_lldesc_read_sync(struct esp_pub *epub, u8 * buf, u32 len);
+int sif_lldesc_write_sync(struct esp_pub *epub, u8 * buf, u32 len);
+int sif_lldesc_read_raw(struct esp_pub *epub, u8 * buf, u32 len,
+ bool noround);
+int sif_lldesc_write_raw(struct esp_pub *epub, u8 * buf, u32 len);
+
+int sif_platform_get_irq_no(void);
+int sif_platform_is_irq_occur(void);
+void sif_platform_irq_clear(void);
+void sif_platform_irq_mask(int enable_mask);
+int sif_platform_irq_init(void);
+void sif_platform_irq_deinit(void);
+
+int esp_common_read(struct esp_pub *epub, u8 * buf, u32 len, int sync,
+ bool noround);
+int esp_common_write(struct esp_pub *epub, u8 * buf, u32 len, int sync);
+int esp_common_read_with_addr(struct esp_pub *epub, u32 addr, u8 * buf,
+ u32 len, int sync);
+int esp_common_write_with_addr(struct esp_pub *epub, u32 addr, u8 * buf,
+ u32 len, int sync);
+
+int esp_common_readbyte_with_addr(struct esp_pub *epub, u32 addr, u8 * buf,
+ int sync);
+int esp_common_writebyte_with_addr(struct esp_pub *epub, u32 addr, u8 buf,
+ int sync);
+
+int sif_read_reg_window(struct esp_pub *epub, unsigned int reg_addr,
+ unsigned char *value);
+int sif_write_reg_window(struct esp_pub *epub, unsigned int reg_addr,
+ unsigned char *value);
+int sif_ack_target_read_err(struct esp_pub *epub);
+int sif_had_io_enable(struct esp_pub *epub);
+
+struct slc_host_regs *sif_get_regs(struct esp_pub *epub);
+
+void sif_lock_bus(struct esp_pub *epub);
+void sif_unlock_bus(struct esp_pub *epub);
+
+int sif_interrupt_target(struct esp_pub *epub, u8 index);
+#ifdef USE_EXT_GPIO
+int sif_config_gpio_mode(struct esp_pub *epub, u8 gpio_num, u8 gpio_mode);
+int sif_set_gpio_output(struct esp_pub *epub, u16 mask, u16 value);
+int sif_get_gpio_intr(struct esp_pub *epub, u16 intr_mask, u16 * value);
+int sif_get_gpio_input(struct esp_pub *epub, u16 * mask, u16 * value);
+#endif
+
+void check_target_id(struct esp_pub *epub);
+
+void sif_record_bt_config(int value);
+int sif_get_bt_config(void);
+void sif_record_rst_config(int value);
+int sif_get_rst_config(void);
+void sif_record_ate_config(int value);
+int sif_get_ate_config(void);
+void sif_record_retry_config(void);
+int sif_get_retry_config(void);
+void sif_record_wakeup_gpio_config(int value);
+int sif_get_wakeup_gpio_config(void);
+
+#define sif_reg_read_sync(epub, addr, buf, len) sif_io_sync((epub), (addr), (buf), (len), SIF_FROM_DEVICE | SIF_BYTE_BASIS | SIF_INC_ADDR)
+
+#define sif_reg_write_sync(epub, addr, buf, len) sif_io_sync((epub), (addr), (buf), (len), SIF_TO_DEVICE | SIF_BYTE_BASIS | SIF_INC_ADDR)
+
+#endif /* _ESP_SIF_H_ */
diff --git a/drivers/net/wireless/esp8089/esp_sip.c b/drivers/net/wireless/esp8089/esp_sip.c
new file mode 100644
index 000000000000..7aaad88b942d
--- /dev/null
+++ b/drivers/net/wireless/esp8089/esp_sip.c
@@ -0,0 +1,2418 @@
+/*
+ * Copyright (c) 2009 - 2014 Espressif System.
+ *
+ * Serial Interconnctor Protocol
+ *
+ * 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.
+ */
+
+#include <linux/ieee80211.h>
+#include <net/mac80211.h>
+#include <net/cfg80211.h>
+#include <linux/skbuff.h>
+#include <linux/bitops.h>
+#include <linux/version.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/sdio_ids.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/sd.h>
+#include <linux/completion.h>
+#include <linux/timer.h>
+
+#include "esp_mac80211.h"
+#include "esp_pub.h"
+#include "esp_sip.h"
+#include "esp_ctrl.h"
+#include "esp_sif.h"
+#include "esp_debug.h"
+#include "slc_host_register.h"
+#include "esp_wmac.h"
+#include "esp_utils.h"
+
+#ifdef USE_EXT_GPIO
+#include "esp_ext.h"
+#endif /* USE_EXT_GPIO */
+
+extern struct completion *gl_bootup_cplx;
+
+static int old_signal = -35;
+static int avg_signal = 0;
+static int signal_loop = 0;
+
+struct esp_mac_prefix esp_mac_prefix_table[] = {
+ {0, {0x18, 0xfe, 0x34}},
+ {1, {0xac, 0xd0, 0x74}},
+ {255, {0x18, 0xfe, 0x34}},
+};
+
+#define SIGNAL_COUNT 300
+
+#define TID_TO_AC(_tid) ((_tid)== 0||((_tid)==3)?WME_AC_BE:((_tid)<3)?WME_AC_BK:((_tid)<6)?WME_AC_VI:WME_AC_VO)
+
+#ifdef SIP_DEBUG
+#define esp_sip_dbg esp_dbg
+struct sip_trace {
+ u32 tx_data;
+ u32 tx_cmd;
+ u32 rx_data;
+ u32 rx_evt;
+ u32 rx_tx_status;
+ u32 tx_out_of_credit;
+ u32 tx_one_shot_overflow;
+};
+static struct sip_trace str;
+#define STRACE_TX_DATA_INC() (str.tx_data++)
+#define STRACE_TX_CMD_INC() (str.tx_cmd++)
+#define STRACE_RX_DATA_INC() (str.rx_data++)
+#define STRACE_RX_EVENT_INC() (str.rx_evt++)
+#define STRACE_RX_TXSTATUS_INC() (str.rx_tx_status++)
+#define STRACE_TX_OUT_OF_CREDIT_INC() (str.tx_out_of_credit++)
+#define STRACE_TX_ONE_SHOT_INC() (str.tx_one_shot_overflow++)
+#define STRACE_SHOW(sip)
+#else
+#define esp_sip_dbg(...)
+#define STRACE_TX_DATA_INC()
+#define STRACE_TX_CMD_INC()
+#define STRACE_RX_DATA_INC()
+#define STRACE_RX_EVENT_INC()
+#define STRACE_RX_TXSTATUS_INC()
+#define STRACE_TX_OUT_OF_CREDIT_INC()
+#define STRACE_TX_ONE_SHOT_INC()
+#define STRACE_SHOW(sip)
+#endif /* SIP_DEBUG */
+
+#define SIP_STOP_QUEUE_THRESHOLD 48
+#define SIP_RESUME_QUEUE_THRESHOLD 12
+
+#define SIP_MIN_DATA_PKT_LEN (sizeof(struct esp_mac_rx_ctrl) + 24) //24 is min 80211hdr
+
+#ifdef ESP_PREALLOC
+extern struct sk_buff *esp_get_sip_skb(int size);
+extern void esp_put_sip_skb(struct sk_buff **skb);
+
+extern u8 *esp_get_tx_aggr_buf(void);
+extern void esp_put_tx_aggr_buf(u8 ** p);
+
+#endif
+
+static void sip_recalc_credit_init(struct esp_sip *sip);
+
+static int sip_recalc_credit_claim(struct esp_sip *sip, int force);
+
+static void sip_recalc_credit_release(struct esp_sip *sip);
+
+static struct sip_pkt *sip_get_ctrl_buf(struct esp_sip *sip,
+ SIP_BUF_TYPE bftype);
+
+static void sip_reclaim_ctrl_buf(struct esp_sip *sip, struct sip_pkt *pkt,
+ SIP_BUF_TYPE bftype);
+
+static void sip_free_init_ctrl_buf(struct esp_sip *sip);
+
+static int sip_pack_pkt(struct esp_sip *sip, struct sk_buff *skb,
+ int *pm_state);
+
+static struct esp_mac_rx_ctrl *sip_parse_normal_mac_ctrl(struct sk_buff
+ *skb,
+ int *pkt_len_enc,
+ int *buf_len,
+ int *pulled_len);
+
+static struct sk_buff *sip_parse_data_rx_info(struct esp_sip *sip,
+ struct sk_buff *skb,
+ int pkt_len_enc, int buf_len,
+ struct esp_mac_rx_ctrl
+ *mac_ctrl, int *pulled_len);
+
+static inline void sip_rx_pkt_enqueue(struct esp_sip *sip,
+ struct sk_buff *skb);
+
+static void sip_after_write_pkts(struct esp_sip *sip);
+
+static void sip_update_tx_credits(struct esp_sip *sip,
+ u16 recycled_credits);
+
+//static void sip_trigger_txq_process(struct esp_sip *sip);
+
+static bool sip_rx_pkt_process(struct esp_sip *sip, struct sk_buff *skb);
+
+static void sip_tx_status_report(struct esp_sip *sip, struct sk_buff *skb,
+ struct ieee80211_tx_info *tx_info,
+ bool success);
+
+#ifdef FPGA_TXDATA
+int sip_send_tx_data(struct esp_sip *sip);
+#endif /* FPGA_TXDATA */
+
+#ifdef FPGA_LOOPBACK
+int sip_send_loopback_cmd_mblk(struct esp_sip *sip);
+#endif /* FPGA_LOOPBACK */
+
+static bool check_ac_tid(u8 * pkt, u8 ac, u8 tid)
+{
+ struct ieee80211_hdr *wh = (struct ieee80211_hdr *) pkt;
+#ifdef TID_DEBUG
+ u16 real_tid = 0;
+#endif //TID_DEBUG
+
+ if (ieee80211_is_data_qos(wh->frame_control)) {
+#ifdef TID_DEBUG
+ real_tid =
+ *ieee80211_get_qos_ctl(wh) &
+ IEEE80211_QOS_CTL_TID_MASK;
+
+ esp_sip_dbg(ESP_SHOW, "ac:%u, tid:%u, tid in pkt:%u\n", ac,
+ tid, real_tid);
+ if (tid != real_tid) {
+ esp_sip_dbg(ESP_DBG_ERROR,
+ "111 ac:%u, tid:%u, tid in pkt:%u\n",
+ ac, tid, real_tid);
+ }
+ if (TID_TO_AC(tid) != ac) {
+ esp_sip_dbg(ESP_DBG_ERROR,
+ "222 ac:%u, tid:%u, tid in pkt:%u\n",
+ ac, tid, real_tid);
+ }
+#endif /* TID_DEBUG */
+ } else if (ieee80211_is_mgmt(wh->frame_control)) {
+#ifdef TID_DEBUG
+ esp_sip_dbg(ESP_SHOW, "ac:%u, tid:%u\n", ac, tid);
+ if (tid != 7 || ac != WME_AC_VO) {
+ esp_sip_dbg(ESP_DBG_ERROR, "333 ac:%u, tid:%u\n",
+ ac, tid);
+ }
+#endif /* TID_DEBUG */
+ } else {
+ if (ieee80211_is_ctl(wh->frame_control)) {
+#ifdef TID_DEBUG
+ esp_sip_dbg(ESP_SHOW,
+ "%s is ctrl pkt fc 0x%04x ac:%u, tid:%u, tid in pkt:%u\n",
+ __func__, wh->frame_control, ac, tid,
+ real_tid);
+#endif /* TID_DEBUG */
+ } else {
+ if (tid != 0 || ac != WME_AC_BE) {
+ //show_buf(pkt, 24);
+ esp_sip_dbg(ESP_DBG_LOG,
+ "444 ac:%u, tid:%u \n", ac,
+ tid);
+ if (tid == 7 && ac == WME_AC_VO)
+ return false;
+ }
+ return true; //hack to modify non-qos null data.
+
+ }
+ }
+
+ return false;
+}
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0))
+static void sip_recalc_credit_timeout(struct timer_list *t)
+#else
+static void sip_recalc_credit_timeout(unsigned long data)
+#endif
+{
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0))
+ struct esp_sip *sip = from_timer(sip, t, credit_timer);
+#else
+ struct esp_sip *sip = (struct esp_sip *) data;
+#endif
+
+ esp_dbg(ESP_DBG_ERROR, "rct");
+
+ sip_recalc_credit_claim(sip, 1); /* recalc again */
+}
+
+static void sip_recalc_credit_init(struct esp_sip *sip)
+{
+ atomic_set(&sip->credit_status, RECALC_CREDIT_DISABLE); //set it disable
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0))
+ timer_setup(&sip->credit_timer, sip_recalc_credit_timeout, 0);
+#else
+ init_timer(&sip->credit_timer);
+ sip->credit_timer.data = (unsigned long) sip;
+ sip->credit_timer.function = sip_recalc_credit_timeout;
+#endif
+}
+
+static int sip_recalc_credit_claim(struct esp_sip *sip, int force)
+{
+ int ret;
+
+ if (atomic_read(&sip->credit_status) == RECALC_CREDIT_ENABLE
+ && force == 0)
+ return 1;
+
+ atomic_set(&sip->credit_status, RECALC_CREDIT_ENABLE);
+ ret = sip_send_recalc_credit(sip->epub);
+ if (ret) {
+ esp_dbg(ESP_DBG_ERROR, "%s error %d", __func__, ret);
+ return ret;
+ }
+ /*setup a timer for handle the abs_credit not receive */
+ mod_timer(&sip->credit_timer, jiffies + msecs_to_jiffies(2000));
+
+ esp_dbg(ESP_SHOW, "rcc");
+
+ return ret;
+}
+
+static void sip_recalc_credit_release(struct esp_sip *sip)
+{
+ esp_dbg(ESP_SHOW, "rcr");
+
+ if (atomic_read(&sip->credit_status) == RECALC_CREDIT_ENABLE) {
+ atomic_set(&sip->credit_status, RECALC_CREDIT_DISABLE);
+ del_timer_sync(&sip->credit_timer);
+ } else
+ esp_dbg(ESP_SHOW, "maybe bogus credit");
+}
+
+static void sip_update_tx_credits(struct esp_sip *sip,
+ u16 recycled_credits)
+{
+ esp_sip_dbg(ESP_DBG_TRACE, "%s:before add, credits is %d\n",
+ __func__, atomic_read(&sip->tx_credits));
+
+ if (recycled_credits & 0x800) {
+ atomic_set(&sip->tx_credits, (recycled_credits & 0x7ff));
+ sip_recalc_credit_release(sip);
+ } else
+ atomic_add(recycled_credits, &sip->tx_credits);
+
+ esp_sip_dbg(ESP_DBG_TRACE, "%s:after add %d, credits is %d\n",
+ __func__, recycled_credits,
+ atomic_read(&sip->tx_credits));
+}
+
+void sip_trigger_txq_process(struct esp_sip *sip)
+{
+ if (atomic_read(&sip->tx_credits) <= sip->credit_to_reserve + SIP_CTRL_CREDIT_RESERVE //no credits, do nothing
+ || atomic_read(&sip->credit_status) == RECALC_CREDIT_ENABLE)
+ return;
+
+ if (sip_queue_may_resume(sip)) {
+ /* wakeup upper queue only if we have sufficient credits */
+ esp_sip_dbg(ESP_DBG_TRACE, "%s wakeup ieee80211 txq \n",
+ __func__);
+ atomic_set(&sip->epub->txq_stopped, false);
+ ieee80211_wake_queues(sip->epub->hw);
+ } else if (atomic_read(&sip->epub->txq_stopped)) {
+ esp_sip_dbg(ESP_DBG_TRACE,
+ "%s can't wake txq, credits: %d \n", __func__,
+ atomic_read(&sip->tx_credits));
+ }
+
+ if (!skb_queue_empty(&sip->epub->txq)) {
+ /* try to send out pkt already in sip queue once we have credits */
+ esp_sip_dbg(ESP_DBG_TRACE, "%s resume sip txq \n",
+ __func__);
+
+#if !defined(FPGA_TXDATA)
+ if (sif_get_ate_config() == 0) {
+ ieee80211_queue_work(sip->epub->hw,
+ &sip->epub->tx_work);
+ } else {
+ queue_work(sip->epub->esp_wkq,
+ &sip->epub->tx_work);
+ }
+#else
+ queue_work(sip->epub->esp_wkq, &sip->epub->tx_work);
+#endif
+ }
+}
+
+static bool sip_ampdu_occupy_buf(struct esp_sip *sip,
+ struct esp_rx_ampdu_len *ampdu_len)
+{
+ return (ampdu_len->substate == 0
+ || esp_wmac_rxsec_error(ampdu_len->substate)
+ || (sip->dump_rpbm_err
+ && ampdu_len->substate == RX_RPBM_ERR));
+}
+
+static bool sip_rx_pkt_process(struct esp_sip *sip, struct sk_buff *skb)
+{
+#define DO_NOT_COPY false
+#define DO_COPY true
+
+ struct sip_hdr *hdr = NULL;
+ struct sk_buff *rskb = NULL;
+ int remains_len = 0;
+ int first_pkt_len = 0;
+ u8 *bufptr = NULL;
+ int ret = 0;
+ bool trigger_rxq = false;
+
+ if (skb == NULL) {
+ esp_sip_dbg(ESP_DBG_ERROR, "%s NULL SKB!!!!!!!! \n",
+ __func__);
+ return trigger_rxq;
+ }
+
+ hdr = (struct sip_hdr *) skb->data;
+ bufptr = skb->data;
+
+
+ esp_sip_dbg(ESP_DBG_TRACE, "%s Hcredits 0x%08x, realCredits %d\n",
+ __func__, hdr->h_credits,
+ hdr->h_credits & SIP_CREDITS_MASK);
+ if (hdr->h_credits & SIP_CREDITS_MASK) {
+ sip_update_tx_credits(sip,
+ hdr->h_credits & SIP_CREDITS_MASK);
+ }
+
+ hdr->h_credits &= ~SIP_CREDITS_MASK; /* clean credits in sip_hdr, prevent over-add */
+
+ esp_sip_dbg(ESP_DBG_TRACE, "%s credits %d\n", __func__,
+ hdr->h_credits);
+
+ /*
+ * first pkt's length is stored in recycled_credits first 20 bits
+ * config w3 [31:12]
+ * repair hdr->len of first pkt
+ */
+ remains_len = hdr->len;
+ first_pkt_len = hdr->h_credits >> 12;
+ hdr->len = first_pkt_len;
+
+ esp_dbg(ESP_DBG_TRACE, "%s first_pkt_len %d, whole pkt len %d \n",
+ __func__, first_pkt_len, remains_len);
+ if (first_pkt_len > remains_len) {
+ sip_recalc_credit_claim(sip, 0);
+ esp_dbg(ESP_DBG_ERROR,
+ "first_pkt_len %d, whole pkt len %d\n",
+ first_pkt_len, remains_len);
+ show_buf((u8 *) hdr, first_pkt_len);
+ ESSERT(0);
+ goto _exit;
+ }
+
+ /*
+ * pkts handling, including the first pkt, should alloc new skb for each data pkt.
+ * free the original whole skb after parsing is done.
+ */
+ while (remains_len) {
+ if (remains_len < sizeof(struct sip_hdr)) {
+ sip_recalc_credit_claim(sip, 0);
+ ESSERT(0);
+ show_buf((u8 *) hdr, 512);
+ goto _exit;
+ }
+
+ hdr = (struct sip_hdr *) bufptr;
+ if (hdr->len <= 0) {
+ sip_recalc_credit_claim(sip, 0);
+ show_buf((u8 *) hdr, 512);
+ ESSERT(0);
+ goto _exit;
+ }
+
+ if ((hdr->len & 3) != 0) {
+ sip_recalc_credit_claim(sip, 0);
+ show_buf((u8 *) hdr, 512);
+ ESSERT(0);
+ goto _exit;
+ }
+ if (unlikely(hdr->seq != sip->rxseq++)) {
+ sip_recalc_credit_claim(sip, 0);
+ esp_dbg(ESP_DBG_ERROR,
+ "%s seq mismatch! got %u, expect %u\n",
+ __func__, hdr->seq, sip->rxseq - 1);
+ sip->rxseq = hdr->seq + 1;
+ show_buf(bufptr, 32);
+ ESSERT(0);
+ }
+
+ if (SIP_HDR_IS_CTRL(hdr)) {
+ STRACE_RX_EVENT_INC();
+ esp_sip_dbg(ESP_DBG_TRACE, "seq %u \n", hdr->seq);
+
+ ret = sip_parse_events(sip, bufptr);
+
+ skb_pull(skb, hdr->len);
+
+ } else if (SIP_HDR_IS_DATA(hdr)) {
+ struct esp_mac_rx_ctrl *mac_ctrl = NULL;
+ int pkt_len_enc = 0, buf_len = 0, pulled_len = 0;
+
+ STRACE_RX_DATA_INC();
+ esp_sip_dbg(ESP_DBG_TRACE, "seq %u \n", hdr->seq);
+ mac_ctrl =
+ sip_parse_normal_mac_ctrl(skb, &pkt_len_enc,
+ &buf_len,
+ &pulled_len);
+ rskb =
+ sip_parse_data_rx_info(sip, skb, pkt_len_enc,
+ buf_len, mac_ctrl,
+ &pulled_len);
+
+ if (rskb == NULL)
+ goto _move_on;
+
+ if (likely(atomic_read(&sip->epub->wl.off) == 0)) {
+#ifdef RX_CHECKSUM_TEST
+ esp_rx_checksum_test(rskb);
+#endif
+ local_bh_disable();
+ ieee80211_rx(sip->epub->hw, rskb);
+ local_bh_enable();
+ } else {
+ /* still need go thro parsing as skb_pull should invoke */
+ kfree_skb(rskb);
+ }
+ } else if (SIP_HDR_IS_AMPDU(hdr)) {
+ struct esp_mac_rx_ctrl *mac_ctrl = NULL;
+ struct esp_mac_rx_ctrl new_mac_ctrl;
+ struct esp_rx_ampdu_len *ampdu_len;
+ int pkt_num;
+ int pulled_len = 0;
+ static int pkt_dropped = 0;
+ static int pkt_total = 0;
+ bool have_rxabort = false;
+ bool have_goodpkt = false;
+ static u8 frame_head[16];
+ static u8 frame_buf_ttl = 0;
+
+ ampdu_len =
+ (struct esp_rx_ampdu_len *) (skb->data +
+ hdr->len /
+ sip->rx_blksz *
+ sip->rx_blksz);
+ esp_sip_dbg(ESP_DBG_TRACE,
+ "%s rx ampdu total len %u\n", __func__,
+ hdr->len);
+ if (skb->data != (u8 *) hdr) {
+ printk("%p %p\n", skb->data, hdr);
+ show_buf(skb->data, 512);
+ show_buf((u8 *) hdr, 512);
+ ESSERT(0);
+ goto _exit;
+ }
+ mac_ctrl =
+ sip_parse_normal_mac_ctrl(skb, NULL, NULL,
+ &pulled_len);
+ memcpy(&new_mac_ctrl, mac_ctrl,
+ sizeof(struct esp_mac_rx_ctrl));
+ mac_ctrl = &new_mac_ctrl;
+ pkt_num = mac_ctrl->ampdu_cnt;
+ esp_sip_dbg(ESP_DBG_TRACE,
+ "%s %d rx ampdu %u pkts, %d pkts dumped, first len %u\n",
+ __func__, __LINE__,
+ (unsigned
+ int) ((hdr->len % sip->rx_blksz) /
+ sizeof(struct
+ esp_rx_ampdu_len)),
+ pkt_num,
+ (unsigned int) ampdu_len->sublen);
+
+ pkt_total += mac_ctrl->ampdu_cnt;
+ //esp_sip_dbg(ESP_DBG_ERROR, "%s ampdu dropped %d/%d\n", __func__, pkt_dropped, pkt_total);
+ while (pkt_num > 0) {
+ esp_sip_dbg(ESP_DBG_TRACE,
+ "%s %d ampdu sub state %02x,\n",
+ __func__, __LINE__,
+ ampdu_len->substate);
+
+ if (sip_ampdu_occupy_buf(sip, ampdu_len)) { //pkt is dumped
+
+ rskb =
+ sip_parse_data_rx_info(sip,
+ skb,
+ ampdu_len->
+ sublen -
+ FCS_LEN,
+ 0,
+ mac_ctrl,
+ &pulled_len);
+ if (!rskb) {
+ ESSERT(0);
+ goto _exit;
+ }
+
+ if (likely
+ (atomic_read
+ (&sip->epub->wl.off) == 0)
+ && (ampdu_len->substate == 0
+ || ampdu_len->substate ==
+ RX_TKIPMIC_ERR
+ || (sip->sendup_rpbm_pkt
+ && ampdu_len->
+ substate ==
+ RX_RPBM_ERR))
+ && (sip->rxabort_fixed
+ || !have_rxabort)) {
+ if (!have_goodpkt) {
+ have_goodpkt =
+ true;
+ memcpy(frame_head,
+ rskb->data,
+ 16);
+ frame_head[1] &=
+ ~0x80;
+ frame_buf_ttl = 3;
+ }
+#ifdef RX_CHECKSUM_TEST
+ esp_rx_checksum_test(rskb);
+#endif
+ local_bh_disable();
+ ieee80211_rx(sip->epub->hw,
+ rskb);
+ local_bh_enable();
+
+ } else {
+ kfree_skb(rskb);
+ }
+ } else {
+ if (ampdu_len->substate ==
+ RX_ABORT) {
+ u8 *a;
+ have_rxabort = true;
+ esp_sip_dbg(ESP_DBG_TRACE,
+ "rx abort %d %d\n",
+ frame_buf_ttl,
+ pkt_num);
+ if (frame_buf_ttl
+ && !sip->
+ rxabort_fixed) {
+ struct
+ esp_rx_ampdu_len
+ *next_good_ampdu_len
+ =
+ ampdu_len + 1;
+ a = frame_head;
+ esp_sip_dbg
+ (ESP_DBG_TRACE,
+ "frame:%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ a[0], a[1],
+ a[2], a[3],
+ a[4], a[5],
+ a[6], a[7],
+ a[8], a[9],
+ a[10], a[11],
+ a[12], a[13],
+ a[14], a[15]);
+ while
+ (!sip_ampdu_occupy_buf
+ (sip,
+ next_good_ampdu_len))
+ {
+ if (next_good_ampdu_len > ampdu_len + pkt_num - 1)
+ break;
+ next_good_ampdu_len++;
+
+ }
+ if (next_good_ampdu_len <= ampdu_len + pkt_num - 1) {
+ bool b0,
+ b10,
+ b11;
+ a = skb->
+ data;
+ esp_sip_dbg
+ (ESP_DBG_TRACE,
+ "buf:%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ a[0],
+ a[1],
+ a[2],
+ a[3],
+ a[4],
+ a[5],
+ a[6],
+ a[7],
+ a[8],
+ a[9],
+ a[10],
+ a[11],
+ a[12],
+ a[13],
+ a[14],
+ a
+ [15]);
+ b0 = memcmp
+ (frame_head
+ + 4,
+ skb->
+ data +
+ 4,
+ 12) ==
+ 0;
+ b10 =
+ memcmp
+ (frame_head
+ + 10,
+ skb->
+ data,
+ 6) ==
+ 0;
+ b11 =
+ memcpy
+ (frame_head
+ + 11,
+ skb->
+ data,
+ 5) ==
+ 0;
+ esp_sip_dbg
+ (ESP_DBG_TRACE,
+ "com %d %d %d\n",
+ b0,
+ b10,
+ b11);
+ if (b0
+ && !b10
+ &&
+ !b11) {
+ have_rxabort
+ =
+ false;
+ esp_sip_dbg
+ (ESP_DBG_TRACE,
+ "repair 0\n");
+ } else
+ if (!b0
+ &&
+ b10
+ &&
+ !b11)
+ {
+ skb_push
+ (skb,
+ 10);
+ memcpy
+ (skb->
+ data,
+ frame_head,
+ 10);
+ have_rxabort
+ =
+ false;
+ pulled_len
+ -=
+ 10;
+ esp_sip_dbg
+ (ESP_DBG_TRACE,
+ "repair 10\n");
+ } else
+ if (!b0
+ &&
+ !b10
+ &&
+ b11)
+ {
+ skb_push
+ (skb,
+ 11);
+ memcpy
+ (skb->
+ data,
+ frame_head,
+ 11);
+ have_rxabort
+ =
+ false;
+ pulled_len
+ -=
+ 11;
+ esp_sip_dbg
+ (ESP_DBG_TRACE,
+ "repair 11\n");
+ }
+ }
+ }
+ }
+ pkt_dropped++;
+ esp_sip_dbg(ESP_DBG_LOG,
+ "%s ampdu dropped %d/%d\n",
+ __func__, pkt_dropped,
+ pkt_total);
+ }
+ pkt_num--;
+ ampdu_len++;
+ }
+ if (frame_buf_ttl)
+ frame_buf_ttl--;
+ skb_pull(skb, hdr->len - pulled_len);
+ } else {
+ esp_sip_dbg(ESP_DBG_ERROR, "%s %d unknown type\n",
+ __func__, __LINE__);
+ }
+
+ _move_on:
+ if (hdr->len < remains_len) {
+ remains_len -= hdr->len;
+ } else {
+ break;
+ }
+ bufptr += hdr->len;
+ }
+
+ _exit:
+#ifdef ESP_PREALLOC
+ esp_put_sip_skb(&skb);
+#else
+ kfree_skb(skb);
+#endif
+
+ return trigger_rxq;
+
+#undef DO_NOT_COPY
+#undef DO_COPY
+}
+
+static void _sip_rxq_process(struct esp_sip *sip)
+{
+ struct sk_buff *skb = NULL;
+ bool sendup = false;
+
+ while ((skb = skb_dequeue(&sip->rxq))) {
+ if (sip_rx_pkt_process(sip, skb))
+ sendup = true;
+ }
+ if (sendup) {
+ queue_work(sip->epub->esp_wkq, &sip->epub->sendup_work);
+ }
+
+ /* probably tx_credit is updated, try txq */
+ sip_trigger_txq_process(sip);
+}
+
+void sip_rxq_process(struct work_struct *work)
+{
+ struct esp_sip *sip =
+ container_of(work, struct esp_sip, rx_process_work);
+ if (sip == NULL) {
+ ESSERT(0);
+ return;
+ }
+
+ if (unlikely(atomic_read(&sip->state) == SIP_SEND_INIT)) {
+ sip_send_chip_init(sip);
+ atomic_set(&sip->state, SIP_WAIT_BOOTUP);
+ return;
+ }
+
+ mutex_lock(&sip->rx_mtx);
+ _sip_rxq_process(sip);
+ mutex_unlock(&sip->rx_mtx);
+}
+
+static inline void sip_rx_pkt_enqueue(struct esp_sip *sip,
+ struct sk_buff *skb)
+{
+ skb_queue_tail(&sip->rxq, skb);
+}
+
+static inline struct sk_buff *sip_rx_pkt_dequeue(struct esp_sip *sip)
+{
+ return skb_dequeue(&sip->rxq);
+}
+
+static u32 sip_rx_count = 0;
+void sip_debug_show(struct esp_sip *sip)
+{
+ esp_sip_dbg(ESP_DBG_ERROR, "txq left %d %d\n",
+ skb_queue_len(&sip->epub->txq),
+ atomic_read(&sip->tx_data_pkt_queued));
+ esp_sip_dbg(ESP_DBG_ERROR, "tx queues stop ? %d\n",
+ atomic_read(&sip->epub->txq_stopped));
+ esp_sip_dbg(ESP_DBG_ERROR, "txq stop? %d\n",
+ test_bit(ESP_WL_FLAG_STOP_TXQ, &sip->epub->wl.flags));
+ esp_sip_dbg(ESP_DBG_ERROR, "tx credit %d\n",
+ atomic_read(&sip->tx_credits));
+ esp_sip_dbg(ESP_DBG_ERROR, "rx collect %d\n", sip_rx_count);
+ sip_rx_count = 0;
+}
+
+int sip_rx(struct esp_pub *epub)
+{
+ struct sip_hdr *shdr = NULL;
+ struct esp_sip *sip = epub->sip;
+ int err = 0;
+ struct sk_buff *first_skb = NULL;
+ u8 *rx_buf = NULL;
+ u32 rx_blksz;
+ struct sk_buff *rx_skb = NULL;
+
+ u32 first_sz;
+
+ first_sz = sif_get_regs(epub)->config_w0;
+
+ if (likely(sif_get_ate_config() != 1)) {
+ do {
+ u8 raw_seq = sif_get_regs(epub)->intr_raw & 0xff;
+
+ if (raw_seq != sip->to_host_seq) {
+ if (raw_seq == sip->to_host_seq + 1) { /* when last read pkt crc err, this situation may occur, but raw_seq mustn't < to_host_Seq */
+ sip->to_host_seq = raw_seq;
+ esp_dbg(ESP_DBG_TRACE,
+ "warn: to_host_seq reg 0x%02x, seq 0x%02x",
+ raw_seq, sip->to_host_seq);
+ break;
+ }
+ esp_dbg(ESP_DBG_ERROR,
+ "err: to_host_seq reg 0x%02x, seq 0x%02x",
+ raw_seq, sip->to_host_seq);
+ goto _err;
+ }
+ } while (0);
+ }
+ esp_sip_dbg(ESP_DBG_LOG, "%s enter\n", __func__);
+
+
+ /* first read one block out, if we luck enough, that's it
+ *
+ * To make design as simple as possible, we allocate skb(s)
+ * separately for each sif read operation to avoid global
+ * read_buf_pointe access. It coule be optimized late.
+ */
+ rx_blksz = sif_get_blksz(epub);
+#ifdef ESP_PREALLOC
+ first_skb = esp_get_sip_skb(roundup(first_sz, rx_blksz));
+#else
+ first_skb =
+ __dev_alloc_skb(roundup(first_sz, rx_blksz), GFP_KERNEL);
+#endif /* ESP_PREALLOC */
+
+ if (first_skb == NULL) {
+ sif_unlock_bus(epub);
+ esp_sip_dbg(ESP_DBG_ERROR, "%s first no memory \n",
+ __func__);
+ goto _err;
+ }
+
+ rx_buf = skb_put(first_skb, first_sz);
+ esp_sip_dbg(ESP_DBG_LOG, "%s rx_buf ptr %p, first_sz %d\n",
+ __func__, rx_buf, first_sz);
+
+
+#ifdef USE_EXT_GPIO
+ do {
+ int err2 = 0;
+ u16 value = 0;
+ u16 intr_mask = ext_gpio_get_int_mask_reg();
+ if (!intr_mask)
+ break;
+ value = sif_get_regs(epub)->config_w3 & intr_mask;
+ if (value) {
+ err2 = sif_interrupt_target(epub, 6);
+ esp_sip_dbg(ESP_DBG, "write gpio\n");
+ }
+
+ if (!err2 && value) {
+ esp_sip_dbg(ESP_DBG_TRACE,
+ "%s intr_mask[0x%04x] value[0x%04x]\n",
+ __func__, intr_mask, value);
+ ext_gpio_int_process(value);
+ }
+ } while (0);
+#endif
+
+ err =
+ esp_common_read(epub, rx_buf, first_sz, ESP_SIF_NOSYNC, false);
+ sip_rx_count++;
+ if (unlikely(err)) {
+ esp_dbg(ESP_DBG_ERROR, " %s first read err %d %d\n",
+ __func__, err, sif_get_regs(epub)->config_w0);
+#ifdef ESP_PREALLOC
+ esp_put_sip_skb(&first_skb);
+#else
+ kfree_skb(first_skb);
+#endif /* ESP_PREALLOC */
+ sif_unlock_bus(epub);
+ goto _err;
+ }
+
+ shdr = (struct sip_hdr *) rx_buf;
+ if (SIP_HDR_IS_CTRL(shdr) && (shdr->c_evtid == SIP_EVT_SLEEP)) {
+ atomic_set(&sip->epub->ps.state, ESP_PM_ON);
+ esp_dbg(ESP_DBG_TRACE, "s\n");
+ }
+
+ if (likely(sif_get_ate_config() != 1)) {
+ sip->to_host_seq++;
+ }
+
+ if ((shdr->len & 3) != 0) {
+ esp_sip_dbg(ESP_DBG_ERROR, "%s shdr->len[%d] error\n",
+ __func__, shdr->len);
+#ifdef ESP_PREALLOC
+ esp_put_sip_skb(&first_skb);
+#else
+ kfree_skb(first_skb);
+#endif /* ESP_PREALLOC */
+ sif_unlock_bus(epub);
+ err = -EIO;
+ goto _err;
+ }
+ if (shdr->len != first_sz) {
+ esp_sip_dbg(ESP_DBG_ERROR,
+ "%s shdr->len[%d] first_size[%d] error\n",
+ __func__, shdr->len, first_sz);
+#ifdef ESP_PREALLOC
+ esp_put_sip_skb(&first_skb);
+#else
+ kfree_skb(first_skb);
+#endif /* ESP_PREALLOC */
+ sif_unlock_bus(epub);
+ err = -EIO;
+ goto _err;
+ } else {
+ sif_unlock_bus(epub);
+ skb_trim(first_skb, shdr->len);
+ esp_dbg(ESP_DBG_TRACE, " %s first_skb only\n", __func__);
+
+ rx_skb = first_skb;
+ }
+
+ if (atomic_read(&sip->state) == SIP_STOP) {
+#ifdef ESP_PREALLOC
+ esp_put_sip_skb(&rx_skb);
+#else
+ kfree_skb(rx_skb);
+#endif /* ESP_PREALLOC */
+ esp_sip_dbg(ESP_DBG_ERROR, "%s when sip stopped\n",
+ __func__);
+ return 0;
+ }
+
+ sip_rx_pkt_enqueue(sip, rx_skb);
+ queue_work(sip->epub->esp_wkq, &sip->rx_process_work);
+
+ _err:
+ return err;
+}
+
+int sip_post_init(struct esp_sip *sip, struct sip_evt_bootup2 *bevt)
+{
+ struct esp_pub *epub;
+
+ u8 mac_id = bevt->mac_addr[0];
+ int mac_index = 0;
+ int i = 0;
+
+ if (sip == NULL) {
+ ESSERT(0);
+ return -EINVAL;
+ }
+
+ epub = sip->epub;
+
+
+ sip->tx_aggr_write_ptr = sip->tx_aggr_buf;
+
+ sip->tx_blksz = bevt->tx_blksz;
+ sip->rx_blksz = bevt->rx_blksz;
+ sip->credit_to_reserve = bevt->credit_to_reserve;
+
+ sip->dump_rpbm_err = (bevt->options & SIP_DUMP_RPBM_ERR);
+ sip->rxabort_fixed = (bevt->options & SIP_RXABORT_FIXED);
+ sip->support_bgscan = (bevt->options & SIP_SUPPORT_BGSCAN);
+
+ sip->sendup_rpbm_pkt = sip->dump_rpbm_err && false;
+
+ /* print out MAC addr... */
+ memcpy(epub->mac_addr, bevt->mac_addr, ETH_ALEN);
+ for (i = 0;
+ i <
+ sizeof(esp_mac_prefix_table) / sizeof(struct esp_mac_prefix);
+ i++) {
+ if (esp_mac_prefix_table[i].mac_index == mac_id) {
+ mac_index = i;
+ break;
+ }
+ }
+
+ epub->mac_addr[0] =
+ esp_mac_prefix_table[mac_index].mac_addr_prefix[0];
+ epub->mac_addr[1] =
+ esp_mac_prefix_table[mac_index].mac_addr_prefix[1];
+ epub->mac_addr[2] =
+ esp_mac_prefix_table[mac_index].mac_addr_prefix[2];
+
+#ifdef SELF_MAC
+ epub->mac_addr[0] = 0xff;
+ epub->mac_addr[1] = 0xff;
+ epub->mac_addr[2] = 0xff;
+#endif
+ atomic_set(&sip->noise_floor, bevt->noise_floor);
+
+ sip_recalc_credit_init(sip);
+
+ esp_sip_dbg(ESP_DBG_TRACE,
+ "%s tx_blksz %d rx_blksz %d mac addr %pM\n", __func__,
+ sip->tx_blksz, sip->rx_blksz, epub->mac_addr);
+
+ return 0;
+}
+
+/* write pkts in aggr buf to target memory */
+static void sip_write_pkts(struct esp_sip *sip, int pm_state)
+{
+ int tx_aggr_len = 0;
+ struct sip_hdr *first_shdr = NULL;
+ int err = 0;
+
+ tx_aggr_len = sip->tx_aggr_write_ptr - sip->tx_aggr_buf;
+ if (tx_aggr_len < sizeof(struct sip_hdr)) {
+ printk("%s tx_aggr_len %d \n", __func__, tx_aggr_len);
+ ESSERT(0);
+ return;
+ }
+ if ((tx_aggr_len & 0x3) != 0) {
+ ESSERT(0);
+ return;
+ }
+
+ first_shdr = (struct sip_hdr *) sip->tx_aggr_buf;
+
+ if (atomic_read(&sip->tx_credits) <= SIP_CREDITS_LOW_THRESHOLD) {
+ first_shdr->fc[1] |= SIP_HDR_F_NEED_CRDT_RPT;
+ }
+
+ /* still use lock bus instead of sif_lldesc_write_sync since we want to protect several global varibles assignments */
+ sif_lock_bus(sip->epub);
+
+ err =
+ esp_common_write(sip->epub, sip->tx_aggr_buf, tx_aggr_len,
+ ESP_SIF_NOSYNC);
+
+ sip->tx_aggr_write_ptr = sip->tx_aggr_buf;
+ sip->tx_tot_len = 0;
+
+ sif_unlock_bus(sip->epub);
+
+ if (err)
+ esp_sip_dbg(ESP_DBG_ERROR, "func %s err!!!!!!!!!: %d\n",
+ __func__, err);
+
+}
+
+/* setup sip header and tx info, copy pkt into aggr buf */
+static int sip_pack_pkt(struct esp_sip *sip, struct sk_buff *skb,
+ int *pm_state)
+{
+ struct ieee80211_tx_info *itx_info;
+ struct sip_hdr *shdr;
+ u32 tx_len = 0, offset = 0;
+ bool is_data = true;
+
+ itx_info = IEEE80211_SKB_CB(skb);
+
+ if (itx_info->flags == 0xffffffff) {
+ shdr = (struct sip_hdr *) skb->data;
+ is_data = false;
+ tx_len = skb->len;
+ } else {
+ struct ieee80211_hdr *wh =
+ (struct ieee80211_hdr *) skb->data;
+ struct esp_vif *evif =
+ (struct esp_vif *) itx_info->control.vif->drv_priv;
+ u8 sta_index;
+ struct esp_node *node;
+ /* update sip header */
+ shdr = (struct sip_hdr *) sip->tx_aggr_write_ptr;
+
+ shdr->fc[0] = 0;
+ shdr->fc[1] = 0;
+
+ if ((itx_info->flags & IEEE80211_TX_CTL_AMPDU)
+ && (true || esp_is_ip_pkt(skb)))
+ SIP_HDR_SET_TYPE(shdr->fc[0], SIP_DATA_AMPDU);
+ else
+ SIP_HDR_SET_TYPE(shdr->fc[0], SIP_DATA);
+
+ if (evif->epub == NULL) {
+ sip_tx_status_report(sip, skb, itx_info, false);
+ atomic_dec(&sip->tx_data_pkt_queued);
+ return -EINVAL;
+ }
+
+ /* make room for encrypted pkt */
+ if (itx_info->control.hw_key) {
+ int alg =
+ esp_cipher2alg(itx_info->control.hw_key->
+ cipher);
+ if (unlikely(alg == -1)) {
+ sip_tx_status_report(sip, skb, itx_info,
+ false);
+ atomic_dec(&sip->tx_data_pkt_queued);
+ return -1;
+ } else {
+ shdr->d_enc_flag = alg + 1;
+ }
+
+ shdr->d_hw_kid =
+ itx_info->control.hw_key->hw_key_idx | (evif->
+ index
+ << 7);
+ } else {
+ shdr->d_enc_flag = 0;
+ shdr->d_hw_kid = (evif->index << 7 | evif->index);
+ }
+
+ /* update sip tx info */
+ node = esp_get_node_by_addr(sip->epub, wh->addr1);
+ if (node != NULL)
+ sta_index = node->index;
+ else
+ sta_index = ESP_PUB_MAX_STA + 1;
+ SIP_HDR_SET_IFIDX(shdr->fc[0],
+ evif->index << 3 | sta_index);
+ shdr->d_p2p = itx_info->control.vif->p2p;
+ if (evif->index == 1)
+ shdr->d_p2p = 1;
+ shdr->d_ac = skb_get_queue_mapping(skb);
+ shdr->d_tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
+ wh = (struct ieee80211_hdr *) skb->data;
+ if (ieee80211_is_mgmt(wh->frame_control)) {
+ /* addba/delba/bar may use different tid/ac */
+ if (shdr->d_ac == WME_AC_VO) {
+ shdr->d_tid = 7;
+ }
+ if (ieee80211_is_beacon(wh->frame_control)) {
+ shdr->d_tid = 8;
+ shdr->d_ac = 4;
+ }
+ }
+ if (check_ac_tid(skb->data, shdr->d_ac, shdr->d_tid)) {
+ shdr->d_ac = WME_AC_BE;
+ shdr->d_tid = 0;
+ }
+
+
+ /* make sure data is start at 4 bytes aligned addr. */
+ offset = roundup(sizeof(struct sip_hdr), 4);
+
+#ifdef HOST_RC
+ esp_sip_dbg(ESP_DBG_TRACE, "%s offset0 %d \n", __func__,
+ offset);
+ memcpy(sip->tx_aggr_write_ptr + offset,
+ (void *) &itx_info->control,
+ sizeof(struct sip_tx_rc));
+
+ offset += roundup(sizeof(struct sip_tx_rc), 4);
+ esp_show_tx_rates(&itx_info->control.rates[0]);
+
+#endif /* HOST_RC */
+
+ if (SIP_HDR_IS_AMPDU(shdr)) {
+ memset(sip->tx_aggr_write_ptr + offset, 0,
+ sizeof(struct esp_tx_ampdu_entry));
+ offset +=
+ roundup(sizeof(struct esp_tx_ampdu_entry), 4);
+ }
+
+ tx_len = offset + skb->len;
+ shdr->len = tx_len; /* actual len */
+
+ esp_sip_dbg(ESP_DBG_TRACE,
+ "%s offset %d skblen %d txlen %d\n", __func__,
+ offset, skb->len, tx_len);
+
+ }
+
+ shdr->seq = sip->txseq++;
+ //esp_sip_dbg(ESP_DBG_ERROR, "%s seq %u, %u %u\n", __func__, shdr->seq, SIP_HDR_GET_TYPE(shdr->fc[0]),shdr->c_cmdid);
+
+ /* copy skb to aggr buf */
+ memcpy(sip->tx_aggr_write_ptr + offset, skb->data, skb->len);
+
+ if (is_data) {
+ spin_lock_bh(&sip->epub->tx_lock);
+ sip->txdataseq = shdr->seq;
+ spin_unlock_bh(&sip->epub->tx_lock);
+ /* fake a tx_status and report to mac80211 stack to speed up tx, may affect
+ * 1) rate control (now it's all in target, so should be OK)
+ * 2) ps mode, mac80211 want to check ACK of ps/nulldata to see if AP is awake
+ * 3) BAR, mac80211 do BAR by checking ACK
+ */
+ /*
+ * XXX: need to adjust for 11n, e.g. report tx_status according to BA received in target
+ *
+ */
+ sip_tx_status_report(sip, skb, itx_info, true);
+ atomic_dec(&sip->tx_data_pkt_queued);
+
+ STRACE_TX_DATA_INC();
+ } else {
+ /* check pm state here */
+
+ /* no need to hold ctrl skb */
+ sip_free_ctrl_skbuff(sip, skb);
+ STRACE_TX_CMD_INC();
+ }
+
+ /* TBD: roundup here or whole aggr-buf */
+ tx_len = roundup(tx_len, sip->tx_blksz);
+
+ sip->tx_aggr_write_ptr += tx_len;
+ sip->tx_tot_len += tx_len;
+
+ return 0;
+}
+
+#ifdef HOST_RC
+static void sip_set_tx_rate_status(struct sip_rc_status *rcstatus,
+ struct ieee80211_tx_rate *irates)
+{
+ int i;
+ u8 shift = 0;
+ u32 cnt = 0;
+
+ for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
+ if (rcstatus->rc_map & BIT(i)) {
+ shift = i << 2;
+ cnt =
+ (rcstatus->
+ rc_cnt_store >> shift) & RC_CNT_MASK;
+ irates[i].idx = i;
+ irates[i].count = (u8) cnt;
+ } else {
+ irates[i].idx = -1;
+ irates[i].count = 0;
+ }
+ }
+
+ esp_show_rcstatus(rcstatus);
+ esp_show_tx_rates(irates);
+}
+#endif /* HOST_RC */
+
+static void sip_tx_status_report(struct esp_sip *sip, struct sk_buff *skb,
+ struct ieee80211_tx_info *tx_info,
+ bool success)
+{
+ if (!(tx_info->flags & IEEE80211_TX_CTL_AMPDU)) {
+ if (likely(success))
+ tx_info->flags |= IEEE80211_TX_STAT_ACK;
+ else
+ tx_info->flags &= ~IEEE80211_TX_STAT_ACK;
+
+ /* manipulate rate status... */
+ tx_info->status.rates[0].idx = 11;
+ tx_info->status.rates[0].count = 1;
+ tx_info->status.rates[0].flags = 0;
+ tx_info->status.rates[1].idx = -1;
+
+ } else {
+ tx_info->flags |=
+ IEEE80211_TX_STAT_AMPDU | IEEE80211_TX_STAT_ACK;
+ tx_info->status.ampdu_len = 1;
+ tx_info->status.ampdu_ack_len = 1;
+
+ /* manipulate rate status... */
+ tx_info->status.rates[0].idx = 7;
+ tx_info->status.rates[0].count = 1;
+ tx_info->status.rates[0].flags =
+ IEEE80211_TX_RC_MCS | IEEE80211_TX_RC_SHORT_GI;
+ tx_info->status.rates[1].idx = -1;
+
+ }
+
+ if (tx_info->flags & IEEE80211_TX_STAT_AMPDU)
+ esp_sip_dbg(ESP_DBG_TRACE, "%s ampdu status! \n",
+ __func__);
+
+ if (!mod_support_no_txampdu() &&
+ cfg80211_get_chandef_type(&sip->epub->hw->conf.chandef) !=
+ NL80211_CHAN_NO_HT) {
+ struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_hdr *wh =
+ (struct ieee80211_hdr *) skb->data;
+ if (ieee80211_is_data_qos(wh->frame_control)) {
+ if (!(tx_info->flags & IEEE80211_TX_CTL_AMPDU)) {
+ u8 tidno =
+ ieee80211_get_qos_ctl(wh)[0] &
+ IEEE80211_QOS_CTL_TID_MASK;
+ struct esp_node *node;
+ struct esp_tx_tid *tid;
+ struct ieee80211_sta *sta;
+
+ node =
+ esp_get_node_by_addr(sip->epub,
+ wh->addr1);
+ if (node == NULL)
+ goto _exit;
+ if (node->sta == NULL)
+ goto _exit;
+ sta = node->sta;
+ tid = &node->tid[tidno];
+ spin_lock_bh(&sip->epub->tx_ampdu_lock);
+ //start session
+ if (tid == NULL) {
+ spin_unlock_bh(&sip->epub->
+ tx_ampdu_lock);
+ ESSERT(0);
+ goto _exit;
+ }
+ if ((tid->state == ESP_TID_STATE_INIT) &&
+ (TID_TO_AC(tidno) != WME_AC_VO)
+ && tid->cnt >= 10) {
+ tid->state = ESP_TID_STATE_TRIGGER;
+ esp_sip_dbg(ESP_DBG_ERROR,
+ "start tx ba session,addr:%pM,tid:%u\n",
+ wh->addr1, tidno);
+ spin_unlock_bh(&sip->epub->
+ tx_ampdu_lock);
+ ieee80211_start_tx_ba_session(sta,
+ tidno,
+ 0);
+ } else {
+ if (tid->state ==
+ ESP_TID_STATE_INIT)
+ tid->cnt++;
+ else
+ tid->cnt = 0;
+ spin_unlock_bh(&sip->epub->
+ tx_ampdu_lock);
+ }
+ }
+ }
+ }
+ _exit:
+ ieee80211_tx_status(sip->epub->hw, skb);
+}
+
+/*
+ * NB: this routine should be locked when calling
+ */
+void sip_txq_process(struct esp_pub *epub)
+{
+ struct sk_buff *skb;
+ struct esp_sip *sip = epub->sip;
+ u32 pkt_len = 0, tx_len = 0;
+ int blknum = 0;
+ bool queued_back = false;
+ bool out_of_credits = false;
+ struct ieee80211_tx_info *itx_info;
+ int pm_state = 0;
+
+ while ((skb = skb_dequeue(&epub->txq))) {
+
+ /* cmd skb->len does not include sip_hdr too */
+ pkt_len = skb->len;
+ itx_info = IEEE80211_SKB_CB(skb);
+ if (itx_info->flags != 0xffffffff) {
+ pkt_len += roundup(sizeof(struct sip_hdr), 4);
+ if ((itx_info->flags & IEEE80211_TX_CTL_AMPDU)
+ && (true || esp_is_ip_pkt(skb)))
+ pkt_len +=
+ roundup(sizeof
+ (struct esp_tx_ampdu_entry),
+ 4);
+ }
+
+ /* current design simply requires every sip_hdr must be at the begin of mblk, that definitely
+ * need to be optimized, e.g. calulate remain length in the previous mblk, if it larger than
+ * certain threshold (e.g, whole pkt or > 50% of pkt or 2 x sizeof(struct sip_hdr), append pkt
+ * to the previous mblk. This might be done in sip_pack_pkt()
+ */
+ pkt_len = roundup(pkt_len, sip->tx_blksz);
+ blknum = pkt_len / sip->tx_blksz;
+ esp_dbg(ESP_DBG_TRACE,
+ "%s skb_len %d pkt_len %d blknum %d\n", __func__,
+ skb->len, pkt_len, blknum);
+
+ if (unlikely(atomic_read(&sip->credit_status) == RECALC_CREDIT_ENABLE)) { /* need recalc credit */
+ struct sip_hdr *hdr = (struct sip_hdr *) skb->data;
+ itx_info = IEEE80211_SKB_CB(skb);
+ if (!(itx_info->flags == 0xffffffff && SIP_HDR_GET_TYPE(hdr->fc[0]) == SIP_CTRL && hdr->c_cmdid == SIP_CMD_RECALC_CREDIT && blknum <= atomic_read(&sip->tx_credits) - sip->credit_to_reserve)) { /* except cmd recalc credit */
+ esp_dbg(ESP_DBG_ERROR,
+ "%s recalc credits!\n", __func__);
+ STRACE_TX_OUT_OF_CREDIT_INC();
+ queued_back = true;
+ out_of_credits = true;
+ break;
+ }
+ } else { /* normal situation */
+ if (unlikely
+ (blknum >
+ (atomic_read(&sip->tx_credits) -
+ sip->credit_to_reserve -
+ SIP_CTRL_CREDIT_RESERVE))) {
+ itx_info = IEEE80211_SKB_CB(skb);
+ if (itx_info->flags == 0xffffffff) { /* priv ctrl pkt */
+ if (blknum >
+ atomic_read(&sip->tx_credits) -
+ sip->credit_to_reserve) {
+ esp_dbg(ESP_DBG_TRACE,
+ "%s cmd pkt out of credits!\n",
+ __func__);
+ STRACE_TX_OUT_OF_CREDIT_INC
+ ();
+ queued_back = true;
+ out_of_credits = true;
+ break;
+ }
+ } else {
+ esp_dbg(ESP_DBG_TRACE,
+ "%s out of credits!\n",
+ __func__);
+ STRACE_TX_OUT_OF_CREDIT_INC();
+ queued_back = true;
+ out_of_credits = true;
+ break;
+ }
+ }
+ }
+ tx_len += pkt_len;
+ if (tx_len >= SIP_TX_AGGR_BUF_SIZE) {
+ /* do we need to have limitation likemax 8 pkts in a row? */
+ esp_dbg(ESP_DBG_TRACE,
+ "%s too much pkts in one shot!\n",
+ __func__);
+ STRACE_TX_ONE_SHOT_INC();
+ tx_len -= pkt_len;
+ queued_back = true;
+ break;
+ }
+
+ if (sip_pack_pkt(sip, skb, &pm_state) != 0) {
+ /* wrong pkt, won't send to target */
+ tx_len -= pkt_len;
+ continue;
+ }
+
+ esp_sip_dbg(ESP_DBG_TRACE,
+ "%s:before sub, credits is %d\n", __func__,
+ atomic_read(&sip->tx_credits));
+ atomic_sub(blknum, &sip->tx_credits);
+ esp_sip_dbg(ESP_DBG_TRACE,
+ "%s:after sub %d,credits remains %d\n",
+ __func__, blknum,
+ atomic_read(&sip->tx_credits));
+
+ }
+
+ if (queued_back) {
+ skb_queue_head(&epub->txq, skb);
+ }
+
+ if (atomic_read(&sip->state) == SIP_STOP
+#ifdef HOST_RESET_BUG
+ || atomic_read(&epub->wl.off) == 1
+#endif
+ ) {
+ queued_back = 1;
+ tx_len = 0;
+ sip_after_write_pkts(sip);
+ }
+
+ if (tx_len) {
+
+ sip_write_pkts(sip, pm_state);
+
+ sip_after_write_pkts(sip);
+ }
+
+ if (queued_back && !out_of_credits) {
+
+ /* skb pending, do async process again */
+ sip_trigger_txq_process(sip);
+ }
+}
+
+static void sip_after_write_pkts(struct esp_sip *sip)
+{
+
+}
+
+#ifndef NO_WMM_DUMMY
+static struct esp_80211_wmm_param_element esp_wmm_param = {
+ .oui = {0x00, 0x50, 0xf2},
+ .oui_type = 0x02,
+ .oui_subtype = 0x01,
+ .version = 0x01,
+ .qos_info = 0x00,
+ .reserved = 0x00,
+ .ac = {
+ {
+ .aci_aifsn = 0x03,
+ .cw = 0xa4,
+ .txop_limit = 0x0000,
+ },
+ {
+ .aci_aifsn = 0x27,
+ .cw = 0xa4,
+ .txop_limit = 0x0000,
+ },
+ {
+ .aci_aifsn = 0x42,
+ .cw = 0x43,
+ .txop_limit = 0x005e,
+ },
+ {
+ .aci_aifsn = 0x62,
+ .cw = 0x32,
+ .txop_limit = 0x002f,
+ },
+ },
+};
+
+static int esp_add_wmm(struct sk_buff *skb)
+{
+ u8 *p;
+ int flag = 0;
+ int remain_len;
+ int base_len;
+ int len;
+ struct ieee80211_mgmt *mgmt;
+ struct ieee80211_hdr *wh;
+
+ if (!skb)
+ return -1;
+
+ wh = (struct ieee80211_hdr *) skb->data;
+ mgmt = (struct ieee80211_mgmt *) ((u8 *) skb->data);
+
+ if (ieee80211_is_assoc_resp(wh->frame_control)) {
+ p = mgmt->u.assoc_resp.variable;
+ base_len =
+ (u8 *) mgmt->u.assoc_resp.variable - (u8 *) mgmt;
+ } else if (ieee80211_is_reassoc_resp(wh->frame_control)) {
+ p = mgmt->u.reassoc_resp.variable;
+ base_len =
+ (u8 *) mgmt->u.reassoc_resp.variable - (u8 *) mgmt;
+ } else if (ieee80211_is_probe_resp(wh->frame_control)) {
+ p = mgmt->u.probe_resp.variable;
+ base_len =
+ (u8 *) mgmt->u.probe_resp.variable - (u8 *) mgmt;
+ } else if (ieee80211_is_beacon(wh->frame_control)) {
+ p = mgmt->u.beacon.variable;
+ base_len = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt;
+ } else
+ return 1;
+
+
+ remain_len = skb->len - base_len;
+
+ while (remain_len > 0) {
+ if (*p == 0xdd && *(p + 5) == 0x02) //wmm type
+ return 0;
+ else if (*p == 0x2d) //has ht cap
+ flag = 1;
+
+ len = *(++p);
+ p += (len + 1);
+ remain_len -= (len + 2);
+ }
+
+ if (remain_len < 0) {
+ esp_dbg(ESP_DBG_TRACE,
+ "%s remain_len %d, skb->len %d, base_len %d, flag %d",
+ __func__, remain_len, skb->len, base_len, flag);
+ return -2;
+ }
+
+ if (flag == 1) {
+ skb_put(skb, 2 + sizeof(esp_wmm_param));
+
+ memset(p, 0xdd, sizeof(u8));
+ memset(p + 1, sizeof(esp_wmm_param), sizeof(u8));
+ memcpy(p + 2, &esp_wmm_param, sizeof(esp_wmm_param));
+
+ esp_dbg(ESP_DBG_TRACE, "esp_wmm_param");
+ }
+
+ return 0;
+}
+#endif /* NO_WMM_DUMMY */
+
+/* parse mac_rx_ctrl and return length */
+static int sip_parse_mac_rx_info(struct esp_sip *sip,
+ struct esp_mac_rx_ctrl *mac_ctrl,
+ struct sk_buff *skb)
+{
+ struct ieee80211_rx_status *rx_status = NULL;
+ struct ieee80211_hdr *hdr;
+
+ rx_status = IEEE80211_SKB_RXCB(skb);
+ rx_status->freq = esp_ieee2mhz(mac_ctrl->channel);
+
+ rx_status->signal = mac_ctrl->rssi + mac_ctrl->noise_floor; /* snr actually, need to offset noise floor e.g. -85 */
+
+ hdr = (struct ieee80211_hdr *) skb->data;
+ if (mac_ctrl->damatch0 == 1 && mac_ctrl->bssidmatch0 == 1 /*match bssid and da, but beacon package contain other bssid */
+ && strncmp(hdr->addr2, sip->epub->wl.bssid, ETH_ALEN) == 0) { /* force match addr2 */
+ if (++signal_loop >= SIGNAL_COUNT) {
+ avg_signal += rx_status->signal;
+ avg_signal /= SIGNAL_COUNT;
+ old_signal = rx_status->signal = (avg_signal + 5);
+ signal_loop = 0;
+ avg_signal = 0;
+ } else {
+ avg_signal += rx_status->signal;
+ rx_status->signal = old_signal;
+ }
+ }
+
+ rx_status->antenna = 0; /* one antenna for now */
+ rx_status->band = NL80211_BAND_2GHZ;
+ rx_status->flag = RX_FLAG_DECRYPTED | RX_FLAG_MMIC_STRIPPED;
+ if (mac_ctrl->sig_mode) {
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0))
+ rx_status->encoding = RX_ENC_HT;
+#else
+ rx_status->flag |= RX_FLAG_HT;
+#endif
+ rx_status->rate_idx = mac_ctrl->MCS;
+ if (mac_ctrl->SGI)
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0))
+ rx_status->enc_flags |= RX_ENC_FLAG_SHORT_GI;
+#else
+ rx_status->flag |= RX_FLAG_SHORT_GI;
+#endif
+ } else {
+ rx_status->rate_idx = esp_wmac_rate2idx(mac_ctrl->rate);
+ }
+ if (mac_ctrl->rxend_state == RX_FCS_ERR)
+ rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
+
+ /* Mic error frame flag */
+ if (mac_ctrl->rxend_state == RX_TKIPMIC_ERR
+ || mac_ctrl->rxend_state == RX_CCMPMIC_ERR) {
+ if (atomic_read(&sip->epub->wl.tkip_key_set) == 1) {
+ rx_status->flag |= RX_FLAG_MMIC_ERROR;
+ atomic_set(&sip->epub->wl.tkip_key_set, 0);
+ printk("mic err\n");
+ } else {
+ printk("mic err discard\n");
+ }
+ }
+ //esp_dbg(ESP_DBG_LOG, "%s freq: %u; signal: %d; rate_idx %d; flag: %d \n", __func__, rx_status->freq, rx_status->signal, rx_status->rate_idx, rx_status->flag);
+
+ do {
+ struct ieee80211_hdr *wh =
+ (struct ieee80211_hdr *) ((u8 *) skb->data);
+
+#ifndef NO_WMM_DUMMY
+ if (ieee80211_is_mgmt(wh->frame_control))
+ esp_add_wmm(skb);
+#endif
+
+ /* some kernel e.g. 3.0.8 wrongly handles non-encrypted pkt like eapol */
+ if (ieee80211_is_data(wh->frame_control)) {
+ if (!ieee80211_has_protected(wh->frame_control)) {
+ esp_sip_dbg(ESP_DBG_TRACE,
+ "%s kiv_war, add iv_stripped flag \n",
+ __func__);
+ rx_status->flag |= RX_FLAG_IV_STRIPPED;
+ } else {
+ if ((atomic_read(&sip->epub->wl.ptk_cnt) ==
+ 0 && !(wh->addr1[0] & 0x1))
+ || (atomic_read(&sip->epub->wl.gtk_cnt)
+ == 0 && (wh->addr1[0] & 0x1))) {
+ esp_dbg(ESP_DBG_TRACE,
+ "%s ==kiv_war, got bogus enc pkt==\n",
+ __func__);
+ rx_status->flag |=
+ RX_FLAG_IV_STRIPPED;
+ //show_buf(skb->data, 32);
+ }
+
+ esp_sip_dbg(ESP_DBG_TRACE,
+ "%s kiv_war, got enc pkt \n",
+ __func__);
+ }
+ }
+ } while (0);
+
+ return 0;
+}
+
+static struct esp_mac_rx_ctrl *sip_parse_normal_mac_ctrl(struct sk_buff
+ *skb,
+ int *pkt_len_enc,
+ int *buf_len,
+ int *pulled_len)
+{
+ struct esp_mac_rx_ctrl *mac_ctrl = NULL;
+ struct sip_hdr *hdr = (struct sip_hdr *) skb->data;
+ int len_in_hdr = hdr->len;
+
+ ESSERT(skb != NULL);
+ ESSERT(skb->len > SIP_MIN_DATA_PKT_LEN);
+
+ skb_pull(skb, sizeof(struct sip_hdr));
+ *pulled_len += sizeof(struct sip_hdr);
+ mac_ctrl = (struct esp_mac_rx_ctrl *) skb->data;
+ if (!mac_ctrl->Aggregation) {
+ ESSERT(pkt_len_enc != NULL);
+ ESSERT(buf_len != NULL);
+ *pkt_len_enc =
+ (mac_ctrl->sig_mode ? mac_ctrl->HT_length : mac_ctrl->
+ legacy_length) - FCS_LEN;
+ *buf_len =
+ len_in_hdr - sizeof(struct sip_hdr) -
+ sizeof(struct esp_mac_rx_ctrl);
+ }
+ skb_pull(skb, sizeof(struct esp_mac_rx_ctrl));
+ *pulled_len += sizeof(struct esp_mac_rx_ctrl);
+
+ return mac_ctrl;
+}
+
+/*
+ * for one MPDU (including subframe in AMPDU)
+ *
+ */
+static struct sk_buff *sip_parse_data_rx_info(struct esp_sip *sip,
+ struct sk_buff *skb,
+ int pkt_len_enc, int buf_len,
+ struct esp_mac_rx_ctrl
+ *mac_ctrl, int *pulled_len)
+{
+ /*
+ * | mac_rx_ctrl | real_data_payload | ampdu_entries |
+ */
+ //without enc
+ int pkt_len = 0;
+ struct sk_buff *rskb = NULL;
+ int ret;
+
+ if (mac_ctrl->Aggregation) {
+ struct ieee80211_hdr *wh =
+ (struct ieee80211_hdr *) skb->data;
+ pkt_len = pkt_len_enc;
+ if (ieee80211_has_protected(wh->frame_control)) //ampdu, it is CCMP enc
+ pkt_len -= 8;
+ buf_len = roundup(pkt_len, 4);
+ } else
+ pkt_len = buf_len - 3 + ((pkt_len_enc - 1) & 0x3);
+ esp_dbg(ESP_DBG_TRACE,
+ "%s pkt_len %u, pkt_len_enc %u!, delta %d \n", __func__,
+ pkt_len, pkt_len_enc, pkt_len_enc - pkt_len);
+ do {
+#ifndef NO_WMM_DUMMY
+ rskb =
+ __dev_alloc_skb(pkt_len_enc + sizeof(esp_wmm_param) +
+ 2, GFP_ATOMIC);
+#else
+ rskb = __dev_alloc_skb(pkt_len_enc, GFP_ATOMIC);
+#endif /* NO_WMM_DUMMY */
+ if (unlikely(rskb == NULL)) {
+ esp_sip_dbg(ESP_DBG_ERROR, "%s no mem for rskb\n",
+ __func__);
+ return NULL;
+ }
+ skb_put(rskb, pkt_len_enc);
+ } while (0);
+
+ do {
+ memcpy(rskb->data, skb->data, pkt_len);
+ if (pkt_len_enc > pkt_len) {
+ memset(rskb->data + pkt_len, 0,
+ pkt_len_enc - pkt_len);
+ }
+ /* strip out current pkt, move to the next one */
+ skb_pull(skb, buf_len);
+ *pulled_len += buf_len;
+ } while (0);
+
+ ret = sip_parse_mac_rx_info(sip, mac_ctrl, rskb);
+ if (ret == -1 && !mac_ctrl->Aggregation) {
+ kfree_skb(rskb);
+ return NULL;
+ }
+
+ esp_dbg(ESP_DBG_LOG,
+ "%s after pull headers, skb->len %d rskb->len %d \n",
+ __func__, skb->len, rskb->len);
+
+ return rskb;
+}
+
+struct esp_sip *sip_attach(struct esp_pub *epub)
+{
+ struct esp_sip *sip = NULL;
+ struct sip_pkt *pkt = NULL;
+ int i;
+#ifndef ESP_PREALLOC
+ int po = 0;
+#endif
+
+ sip = kzalloc(sizeof(struct esp_sip), GFP_KERNEL);
+ if (sip == NULL) {
+ esp_dbg(ESP_DBG_ERROR, "no mem for sip! \n");
+ goto _err_sip;
+ }
+#ifdef ESP_PREALLOC
+ sip->tx_aggr_buf = (u8 *) esp_get_tx_aggr_buf();
+#else
+ po = get_order(SIP_TX_AGGR_BUF_SIZE);
+ sip->tx_aggr_buf = (u8 *) __get_free_pages(GFP_ATOMIC, po);
+#endif
+ if (sip->tx_aggr_buf == NULL) {
+ esp_dbg(ESP_DBG_ERROR, "no mem for tx_aggr_buf! \n");
+ goto _err_aggr;
+ }
+
+ spin_lock_init(&sip->lock);
+
+ INIT_LIST_HEAD(&sip->free_ctrl_txbuf);
+ INIT_LIST_HEAD(&sip->free_ctrl_rxbuf);
+
+ for (i = 0; i < SIP_CTRL_BUF_N; i++) {
+ pkt = kzalloc(sizeof(struct sip_pkt), GFP_KERNEL);
+
+ if (!pkt)
+ goto _err_pkt;
+
+ pkt->buf_begin = kzalloc(SIP_CTRL_BUF_SZ, GFP_KERNEL);
+
+ if (pkt->buf_begin == NULL) {
+ kfree(pkt);
+ pkt = NULL;
+ goto _err_pkt;
+ }
+
+ pkt->buf_len = SIP_CTRL_BUF_SZ;
+ pkt->buf = pkt->buf_begin;
+
+ if (i < SIP_CTRL_TXBUF_N) {
+ list_add_tail(&pkt->list, &sip->free_ctrl_txbuf);
+ } else {
+ list_add_tail(&pkt->list, &sip->free_ctrl_rxbuf);
+ }
+ }
+
+ mutex_init(&sip->rx_mtx);
+ skb_queue_head_init(&sip->rxq);
+ INIT_WORK(&sip->rx_process_work, sip_rxq_process);
+
+ sip->epub = epub;
+ atomic_set(&sip->noise_floor, -96);
+
+ atomic_set(&sip->state, SIP_INIT);
+ atomic_set(&sip->tx_credits, 0);
+
+ if (sip->rawbuf == NULL) {
+ sip->rawbuf = kzalloc(SIP_BOOT_BUF_SIZE, GFP_KERNEL);
+ if (sip->rawbuf == NULL) {
+ esp_dbg(ESP_DBG_ERROR, "no mem for rawbuf! \n");
+ goto _err_pkt;
+ }
+ }
+
+ atomic_set(&sip->state, SIP_PREPARE_BOOT);
+
+ return sip;
+
+ _err_pkt:
+ sip_free_init_ctrl_buf(sip);
+
+ if (sip->tx_aggr_buf) {
+#ifdef ESP_PREALLOC
+ esp_put_tx_aggr_buf(&sip->tx_aggr_buf);
+#else
+ po = get_order(SIP_TX_AGGR_BUF_SIZE);
+ free_pages((unsigned long) sip->tx_aggr_buf, po);
+ sip->tx_aggr_buf = NULL;
+#endif
+ }
+ _err_aggr:
+ if (sip) {
+ kfree(sip);
+ sip = NULL;
+ }
+ _err_sip:
+ return NULL;
+
+}
+
+static void sip_free_init_ctrl_buf(struct esp_sip *sip)
+{
+ struct sip_pkt *pkt, *tpkt;
+
+ list_for_each_entry_safe(pkt, tpkt, &sip->free_ctrl_txbuf, list) {
+ list_del(&pkt->list);
+ kfree(pkt->buf_begin);
+ kfree(pkt);
+ }
+
+ list_for_each_entry_safe(pkt, tpkt, &sip->free_ctrl_rxbuf, list) {
+ list_del(&pkt->list);
+ kfree(pkt->buf_begin);
+ kfree(pkt);
+ }
+}
+
+void sip_detach(struct esp_sip *sip)
+{
+#ifndef ESP_PREALLOC
+ int po;
+#endif
+ if (sip == NULL)
+ return;
+
+ sip_free_init_ctrl_buf(sip);
+
+ if (atomic_read(&sip->state) == SIP_RUN) {
+
+ sif_disable_target_interrupt(sip->epub);
+
+ atomic_set(&sip->state, SIP_STOP);
+
+ /* disable irq here */
+ sif_disable_irq(sip->epub);
+ cancel_work_sync(&sip->rx_process_work);
+
+ skb_queue_purge(&sip->rxq);
+ mutex_destroy(&sip->rx_mtx);
+ cancel_work_sync(&sip->epub->sendup_work);
+ skb_queue_purge(&sip->epub->rxq);
+
+#ifdef ESP_NO_MAC80211
+ unregister_netdev(sip->epub->net_dev);
+ wiphy_unregister(sip->epub->wdev->wiphy);
+#else
+ if (test_and_clear_bit
+ (ESP_WL_FLAG_HW_REGISTERED, &sip->epub->wl.flags)) {
+ ieee80211_unregister_hw(sip->epub->hw);
+ }
+#endif
+
+ /* cancel all worker/timer */
+ cancel_work_sync(&sip->epub->tx_work);
+ skb_queue_purge(&sip->epub->txq);
+ skb_queue_purge(&sip->epub->txdoneq);
+
+#ifdef ESP_PREALLOC
+ esp_put_tx_aggr_buf(&sip->tx_aggr_buf);
+#else
+ po = get_order(SIP_TX_AGGR_BUF_SIZE);
+ free_pages((unsigned long) sip->tx_aggr_buf, po);
+ sip->tx_aggr_buf = NULL;
+#endif
+
+ atomic_set(&sip->state, SIP_INIT);
+ } else if (atomic_read(&sip->state) >= SIP_BOOT
+ && atomic_read(&sip->state) <= SIP_WAIT_BOOTUP) {
+
+ sif_disable_target_interrupt(sip->epub);
+ atomic_set(&sip->state, SIP_STOP);
+ sif_disable_irq(sip->epub);
+
+ if (sip->rawbuf)
+ kfree(sip->rawbuf);
+
+ if (atomic_read(&sip->state) == SIP_SEND_INIT) {
+ cancel_work_sync(&sip->rx_process_work);
+ skb_queue_purge(&sip->rxq);
+ mutex_destroy(&sip->rx_mtx);
+ cancel_work_sync(&sip->epub->sendup_work);
+ skb_queue_purge(&sip->epub->rxq);
+ }
+#ifdef ESP_NO_MAC80211
+ unregister_netdev(sip->epub->net_dev);
+ wiphy_unregister(sip->epub->wdev->wiphy);
+#else
+ if (test_and_clear_bit
+ (ESP_WL_FLAG_HW_REGISTERED, &sip->epub->wl.flags)) {
+ ieee80211_unregister_hw(sip->epub->hw);
+ }
+#endif
+ atomic_set(&sip->state, SIP_INIT);
+ } else
+ esp_dbg(ESP_DBG_ERROR, "%s wrong state %d\n", __func__,
+ atomic_read(&sip->state));
+
+ kfree(sip);
+}
+
+int sip_write_memory(struct esp_sip *sip, u32 addr, u8 * buf, u16 len)
+{
+ struct sip_cmd_write_memory *cmd;
+ struct sip_hdr *chdr;
+ u16 remains, hdrs, bufsize;
+ u32 loadaddr;
+ u8 *src;
+ int err = 0;
+ u32 *t = NULL;
+
+ if (sip == NULL || sip->rawbuf == NULL) {
+ ESSERT(sip != NULL);
+ ESSERT(sip->rawbuf != NULL);
+ return -EINVAL;
+ }
+
+ memset(sip->rawbuf, 0, SIP_BOOT_BUF_SIZE);
+
+ chdr = (struct sip_hdr *) sip->rawbuf;
+ SIP_HDR_SET_TYPE(chdr->fc[0], SIP_CTRL);
+ chdr->c_cmdid = SIP_CMD_WRITE_MEMORY;
+
+ remains = len;
+ hdrs =
+ sizeof(struct sip_hdr) + sizeof(struct sip_cmd_write_memory);
+
+ while (remains) {
+ src = &buf[len - remains];
+ loadaddr = addr + (len - remains);
+
+ if (remains < (SIP_BOOT_BUF_SIZE - hdrs)) {
+ /* aligned with 4 bytes */
+ bufsize = roundup(remains, 4);
+ memset(sip->rawbuf + hdrs, 0, bufsize);
+ remains = 0;
+ } else {
+ bufsize = SIP_BOOT_BUF_SIZE - hdrs;
+ remains -= bufsize;
+ }
+
+ chdr->len = bufsize + hdrs;
+ chdr->seq = sip->txseq++;
+ cmd =
+ (struct sip_cmd_write_memory *) (sip->rawbuf +
+ SIP_CTRL_HDR_LEN);
+ cmd->len = bufsize;
+ cmd->addr = loadaddr;
+ memcpy(sip->rawbuf + hdrs, src, bufsize);
+
+ t = (u32 *) sip->rawbuf;
+ esp_dbg(ESP_DBG_TRACE,
+ "%s t0: 0x%08x t1: 0x%08x t2:0x%08x loadaddr 0x%08x \n",
+ __func__, t[0], t[1], t[2], loadaddr);
+
+ err =
+ esp_common_write(sip->epub, sip->rawbuf, chdr->len,
+ ESP_SIF_SYNC);
+
+ if (err) {
+ esp_dbg(ESP_DBG_ERROR, "%s send buffer failed\n",
+ __func__);
+ return err;
+ }
+ // 1ms is enough, in fact on dell-d430, need not delay at all.
+ mdelay(1);
+
+ }
+
+ return err;
+}
+
+int sip_send_cmd(struct esp_sip *sip, int cid, u32 cmdlen, void *cmd)
+{
+ struct sip_hdr *chdr;
+ struct sip_pkt *pkt = NULL;
+ int ret = 0;
+
+ pkt = sip_get_ctrl_buf(sip, SIP_TX_CTRL_BUF);
+
+ if (pkt == NULL)
+ return -ENOMEM;
+
+ chdr = (struct sip_hdr *) pkt->buf_begin;
+ chdr->len = SIP_CTRL_HDR_LEN + cmdlen;
+ chdr->seq = sip->txseq++;
+ chdr->c_cmdid = cid;
+
+
+ if (cmd) {
+ memset(pkt->buf, 0, cmdlen);
+ memcpy(pkt->buf, (u8 *) cmd, cmdlen);
+ }
+
+ esp_dbg(ESP_DBG_TRACE, "cid %d, len %u, seq %u \n", chdr->c_cmdid,
+ chdr->len, chdr->seq);
+
+ esp_dbg(ESP_DBG_TRACE, "c1 0x%08x c2 0x%08x\n",
+ *(u32 *) & pkt->buf[0], *(u32 *) & pkt->buf[4]);
+
+ ret =
+ esp_common_write(sip->epub, pkt->buf_begin, chdr->len,
+ ESP_SIF_SYNC);
+
+ if (ret)
+ esp_dbg(ESP_DBG_ERROR, "%s send cmd %d failed \n",
+ __func__, cid);
+
+ sip_reclaim_ctrl_buf(sip, pkt, SIP_TX_CTRL_BUF);
+
+ /*
+ * Hack here: reset tx/rx seq before target ram code is up...
+ */
+ if (cid == SIP_CMD_BOOTUP) {
+ sip->rxseq = 0;
+ sip->txseq = 0;
+ sip->txdataseq = 0;
+ }
+
+ return ret;
+}
+
+struct sk_buff *sip_alloc_ctrl_skbuf(struct esp_sip *sip, u16 len, u32 cid)
+{
+ struct sip_hdr *si = NULL;
+ struct ieee80211_tx_info *ti = NULL;
+ struct sk_buff *skb = NULL;
+
+ ESSERT(len <= sip->tx_blksz);
+
+ /* no need to reserve space for net stack */
+ skb = __dev_alloc_skb(len, GFP_KERNEL);
+
+ if (skb == NULL) {
+ esp_dbg(ESP_DBG_ERROR, "no skb for ctrl !\n");
+ return NULL;
+ }
+
+ skb->len = len;
+
+ ti = IEEE80211_SKB_CB(skb);
+ /* set tx_info flags to 0xffffffff to indicate sip_ctrl pkt */
+ ti->flags = 0xffffffff;
+ si = (struct sip_hdr *) skb->data;
+ memset(si, 0, sizeof(struct sip_hdr));
+ SIP_HDR_SET_TYPE(si->fc[0], SIP_CTRL);
+ si->len = len;
+ si->c_cmdid = cid;
+
+ return skb;
+}
+
+void sip_free_ctrl_skbuff(struct esp_sip *sip, struct sk_buff *skb)
+{
+ memset(IEEE80211_SKB_CB(skb), 0, sizeof(struct ieee80211_tx_info));
+ kfree_skb(skb);
+}
+
+static struct sip_pkt *sip_get_ctrl_buf(struct esp_sip *sip,
+ SIP_BUF_TYPE bftype)
+{
+ struct sip_pkt *pkt = NULL;
+ struct list_head *bflist;
+ struct sip_hdr *chdr;
+
+ bflist =
+ (bftype ==
+ SIP_TX_CTRL_BUF) ? &sip->free_ctrl_txbuf : &sip->
+ free_ctrl_rxbuf;
+
+ spin_lock_bh(&sip->lock);
+
+ if (list_empty(bflist)) {
+ spin_unlock_bh(&sip->lock);
+ return NULL;
+ }
+
+ pkt = list_first_entry(bflist, struct sip_pkt, list);
+ list_del(&pkt->list);
+ spin_unlock_bh(&sip->lock);
+
+ if (bftype == SIP_TX_CTRL_BUF) {
+ chdr = (struct sip_hdr *) pkt->buf_begin;
+ SIP_HDR_SET_TYPE(chdr->fc[0], SIP_CTRL);
+ pkt->buf = pkt->buf_begin + SIP_CTRL_HDR_LEN;
+ } else {
+ pkt->buf = pkt->buf_begin;
+ }
+
+ return pkt;
+}
+
+static void
+sip_reclaim_ctrl_buf(struct esp_sip *sip, struct sip_pkt *pkt,
+ SIP_BUF_TYPE bftype)
+{
+ struct list_head *bflist = NULL;
+
+ if (bftype == SIP_TX_CTRL_BUF)
+ bflist = &sip->free_ctrl_txbuf;
+ else if (bftype == SIP_RX_CTRL_BUF)
+ bflist = &sip->free_ctrl_rxbuf;
+ else
+ return;
+
+ pkt->buf = pkt->buf_begin;
+
+ spin_lock_bh(&sip->lock);
+ list_add_tail(&pkt->list, bflist);
+ spin_unlock_bh(&sip->lock);
+}
+
+int sip_poll_bootup_event(struct esp_sip *sip)
+{
+ int ret = 0;
+
+ esp_dbg(ESP_DBG_TRACE, "polling bootup event... \n");
+
+ if (gl_bootup_cplx)
+ ret = wait_for_completion_timeout(gl_bootup_cplx, 2 * HZ);
+
+ esp_dbg(ESP_DBG_TRACE, "******time remain****** = [%d]\n", ret);
+ if (ret <= 0) {
+ esp_dbg(ESP_DBG_ERROR, "bootup event timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ if (sif_get_ate_config() == 0) {
+ ret = esp_register_mac80211(sip->epub);
+ }
+#ifdef TEST_MODE
+ ret = test_init_netlink(sip);
+ if (ret < 0) {
+ esp_sip_dbg(ESP_DBG_TRACE,
+ "esp_sdio: failed initializing netlink\n");
+ return ret;
+ }
+#endif
+
+ atomic_set(&sip->state, SIP_RUN);
+ esp_dbg(ESP_DBG_TRACE, "target booted up\n");
+
+ return ret;
+}
+
+int sip_poll_resetting_event(struct esp_sip *sip)
+{
+ int ret = 0;
+
+ esp_dbg(ESP_DBG_TRACE, "polling resetting event... \n");
+
+ if (gl_bootup_cplx)
+ ret = wait_for_completion_timeout(gl_bootup_cplx, 10 * HZ);
+
+ esp_dbg(ESP_DBG_TRACE, "******time remain****** = [%d]\n", ret);
+ if (ret <= 0) {
+ esp_dbg(ESP_DBG_ERROR, "resetting event timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ esp_dbg(ESP_DBG_TRACE, "target resetting %d %p\n", ret,
+ gl_bootup_cplx);
+
+ return 0;
+}
+
+
+#ifdef FPGA_DEBUG
+
+/* bogus bootup cmd for FPGA debugging */
+int sip_send_bootup(struct esp_sip *sip)
+{
+ int ret;
+ struct sip_cmd_bootup bootcmd;
+
+ esp_dbg(ESP_DBG_LOG, "sending bootup\n");
+
+ bootcmd.boot_addr = 0;
+ ret =
+ sip_send_cmd(sip, SIP_CMD_BOOTUP,
+ sizeof(struct sip_cmd_bootup), &bootcmd);
+
+ return ret;
+}
+
+#endif /* FPGA_DEBUG */
+
+bool sip_queue_need_stop(struct esp_sip * sip)
+{
+ return atomic_read(&sip->tx_data_pkt_queued) >=
+ SIP_STOP_QUEUE_THRESHOLD || (atomic_read(&sip->tx_credits) < 8
+ && atomic_read(&sip->
+ tx_data_pkt_queued)
+ >=
+ SIP_STOP_QUEUE_THRESHOLD / 4 * 3);
+}
+
+bool sip_queue_may_resume(struct esp_sip * sip)
+{
+ return atomic_read(&sip->epub->txq_stopped)
+ && !test_bit(ESP_WL_FLAG_STOP_TXQ, &sip->epub->wl.flags)
+ && ((atomic_read(&sip->tx_credits) >= 16
+ && atomic_read(&sip->tx_data_pkt_queued) <
+ SIP_RESUME_QUEUE_THRESHOLD * 2)
+ || atomic_read(&sip->tx_data_pkt_queued) <
+ SIP_RESUME_QUEUE_THRESHOLD);
+}
+
+int sip_cmd_enqueue(struct esp_sip *sip, struct sk_buff *skb, int prior)
+{
+ if (!sip || !sip->epub) {
+ esp_dbg(ESP_DBG_ERROR, "func %s, sip->epub->txq is NULL\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (!skb) {
+ esp_dbg(ESP_DBG_ERROR, "func %s, skb is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ if (prior == ENQUEUE_PRIOR_HEAD)
+ skb_queue_head(&sip->epub->txq, skb);
+ else
+ skb_queue_tail(&sip->epub->txq, skb);
+
+ if (sif_get_ate_config() == 0) {
+ ieee80211_queue_work(sip->epub->hw, &sip->epub->tx_work);
+ } else {
+ queue_work(sip->epub->esp_wkq, &sip->epub->tx_work);
+ }
+ return 0;
+}
+
+void sip_tx_data_pkt_enqueue(struct esp_pub *epub, struct sk_buff *skb)
+{
+ if (!epub || !epub->sip) {
+ if (!epub)
+ esp_dbg(ESP_DBG_ERROR, "func %s, epub is NULL\n",
+ __func__);
+ else
+ esp_dbg(ESP_DBG_ERROR,
+ "func %s, epub->sip is NULL\n", __func__);
+
+ return;
+ }
+ if (!skb) {
+ esp_dbg(ESP_DBG_ERROR, "func %s, skb is NULL\n", __func__);
+ return;
+ }
+ skb_queue_tail(&epub->txq, skb);
+ atomic_inc(&epub->sip->tx_data_pkt_queued);
+ if (sip_queue_need_stop(epub->sip)) {
+ if (epub->hw) {
+ ieee80211_stop_queues(epub->hw);
+ atomic_set(&epub->txq_stopped, true);
+ }
+
+ }
+}
+
+#ifdef FPGA_TXDATA
+int sip_send_tx_data(struct esp_sip *sip)
+{
+ struct sk_buff *skb = NULL;
+ struct sip_cmd_bss_info_update *bsscmd;
+
+ skb =
+ sip_alloc_ctrl_skbuf(epub->sip,
+ sizeof(struct sip_cmd_bss_info_update),
+ SIP_CMD_BSS_INFO_UPDATE);
+ if (!skb)
+ return -EINVAL;
+
+ bsscmd =
+ (struct sip_cmd_bss_info_update *) (skb->data +
+ sizeof(struct
+ sip_tx_info));
+ bsscmd->isassoc = (assoc == true) ? 1 : 0;
+ memcpy(bsscmd->bssid, bssid, ETH_ALEN);
+ STRACE_SHOW(epub->sip);
+ return sip_cmd_enqueue(epub->sip, skb, ENQUEUE_PRIOR_TAIL);
+}
+#endif /* FPGA_TXDATA */
diff --git a/drivers/net/wireless/esp8089/esp_sip.h b/drivers/net/wireless/esp8089/esp_sip.h
new file mode 100644
index 000000000000..95cc42989b2c
--- /dev/null
+++ b/drivers/net/wireless/esp8089/esp_sip.h
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2009- 2014 Espressif System.
+ *
+ * Serial Interconnctor Protocol
+ *
+ * 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.
+ */
+
+#ifndef _ESP_SIP_H
+#define _ESP_SIP_H
+
+#include "sip2_common.h"
+
+#define SIP_CTRL_CREDIT_RESERVE 2
+
+#define SIP_PKT_MAX_LEN (1024*16)
+
+/* 16KB on normal X86 system, should check before porting to orhters */
+
+#define SIP_TX_AGGR_BUF_SIZE (4 * PAGE_SIZE)
+#define SIP_RX_AGGR_BUF_SIZE (4 * PAGE_SIZE)
+
+struct sk_buff;
+
+struct sip_pkt {
+ struct list_head list;
+
+ u8 *buf_begin;
+ u32 buf_len;
+ u8 *buf;
+};
+
+typedef enum RECALC_CREDIT_STATE {
+ RECALC_CREDIT_DISABLE = 0,
+ RECALC_CREDIT_ENABLE = 1,
+} RECALC_CREDIT_STATE;
+
+typedef enum ENQUEUE_PRIOR {
+ ENQUEUE_PRIOR_TAIL = 0,
+ ENQUEUE_PRIOR_HEAD,
+} ENQUEUE_PRIOR;
+
+typedef enum SIP_STATE {
+ SIP_INIT = 0,
+ SIP_PREPARE_BOOT,
+ SIP_BOOT,
+ SIP_SEND_INIT,
+ SIP_WAIT_BOOTUP,
+ SIP_RUN,
+ SIP_SUSPEND,
+ SIP_STOP
+} SIP_STATE;
+
+enum sip_notifier {
+ SIP_TX_DONE = 1,
+ SIP_RX_DONE = 2,
+};
+
+#define SIP_CREDITS_LOW_THRESHOLD 64 //i.e. 4k
+
+struct esp_sip {
+ struct list_head free_ctrl_txbuf;
+ struct list_head free_ctrl_rxbuf;
+
+ u32 rxseq; /* sip pkt seq, should match target side */
+ u32 txseq;
+ u32 txdataseq;
+
+ u8 to_host_seq;
+
+ atomic_t state;
+ spinlock_t lock;
+ atomic_t tx_credits;
+
+ atomic_t tx_ask_credit_update;
+
+ u8 *rawbuf; /* used in boot stage, free once chip is fully up */
+ u8 *tx_aggr_buf;
+ u8 *tx_aggr_write_ptr; /* update after insertion of each pkt */
+ u8 *tx_aggr_lastpkt_ptr;
+
+ struct mutex rx_mtx;
+ struct sk_buff_head rxq;
+ struct work_struct rx_process_work;
+
+ u16 tx_blksz;
+ u16 rx_blksz;
+
+ bool dump_rpbm_err;
+ bool sendup_rpbm_pkt;
+ bool rxabort_fixed;
+ bool support_bgscan;
+ u8 credit_to_reserve;
+
+ atomic_t credit_status;
+ struct timer_list credit_timer;
+
+ atomic_t noise_floor;
+
+ u32 tx_tot_len; /* total len for one transaction */
+ u32 rx_tot_len;
+
+ atomic_t rx_handling;
+ atomic_t tx_data_pkt_queued;
+
+ atomic_t data_tx_stopped;
+ atomic_t tx_stopped;
+
+ struct esp_pub *epub;
+};
+
+int sip_rx(struct esp_pub *epub);
+//int sip_download_fw(struct esp_sip *sip, u32 load_addr, u32 boot_addr);
+
+
+int sip_write_memory(struct esp_sip *, u32 addr, u8 * buf, u16 len);
+
+void sip_credit_process(struct esp_pub *, u8 credits);
+
+int sip_send_cmd(struct esp_sip *sip, int cid, u32 cmdlen, void *cmd);
+
+struct esp_sip *sip_attach(struct esp_pub *);
+
+int sip_post_init(struct esp_sip *sip, struct sip_evt_bootup2 *bevt);
+
+void sip_detach(struct esp_sip *sip);
+
+void sip_txq_process(struct esp_pub *epub);
+
+struct sk_buff *sip_alloc_ctrl_skbuf(struct esp_sip *sip, u16 len,
+ u32 cid);
+
+void sip_free_ctrl_skbuff(struct esp_sip *sip, struct sk_buff *skb);
+
+bool sip_queue_need_stop(struct esp_sip *sip);
+bool sip_queue_may_resume(struct esp_sip *sip);
+bool sip_tx_data_need_stop(struct esp_sip *sip);
+bool sip_tx_data_may_resume(struct esp_sip *sip);
+
+void sip_tx_data_pkt_enqueue(struct esp_pub *epub, struct sk_buff *skb);
+void sip_rx_data_pkt_enqueue(struct esp_pub *epub, struct sk_buff *skb);
+
+int sip_cmd_enqueue(struct esp_sip *sip, struct sk_buff *skb, int prior);
+
+int sip_poll_bootup_event(struct esp_sip *sip);
+
+int sip_poll_resetting_event(struct esp_sip *sip);
+
+void sip_trigger_txq_process(struct esp_sip *sip);
+
+void sip_send_chip_init(struct esp_sip *sip);
+
+bool mod_support_no_txampdu(void);
+
+bool mod_support_no_rxampdu(void);
+
+void mod_support_no_txampdu_set(bool value);
+
+#ifdef FPGA_DEBUG
+int sip_send_bootup(struct esp_sip *sip);
+#endif /* FPGA_DEBUG */
+void sip_debug_show(struct esp_sip *sip);
+#endif
diff --git a/drivers/net/wireless/esp8089/esp_utils.c b/drivers/net/wireless/esp8089/esp_utils.c
new file mode 100644
index 000000000000..8b188de79b2c
--- /dev/null
+++ b/drivers/net/wireless/esp8089/esp_utils.c
@@ -0,0 +1,262 @@
+/*
+ * Copyright (c) 2009 - 2014 Espressif System.
+ *
+ * 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.
+ */
+
+#include "linux/types.h"
+#include "linux/kernel.h"
+#include <linux/ieee80211.h>
+#include <net/mac80211.h>
+#include <linux/skbuff.h>
+
+#include <net/tcp.h>
+#include <linux/ip.h>
+#include <asm/checksum.h>
+
+#include "esp_pub.h"
+#include "esp_utils.h"
+#include "esp_wmac.h"
+#include "esp_debug.h"
+
+/*
+ * Convert IEEE channel number to MHz frequency.
+ */
+u32 esp_ieee2mhz(u8 chan)
+{
+ if (chan == 14)
+ return 2484;
+
+ if (chan < 14)
+ return 2407 + chan * 5;
+ else
+ return 2512 + ((chan - 15) * 20);
+ //TODO, add 5GHz
+}
+
+enum {
+ ESP_RATE_1_LONG = 0x0,
+ ESP_RATE_2_LONG = 0x1,
+ ESP_RATE_2_SHORT = 0x5,
+ ESP_RATE_5_SHORT = 0x6,
+ ESP_RATE_5_LONG = 0x2,
+ ESP_RATE_11_SHORT = 0x7,
+ ESP_RATE_11_LONG = 0x3,
+ ESP_RATE_6 = 0xb,
+ ESP_RATE_9 = 0xf,
+ ESP_RATE_12 = 0xa,
+ ESP_RATE_18 = 0xe,
+ ESP_RATE_24 = 0x9,
+ ESP_RATE_36 = 0xd,
+ ESP_RATE_48 = 0x8,
+ ESP_RATE_54 = 0xc,
+ /* ESP_RATE_MCS0 =0x10,
+ ESP_RATE_MCS1 =0x11,
+ ESP_RATE_MCS2 =0x12,
+ ESP_RATE_MCS3 =0x13,
+ ESP_RATE_MCS4 =0x14,
+ ESP_RATE_MCS5 =0x15,
+ ESP_RATE_MCS6 =0x16,
+ ESP_RATE_MCS7 =0x17,
+ */
+};
+
+static u8 esp_rate_table[20] = {
+ ESP_RATE_1_LONG,
+ ESP_RATE_2_SHORT,
+ ESP_RATE_5_SHORT,
+ ESP_RATE_11_SHORT,
+ ESP_RATE_6,
+ ESP_RATE_9,
+ ESP_RATE_12,
+ ESP_RATE_18,
+ ESP_RATE_24,
+ ESP_RATE_36,
+ ESP_RATE_48,
+ ESP_RATE_54,
+ /* ESP_RATE_MCS0,
+ ESP_RATE_MCS1,
+ ESP_RATE_MCS2,
+ ESP_RATE_MCS3,
+ ESP_RATE_MCS4,
+ ESP_RATE_MCS5,
+ ESP_RATE_MCS6,
+ ESP_RATE_MCS7,
+ */
+};
+
+s8 esp_wmac_rate2idx(u8 rate)
+{
+ int i;
+
+ if (rate == ESP_RATE_2_LONG)
+ return 1;
+ if (rate == ESP_RATE_5_LONG)
+ return 2;
+ if (rate == ESP_RATE_11_LONG)
+ return 3;
+
+ for (i = 0; i < 20; i++) {
+ if (rate == esp_rate_table[i])
+ return i;
+ }
+
+ esp_dbg(ESP_DBG_ERROR, "%s unknown rate 0x%02x \n", __func__,
+ rate);
+
+ return 0;
+}
+
+bool esp_wmac_rxsec_error(u8 error)
+{
+ return (error >= RX_SECOV_ERR && error <= RX_SECFIFO_TIMEOUT)
+ || (error >= RX_WEPICV_ERR && error <= RX_WAPIMIC_ERR);
+}
+
+int esp_cipher2alg(int cipher)
+{
+ if (cipher == WLAN_CIPHER_SUITE_TKIP)
+ return ALG_TKIP;
+
+ if (cipher == WLAN_CIPHER_SUITE_CCMP)
+ return ALG_CCMP;
+
+ if (cipher == WLAN_CIPHER_SUITE_WEP40
+ || cipher == WLAN_CIPHER_SUITE_WEP104)
+ return ALG_WEP;
+
+ if (cipher == WLAN_CIPHER_SUITE_AES_CMAC)
+ return ALG_AES_CMAC;
+
+ //printk("%s wrong cipher 0x%x!\n",__func__,cipher);
+
+ return -1;
+}
+
+#ifdef RX_CHECKSUM_TEST
+atomic_t g_iv_len;
+void esp_rx_checksum_test(struct sk_buff *skb)
+{
+ static u32 ip_err = 0;
+ static u32 tcp_err = 0;
+ struct ieee80211_hdr *pwh = (struct ieee80211_hdr *) skb->data;
+ int hdrlen = ieee80211_hdrlen(pwh->frame_control);
+
+ if (ieee80211_has_protected(pwh->frame_control))
+ hdrlen += atomic_read(&g_iv_len);
+
+ if (ieee80211_is_data(pwh->frame_control)) {
+ struct llc_snap_hdr *llc =
+ (struct llc_snap_hdr *) (skb->data + hdrlen);
+ if (ntohs(llc->eth_type) == ETH_P_IP) {
+ int llclen = sizeof(struct llc_snap_hdr);
+ struct iphdr *iph =
+ (struct iphdr *) (skb->data + hdrlen + llclen);
+ __sum16 csum_bak = iph->check;
+
+ iph->check = 0;
+ iph->check = ip_fast_csum(iph, iph->ihl);
+ if (iph->check != csum_bak) {
+ esp_dbg(ESP_DBG_ERROR,
+ "total ip checksum error %d\n",
+ ++ip_err);
+ }
+ iph->check = csum_bak;
+
+ if (iph->protocol == 0x06) {
+ struct tcphdr *tcph =
+ (struct tcphdr *) (skb->data + hdrlen +
+ llclen +
+ iph->ihl * 4);
+ int datalen =
+ skb->len - (hdrlen + llclen +
+ iph->ihl * 4);
+ csum_bak = tcph->check;
+
+ tcph->check = 0;
+ tcph->check =
+ tcp_v4_check(datalen, iph->saddr,
+ iph->daddr,
+ csum_partial((char *)
+ tcph,
+ datalen, 0));
+ if (tcph->check != csum_bak) {
+ esp_dbg(ESP_DBG_ERROR,
+ "total tcp checksum error %d\n",
+ ++tcp_err);
+ }
+ tcph->check = csum_bak;
+ }
+ }
+ }
+}
+
+#endif
+
+#ifdef GEN_ERR_CHECKSUM
+
+void esp_gen_err_checksum(struct sk_buff *skb)
+{
+ static u32 tx_seq = 0;
+ if ((tx_seq++ % 16) == 0) {
+ struct ieee80211_hdr *hdr =
+ (struct ieee80211_hdr *) skb->data;
+ int hdrlen = ieee80211_hdrlen(hdr->frame_control);
+
+ if (ieee80211_has_protected(pwh->frame_control))
+ hdrlen +=
+ IEEE80211_SKB_CB(skb)->control.hw_key->iv_len;
+
+ struct llc_snap_hdr *llc =
+ (struct llc_snap_hdr *) (skb->data + hdrlen);
+ if (ntohs(llc->eth_type) == ETH_P_IP) {
+ int llclen = sizeof(struct llc_snap_hdr);
+ struct iphdr *iph =
+ (struct iphdr *) (skb->data + hdrlen + llclen);
+
+ iph->check = ~iph->check;
+
+ if (iph->protocol == 0x06) {
+ struct tcphdr *tcph =
+ (struct tcphdr *) (skb->data + hdrlen +
+ llclen +
+ iph->ihl * 4);
+ tcph->check = ~tcph->check;
+ }
+ }
+ }
+}
+#endif
+
+bool esp_is_ip_pkt(struct sk_buff *skb)
+{
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+ int hdrlen;
+ struct llc_snap_hdr *llc;
+
+ if (!ieee80211_is_data(hdr->frame_control))
+ return false;
+
+ hdrlen = ieee80211_hdrlen(hdr->frame_control);
+ if (ieee80211_has_protected(hdr->frame_control))
+ hdrlen += IEEE80211_SKB_CB(skb)->control.hw_key->iv_len;
+#ifdef RX_CHECKSUM_TEST
+ atomic_set(&g_iv_len,
+ IEEE80211_SKB_CB(skb)->control.hw_key->iv_len);
+#endif
+ if (skb->len < hdrlen + sizeof(struct llc_snap_hdr))
+ return false;
+ llc = (struct llc_snap_hdr *) (skb->data + hdrlen);
+ if (ntohs(llc->eth_type) != ETH_P_IP)
+ return false;
+ else
+ return true;
+}
diff --git a/drivers/net/wireless/esp8089/esp_utils.h b/drivers/net/wireless/esp8089/esp_utils.h
new file mode 100644
index 000000000000..ed16d9ca0a65
--- /dev/null
+++ b/drivers/net/wireless/esp8089/esp_utils.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2011-2012 Espressif System.
+ *
+ * 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.
+ */
+
+#ifndef _ESP_UTILS_H_
+#define _ESP_UTILS_H_
+
+#include "linux/types.h"
+#include <linux/version.h>
+
+#ifndef BIT
+#define BIT(x) (0x1 << (x))
+#endif
+
+u32 esp_ieee2mhz(u8 chan);
+
+enum ieee80211_key_alg {
+ ALG_WEP,
+ ALG_TKIP,
+ ALG_CCMP,
+ ALG_AES_CMAC
+};
+
+int esp_cipher2alg(int cipher);
+
+void esp_rx_checksum_test(struct sk_buff *skb);
+void esp_gen_err_checksum(struct sk_buff *skb);
+
+bool esp_is_ip_pkt(struct sk_buff *skb);
+
+#endif
diff --git a/drivers/net/wireless/esp8089/esp_version.h b/drivers/net/wireless/esp8089/esp_version.h
new file mode 100644
index 000000000000..481d98841fc2
--- /dev/null
+++ b/drivers/net/wireless/esp8089/esp_version.h
@@ -0,0 +1 @@
+#define DRIVER_VER 0xbdf5087c3debll
diff --git a/drivers/net/wireless/esp8089/esp_wl.h b/drivers/net/wireless/esp8089/esp_wl.h
new file mode 100644
index 000000000000..e3e62a83d505
--- /dev/null
+++ b/drivers/net/wireless/esp8089/esp_wl.h
@@ -0,0 +1,63 @@
+#ifndef _ESP_WL_H_
+#define _ESP_WL_H_
+
+//#define MAX_PROBED_SSID_INDEX 9
+
+
+enum {
+ CONF_HW_BIT_RATE_1MBPS = BIT(0),
+ CONF_HW_BIT_RATE_2MBPS = BIT(1),
+ CONF_HW_BIT_RATE_5_5MBPS = BIT(2),
+ CONF_HW_BIT_RATE_11MBPS = BIT(3),
+ CONF_HW_BIT_RATE_6MBPS = BIT(4),
+ CONF_HW_BIT_RATE_9MBPS = BIT(5),
+ CONF_HW_BIT_RATE_12MBPS = BIT(6),
+ CONF_HW_BIT_RATE_18MBPS = BIT(7),
+ CONF_HW_BIT_RATE_22MBPS = BIT(8),
+ CONF_HW_BIT_RATE_24MBPS = BIT(9),
+ CONF_HW_BIT_RATE_36MBPS = BIT(10),
+ CONF_HW_BIT_RATE_48MBPS = BIT(11),
+ CONF_HW_BIT_RATE_54MBPS = BIT(12),
+ CONF_HW_BIT_RATE_11B_MASK =
+ (CONF_HW_BIT_RATE_1MBPS | CONF_HW_BIT_RATE_2MBPS |
+ CONF_HW_BIT_RATE_5_5MBPS | CONF_HW_BIT_RATE_11MBPS),
+};
+
+#if 0
+enum {
+ CONF_HW_RATE_INDEX_1MBPS = 0,
+ CONF_HW_RATE_INDEX_2MBPS = 1,
+ CONF_HW_RATE_INDEX_5_5MBPS = 2,
+ CONF_HW_RATE_INDEX_6MBPS = 3,
+ CONF_HW_RATE_INDEX_9MBPS = 4,
+ CONF_HW_RATE_INDEX_11MBPS = 5,
+ CONF_HW_RATE_INDEX_12MBPS = 6,
+ CONF_HW_RATE_INDEX_18MBPS = 7,
+ CONF_HW_RATE_INDEX_22MBPS = 8,
+ CONF_HW_RATE_INDEX_24MBPS = 9,
+ CONF_HW_RATE_INDEX_36MBPS = 10,
+ CONF_HW_RATE_INDEX_48MBPS = 11,
+ CONF_HW_RATE_INDEX_54MBPS = 12,
+ CONF_HW_RATE_INDEX_MAX,
+};
+
+enum {
+ CONF_HW_RXTX_RATE_54 = 0,
+ CONF_HW_RXTX_RATE_48,
+ CONF_HW_RXTX_RATE_36,
+ CONF_HW_RXTX_RATE_24,
+ CONF_HW_RXTX_RATE_22,
+ CONF_HW_RXTX_RATE_18,
+ CONF_HW_RXTX_RATE_12,
+ CONF_HW_RXTX_RATE_11,
+ CONF_HW_RXTX_RATE_9,
+ CONF_HW_RXTX_RATE_6,
+ CONF_HW_RXTX_RATE_5_5,
+ CONF_HW_RXTX_RATE_2,
+ CONF_HW_RXTX_RATE_1,
+ CONF_HW_RXTX_RATE_MAX,
+ CONF_HW_RXTX_RATE_UNSUPPORTED = 0xff
+};
+#endif
+
+#endif /* _ESP_WL_H_ */
diff --git a/drivers/net/wireless/esp8089/esp_wmac.h b/drivers/net/wireless/esp8089/esp_wmac.h
new file mode 100644
index 000000000000..72d13cbfc0e5
--- /dev/null
+++ b/drivers/net/wireless/esp8089/esp_wmac.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2011-2012 Espressif System.
+ *
+ * MAC header
+ *
+ * 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.
+ */
+
+#ifndef _ESP_WMAC_H_
+#define _ESP_WMAC_H_
+
+struct esp_mac_rx_ctrl {
+ signed rssi:8;
+ unsigned rate:4;
+ unsigned is_group:1;
+ unsigned:1;
+ unsigned sig_mode:2;
+ unsigned legacy_length:12;
+ unsigned damatch0:1;
+ unsigned damatch1:1;
+ unsigned bssidmatch0:1;
+ unsigned bssidmatch1:1;
+ unsigned MCS:7;
+ unsigned CWB:1;
+ unsigned HT_length:16;
+ unsigned Smoothing:1;
+ unsigned Not_Sounding:1;
+ unsigned:1;
+ unsigned Aggregation:1;
+ unsigned STBC:2;
+ unsigned FEC_CODING:1;
+ unsigned SGI:1;
+ unsigned rxend_state:8;
+ unsigned ampdu_cnt:8;
+ unsigned channel:4;
+ unsigned:4;
+ signed noise_floor:8;
+};
+
+struct esp_rx_ampdu_len {
+ unsigned substate:8;
+ unsigned sublen:12;
+ unsigned:12;
+};
+
+struct esp_tx_ampdu_entry {
+ u32 sub_len:12, dili_num:7,:1, null_byte:2, data:1, enc:1, seq:8;
+};
+
+//rxend_state flags
+#define RX_PYH_ERR_MIN 0x42
+#define RX_AGC_ERR_MIN 0x42
+#define RX_AGC_ERR_MAX 0x47
+#define RX_OFDM_ERR_MIN 0x50
+#define RX_OFDM_ERR_MAX 0x58
+#define RX_CCK_ERR_MIN 0x59
+#define RX_CCK_ERR_MAX 0x5F
+#define RX_ABORT 0x80
+#define RX_SF_ERR 0x40
+#define RX_FCS_ERR 0x41
+#define RX_AHBOV_ERR 0xC0
+#define RX_BUFOV_ERR 0xC1
+#define RX_BUFINV_ERR 0xC2
+#define RX_AMPDUSF_ERR 0xC3
+#define RX_AMPDUBUFOV_ERR 0xC4
+#define RX_MACBBFIFOOV_ERR 0xC5
+#define RX_RPBM_ERR 0xC6
+#define RX_BTFORCE_ERR 0xC7
+#define RX_SECOV_ERR 0xE1
+#define RX_SECPROT_ERR0 0xE2
+#define RX_SECPROT_ERR1 0xE3
+#define RX_SECKEY_ERR 0xE4
+#define RX_SECCRLEN_ERR 0xE5
+#define RX_SECFIFO_TIMEOUT 0xE6
+#define RX_WEPICV_ERR 0xF0
+#define RX_TKIPICV_ERR 0xF4
+#define RX_TKIPMIC_ERR 0xF5
+#define RX_CCMPMIC_ERR 0xF8
+#define RX_WAPIMIC_ERR 0xFC
+
+s8 esp_wmac_rate2idx(u8 rate);
+bool esp_wmac_rxsec_error(u8 error);
+
+#endif /* _ESP_WMAC_H_ */
diff --git a/drivers/net/wireless/esp8089/firmware/LICENSE-2.0.txt b/drivers/net/wireless/esp8089/firmware/LICENSE-2.0.txt
new file mode 100644
index 000000000000..0dd35c82a001
--- /dev/null
+++ b/drivers/net/wireless/esp8089/firmware/LICENSE-2.0.txt
@@ -0,0 +1,203 @@
+The esp8089 firmware files are licensed under the Apache License, Version 2.0:
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/drivers/net/wireless/esp8089/sdio_sif_esp.c b/drivers/net/wireless/esp8089/sdio_sif_esp.c
new file mode 100644
index 000000000000..97718f42e1a6
--- /dev/null
+++ b/drivers/net/wireless/esp8089/sdio_sif_esp.c
@@ -0,0 +1,811 @@
+/*
+ * Copyright (c) 2010 -2013 Espressif System.
+ *
+ * sdio serial i/f driver
+ * - sdio device control routines
+ * - sync/async DMA/PIO read/write
+ *
+ * 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.
+ */
+#include <linux/mmc/card.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/core.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/sdio_ids.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/sd.h>
+#include <linux/module.h>
+#include <net/mac80211.h>
+#include <linux/time.h>
+#include <linux/pm.h>
+
+#include "esp_pub.h"
+#include "esp_sif.h"
+#include "esp_sip.h"
+#include "esp_debug.h"
+#include "slc_host_register.h"
+#include "esp_version.h"
+#include "esp_ctrl.h"
+#include "esp_file.h"
+#ifdef USE_EXT_GPIO
+#include "esp_ext.h"
+#endif /* USE_EXT_GPIO */
+
+#define MANUFACTURER_ID_EAGLE_BASE 0x1110
+#define MANUFACTURER_ID_EAGLE_BASE_MASK 0xFF00
+#define MANUFACTURER_CODE 0x6666
+
+static const struct sdio_device_id esp_sdio_devices[] = {
+ {SDIO_DEVICE
+ (MANUFACTURER_CODE, (MANUFACTURER_ID_EAGLE_BASE | 0x1))},
+ {},
+};
+
+static const struct of_device_id esp_of_match_table[] = {
+ { .compatible = "esp,esp8089", .data = NULL},
+ { }
+};
+
+static int /*__init*/ esp_sdio_init(void);
+static void /*__exit*/ esp_sdio_exit(void);
+
+
+#define ESP_DMA_IBUFSZ 2048
+
+//unsigned int esp_msg_level = 0;
+unsigned int esp_msg_level = ESP_DBG_ERROR | ESP_SHOW;
+
+struct esp_sdio_ctrl *sif_sctrl = NULL;
+
+#ifdef ESP_ANDROID_LOGGER
+bool log_off = false;
+#endif /* ESP_ANDROID_LOGGER */
+
+static int esdio_power_off(struct esp_sdio_ctrl *sctrl);
+static int esdio_power_on(struct esp_sdio_ctrl *sctrl);
+
+void sif_set_clock(struct sdio_func *func, int clk);
+
+void sif_lock_bus(struct esp_pub *epub)
+{
+ EPUB_FUNC_CHECK(epub, _exit);
+
+ sdio_claim_host(EPUB_TO_FUNC(epub));
+ _exit:
+ return;
+}
+
+void sif_unlock_bus(struct esp_pub *epub)
+{
+ EPUB_FUNC_CHECK(epub, _exit);
+
+ sdio_release_host(EPUB_TO_FUNC(epub));
+ _exit:
+ return;
+}
+
+static inline bool bad_buf(u8 * buf)
+{
+ return ((unsigned long) buf & 0x3) || !virt_addr_valid(buf);
+}
+
+u8 sdio_io_readb(struct esp_pub *epub, int addr, int *res)
+{
+ struct esp_sdio_ctrl *sctrl = NULL;
+ struct sdio_func *func = NULL;
+ sctrl = (struct esp_sdio_ctrl *) epub->sif;
+ func = sctrl->func;
+
+ if (func->num == 0)
+ return sdio_f0_readb(func, addr, res);
+ else
+ return sdio_readb(func, addr, res);
+}
+
+void sdio_io_writeb(struct esp_pub *epub, u8 value, int addr, int *res)
+{
+ struct esp_sdio_ctrl *sctrl = NULL;
+ struct sdio_func *func = NULL;
+ sctrl = (struct esp_sdio_ctrl *) epub->sif;
+ func = sctrl->func;
+
+ if (func->num == 0)
+ sdio_f0_writeb(func, value, addr, res);
+ else
+ sdio_writeb(func, value, addr, res);
+}
+
+int sif_io_raw(struct esp_pub *epub, u32 addr, u8 * buf, u32 len, u32 flag)
+{
+ int err = 0;
+ u8 *ibuf = NULL;
+ bool need_ibuf = false;
+ struct esp_sdio_ctrl *sctrl = NULL;
+ struct sdio_func *func = NULL;
+
+ if (epub == NULL || buf == NULL) {
+ ESSERT(0);
+ err = -EINVAL;
+ goto _exit;
+ }
+
+ sctrl = (struct esp_sdio_ctrl *) epub->sif;
+ func = sctrl->func;
+ if (func == NULL) {
+ ESSERT(0);
+ err = -EINVAL;
+ goto _exit;
+ }
+
+ if (bad_buf(buf)) {
+ esp_dbg(ESP_DBG_TRACE, "%s dst 0x%08x, len %d badbuf\n",
+ __func__, addr, len);
+ need_ibuf = true;
+ ibuf = sctrl->dma_buffer;
+ } else {
+ ibuf = buf;
+ }
+
+ if (flag & SIF_BLOCK_BASIS) {
+ /* round up for block data transcation */
+ }
+
+ if (flag & SIF_TO_DEVICE) {
+
+ if (need_ibuf)
+ memcpy(ibuf, buf, len);
+
+ if (flag & SIF_FIXED_ADDR)
+ err = sdio_writesb(func, addr, ibuf, len);
+ else if (flag & SIF_INC_ADDR) {
+ err = sdio_memcpy_toio(func, addr, ibuf, len);
+ }
+ } else if (flag & SIF_FROM_DEVICE) {
+
+ if (flag & SIF_FIXED_ADDR)
+ err = sdio_readsb(func, ibuf, addr, len);
+ else if (flag & SIF_INC_ADDR) {
+ err = sdio_memcpy_fromio(func, ibuf, addr, len);
+ }
+
+
+ if (!err && need_ibuf)
+ memcpy(buf, ibuf, len);
+ }
+
+ _exit:
+ return err;
+}
+
+int sif_io_sync(struct esp_pub *epub, u32 addr, u8 * buf, u32 len,
+ u32 flag)
+{
+ int err = 0;
+ u8 *ibuf = NULL;
+ bool need_ibuf = false;
+ struct esp_sdio_ctrl *sctrl = NULL;
+ struct sdio_func *func = NULL;
+
+ if (epub == NULL || buf == NULL) {
+ ESSERT(0);
+ err = -EINVAL;
+ goto _exit;
+ }
+
+ sctrl = (struct esp_sdio_ctrl *) epub->sif;
+ func = sctrl->func;
+ if (func == NULL) {
+ ESSERT(0);
+ err = -EINVAL;
+ goto _exit;
+ }
+
+ if (bad_buf(buf)) {
+ esp_dbg(ESP_DBG_TRACE, "%s dst 0x%08x, len %d badbuf\n",
+ __func__, addr, len);
+ need_ibuf = true;
+ ibuf = sctrl->dma_buffer;
+ } else {
+ ibuf = buf;
+ }
+
+ if (flag & SIF_BLOCK_BASIS) {
+ /* round up for block data transcation */
+ }
+
+ if (flag & SIF_TO_DEVICE) {
+
+ esp_dbg(ESP_DBG_TRACE, "%s to addr 0x%08x, len %d \n",
+ __func__, addr, len);
+ if (need_ibuf)
+ memcpy(ibuf, buf, len);
+
+ sdio_claim_host(func);
+
+ if (flag & SIF_FIXED_ADDR)
+ err = sdio_writesb(func, addr, ibuf, len);
+ else if (flag & SIF_INC_ADDR) {
+ err = sdio_memcpy_toio(func, addr, ibuf, len);
+ }
+ sdio_release_host(func);
+ } else if (flag & SIF_FROM_DEVICE) {
+
+ esp_dbg(ESP_DBG_TRACE, "%s from addr 0x%08x, len %d \n",
+ __func__, addr, len);
+
+ sdio_claim_host(func);
+
+ if (flag & SIF_FIXED_ADDR)
+ err = sdio_readsb(func, ibuf, addr, len);
+ else if (flag & SIF_INC_ADDR) {
+ err = sdio_memcpy_fromio(func, ibuf, addr, len);
+ }
+
+ sdio_release_host(func);
+
+ if (!err && need_ibuf)
+ memcpy(buf, ibuf, len);
+ }
+
+ _exit:
+ return err;
+}
+
+int sif_lldesc_read_sync(struct esp_pub *epub, u8 * buf, u32 len)
+{
+ struct esp_sdio_ctrl *sctrl = NULL;
+ u32 read_len;
+
+ if (epub == NULL || buf == NULL) {
+ ESSERT(0);
+ return -EINVAL;
+ }
+
+ sctrl = (struct esp_sdio_ctrl *) epub->sif;
+
+ switch (sctrl->target_id) {
+ case 0x100:
+ read_len = len;
+ break;
+ case 0x600:
+ read_len = roundup(len, sctrl->slc_blk_sz);
+ break;
+ default:
+ read_len = len;
+ break;
+ }
+
+ return sif_io_sync((epub),
+ (sctrl->slc_window_end_addr - 2 - (len)), (buf),
+ (read_len),
+ SIF_FROM_DEVICE | SIF_BYTE_BASIS |
+ SIF_INC_ADDR);
+}
+
+int sif_lldesc_write_sync(struct esp_pub *epub, u8 * buf, u32 len)
+{
+ struct esp_sdio_ctrl *sctrl = NULL;
+ u32 write_len;
+
+ if (epub == NULL || buf == NULL) {
+ ESSERT(0);
+ return -EINVAL;
+ }
+
+ sctrl = (struct esp_sdio_ctrl *) epub->sif;
+
+ switch (sctrl->target_id) {
+ case 0x100:
+ write_len = len;
+ break;
+ case 0x600:
+ write_len = roundup(len, sctrl->slc_blk_sz);
+ break;
+ default:
+ write_len = len;
+ break;
+ }
+
+ return sif_io_sync((epub), (sctrl->slc_window_end_addr - (len)),
+ (buf), (write_len),
+ SIF_TO_DEVICE | SIF_BYTE_BASIS | SIF_INC_ADDR);
+}
+
+int sif_lldesc_read_raw(struct esp_pub *epub, u8 * buf, u32 len,
+ bool noround)
+{
+ struct esp_sdio_ctrl *sctrl = NULL;
+ u32 read_len;
+
+ if (epub == NULL || buf == NULL) {
+ ESSERT(0);
+ return -EINVAL;
+ }
+
+ sctrl = (struct esp_sdio_ctrl *) epub->sif;
+
+ switch (sctrl->target_id) {
+ case 0x100:
+ read_len = len;
+ break;
+ case 0x600:
+ if (!noround)
+ read_len = roundup(len, sctrl->slc_blk_sz);
+ else
+ read_len = len;
+ break;
+ default:
+ read_len = len;
+ break;
+ }
+
+ return sif_io_raw((epub), (sctrl->slc_window_end_addr - 2 - (len)),
+ (buf), (read_len),
+ SIF_FROM_DEVICE | SIF_BYTE_BASIS | SIF_INC_ADDR);
+}
+
+int sif_lldesc_write_raw(struct esp_pub *epub, u8 * buf, u32 len)
+{
+ struct esp_sdio_ctrl *sctrl = NULL;
+ u32 write_len;
+
+ if (epub == NULL || buf == NULL) {
+ ESSERT(0);
+ return -EINVAL;
+ }
+
+ sctrl = (struct esp_sdio_ctrl *) epub->sif;
+
+ switch (sctrl->target_id) {
+ case 0x100:
+ write_len = len;
+ break;
+ case 0x600:
+ write_len = roundup(len, sctrl->slc_blk_sz);
+ break;
+ default:
+ write_len = len;
+ break;
+ }
+ return sif_io_raw((epub), (sctrl->slc_window_end_addr - (len)),
+ (buf), (write_len),
+ SIF_TO_DEVICE | SIF_BYTE_BASIS | SIF_INC_ADDR);
+
+}
+
+static int esdio_power_on(struct esp_sdio_ctrl *sctrl)
+{
+ int err = 0;
+
+ if (sctrl->off == false)
+ return err;
+
+ sdio_claim_host(sctrl->func);
+ err = sdio_enable_func(sctrl->func);
+
+ if (err) {
+ esp_dbg(ESP_DBG_ERROR, "Unable to enable sdio func: %d\n",
+ err);
+ sdio_release_host(sctrl->func);
+ return err;
+ }
+
+ sdio_release_host(sctrl->func);
+
+ /* ensure device is up */
+ msleep(5);
+
+ sctrl->off = false;
+
+ return err;
+}
+
+static int esdio_power_off(struct esp_sdio_ctrl *sctrl)
+{
+ int err;
+
+ if (sctrl->off)
+ return 0;
+
+ sdio_claim_host(sctrl->func);
+ err = sdio_disable_func(sctrl->func);
+ sdio_release_host(sctrl->func);
+
+ if (err)
+ return err;
+
+ sctrl->off = true;
+
+ return err;
+}
+
+void sif_enable_irq(struct esp_pub *epub)
+{
+ int err;
+ struct esp_sdio_ctrl *sctrl = NULL;
+
+ sctrl = (struct esp_sdio_ctrl *) epub->sif;
+
+ sdio_claim_host(sctrl->func);
+
+ err = sdio_claim_irq(sctrl->func, sif_dsr);
+
+ if (err)
+ esp_dbg(ESP_DBG_ERROR, "sif %s failed\n", __func__);
+
+ atomic_set(&epub->sip->state, SIP_BOOT);
+
+ atomic_set(&sctrl->irq_installed, 1);
+
+ sdio_release_host(sctrl->func);
+}
+
+void sif_disable_irq(struct esp_pub *epub)
+{
+ struct esp_sdio_ctrl *sctrl = (struct esp_sdio_ctrl *) epub->sif;
+ int i = 0;
+
+ if (atomic_read(&sctrl->irq_installed) == 0)
+ return;
+
+ sdio_claim_host(sctrl->func);
+
+ while (atomic_read(&sctrl->irq_handling)) {
+ sdio_release_host(sctrl->func);
+ schedule_timeout(HZ / 100);
+ sdio_claim_host(sctrl->func);
+ if (i++ >= 400) {
+ esp_dbg(ESP_DBG_ERROR, "%s force to stop irq\n",
+ __func__);
+ break;
+ }
+ }
+
+ /* Ignore errors, we don't always use an irq. */
+ sdio_release_irq(sctrl->func);
+
+ atomic_set(&sctrl->irq_installed, 0);
+
+ sdio_release_host(sctrl->func);
+
+}
+
+void sif_set_clock(struct sdio_func *func, int clk)
+{
+ struct mmc_host *host = NULL;
+ struct mmc_card *card = NULL;
+
+ card = func->card;
+ host = card->host;
+
+ sdio_claim_host(func);
+
+ //currently only set clock
+ host->ios.clock = clk * 1000000;
+
+ esp_dbg(ESP_SHOW, "%s clock is %u\n", __func__, host->ios.clock);
+ if (host->ios.clock > host->f_max) {
+ host->ios.clock = host->f_max;
+ }
+ host->ops->set_ios(host, &host->ios);
+
+ mdelay(2);
+
+ sdio_release_host(func);
+}
+
+static int esp_sdio_probe(struct sdio_func *func,
+ const struct sdio_device_id *id);
+static void esp_sdio_remove(struct sdio_func *func);
+
+static int esp_sdio_probe(struct sdio_func *func,
+ const struct sdio_device_id *id)
+{
+ int err = 0;
+ struct esp_pub *epub = NULL;
+ struct esp_sdio_ctrl *sctrl;
+
+ esp_dbg(ESP_DBG_TRACE,
+ "sdio_func_num: 0x%X, vendor id: 0x%X, dev id: 0x%X, block size: 0x%X/0x%X\n",
+ func->num, func->vendor, func->device, func->max_blksize,
+ func->cur_blksize);
+
+ if (sif_sctrl == NULL) {
+
+ esp_conf_init(&func->dev);
+
+ esp_conf_upload_first();
+
+ sctrl = kzalloc(sizeof(struct esp_sdio_ctrl), GFP_KERNEL);
+
+ if (sctrl == NULL) {
+ return -ENOMEM;
+ }
+
+ /* temp buffer reserved for un-dma-able request */
+ sctrl->dma_buffer = kzalloc(ESP_DMA_IBUFSZ, GFP_KERNEL);
+
+ if (sctrl->dma_buffer == NULL) {
+ err = -ENOMEM;
+ goto _err_last;
+ }
+ sif_sctrl = sctrl;
+ sctrl->slc_blk_sz = SIF_SLC_BLOCK_SIZE;
+
+ epub = esp_pub_alloc_mac80211(&func->dev);
+
+ if (epub == NULL) {
+ esp_dbg(ESP_DBG_ERROR, "no mem for epub \n");
+ err = -ENOMEM;
+ goto _err_dma;
+ }
+ epub->sif = (void *) sctrl;
+ epub->sdio_state = ESP_SDIO_STATE_FIRST_INIT;
+ sctrl->epub = epub;
+
+#ifdef USE_EXT_GPIO
+ if (sif_get_ate_config() == 0) {
+ err = ext_gpio_init(epub);
+ if (err) {
+ esp_dbg(ESP_DBG_ERROR,
+ "ext_irq_work_init failed %d\n",
+ err);
+ goto _err_epub;
+ }
+ }
+#endif
+
+ } else {
+ sctrl = sif_sctrl;
+ sif_sctrl = NULL;
+ epub = sctrl->epub;
+ epub->sdio_state = ESP_SDIO_STATE_SECOND_INIT;
+ SET_IEEE80211_DEV(epub->hw, &func->dev);
+ epub->dev = &func->dev;
+ }
+
+ sctrl->func = func;
+ sdio_set_drvdata(func, sctrl);
+
+ sctrl->id = id;
+ sctrl->off = true;
+
+ /* give us some time to enable, in ms */
+ func->enable_timeout = 100;
+
+ err = esdio_power_on(sctrl);
+ esp_dbg(ESP_DBG_TRACE, " %s >> power_on err %d \n", __func__, err);
+
+ if (err) {
+ if (epub->sdio_state == ESP_SDIO_STATE_FIRST_INIT)
+ goto _err_ext_gpio;
+ else
+ goto _err_second_init;
+ }
+ check_target_id(epub);
+
+ sdio_claim_host(func);
+
+ err = sdio_set_block_size(func, sctrl->slc_blk_sz);
+
+ if (err) {
+ esp_dbg(ESP_DBG_ERROR,
+ "Set sdio block size %d failed: %d)\n",
+ sctrl->slc_blk_sz, err);
+ sdio_release_host(func);
+ if (epub->sdio_state == ESP_SDIO_STATE_FIRST_INIT)
+ goto _err_off;
+ else
+ goto _err_second_init;
+ }
+
+ sdio_release_host(func);
+
+#ifdef LOWER_CLK
+ /* fix clock for dongle */
+ sif_set_clock(func, 23);
+#endif //LOWER_CLK
+
+ err = esp_pub_init_all(epub);
+
+ if (err) {
+ esp_dbg(ESP_DBG_ERROR, "esp_init_all failed: %d\n", err);
+ if (epub->sdio_state == ESP_SDIO_STATE_FIRST_INIT) {
+ err = 0;
+ goto _err_first_init;
+ }
+ if (epub->sdio_state == ESP_SDIO_STATE_SECOND_INIT)
+ goto _err_second_init;
+ }
+
+ esp_dbg(ESP_DBG_TRACE, " %s return %d\n", __func__, err);
+ if (epub->sdio_state == ESP_SDIO_STATE_FIRST_INIT) {
+ esp_dbg(ESP_DBG_TRACE, "first normal exit\n");
+ epub->sdio_state = ESP_SDIO_STATE_FIRST_NORMAL_EXIT;
+ /* Rescan the esp8089 after loading the initial firmware */
+ sdio_claim_host(func);
+ mmc_sw_reset(func->card);
+ sdio_release_host(func);
+ }
+
+ return err;
+
+ _err_off:
+ esdio_power_off(sctrl);
+ _err_ext_gpio:
+#ifdef USE_EXT_GPIO
+ if (sif_get_ate_config() == 0)
+ ext_gpio_deinit();
+ _err_epub:
+#endif
+ esp_pub_dealloc_mac80211(epub);
+ _err_dma:
+ kfree(sctrl->dma_buffer);
+ _err_last:
+ kfree(sctrl);
+ _err_first_init:
+ if (epub && epub->sdio_state == ESP_SDIO_STATE_FIRST_INIT) {
+ esp_dbg(ESP_DBG_ERROR, "first error exit\n");
+ epub->sdio_state = ESP_SDIO_STATE_FIRST_ERROR_EXIT;
+ }
+ return err;
+ _err_second_init:
+ epub->sdio_state = ESP_SDIO_STATE_SECOND_ERROR_EXIT;
+ esp_sdio_remove(func);
+ return err;
+}
+
+static void esp_sdio_remove(struct sdio_func *func)
+{
+ struct esp_sdio_ctrl *sctrl = NULL;
+ struct esp_pub *epub = NULL;
+
+ esp_dbg(ESP_DBG_TRACE, "%s enter\n", __func__);
+
+ sctrl = sdio_get_drvdata(func);
+
+ if (sctrl == NULL) {
+ esp_dbg(ESP_DBG_ERROR, "%s no sctrl\n", __func__);
+ return;
+ }
+
+ do {
+ epub = sctrl->epub;
+ if (epub == NULL) {
+ esp_dbg(ESP_DBG_ERROR, "%s epub null\n", __func__);
+ break;
+ }
+ if (epub->sdio_state != ESP_SDIO_STATE_FIRST_NORMAL_EXIT) {
+ if (epub->sip) {
+ sip_detach(epub->sip);
+ epub->sip = NULL;
+ esp_dbg(ESP_DBG_TRACE,
+ "%s sip detached \n", __func__);
+ }
+#ifdef USE_EXT_GPIO
+ if (sif_get_ate_config() == 0)
+ ext_gpio_deinit();
+#endif
+ } else {
+ //sif_disable_target_interrupt(epub);
+ atomic_set(&epub->sip->state, SIP_STOP);
+ sif_disable_irq(epub);
+ }
+
+ if (epub->sdio_state != ESP_SDIO_STATE_FIRST_NORMAL_EXIT) {
+ esp_pub_dealloc_mac80211(epub);
+ esp_dbg(ESP_DBG_TRACE, "%s dealloc mac80211 \n",
+ __func__);
+
+ if (sctrl->dma_buffer) {
+ kfree(sctrl->dma_buffer);
+ sctrl->dma_buffer = NULL;
+ esp_dbg(ESP_DBG_TRACE,
+ "%s free dma_buffer \n", __func__);
+ }
+
+ kfree(sctrl);
+ }
+
+ } while (0);
+
+ sdio_set_drvdata(func, NULL);
+
+ esp_dbg(ESP_DBG_TRACE, "eagle sdio remove complete\n");
+}
+
+static int esp_sdio_suspend(struct device *dev)
+{
+ struct sdio_func *func = dev_to_sdio_func(dev);
+ struct esp_sdio_ctrl *sctrl = sdio_get_drvdata(func);
+ struct esp_pub *epub = sctrl->epub;
+
+ printk("%s", __func__);
+ atomic_set(&epub->ps.state, ESP_PM_ON);
+
+ do {
+ u32 sdio_flags = 0;
+ int ret = 0;
+ sdio_flags = sdio_get_host_pm_caps(func);
+
+ if (!(sdio_flags & MMC_PM_KEEP_POWER)) {
+ printk
+ ("%s can't keep power while host is suspended\n",
+ __func__);
+ }
+
+ /* keep power while host suspended */
+ ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
+ if (ret) {
+ printk("%s error while trying to keep power\n",
+ __func__);
+ }
+ } while (0);
+
+
+ return 0;
+
+}
+
+static int esp_sdio_resume(struct device *dev)
+{
+ esp_dbg(ESP_DBG_ERROR, "%s", __func__);
+
+ return 0;
+}
+
+static const struct dev_pm_ops esp_sdio_pm_ops = {
+ .suspend = esp_sdio_suspend,
+ .resume = esp_sdio_resume,
+};
+
+static struct sdio_driver esp_sdio_driver = {
+ .name = "eagle_sdio",
+ .id_table = esp_sdio_devices,
+ .probe = esp_sdio_probe,
+ .remove = esp_sdio_remove,
+ .drv = {
+ .pm = &esp_sdio_pm_ops,
+ .of_match_table = esp_of_match_table,
+ },
+};
+
+static int /*__init*/ esp_sdio_init(void)
+{
+
+ esp_debugfs_init();
+ sdio_register_driver(&esp_sdio_driver);
+
+ msleep(1000);
+
+ sdio_unregister_driver(&esp_sdio_driver);
+ msleep(100);
+ sdio_register_driver(&esp_sdio_driver);
+
+ return 0;
+}
+
+static void /*__exit*/ esp_sdio_exit(void)
+{
+ sdio_unregister_driver(&esp_sdio_driver);
+ esp_debugfs_exit();
+}
+
+MODULE_DEVICE_TABLE(sdio, esp_sdio_devices);
+MODULE_DEVICE_TABLE(of, esp_of_match_table);
+MODULE_AUTHOR("Espressif System");
+MODULE_DESCRIPTION
+ ("Driver for SDIO interconnected eagle low-power WLAN devices");
+MODULE_LICENSE("GPL");
+
+module_init(esp_sdio_init);
+module_exit(esp_sdio_exit);
diff --git a/drivers/net/wireless/esp8089/sip2_common.h b/drivers/net/wireless/esp8089/sip2_common.h
new file mode 100644
index 000000000000..d46e87589b0b
--- /dev/null
+++ b/drivers/net/wireless/esp8089/sip2_common.h
@@ -0,0 +1,475 @@
+/*
+ * Copyright (c) 2010 - 2014 Espressif System.
+ *
+ * Common definitions of Serial Interconnctor Protocol
+ *
+ * little endian
+ *
+ * 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.
+ */
+
+#ifndef _SIP2_COMMON_H
+#define _SIP2_COMMON_H
+
+#ifdef __ets__
+#include "utils.h"
+#endif /*__ets__*/
+
+/* max 16 types */
+typedef enum {
+ SIP_CTRL = 0,
+ SIP_DATA,
+ SIP_DATA_AMPDU
+} SIP_TYPE;
+
+typedef enum {
+ SIP_TX_CTRL_BUF = 0, /* from host */
+ SIP_RX_CTRL_BUF, /* to host */
+ SIP_TX_DATA_BUF, /* from host */
+ SIP_RX_DATA_BUF /* to host */
+} SIP_BUF_TYPE;
+
+enum sip_cmd_id {
+ SIP_CMD_GET_VER = 0,
+ SIP_CMD_WRITE_MEMORY, //1 ROM code
+ SIP_CMD_READ_MEMORY, //2
+ SIP_CMD_WRITE_REG, //3 ROM code
+ SIP_CMD_READ_REG, //4
+ SIP_CMD_BOOTUP, //5 ROM code
+ SIP_CMD_COPYBACK, //6
+ SIP_CMD_INIT, //7
+ SIP_CMD_SCAN, //8
+ SIP_CMD_SETKEY, //9
+ SIP_CMD_CONFIG, //10
+ SIP_CMD_BSS_INFO_UPDATE, //11
+ SIP_CMD_LOOPBACK, //12 ROM code
+ //do not add cmd before this line
+ SIP_CMD_SET_WMM_PARAM,
+ SIP_CMD_AMPDU_ACTION,
+ SIP_CMD_HB_REQ, //15
+ SIP_CMD_RESET_MAC, //16
+ SIP_CMD_PRE_DOWN, //17
+ SIP_CMD_SLEEP, /* for sleep testing */
+ SIP_CMD_WAKEUP, /* for sleep testing */
+ SIP_CMD_DEBUG, /* for general testing */
+ SIP_CMD_GET_FW_VER, /* get fw rev. */
+ SIP_CMD_SETVIF,
+ SIP_CMD_SETSTA,
+ SIP_CMD_PS,
+ SIP_CMD_ATE,
+ SIP_CMD_SUSPEND,
+ SIP_CMD_RECALC_CREDIT,
+ SIP_CMD_MAX,
+};
+
+enum {
+ SIP_EVT_TARGET_ON = 0, //
+ SIP_EVT_BOOTUP, //1 in ROM code
+ SIP_EVT_COPYBACK, //2
+ SIP_EVT_SCAN_RESULT, //3
+ SIP_EVT_TX_STATUS, //4
+ SIP_EVT_CREDIT_RPT, //5, in ROM code
+ SIP_EVT_ERROR, //6
+ SIP_EVT_LOOPBACK, //7, in ROM code
+ SIP_EVT_SNPRINTF_TO_HOST, //8 in ROM code
+ //do not add evt before this line
+ SIP_EVT_HB_ACK, //9
+ SIP_EVT_RESET_MAC_ACK, //10
+ SIP_EVT_WAKEUP, //11 /* for sleep testing */
+ SIP_EVT_DEBUG, //12 /* for general testing */
+ SIP_EVT_PRINT_TO_HOST, //13
+ SIP_EVT_TRC_AMPDU, //14
+ SIP_EVT_ROC, //15
+ SIP_EVT_RESETTING,
+ SIP_EVT_ATE,
+ SIP_EVT_EP,
+ SIP_EVT_INIT_EP,
+ SIP_EVT_SLEEP,
+ SIP_EVT_TXIDLE,
+ SIP_EVT_NOISEFLOOR,
+ SIP_EVT_MAX
+};
+
+#define SIP_IFIDX_MASK 0xf0
+#define SIP_IFIDX_S 4
+#define SIP_TYPE_MASK 0x0f
+#define SIP_TYPE_S 0
+
+#define SIP_HDR_GET_IFIDX(fc0) (((fc0) & SIP_IFIDX_MASK) >> SIP_IFIDX_S)
+#define SIP_HDR_SET_IFIDX(fc0, ifidx) ( (fc0) = ((fc0) & ~SIP_IFIDX_MASK) | ((ifidx) << SIP_IFIDX_S & SIP_IFIDX_MASK) )
+#define SIP_HDR_GET_TYPE(fc0) ((fc0) & SIP_TYPE_MASK )
+/* assume type field is cleared */
+#define SIP_HDR_SET_TYPE(fc0, type) ((fc0) = ((fc0) & ~ SIP_TYPE_MASK) | ((type) & SIP_TYPE_MASK))
+
+/* sip 2.0, not hybrid header so far */
+#define SIP_HDR_IS_CTRL(hdr) (SIP_HDR_GET_TYPE((hdr)->fc[0]) == SIP_CTRL)
+#define SIP_HDR_IS_DATA(hdr) (SIP_HDR_GET_TYPE((hdr)->fc[0]) == SIP_DATA)
+#define SIP_HDR_IS_AMPDU(hdr) (SIP_HDR_GET_TYPE((hdr)->fc[0]) == SIP_DATA_AMPDU)
+
+/* fc[1] flags, only for data pkt. Ctrl pkts use fc[1] as eventID */
+#define SIP_HDR_SET_FLAGS(hdr, flags) ((hdr)->fc[1] |= (flags))
+#define SIP_HDR_F_MORE_PKT 0x1
+#define SIP_HDR_F_NEED_CRDT_RPT 0x2
+#define SIP_HDR_F_SYNC 0x4
+#define SIP_HDR_F_SYNC_RESET 0x8
+#define SIP_HDR_F_PM_TURNING_ON 0x10
+#define SIP_HDR_F_PM_TURNING_OFF 0x20
+
+#define SIP_HDR_NEED_CREDIT_UPDATE(hdr) ((hdr)->fc[1] & SIP_HDR_F_NEED_CRDT_RPT)
+#define SIP_HDR_IS_MORE_PKT(hdr) ((hdr)->fc[1] & SIP_HDR_F_MORE_PKT)
+#define SIP_HDR_IS_CRDT_RPT(hdr) ((hdr)->fc[1] & SIP_HDR_F_CRDT_RPT)
+#define SIP_HDR_IS_SYNC(hdr) ((hdr)->fc[1] & SIP_HDR_F_SYNC)
+#define SIP_HDR_IS_SYNC_RESET(hdr) ((hdr)->fc[1] & SIP_HDR_F_SYNC_RESET)
+#define SIP_HDR_IS_SYNC_PKT(hdr) (SIP_HDR_IS_SYNC(hdr) | SIP_HDR_IS_SYNC_RESET(hdr))
+#define SIP_HDR_SET_SYNC(hdr) SIP_HDR_SET_FLAGS((hdr), SIP_HDR_F_SYNC)
+#define SIP_HDR_SET_SYNC_RESET(hdr) SIP_HDR_SET_FLAGS((hdr), SIP_HDR_F_SYNC_RESET)
+#define SIP_HDR_SET_MORE_PKT(hdr) SIP_HDR_SET_FLAGS((hdr), SIP_HDR_F_MORE_PKT)
+#define SIP_HDR_SET_PM_TURNING_ON(hdr) SIP_HDR_SET_FLAGS((hdr), SIP_HDR_F_PM_TURNING_ON)
+#define SIP_HDR_IS_PM_TURNING_ON(hdr) ((hdr)->fc[1] & SIP_HDR_F_PM_TURNING_ON)
+#define SIP_HDR_SET_PM_TURNING_OFF(hdr) SIP_HDR_SET_FLAGS((hdr), SIP_HDR_F_PM_TURNING_OFF)
+#define SIP_HDR_IS_PM_TURNING_OFF(hdr) ((hdr)->fc[1] & SIP_HDR_F_PM_TURNING_OFF)
+
+/*
+ * fc[0]: first 4bit: ifidx; last 4bit: type
+ * fc[1]: flags
+ *
+ * Don't touch the header definitons
+ */
+struct sip_hdr_min {
+ u8 fc[2];
+ __le16 len;
+} __packed;
+
+/* not more than 4byte long */
+struct sip_tx_data_info {
+ u8 tid;
+ u8 ac;
+ u8 p2p:1, enc_flag:7;
+ u8 hw_kid;
+} __packed;
+
+/* NB: this structure should be not more than 4byte !! */
+struct sip_tx_info {
+ union {
+ u32 cmdid;
+ struct sip_tx_data_info dinfo;
+ } u;
+} __packed;
+
+struct sip_hdr {
+ u8 fc[2]; //fc[0]: type and ifidx ; fc[1] is eventID if the first ctrl pkt in the chain. data pkt still can use fc[1] to set flag
+ __le16 len;
+ union {
+ volatile u32 recycled_credits; /* last 12bits is credits, first 20 bits is actual length of the first pkt in the chain */
+ struct sip_tx_info tx_info;
+ } u;
+ u32 seq;
+} __packed;
+
+#define h_credits u.recycled_credits
+#define c_evtid fc[1]
+#define c_cmdid u.tx_info.u.cmdid
+#define d_ac u.tx_info.u.dinfo.ac
+#define d_tid u.tx_info.u.dinfo.tid
+#define d_p2p u.tx_info.u.dinfo.p2p
+#define d_enc_flag u.tx_info.u.dinfo.enc_flag
+#define d_hw_kid u.tx_info.u.dinfo.hw_kid
+
+#define SIP_CREDITS_MASK 0xfff /* last 12 bits */
+
+#ifdef HOST_RC
+
+#define RC_CNT_MASK 0xf
+
+struct sip_rc_status {
+ u32 rc_map;
+ union {
+ u32 rc_cnt1:4, rc_cnt2:4, rc_cnt3:4, rc_cnt4:4, rc_cnt5:4;
+
+ u32 rc_cnt_store;
+ };
+};
+
+/* copy from mac80211.h */
+struct sip_tx_rc {
+ struct ieee80211_tx_rate rates[IEEE80211_TX_MAX_RATES];
+ s8 rts_cts_rate_idx;
+};
+#endif /* HOST_RC */
+
+#define SIP_HDR_MIN_LEN 4
+#define SIP_HDR_LEN sizeof(struct sip_hdr)
+#define SIP_CTRL_HDR_LEN SIP_HDR_LEN /* same as sip_hdr in sip2 design */
+#define SIP_BOOT_BUF_SIZE 256
+#define SIP_CTRL_BUF_SZ 256 /* too much?? */
+#define SIP_CTRL_BUF_N 6
+#define SIP_CTRL_TXBUF_N 2
+#define SIP_CTRL_RXBUF_N 4
+
+/* WAR for mblk */
+#define SIP_RX_ADDR_PREFIX_MASK 0xfc000000
+#define SIP_RX_ADDR_SHIFT 6 /* [31:5], shift 6 bits */
+
+struct sip_cmd_write_memory {
+ u32 addr;
+ u32 len;
+} __packed;
+
+struct sip_cmd_read_memory {
+ u32 addr;
+ u32 len;
+} __packed;
+
+struct sip_cmd_write_reg {
+ u32 addr;
+ u32 val;
+} __packed;
+
+struct sip_cmd_bootup {
+ u32 boot_addr;
+} __packed;
+
+struct sip_cmd_loopback {
+ u32 txlen; //host to target packet len, 0 means no txpacket
+ u32 rxlen; //target to host packet len, 0 means no rxpacket
+ u32 pack_id; //sequence of packet
+} __packed;
+
+struct sip_evt_loopback {
+ u32 txlen; //host to target packet len, 0 means no txpacket
+ u32 rxlen; //target to host packet len, 0 means no rxpacket
+ u32 pack_id; //sequence of packet
+} __packed;
+
+struct sip_cmd_copyback {
+ u32 addr;
+ u32 len;
+} __packed;
+
+struct sip_cmd_scan {
+// u8 ssid[32];
+ u8 ssid_len;
+// u8 hw_channel[14];
+ u8 n_channels;
+ u8 ie_len;
+ u8 aborted;
+} __packed; // ie[] append at the end
+
+
+#ifndef ETH_ALEN
+#define ETH_ALEN 6
+#endif /* ETH_ALEN */
+
+struct sip_cmd_setkey {
+ u8 bssid_no;
+ u8 addr[ETH_ALEN];
+ u8 alg;
+ u8 keyidx;
+ u8 hw_key_idx;
+ u8 flags;
+ u8 keylen;
+ u8 key[32];
+} __packed;
+
+struct sip_cmd_config {
+ u16 center_freq;
+ u16 duration;
+} __packed;
+
+struct sip_cmd_bss_info_update {
+ u8 bssid[ETH_ALEN];
+ u16 isassoc;
+ u32 beacon_int;
+ u8 bssid_no;
+} __packed;
+
+struct sip_evt_bootup {
+ u16 tx_blksz;
+ u8 mac_addr[ETH_ALEN];
+ /* anything else ? */
+} __packed;
+
+struct sip_cmd_setvif {
+ u8 index;
+ u8 mac[ETH_ALEN];
+ u8 set;
+ u8 op_mode;
+ u8 is_p2p;
+} __packed;
+
+enum esp_ieee80211_phytype {
+ ESP_IEEE80211_T_CCK = 0,
+ ESP_IEEE80211_T_OFDM = 1,
+ ESP_IEEE80211_T_HT20_L = 2,
+ ESP_IEEE80211_T_HT20_S = 3,
+};
+
+struct sip_cmd_setsta {
+ u8 ifidx;
+ u8 index;
+ u8 set;
+ u8 phymode;
+ u8 mac[ETH_ALEN];
+ u16 aid;
+ u8 ampdu_factor;
+ u8 ampdu_density;
+ u16 resv;
+} __packed;
+
+struct sip_cmd_ps {
+ u8 dtim_period;
+ u8 max_sleep_period;
+ u8 on;
+ u8 resv;
+} __packed;
+
+struct sip_cmd_suspend {
+ u8 suspend;
+ u8 resv[3];
+} __packed;
+
+#define SIP_DUMP_RPBM_ERR BIT(0)
+#define SIP_RXABORT_FIXED BIT(1)
+#define SIP_SUPPORT_BGSCAN BIT(2)
+struct sip_evt_bootup2 {
+ u16 tx_blksz;
+ u8 mac_addr[ETH_ALEN];
+ u16 rx_blksz;
+ u8 credit_to_reserve;
+ u8 options;
+ s16 noise_floor;
+ u8 resv[2];
+ /* anything else ? */
+} __packed;
+
+typedef enum {
+ TRC_TX_AMPDU_STOPPED = 1,
+ TRC_TX_AMPDU_OPERATIONAL,
+ TRC_TX_AMPDU_WAIT_STOP,
+ TRC_TX_AMPDU_WAIT_OPERATIONAL,
+ TRC_TX_AMPDU_START,
+} trc_ampdu_state_t;
+
+struct sip_evt_trc_ampdu {
+ u8 state;
+ u8 tid;
+ u8 addr[ETH_ALEN];
+} __packed;
+
+struct sip_cmd_set_wmm_params {
+ u8 aci;
+ u8 aifs;
+ u8 ecw_min;
+ u8 ecw_max;
+ u16 txop_us;
+} __packed;
+
+#define SIP_AMPDU_RX_START 0
+#define SIP_AMPDU_RX_STOP 1
+#define SIP_AMPDU_TX_OPERATIONAL 2
+#define SIP_AMPDU_TX_STOP 3
+struct sip_cmd_ampdu_action {
+ u8 action;
+ u8 index;
+ u8 tid;
+ u8 win_size;
+ u16 ssn;
+ u8 addr[ETH_ALEN];
+} __packed;
+
+#define SIP_TX_ST_OK 0
+#define SIP_TX_ST_NOEB 1
+#define SIP_TX_ST_ACKTO 2
+#define SIP_TX_ST_ENCERR 3
+
+//NB: sip_tx_status must be 4 bytes aligned
+struct sip_tx_status {
+ u32 sip_seq;
+#ifdef HOST_RC
+ struct sip_rc_status rcstatus;
+#endif /* HOST_RC */
+ u8 errno; /* success or failure code */
+ u8 rate_index;
+ char ack_signal;
+ u8 pad;
+} __packed;
+
+struct sip_evt_tx_report {
+ u32 pkts;
+ struct sip_tx_status status[0];
+} __packed;
+
+struct sip_evt_tx_mblk {
+ u32 mblk_map;
+} __packed;
+
+struct sip_evt_scan_report {
+ u16 scan_id;
+ u16 aborted;
+} __packed;
+
+struct sip_evt_roc {
+ u16 state; //start:1, end :0
+ u16 is_ok;
+} __packed;
+
+struct sip_evt_txidle {
+ u32 last_seq;
+} __packed;
+
+struct sip_evt_noisefloor {
+ s16 noise_floor;
+ u16 pad;
+} __packed;
+/*
+ * for mblk direct memory access, no need for sip_hdr. tx: first 2k for contrl msg,
+ * rest of 14k for data. rx, same.
+ */
+#ifdef TEST_MODE
+
+struct sip_cmd_sleep {
+ u32 sleep_mode;
+ u32 sleep_tm_ms;
+ u32 wakeup_tm_ms; //zero: after receive bcn, then sleep, nozero: delay nozero ms to sleep
+ u32 sleep_times; //zero: always sleep, nozero: after nozero number sleep/wakeup, then end up sleep
+} __packed;
+
+struct sip_cmd_wakeup {
+ u32 check_data; //0:copy to event
+} __packed;
+
+struct sip_evt_wakeup {
+ u32 check_data;
+} __packed;
+
+//general debug command
+struct sip_cmd_debug {
+ u32 cmd_type;
+ u32 para_num;
+ u32 para[10];
+} __packed;
+
+struct sip_evt_debug {
+ u16 len;
+ u32 results[12];
+ u16 pad;
+} __packed;
+
+struct sip_cmd_ate {
+ //u8 len;
+ u8 cmdstr[0];
+} __packed;
+
+
+
+#endif //ifdef TEST_MODE
+
+#endif /* _SIP_COMMON_H_ */
diff --git a/drivers/net/wireless/esp8089/slc_host_register.h b/drivers/net/wireless/esp8089/slc_host_register.h
new file mode 100644
index 000000000000..2cdb2c856d15
--- /dev/null
+++ b/drivers/net/wireless/esp8089/slc_host_register.h
@@ -0,0 +1,271 @@
+//Generated at 2012-10-23 20:11:08
+/*
+ * Copyright (c) 2011 Espressif System
+ *
+ * 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.
+ */
+
+#ifndef SLC_HOST_REGISTER_H_INCLUDED
+#define SLC_HOST_REGISTER_H_INCLUDED
+
+/* #define REG_SLC_HOST_BASE 0x00000000 */
+/* skip the token1, since reading it will clean the credit */
+#define REG_SLC_HOST_BASE 0x00000000
+#define REG_SLC_BASE 0x00000000
+
+
+#define SLC_HOST_PF (REG_SLC_HOST_BASE + 0x0)
+#define SLC_HOST_TOKEN_RDATA (REG_SLC_HOST_BASE + 0x4)
+#define SLC_HOST_RX_PF_EOF 0x0000000F
+#define SLC_HOST_RX_PF_EOF_S 28
+#define SLC_HOST_TOKEN1 0x00000FFF
+#define SLC_HOST_TOKEN1_S 16
+#define SLC_HOST_RX_PF_VALID (BIT(15))
+#define SLC_HOST_TOKEN0 0x00000FFF
+#define SLC_HOST_TOKEN0_S 0
+
+#define SLC_HOST_TOKEN0_MASK SLC_HOST_TOKEN0
+
+#define SLC_HOST_INT_RAW (REG_SLC_HOST_BASE + 0x8)
+#define SLC_HOST_EXT_BIT3_INT_RAW (BIT(22))
+#define SLC_HOST_EXT_BIT2_INT_RAW (BIT(21))
+#define SLC_HOST_EXT_BIT1_INT_RAW (BIT(20))
+#define SLC_HOST_RXFIFO_NOT_EMPTY_INT_RAW (BIT(19))
+#define SLC_HOST_RX_PF_VALID_INT_RAW (BIT(18))
+#define SLC_HOST_TX_OVF_INT_RAW (BIT(17))
+#define SLC_HOST_RX_UDF_INT_RAW (BIT(16))
+#define SLC_HOST_TX_START_INT_RAW (BIT(15))
+#define SLC_HOST_RX_START_INT_RAW (BIT(14))
+#define SLC_HOST_RX_EOF_INT_RAW (BIT(13))
+#define SLC_HOST_RX_SOF_INT_RAW (BIT(12))
+#define SLC_HOST_TOKEN1_0TO1_INT_RAW (BIT(11))
+#define SLC_HOST_TOKEN0_0TO1_INT_RAW (BIT(10))
+#define SLC_HOST_TOKEN1_1TO0_INT_RAW (BIT(9))
+#define SLC_HOST_TOKEN0_1TO0_INT_RAW (BIT(8))
+#define SLC_HOST_TOHOST_BIT7_INT_RAW (BIT(7))
+#define SLC_HOST_TOHOST_BIT6_INT_RAW (BIT(6))
+#define SLC_HOST_TOHOST_BIT5_INT_RAW (BIT(5))
+#define SLC_HOST_TOHOST_BIT4_INT_RAW (BIT(4))
+#define SLC_HOST_TOHOST_BIT3_INT_RAW (BIT(3))
+#define SLC_HOST_TOHOST_BIT2_INT_RAW (BIT(2))
+#define SLC_HOST_TOHOST_BIT1_INT_RAW (BIT(1))
+#define SLC_HOST_TOHOST_BIT0_INT_RAW (BIT(0))
+
+#define SLC_HOST_STATE_W0 (REG_SLC_HOST_BASE + 0xC)
+#define SLC_HOST_STATE3 0x000000FF
+#define SLC_HOST_STATE3_S 24
+#define SLC_HOST_STATE2 0x000000FF
+#define SLC_HOST_STATE2_S 16
+#define SLC_HOST_STATE1 0x000000FF
+#define SLC_HOST_STATE1_S 8
+#define SLC_HOST_STATE0 0x000000FF
+#define SLC_HOST_STATE0_S 0
+
+#define SLC_HOST_STATE_W1 (REG_SLC_HOST_BASE + 0x10)
+#define SLC_HOST_STATE7 0x000000FF
+#define SLC_HOST_STATE7_S 24
+#define SLC_HOST_STATE6 0x000000FF
+#define SLC_HOST_STATE6_S 16
+#define SLC_HOST_STATE5 0x000000FF
+#define SLC_HOST_STATE5_S 8
+#define SLC_HOST_STATE4 0x000000FF
+#define SLC_HOST_STATE4_S 0
+
+#define SLC_HOST_CONF_W0 (REG_SLC_HOST_BASE + 0x14)
+#define SLC_HOST_CONF3 0x000000FF
+#define SLC_HOST_CONF3_S 24
+#define SLC_HOST_CONF2 0x000000FF
+#define SLC_HOST_CONF2_S 16
+#define SLC_HOST_CONF1 0x000000FF
+#define SLC_HOST_CONF1_S 8
+#define SLC_HOST_CONF0 0x000000FF
+#define SLC_HOST_CONF0_S 0
+
+#define SLC_HOST_CONF_W1 (REG_SLC_HOST_BASE + 0x18)
+#define SLC_HOST_CONF7 0x000000FF
+#define SLC_HOST_CONF7_S 24
+#define SLC_HOST_CONF6 0x000000FF
+#define SLC_HOST_CONF6_S 16
+#define SLC_HOST_CONF5 0x000000FF
+#define SLC_HOST_CONF5_S 8
+#define SLC_HOST_CONF4 0x000000FF
+#define SLC_HOST_CONF4_S 0
+
+#define SLC_HOST_INT_ST (REG_SLC_HOST_BASE + 0x1C)
+#define SLC_HOST_RX_ST (BIT(23))
+#define SLC_HOST_EXT_BIT3_INT_ST (BIT(22))
+#define SLC_HOST_EXT_BIT2_INT_ST (BIT(21))
+#define SLC_HOST_EXT_BIT1_INT_ST (BIT(20))
+#define SLC_HOST_RXFIFO_NOT_EMPTY_INT_ST (BIT(19))
+#define SLC_HOST_RX_PF_VALID_INT_ST (BIT(18))
+#define SLC_HOST_TX_OVF_INT_ST (BIT(17))
+#define SLC_HOST_RX_UDF_INT_ST (BIT(16))
+#define SLC_HOST_TX_START_INT_ST (BIT(15))
+#define SLC_HOST_RX_START_INT_ST (BIT(14))
+#define SLC_HOST_RX_EOF_INT_ST (BIT(13))
+#define SLC_HOST_RX_SOF_INT_ST (BIT(12))
+#define SLC_HOST_TOKEN1_0TO1_INT_ST (BIT(11))
+#define SLC_HOST_TOKEN0_0TO1_INT_ST (BIT(10))
+#define SLC_HOST_TOKEN1_1TO0_INT_ST (BIT(9))
+#define SLC_HOST_TOKEN0_1TO0_INT_ST (BIT(8))
+#define SLC_HOST_TOHOST_BIT7_INT_ST (BIT(7))
+#define SLC_HOST_TOHOST_BIT6_INT_ST (BIT(6))
+#define SLC_HOST_TOHOST_BIT5_INT_ST (BIT(5))
+#define SLC_HOST_TOHOST_BIT4_INT_ST (BIT(4))
+#define SLC_HOST_TOHOST_BIT3_INT_ST (BIT(3))
+#define SLC_HOST_TOHOST_BIT2_INT_ST (BIT(2))
+#define SLC_HOST_TOHOST_BIT1_INT_ST (BIT(1))
+#define SLC_HOST_TOHOST_BIT0_INT_ST (BIT(0))
+
+#define SLC_HOST_CONF_W2 (REG_SLC_HOST_BASE + 0x20)
+#define SLC_HOST_CONF11 0x000000FF
+#define SLC_HOST_CONF11_S 24
+#define SLC_HOST_CONF10 0x000000FF
+#define SLC_HOST_CONF10_S 16
+#define SLC_HOST_CONF9 0x000000FF
+#define SLC_HOST_CONF9_S 8
+#define SLC_HOST_CONF8 0x000000FF
+#define SLC_HOST_CONF8_S 0
+
+#define SLC_HOST_CONF_W3 (REG_SLC_HOST_BASE + 0x24)
+#define SLC_HOST_CONF15 0x000000FF
+#define SLC_HOST_CONF15_S 24
+#define SLC_HOST_CONF14 0x000000FF
+#define SLC_HOST_CONF14_S 16
+#define SLC_HOST_CONF13 0x000000FF
+#define SLC_HOST_CONF13_S 8
+#define SLC_HOST_CONF12 0x000000FF
+#define SLC_HOST_CONF12_S 0
+
+#define SLC_HOST_GEN_TXDONE_INT BIT(16)
+#define SLC_HOST_GEN_RXDONE_INT BIT(17)
+
+#define SLC_HOST_CONF_W4 (REG_SLC_HOST_BASE + 0x28)
+#define SLC_HOST_CONF19 0x000000FF
+#define SLC_HOST_CONF19_S 24
+#define SLC_HOST_CONF18 0x000000FF
+#define SLC_HOST_CONF18_S 16
+#define SLC_HOST_CONF17 0x000000FF
+#define SLC_HOST_CONF17_S 8
+#define SLC_HOST_CONF16 0x000000FF
+#define SLC_HOST_CONF16_S 0
+
+#define SLC_HOST_TOKEN_WDATA (REG_SLC_HOST_BASE + 0x2C)
+#define SLC_HOST_TOKEN1_WD 0x00000FFF
+#define SLC_HOST_TOKEN1_WD_S 16
+#define SLC_HOST_TOKEN0_WD 0x00000FFF
+#define SLC_HOST_TOKEN0_WD_S 0
+
+#define SLC_HOST_INT_CLR (REG_SLC_HOST_BASE + 0x30)
+#define SLC_HOST_TOKEN1_WR (BIT(31))
+#define SLC_HOST_TOKEN0_WR (BIT(30))
+#define SLC_HOST_TOKEN1_DEC (BIT(29))
+#define SLC_HOST_TOKEN0_DEC (BIT(28))
+#define SLC_HOST_EXT_BIT3_INT_CLR (BIT(22))
+#define SLC_HOST_EXT_BIT2_INT_CLR (BIT(21))
+#define SLC_HOST_EXT_BIT1_INT_CLR (BIT(20))
+#define SLC_HOST_EXT_BIT0_INT_CLR (BIT(19))
+#define SLC_HOST_RX_PF_VALID_INT_CLR (BIT(18))
+#define SLC_HOST_TX_OVF_INT_CLR (BIT(17))
+#define SLC_HOST_RX_UDF_INT_CLR (BIT(16))
+#define SLC_HOST_TX_START_INT_CLR (BIT(15))
+#define SLC_HOST_RX_START_INT_CLR (BIT(14))
+#define SLC_HOST_RX_EOF_INT_CLR (BIT(13))
+#define SLC_HOST_RX_SOF_INT_CLR (BIT(12))
+#define SLC_HOST_TOKEN1_0TO1_INT_CLR (BIT(11))
+#define SLC_HOST_TOKEN0_0TO1_INT_CLR (BIT(10))
+#define SLC_HOST_TOKEN1_1TO0_INT_CLR (BIT(9))
+#define SLC_HOST_TOKEN0_1TO0_INT_CLR (BIT(8))
+#define SLC_HOST_TOHOST_BIT7_INT_CLR (BIT(7))
+#define SLC_HOST_TOHOST_BIT6_INT_CLR (BIT(6))
+#define SLC_HOST_TOHOST_BIT5_INT_CLR (BIT(5))
+#define SLC_HOST_TOHOST_BIT4_INT_CLR (BIT(4))
+#define SLC_HOST_TOHOST_BIT3_INT_CLR (BIT(3))
+#define SLC_HOST_TOHOST_BIT2_INT_CLR (BIT(2))
+#define SLC_HOST_TOHOST_BIT1_INT_CLR (BIT(1))
+#define SLC_HOST_TOHOST_BIT0_INT_CLR (BIT(0))
+
+#define SLC_HOST_INT_ENA (REG_SLC_HOST_BASE + 0x34)
+#define SLC_HOST_EXT_BIT3_INT_ENA (BIT(22))
+#define SLC_HOST_EXT_BIT2_INT_ENA (BIT(21))
+#define SLC_HOST_EXT_BIT1_INT_ENA (BIT(20))
+#define SLC_HOST_EXT_BIT0_INT_ENA (BIT(19))
+#define SLC_HOST_RX_PF_VALID_INT_ENA (BIT(18))
+#define SLC_HOST_TX_OVF_INT_ENA (BIT(17))
+#define SLC_HOST_RX_UDF_INT_ENA (BIT(16))
+#define SLC_HOST_TX_START_INT_ENA (BIT(15))
+#define SLC_HOST_RX_START_INT_ENA (BIT(14))
+#define SLC_HOST_RX_EOF_INT_ENA (BIT(13))
+#define SLC_HOST_RX_SOF_INT_ENA (BIT(12))
+#define SLC_HOST_TOKEN1_0TO1_INT_ENA (BIT(11))
+#define SLC_HOST_TOKEN0_0TO1_INT_ENA (BIT(10))
+#define SLC_HOST_TOKEN1_1TO0_INT_ENA (BIT(9))
+#define SLC_HOST_TOKEN0_1TO0_INT_ENA (BIT(8))
+#define SLC_HOST_TOHOST_BIT7_INT_ENA (BIT(7))
+#define SLC_HOST_TOHOST_BIT6_INT_ENA (BIT(6))
+#define SLC_HOST_TOHOST_BIT5_INT_ENA (BIT(5))
+#define SLC_HOST_TOHOST_BIT4_INT_ENA (BIT(4))
+#define SLC_HOST_TOHOST_BIT3_INT_ENA (BIT(3))
+#define SLC_HOST_TOHOST_BIT2_INT_ENA (BIT(2))
+#define SLC_HOST_TOHOST_BIT1_INT_ENA (BIT(1))
+#define SLC_HOST_TOHOST_BIT0_INT_ENA (BIT(0))
+
+#define SLC_HOST_CONF_W5 (REG_SLC_HOST_BASE + 0x3C)
+#define SLC_HOST_CONF23 0x000000FF
+#define SLC_HOST_CONF23_S 24
+#define SLC_HOST_CONF22 0x000000FF
+#define SLC_HOST_CONF22_S 16
+#define SLC_HOST_CONF21 0x000000FF
+#define SLC_HOST_CONF21_S 8
+#define SLC_HOST_CONF20 0x000000FF
+#define SLC_HOST_CONF20_S 0
+
+#define SLC_HOST_WIN_CMD (REG_SLC_HOST_BASE + 0x40)
+
+
+#define SLC_HOST_DATE (REG_SLC_HOST_BASE + 0x78)
+#define SLC_HOST_ID (REG_SLC_HOST_BASE + 0x7C)
+
+#define SLC_ADDR_WINDOW_CLEAR_MASK (~(0xf<<12))
+#define SLC_FROM_HOST_ADDR_WINDOW (0x1<<12)
+#define SLC_TO_HOST_ADDR_WINDOW (0x3<<12)
+
+#define SLC_SET_FROM_HOST_ADDR_WINDOW(v) do { \
+ (v) &= 0xffff; \
+ (v) &= SLC_ADDR_WINDOW_CLEAR_MASK; \
+ (v) |= SLC_FROM_HOST_ADDR_WINDOW; \
+} while (0);
+
+#define SLC_SET_TO_HOST_ADDR_WINDOW(v) do { \
+ (v) &= 0xffff; \
+ (v) &= SLC_ADDR_WINDOW_CLEAR_MASK; \
+ (v) |= SLC_TO_HOST_ADDR_WINDOW; \
+} while (0);
+
+#define SLC_INT_ENA (REG_SLC_BASE + 0xC)
+#define SLC_RX_EOF_INT_ENA BIT(17)
+#define SLC_FRHOST_BIT2_INT_ENA BIT(2)
+
+#define SLC_RX_LINK (REG_SLC_BASE + 0x24)
+#define SLC_RXLINK_START BIT(29)
+
+#define SLC_BRIDGE_CONF (REG_SLC_BASE + 0x44)
+#define SLC_TX_PUSH_IDLE_NUM 0xFFFF
+#define SLC_TX_PUSH_IDLE_NUM_S 16
+#define SLC_HDA_MAP_128K BIT(13)
+#define SLC_TX_DUMMY_MODE BIT(12)
+#define SLC_FIFO_MAP_ENA 0x0000000F
+#define SLC_FIFO_MAP_ENA_S 8
+#define SLC_TXEOF_ENA 0x0000003F
+#define SLC_TXEOF_ENA_S
+
+
+#endif // SLC_HOST_REGISTER_H_INCLUDED
--
2.34.1