distribution/projects/Rockchip/packages/linux/patches/RK3326/002-esp8089-kernel-driver.patch

10931 lines
297 KiB
Diff
Raw Normal View History

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..a19d2437dd82
--- /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..14186365fdd4
--- /dev/null
+++ b/drivers/net/wireless/esp8089/esp_mac80211.c
@@ -0,0 +1,1727 @@
+/*
+ * 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,
+};
+
+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..0d3ad3655cf4
--- /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..6602a1e22ab1
--- /dev/null
+++ b/drivers/net/wireless/esp8089/esp_sip.c
@@ -0,0 +1,2420 @@
+/*
+ * 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;
+
+ esp_dbg(ESP_DBG_TRACE, "sip_detach: sip_free_init_ctrl_buf()");
+ 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(&sip->epub->sendup_work); // Must be non-sync
+ 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..2bd2c63f5388
--- /dev/null
+++ b/drivers/net/wireless/esp8089/sdio_sif_esp.c
@@ -0,0 +1,824 @@
+/*
+ * 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 <linux/delay.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);
+ msleep(10);
+ }
+
+ 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);
+
+ /*
+ * Reset on sdio remove to leave the hardware in cold state,
+ * so a new module insertion will be possible
+ */
+ if (epub->sdio_state == ESP_SDIO_STATE_SECOND_INIT) {
+ sdio_claim_host(func);
+ mmc_hw_reset(func->card);
+ sdio_release_host(func);
+ mdelay(10);
+ }
+
+ 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