11063 lines
299 KiB
Diff
11063 lines
299 KiB
Diff
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
|
||
index d2735fab0..760c0ed71 100644
|
||
--- a/drivers/net/wireless/Kconfig
|
||
+++ b/drivers/net/wireless/Kconfig
|
||
@@ -293,5 +293,6 @@ source "drivers/net/wireless/zd1211rw/Kconfig"
|
||
source "drivers/net/wireless/mwifiex/Kconfig"
|
||
source "drivers/net/wireless/cw1200/Kconfig"
|
||
source "drivers/net/wireless/rsi/Kconfig"
|
||
+source "drivers/net/wireless/esp8089/Kconfig"
|
||
|
||
endif # WLAN
|
||
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
|
||
index 3bd143940..81587dce2 100644
|
||
--- a/drivers/net/wireless/Makefile
|
||
+++ b/drivers/net/wireless/Makefile
|
||
@@ -23,6 +23,7 @@ obj-$(CONFIG_B43) += b43/
|
||
obj-$(CONFIG_B43LEGACY) += b43legacy/
|
||
obj-$(CONFIG_ZD1211RW) += zd1211rw/
|
||
obj-$(CONFIG_WLAN) += realtek/
|
||
+obj-$(CONFIG_ESP8089) += esp8089/
|
||
|
||
# 16-bit wireless PCMCIA client drivers
|
||
obj-$(CONFIG_PCMCIA_RAYCS) += ray_cs.o
|
||
diff --git a/drivers/net/wireless/esp8089/Kconfig b/drivers/net/wireless/esp8089/Kconfig
|
||
new file mode 100644
|
||
index 000000000..8fdd8fa82
|
||
--- /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 000000000..d6a93266f
|
||
--- /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 000000000..bf274ea90
|
||
--- /dev/null
|
||
+++ b/drivers/net/wireless/esp8089/Makefile
|
||
@@ -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/Makefile.old b/drivers/net/wireless/esp8089/Makefile.old
|
||
new file mode 100644
|
||
index 000000000..b7b1a47b1
|
||
--- /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 000000000..56b40db27
|
||
--- /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/dts-4.6/sun8i-a33-aoson-m751s.dtb b/drivers/net/wireless/esp8089/dts-4.6/sun8i-a33-aoson-m751s.dtb
|
||
new file mode 100644
|
||
index 000000000..e69de29bb
|
||
diff --git a/drivers/net/wireless/esp8089/dts-4.6/sun8i-a33-aoson-m751s.dts b/drivers/net/wireless/esp8089/dts-4.6/sun8i-a33-aoson-m751s.dts
|
||
new file mode 100644
|
||
index 000000000..fc6475ede
|
||
--- /dev/null
|
||
+++ b/drivers/net/wireless/esp8089/dts-4.6/sun8i-a33-aoson-m751s.dts
|
||
@@ -0,0 +1,157 @@
|
||
+/*
|
||
+ * Copyright 2015 Hans de Goede <hdegoede@redhat.com>
|
||
+ *
|
||
+ * This file is dual-licensed: you can use it either under the terms
|
||
+ * of the GPL or the X11 license, at your option. Note that this dual
|
||
+ * licensing only applies to this file, and not this project as a
|
||
+ * whole.
|
||
+ *
|
||
+ * a) This file is free software; you can redistribute it and/or
|
||
+ * modify it under the terms of the GNU General Public License as
|
||
+ * published by the Free Software Foundation; either version 2 of the
|
||
+ * License, or (at your option) any later version.
|
||
+ *
|
||
+ * This file is distributed in the hope that it will be useful,
|
||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
+ * GNU General Public License for more details.
|
||
+ *
|
||
+ * Or, alternatively,
|
||
+ *
|
||
+ * b) Permission is hereby granted, free of charge, to any person
|
||
+ * obtaining a copy of this software and associated documentation
|
||
+ * files (the "Software"), to deal in the Software without
|
||
+ * restriction, including without limitation the rights to use,
|
||
+ * copy, modify, merge, publish, distribute, sublicense, and/or
|
||
+ * sell copies of the Software, and to permit persons to whom the
|
||
+ * Software is furnished to do so, subject to the following
|
||
+ * conditions:
|
||
+ *
|
||
+ * The above copyright notice and this permission notice shall be
|
||
+ * included in all copies or substantial portions of the Software.
|
||
+ *
|
||
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||
+ * OTHER DEALINGS IN THE SOFTWARE.
|
||
+ */
|
||
+
|
||
+/dts-v1/;
|
||
+#include "sun8i-a33.dtsi"
|
||
+#include "sun8i-q8-common.dtsi"
|
||
+
|
||
+/ {
|
||
+ model = "Aoson M751s Q8 A33 Tablet";
|
||
+ compatible = "allwinner,q8-a33", "allwinner,sun8i-a33";
|
||
+
|
||
+ /*
|
||
+ * This is actually an active-low reset line for the WLAN
|
||
+ * part of the WiFi CHIP.
|
||
+ *
|
||
+ * Just like for DLDO1 and DLDO2, we cannot tie it to the DT
|
||
+ * node of the SDIO device for now, as there is no bindings
|
||
+ * for it, so we need to have this hack.
|
||
+ */
|
||
+ wifi_reg_on: wifi_reg_on {
|
||
+ compatible = "regulator-fixed";
|
||
+ pinctrl-names = "default";
|
||
+ pinctrl-0 = <&m751s_wifi_reg_on_pin>;
|
||
+
|
||
+ regulator-name = "wifi-reg-on";
|
||
+ regulator-min-microvolt = <3300000>;
|
||
+ regulator-max-microvolt = <3300000>;
|
||
+ regulator-always-on;
|
||
+ gpio = <&r_pio 0 6 GPIO_ACTIVE_HIGH>; /* PL6 */
|
||
+ enable-active-high;
|
||
+ };
|
||
+
|
||
+ mmc1_pwrseq: mmc1_pwrseq {
|
||
+ compatible = "mmc-pwrseq-simple";
|
||
+ pinctrl-names = "default";
|
||
+ pinctrl-0 = <&m751s_wifi_reset_pin>;
|
||
+ reset-gpios = <&r_pio 0 11 GPIO_ACTIVE_LOW>; /* PH9 WIFI_EN */
|
||
+ };
|
||
+
|
||
+ /*
|
||
+ * Both DLDO1 and DLDO2 are used in parallel to power up the
|
||
+ * WiFi Chip.
|
||
+ *
|
||
+ * If those are not enabled, the SDIO part will not enumerate,
|
||
+ * and since there's no way currently to pass DT infos to an
|
||
+ * SDIO device, we cannot really do better than this ugly hack
|
||
+ * for now.
|
||
+ */
|
||
+
|
||
+ vcc_wifi: wifi_reg {
|
||
+ compatible = "coupled-voltage-regulator";
|
||
+ regulator-name = "vcc-wifi";
|
||
+ vin0-supply = <&vcc_wifi_1>;
|
||
+ vin1-supply = <&vcc_wifi_2>;
|
||
+ };
|
||
+};
|
||
+
|
||
+&axp22x {
|
||
+ regulators {
|
||
+ vcc_wifi_1: dldo1 {
|
||
+ regulator-min-microvolt = <3300000>;
|
||
+ regulator-max-microvolt = <3300000>;
|
||
+ regulator-name = "vcc-wifi-1";
|
||
+ };
|
||
+
|
||
+ vcc_wifi_2: dldo2 {
|
||
+ regulator-min-microvolt = <3300000>;
|
||
+ regulator-max-microvolt = <3300000>;
|
||
+ regulator-name = "vcc-wifi-2";
|
||
+ regulator-always-on;
|
||
+ };
|
||
+ };
|
||
+};
|
||
+
|
||
+&mmc1 {
|
||
+ pinctrl-names = "default";
|
||
+ pinctrl-0 = <&mmc1_pins_a>;
|
||
+ vmmc-supply = <&vcc_wifi_1>;
|
||
+ mmc-pwrseq = <&mmc1_pwrseq>;
|
||
+ bus-width = <4>;
|
||
+ //non-removable;
|
||
+ broken-cd;
|
||
+ status = "okay";
|
||
+};
|
||
+
|
||
+&mmc1_pins_a {
|
||
+ allwinner,pull = <SUN4I_PINCTRL_PULL_UP>;
|
||
+};
|
||
+
|
||
+&r_pio {
|
||
+ m751s_wifi_reg_on_pin: m751s_wifi_reg_on_pin@0 {
|
||
+ allwinner,pins = "PL6";
|
||
+ allwinner,function = "gpio_out";
|
||
+ allwinner,drive = <SUN4I_PINCTRL_10_MA>;
|
||
+ allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
|
||
+ };
|
||
+ m751s_wifi_reset_pin: m751s_wifi_reset_pin@0 {
|
||
+ allwinner,pins = "PL11";
|
||
+ allwinner,function = "gpio_out";
|
||
+ allwinner,drive = <SUN4I_PINCTRL_10_MA>;
|
||
+ allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
|
||
+ };
|
||
+};
|
||
+
|
||
+/*
|
||
+ * FIXME for now we only support host mode and rely on u-boot to have
|
||
+ * turned on Vbus which is controlled by the axp223 pmic on the board.
|
||
+ *
|
||
+ * Once we have axp223 support we should switch to fully supporting otg.
|
||
+ */
|
||
+&usb_otg {
|
||
+ dr_mode = "host";
|
||
+ status = "okay";
|
||
+};
|
||
+
|
||
+&usbphy {
|
||
+ status = "okay";
|
||
+};
|
||
diff --git a/drivers/net/wireless/esp8089/esp_ctrl.c b/drivers/net/wireless/esp8089/esp_ctrl.c
|
||
new file mode 100644
|
||
index 000000000..ef3cb1b70
|
||
--- /dev/null
|
||
+++ b/drivers/net/wireless/esp8089/esp_ctrl.c
|
||
@@ -0,0 +1,800 @@
|
||
+/*
|
||
+ * 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);
|
||
+
|
||
+ fix_init_data(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;
|
||
+ case SIP_AMPDU_RX_STOP:
|
||
+ action->index = index;
|
||
+ 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->bss_conf.aid;
|
||
+ else
|
||
+ setstacmd->aid = sta->aid;
|
||
+ memcpy(setstacmd->mac, sta->addr, ETH_ALEN);
|
||
+ if (set) {
|
||
+ if (sta->ht_cap.ht_supported) {
|
||
+ if (sta->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->ht_cap.ampdu_factor;
|
||
+ setstacmd->ampdu_density =
|
||
+ sta->ht_cap.ampdu_density;
|
||
+ } else {
|
||
+ if (sta->
|
||
+ 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 000000000..29c18caa9
|
||
--- /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 000000000..97ecce0c8
|
||
--- /dev/null
|
||
+++ b/drivers/net/wireless/esp8089/esp_debug.c
|
||
@@ -0,0 +1,333 @@
|
||
+/*
|
||
+ * 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,
|
||
+};
|
||
+
|
||
+
|
||
+struct dentry *esp_dump_var(const char *name, struct dentry *parent,
|
||
+ void *value, esp_type type)
|
||
+{
|
||
+ struct dentry *rc = NULL;
|
||
+ umode_t mode = 0644;
|
||
+
|
||
+ if (!esp_debugfs_root)
|
||
+ return NULL;
|
||
+
|
||
+ if (!parent)
|
||
+ parent = esp_debugfs_root;
|
||
+
|
||
+ switch (type) {
|
||
+ case ESP_U8:
|
||
+ rc = debugfs_create_u8(name, mode, parent, (u8 *) value);
|
||
+ break;
|
||
+ case ESP_U16:
|
||
+ rc = debugfs_create_u16(name, mode, parent, (u16 *) value);
|
||
+ break;
|
||
+ case ESP_U32:
|
||
+ rc = debugfs_create_u32(name, mode, parent, (u32 *) value);
|
||
+ break;
|
||
+ case ESP_U64:
|
||
+ rc = debugfs_create_u64(name, mode, parent, (u64 *) value);
|
||
+ break;
|
||
+ case ESP_BOOL:
|
||
+ rc = debugfs_create_bool(name, mode, parent,
|
||
+ (bool *) value);
|
||
+ break;
|
||
+ default: //32
|
||
+ rc = debugfs_create_u32(name, mode, parent, (u32 *) value);
|
||
+ }
|
||
+
|
||
+ if (!rc)
|
||
+ goto Fail;
|
||
+ else
|
||
+ return rc;
|
||
+ Fail:
|
||
+ debugfs_remove_recursive(esp_debugfs_root);
|
||
+ esp_debugfs_root = NULL;
|
||
+ esp_dbg(ESP_DBG_ERROR,
|
||
+ "%s failed, debugfs root removed; var name: %s\n",
|
||
+ __FUNCTION__, name);
|
||
+ return NULL;
|
||
+}
|
||
+
|
||
+struct dentry *esp_dump_array(const char *name, struct dentry *parent,
|
||
+ struct debugfs_blob_wrapper *blob)
|
||
+{
|
||
+ struct dentry *rc = NULL;
|
||
+ umode_t mode = 0644;
|
||
+
|
||
+ if (!esp_debugfs_root)
|
||
+ return NULL;
|
||
+
|
||
+ if (!parent)
|
||
+ parent = esp_debugfs_root;
|
||
+
|
||
+ rc = debugfs_create_blob(name, mode, parent, blob);
|
||
+
|
||
+ if (!rc)
|
||
+ goto Fail;
|
||
+ else
|
||
+ return rc;
|
||
+
|
||
+ Fail:
|
||
+ debugfs_remove_recursive(esp_debugfs_root);
|
||
+ esp_debugfs_root = NULL;
|
||
+ esp_dbg(ESP_DBG_ERROR,
|
||
+ "%s failed, debugfs root removed; var name: %s\n",
|
||
+ __FUNCTION__, name);
|
||
+ return NULL;
|
||
+}
|
||
+
|
||
+struct dentry *esp_dump(const char *name, struct dentry *parent,
|
||
+ void *data, int size)
|
||
+{
|
||
+ struct dentry *rc;
|
||
+ umode_t mode = 0644;
|
||
+
|
||
+ if (!esp_debugfs_root)
|
||
+ return NULL;
|
||
+
|
||
+ if (!parent)
|
||
+ parent = esp_debugfs_root;
|
||
+
|
||
+ rc = debugfs_create_file(name, mode, parent, data,
|
||
+ &esp_debugfs_fops);
|
||
+
|
||
+ if (!rc)
|
||
+ goto Fail;
|
||
+ else
|
||
+ return rc;
|
||
+
|
||
+ Fail:
|
||
+ debugfs_remove_recursive(esp_debugfs_root);
|
||
+ esp_debugfs_root = NULL;
|
||
+ esp_dbg(ESP_DBG_ERROR,
|
||
+ "%s failed, debugfs root removed; var name: %s\n",
|
||
+ __FUNCTION__, name);
|
||
+ return NULL;
|
||
+}
|
||
+
|
||
+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 000000000..1eeef6472
|
||
--- /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;
|
||
+
|
||
+struct dentry *esp_dump_var(const char *name, struct dentry *parent,
|
||
+ void *value, esp_type type);
|
||
+
|
||
+struct dentry *esp_dump_array(const char *name, struct dentry *parent,
|
||
+ struct debugfs_blob_wrapper *blob);
|
||
+
|
||
+struct dentry *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(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 000000000..541f27a68
|
||
--- /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 000000000..0eeba4d22
|
||
--- /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 000000000..53d90d932
|
||
--- /dev/null
|
||
+++ b/drivers/net/wireless/esp8089/esp_file.c
|
||
@@ -0,0 +1,242 @@
|
||
+/*
|
||
+ * 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 char *modparam_init_data_conf;
|
||
+module_param_named(config, modparam_init_data_conf, charp, 0444);
|
||
+MODULE_PARM_DESC(config, "Firmware init config string (format: key=value;)");
|
||
+
|
||
+struct esp_init_table_elem esp_init_table[MAX_ATTR_NUM] = {
|
||
+ /*
|
||
+ * Crystal type:
|
||
+ * 0: 40MHz (default)
|
||
+ * 1: 26MHz (ESP8266 ESP-12F)
|
||
+ */
|
||
+ {"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},
|
||
+};
|
||
+
|
||
+/* update init config table */
|
||
+static int set_init_config_attr(const char *attr, int attr_len, short value)
|
||
+{
|
||
+ int i;
|
||
+
|
||
+ for (i = 0; i < MAX_ATTR_NUM; i++) {
|
||
+ if (!memcmp(esp_init_table[i].attr, attr, attr_len)) {
|
||
+ if (value < 0 || value > 255) {
|
||
+ esp_dbg(ESP_DBG_ERROR, "%s: attribute value for %s is out of range",
|
||
+ __func__, esp_init_table[i].attr);
|
||
+ return -1;
|
||
+ }
|
||
+ esp_init_table[i].value = value;
|
||
+ return 0;
|
||
+ }
|
||
+ }
|
||
+
|
||
+ return -1;
|
||
+}
|
||
+
|
||
+static int update_init_config_attr(const char *attr, int attr_len,
|
||
+ const char *val, int val_len)
|
||
+{
|
||
+ char digits[4];
|
||
+ short value;
|
||
+ int i;
|
||
+
|
||
+ for (i = 0; i < sizeof(digits) - 1 && i < val_len; i++)
|
||
+ digits[i] = val[i];
|
||
+ digits[i] = 0;
|
||
+
|
||
+ if (kstrtou16(digits, 10, &value) < 0) {
|
||
+ esp_dbg(ESP_DBG_ERROR, "%s: invalid attribute value: %s",
|
||
+ __func__, digits);
|
||
+ return -1;
|
||
+ }
|
||
+
|
||
+ return set_init_config_attr(attr, attr_len, value);
|
||
+}
|
||
+
|
||
+/* export config table settings to SDIO driver */
|
||
+static void record_init_config(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);
|
||
+ }
|
||
+}
|
||
+
|
||
+int request_init_conf(struct device *dev)
|
||
+{
|
||
+ char *attr, *str, *p;
|
||
+ int attr_len, str_len;
|
||
+ int ret = 0;
|
||
+ u32 val;
|
||
+
|
||
+ /* Check for any parameters passed through devicetree (or acpi) */
|
||
+ if (device_property_read_u32(dev, "esp,crystal-26M-en", &val) == 0)
|
||
+ set_init_config_attr("crystal_26M_en", strlen("crystal_26M_en"),
|
||
+ val);
|
||
+
|
||
+ /* parse optional parameter in the form of key1=value,key2=value,.. */
|
||
+ attr = NULL;
|
||
+ attr_len = str_len = 0;
|
||
+ for (p = str = modparam_init_data_conf; p && *p; p++) {
|
||
+ if (*p == '=') {
|
||
+ attr = str;
|
||
+ attr_len = str_len;
|
||
+
|
||
+ str = p + 1;
|
||
+ str_len = 0;
|
||
+ } else if (*p == ',' || *p == ';') {
|
||
+ if (attr_len)
|
||
+ ret |= update_init_config_attr(attr, attr_len,
|
||
+ str, str_len);
|
||
+
|
||
+ str = p + 1;
|
||
+ attr_len = str_len = 0;
|
||
+ } else
|
||
+ str_len++;
|
||
+ }
|
||
+
|
||
+ if (attr_len && str != attr)
|
||
+ ret |= update_init_config_attr(attr, attr_len, str, str_len);
|
||
+
|
||
+ record_init_config();
|
||
+
|
||
+ return ret;
|
||
+}
|
||
+
|
||
+void fix_init_data(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);
|
||
+ }
|
||
+ }
|
||
+
|
||
+}
|
||
diff --git a/drivers/net/wireless/esp8089/esp_file.h b/drivers/net/wireless/esp8089/esp_file.h
|
||
new file mode 100644
|
||
index 000000000..2f085cb81
|
||
--- /dev/null
|
||
+++ b/drivers/net/wireless/esp8089/esp_file.h
|
||
@@ -0,0 +1,40 @@
|
||
+/*
|
||
+ * 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;
|
||
+};
|
||
+
|
||
+int request_init_conf(struct device *dev);
|
||
+void fix_init_data(u8 * init_data_buf, int buf_size);
|
||
+
|
||
+#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 000000000..16f451aff
|
||
--- /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 000000000..6c5c01aad
|
||
--- /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 000000000..0f523fd20
|
||
--- /dev/null
|
||
+++ b/drivers/net/wireless/esp8089/esp_mac80211.c
|
||
@@ -0,0 +1,1714 @@
|
||
+/*
|
||
+ * 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;
|
||
+static void drv_handle_beacon(unsigned long data)
|
||
+{
|
||
+ struct ieee80211_vif *vif = (struct ieee80211_vif *) data;
|
||
+ struct esp_vif *evif = (struct esp_vif *) vif->drv_priv;
|
||
+ 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);
|
||
+
|
||
+ 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();
|
||
+ init_timer(&evif->beacon_timer); //TBD, not init here...
|
||
+ 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);
|
||
+ evif->beacon_timer.data = (unsigned long) vif;
|
||
+ evif->beacon_timer.function = drv_handle_beacon;
|
||
+ 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,
|
||
+ u32 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, info->assoc, info->bssid);
|
||
+ // sdata->u.sta.bssid
|
||
+
|
||
+ ESP_IEEE80211_DBG(ESP_DBG_OP,
|
||
+ " %s enter: changed %x, assoc %x, bssid %pM\n",
|
||
+ __func__, changed, info->assoc, info->bssid);
|
||
+
|
||
+ if (vif->type == NL80211_IFTYPE_STATION) {
|
||
+ if ((changed & BSS_CHANGED_BSSID) ||
|
||
+ ((changed & BSS_CHANGED_ASSOC) && (info->assoc))) {
|
||
+ ESP_IEEE80211_DBG(ESP_DBG_TRACE,
|
||
+ " %s STA change bssid or assoc\n",
|
||
+ __func__);
|
||
+ evif->beacon_interval = info->aid;
|
||
+ memcpy(epub->wl.bssid, (u8 *) info->bssid,
|
||
+ ETH_ALEN);
|
||
+ sip_send_bss_info_update(epub, evif,
|
||
+ (u8 *) info->bssid,
|
||
+ info->assoc);
|
||
+ } else if ((changed & BSS_CHANGED_ASSOC) && (!info->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,
|
||
+ info->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,
|
||
+ 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 = ¶ms->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->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->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 000000000..699b27dca
|
||
--- /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 000000000..421c88d9d
|
||
--- /dev/null
|
||
+++ b/drivers/net/wireless/esp8089/esp_main.c
|
||
@@ -0,0 +1,262 @@
|
||
+/*
|
||
+ * 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 <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 000000000..1ceb14bc3
|
||
--- /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 000000000..7bfa56cf3
|
||
--- /dev/null
|
||
+++ b/drivers/net/wireless/esp8089/esp_pub.h
|
||
@@ -0,0 +1,224 @@
|
||
+/*
|
||
+ * 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"
|
||
+
|
||
+#define NUM_NL80211_BANDS IEEE80211_NUM_BANDS
|
||
+
|
||
+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 4 //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 000000000..2d49f2bc8
|
||
--- /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 000000000..82843f028
|
||
--- /dev/null
|
||
+++ b/drivers/net/wireless/esp8089/esp_sip.c
|
||
@@ -0,0 +1,2397 @@
|
||
+/*
|
||
+ * 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 "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;
|
||
+}
|
||
+
|
||
+static void sip_recalc_credit_timeout(unsigned long data)
|
||
+{
|
||
+ struct esp_sip *sip = (struct esp_sip *) data;
|
||
+
|
||
+ 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
|
||
+
|
||
+ init_timer(&sip->credit_timer);
|
||
+ sip->credit_timer.data = (unsigned long) sip;
|
||
+ sip->credit_timer.function = sip_recalc_credit_timeout;
|
||
+}
|
||
+
|
||
+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) {
|
||
+ rx_status->flag |= RX_FLAG_HT;
|
||
+ rx_status->rate_idx = mac_ctrl->MCS;
|
||
+ if (mac_ctrl->SGI)
|
||
+ rx_status->flag |= RX_FLAG_SHORT_GI;
|
||
+ } else {
|
||
+ rx_status->rate_idx = esp_wmac_rate2idx(mac_ctrl->rate);
|
||
+ }
|
||
+ if (mac_ctrl->rxend_state == RX_FCS_ERR)
|
||
+ rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
|
||
+
|
||
+ /* Mic error frame flag */
|
||
+ if (mac_ctrl->rxend_state == RX_TKIPMIC_ERR
|
||
+ || mac_ctrl->rxend_state == RX_CCMPMIC_ERR) {
|
||
+ if (atomic_read(&sip->epub->wl.tkip_key_set) == 1) {
|
||
+ rx_status->flag |= RX_FLAG_MMIC_ERROR;
|
||
+ atomic_set(&sip->epub->wl.tkip_key_set, 0);
|
||
+ printk("mic err\n");
|
||
+ } else {
|
||
+ printk("mic err discard\n");
|
||
+ }
|
||
+ }
|
||
+ //esp_dbg(ESP_DBG_LOG, "%s freq: %u; signal: %d; rate_idx %d; flag: %d \n", __func__, rx_status->freq, rx_status->signal, rx_status->rate_idx, rx_status->flag);
|
||
+
|
||
+ do {
|
||
+ struct ieee80211_hdr *wh =
|
||
+ (struct ieee80211_hdr *) ((u8 *) skb->data);
|
||
+
|
||
+#ifndef NO_WMM_DUMMY
|
||
+ if (ieee80211_is_mgmt(wh->frame_control))
|
||
+ esp_add_wmm(skb);
|
||
+#endif
|
||
+
|
||
+ /* some kernel e.g. 3.0.8 wrongly handles non-encrypted pkt like eapol */
|
||
+ if (ieee80211_is_data(wh->frame_control)) {
|
||
+ if (!ieee80211_has_protected(wh->frame_control)) {
|
||
+ esp_sip_dbg(ESP_DBG_TRACE,
|
||
+ "%s kiv_war, add iv_stripped flag \n",
|
||
+ __func__);
|
||
+ rx_status->flag |= RX_FLAG_IV_STRIPPED;
|
||
+ } else {
|
||
+ if ((atomic_read(&sip->epub->wl.ptk_cnt) ==
|
||
+ 0 && !(wh->addr1[0] & 0x1))
|
||
+ || (atomic_read(&sip->epub->wl.gtk_cnt)
|
||
+ == 0 && (wh->addr1[0] & 0x1))) {
|
||
+ esp_dbg(ESP_DBG_TRACE,
|
||
+ "%s ==kiv_war, got bogus enc pkt==\n",
|
||
+ __func__);
|
||
+ rx_status->flag |=
|
||
+ RX_FLAG_IV_STRIPPED;
|
||
+ //show_buf(skb->data, 32);
|
||
+ }
|
||
+
|
||
+ esp_sip_dbg(ESP_DBG_TRACE,
|
||
+ "%s kiv_war, got enc pkt \n",
|
||
+ __func__);
|
||
+ }
|
||
+ }
|
||
+ } while (0);
|
||
+
|
||
+ return 0;
|
||
+}
|
||
+
|
||
+static struct esp_mac_rx_ctrl *sip_parse_normal_mac_ctrl(struct sk_buff
|
||
+ *skb,
|
||
+ int *pkt_len_enc,
|
||
+ int *buf_len,
|
||
+ int *pulled_len)
|
||
+{
|
||
+ struct esp_mac_rx_ctrl *mac_ctrl = NULL;
|
||
+ struct sip_hdr *hdr = (struct sip_hdr *) skb->data;
|
||
+ int len_in_hdr = hdr->len;
|
||
+
|
||
+ ESSERT(skb != NULL);
|
||
+ ESSERT(skb->len > SIP_MIN_DATA_PKT_LEN);
|
||
+
|
||
+ skb_pull(skb, sizeof(struct sip_hdr));
|
||
+ *pulled_len += sizeof(struct sip_hdr);
|
||
+ mac_ctrl = (struct esp_mac_rx_ctrl *) skb->data;
|
||
+ if (!mac_ctrl->Aggregation) {
|
||
+ ESSERT(pkt_len_enc != NULL);
|
||
+ ESSERT(buf_len != NULL);
|
||
+ *pkt_len_enc =
|
||
+ (mac_ctrl->sig_mode ? mac_ctrl->HT_length : mac_ctrl->
|
||
+ legacy_length) - FCS_LEN;
|
||
+ *buf_len =
|
||
+ len_in_hdr - sizeof(struct sip_hdr) -
|
||
+ sizeof(struct esp_mac_rx_ctrl);
|
||
+ }
|
||
+ skb_pull(skb, sizeof(struct esp_mac_rx_ctrl));
|
||
+ *pulled_len += sizeof(struct esp_mac_rx_ctrl);
|
||
+
|
||
+ return mac_ctrl;
|
||
+}
|
||
+
|
||
+/*
|
||
+ * for one MPDU (including subframe in AMPDU)
|
||
+ *
|
||
+ */
|
||
+static struct sk_buff *sip_parse_data_rx_info(struct esp_sip *sip,
|
||
+ struct sk_buff *skb,
|
||
+ int pkt_len_enc, int buf_len,
|
||
+ struct esp_mac_rx_ctrl
|
||
+ *mac_ctrl, int *pulled_len)
|
||
+{
|
||
+ /*
|
||
+ * | mac_rx_ctrl | real_data_payload | ampdu_entries |
|
||
+ */
|
||
+ //without enc
|
||
+ int pkt_len = 0;
|
||
+ struct sk_buff *rskb = NULL;
|
||
+ int ret;
|
||
+
|
||
+ if (mac_ctrl->Aggregation) {
|
||
+ struct ieee80211_hdr *wh =
|
||
+ (struct ieee80211_hdr *) skb->data;
|
||
+ pkt_len = pkt_len_enc;
|
||
+ if (ieee80211_has_protected(wh->frame_control)) //ampdu, it is CCMP enc
|
||
+ pkt_len -= 8;
|
||
+ buf_len = roundup(pkt_len, 4);
|
||
+ } else
|
||
+ pkt_len = buf_len - 3 + ((pkt_len_enc - 1) & 0x3);
|
||
+ esp_dbg(ESP_DBG_TRACE,
|
||
+ "%s pkt_len %u, pkt_len_enc %u!, delta %d \n", __func__,
|
||
+ pkt_len, pkt_len_enc, pkt_len_enc - pkt_len);
|
||
+ do {
|
||
+#ifndef NO_WMM_DUMMY
|
||
+ rskb =
|
||
+ __dev_alloc_skb(pkt_len_enc + sizeof(esp_wmm_param) +
|
||
+ 2, GFP_ATOMIC);
|
||
+#else
|
||
+ rskb = __dev_alloc_skb(pkt_len_enc, GFP_ATOMIC);
|
||
+#endif /* NO_WMM_DUMMY */
|
||
+ if (unlikely(rskb == NULL)) {
|
||
+ esp_sip_dbg(ESP_DBG_ERROR, "%s no mem for rskb\n",
|
||
+ __func__);
|
||
+ return NULL;
|
||
+ }
|
||
+ skb_put(rskb, pkt_len_enc);
|
||
+ } while (0);
|
||
+
|
||
+ do {
|
||
+ memcpy(rskb->data, skb->data, pkt_len);
|
||
+ if (pkt_len_enc > pkt_len) {
|
||
+ memset(rskb->data + pkt_len, 0,
|
||
+ pkt_len_enc - pkt_len);
|
||
+ }
|
||
+ /* strip out current pkt, move to the next one */
|
||
+ skb_pull(skb, buf_len);
|
||
+ *pulled_len += buf_len;
|
||
+ } while (0);
|
||
+
|
||
+ ret = sip_parse_mac_rx_info(sip, mac_ctrl, rskb);
|
||
+ if (ret == -1 && !mac_ctrl->Aggregation) {
|
||
+ kfree_skb(rskb);
|
||
+ return NULL;
|
||
+ }
|
||
+
|
||
+ esp_dbg(ESP_DBG_LOG,
|
||
+ "%s after pull headers, skb->len %d rskb->len %d \n",
|
||
+ __func__, skb->len, rskb->len);
|
||
+
|
||
+ return rskb;
|
||
+}
|
||
+
|
||
+struct esp_sip *sip_attach(struct esp_pub *epub)
|
||
+{
|
||
+ struct esp_sip *sip = NULL;
|
||
+ struct sip_pkt *pkt = NULL;
|
||
+ int i;
|
||
+#ifndef ESP_PREALLOC
|
||
+ int po = 0;
|
||
+#endif
|
||
+
|
||
+ sip = kzalloc(sizeof(struct esp_sip), GFP_KERNEL);
|
||
+ if (sip == NULL) {
|
||
+ esp_dbg(ESP_DBG_ERROR, "no mem for sip! \n");
|
||
+ goto _err_sip;
|
||
+ }
|
||
+#ifdef ESP_PREALLOC
|
||
+ sip->tx_aggr_buf = (u8 *) esp_get_tx_aggr_buf();
|
||
+#else
|
||
+ po = get_order(SIP_TX_AGGR_BUF_SIZE);
|
||
+ sip->tx_aggr_buf = (u8 *) __get_free_pages(GFP_ATOMIC, po);
|
||
+#endif
|
||
+ if (sip->tx_aggr_buf == NULL) {
|
||
+ esp_dbg(ESP_DBG_ERROR, "no mem for tx_aggr_buf! \n");
|
||
+ goto _err_aggr;
|
||
+ }
|
||
+
|
||
+ spin_lock_init(&sip->lock);
|
||
+
|
||
+ INIT_LIST_HEAD(&sip->free_ctrl_txbuf);
|
||
+ INIT_LIST_HEAD(&sip->free_ctrl_rxbuf);
|
||
+
|
||
+ for (i = 0; i < SIP_CTRL_BUF_N; i++) {
|
||
+ pkt = kzalloc(sizeof(struct sip_pkt), GFP_KERNEL);
|
||
+
|
||
+ if (!pkt)
|
||
+ goto _err_pkt;
|
||
+
|
||
+ pkt->buf_begin = kzalloc(SIP_CTRL_BUF_SZ, GFP_KERNEL);
|
||
+
|
||
+ if (pkt->buf_begin == NULL) {
|
||
+ kfree(pkt);
|
||
+ pkt = NULL;
|
||
+ goto _err_pkt;
|
||
+ }
|
||
+
|
||
+ pkt->buf_len = SIP_CTRL_BUF_SZ;
|
||
+ pkt->buf = pkt->buf_begin;
|
||
+
|
||
+ if (i < SIP_CTRL_TXBUF_N) {
|
||
+ list_add_tail(&pkt->list, &sip->free_ctrl_txbuf);
|
||
+ } else {
|
||
+ list_add_tail(&pkt->list, &sip->free_ctrl_rxbuf);
|
||
+ }
|
||
+ }
|
||
+
|
||
+ mutex_init(&sip->rx_mtx);
|
||
+ skb_queue_head_init(&sip->rxq);
|
||
+ INIT_WORK(&sip->rx_process_work, sip_rxq_process);
|
||
+
|
||
+ sip->epub = epub;
|
||
+ atomic_set(&sip->noise_floor, -96);
|
||
+
|
||
+ atomic_set(&sip->state, SIP_INIT);
|
||
+ atomic_set(&sip->tx_credits, 0);
|
||
+
|
||
+ if (sip->rawbuf == NULL) {
|
||
+ sip->rawbuf = kzalloc(SIP_BOOT_BUF_SIZE, GFP_KERNEL);
|
||
+ if (sip->rawbuf == NULL) {
|
||
+ esp_dbg(ESP_DBG_ERROR, "no mem for rawbuf! \n");
|
||
+ goto _err_pkt;
|
||
+ }
|
||
+ }
|
||
+
|
||
+ atomic_set(&sip->state, SIP_PREPARE_BOOT);
|
||
+
|
||
+ return sip;
|
||
+
|
||
+ _err_pkt:
|
||
+ sip_free_init_ctrl_buf(sip);
|
||
+
|
||
+ if (sip->tx_aggr_buf) {
|
||
+#ifdef ESP_PREALLOC
|
||
+ esp_put_tx_aggr_buf(&sip->tx_aggr_buf);
|
||
+#else
|
||
+ po = get_order(SIP_TX_AGGR_BUF_SIZE);
|
||
+ free_pages((unsigned long) sip->tx_aggr_buf, po);
|
||
+ sip->tx_aggr_buf = NULL;
|
||
+#endif
|
||
+ }
|
||
+ _err_aggr:
|
||
+ if (sip) {
|
||
+ kfree(sip);
|
||
+ sip = NULL;
|
||
+ }
|
||
+ _err_sip:
|
||
+ return NULL;
|
||
+
|
||
+}
|
||
+
|
||
+static void sip_free_init_ctrl_buf(struct esp_sip *sip)
|
||
+{
|
||
+ struct sip_pkt *pkt, *tpkt;
|
||
+
|
||
+ list_for_each_entry_safe(pkt, tpkt, &sip->free_ctrl_txbuf, list) {
|
||
+ list_del(&pkt->list);
|
||
+ kfree(pkt->buf_begin);
|
||
+ kfree(pkt);
|
||
+ }
|
||
+
|
||
+ list_for_each_entry_safe(pkt, tpkt, &sip->free_ctrl_rxbuf, list) {
|
||
+ list_del(&pkt->list);
|
||
+ kfree(pkt->buf_begin);
|
||
+ kfree(pkt);
|
||
+ }
|
||
+}
|
||
+
|
||
+void sip_detach(struct esp_sip *sip)
|
||
+{
|
||
+#ifndef ESP_PREALLOC
|
||
+ int po;
|
||
+#endif
|
||
+ if (sip == NULL)
|
||
+ return;
|
||
+
|
||
+ sip_free_init_ctrl_buf(sip);
|
||
+
|
||
+ if (atomic_read(&sip->state) == SIP_RUN) {
|
||
+
|
||
+ sif_disable_target_interrupt(sip->epub);
|
||
+
|
||
+ atomic_set(&sip->state, SIP_STOP);
|
||
+
|
||
+ /* disable irq here */
|
||
+ sif_disable_irq(sip->epub);
|
||
+ cancel_work_sync(&sip->rx_process_work);
|
||
+
|
||
+ skb_queue_purge(&sip->rxq);
|
||
+ mutex_destroy(&sip->rx_mtx);
|
||
+ cancel_work_sync(&sip->epub->sendup_work);
|
||
+ skb_queue_purge(&sip->epub->rxq);
|
||
+
|
||
+#ifdef ESP_NO_MAC80211
|
||
+ unregister_netdev(sip->epub->net_dev);
|
||
+ wiphy_unregister(sip->epub->wdev->wiphy);
|
||
+#else
|
||
+ if (test_and_clear_bit
|
||
+ (ESP_WL_FLAG_HW_REGISTERED, &sip->epub->wl.flags)) {
|
||
+ ieee80211_unregister_hw(sip->epub->hw);
|
||
+ }
|
||
+#endif
|
||
+
|
||
+ /* cancel all worker/timer */
|
||
+ cancel_work_sync(&sip->epub->tx_work);
|
||
+ skb_queue_purge(&sip->epub->txq);
|
||
+ skb_queue_purge(&sip->epub->txdoneq);
|
||
+
|
||
+#ifdef ESP_PREALLOC
|
||
+ esp_put_tx_aggr_buf(&sip->tx_aggr_buf);
|
||
+#else
|
||
+ po = get_order(SIP_TX_AGGR_BUF_SIZE);
|
||
+ free_pages((unsigned long) sip->tx_aggr_buf, po);
|
||
+ sip->tx_aggr_buf = NULL;
|
||
+#endif
|
||
+
|
||
+ atomic_set(&sip->state, SIP_INIT);
|
||
+ } else if (atomic_read(&sip->state) >= SIP_BOOT
|
||
+ && atomic_read(&sip->state) <= SIP_WAIT_BOOTUP) {
|
||
+
|
||
+ sif_disable_target_interrupt(sip->epub);
|
||
+ atomic_set(&sip->state, SIP_STOP);
|
||
+ sif_disable_irq(sip->epub);
|
||
+
|
||
+ if (sip->rawbuf)
|
||
+ kfree(sip->rawbuf);
|
||
+
|
||
+ if (atomic_read(&sip->state) == SIP_SEND_INIT) {
|
||
+ cancel_work_sync(&sip->rx_process_work);
|
||
+ skb_queue_purge(&sip->rxq);
|
||
+ mutex_destroy(&sip->rx_mtx);
|
||
+ cancel_work_sync(&sip->epub->sendup_work);
|
||
+ skb_queue_purge(&sip->epub->rxq);
|
||
+ }
|
||
+#ifdef ESP_NO_MAC80211
|
||
+ unregister_netdev(sip->epub->net_dev);
|
||
+ wiphy_unregister(sip->epub->wdev->wiphy);
|
||
+#else
|
||
+ if (test_and_clear_bit
|
||
+ (ESP_WL_FLAG_HW_REGISTERED, &sip->epub->wl.flags)) {
|
||
+ ieee80211_unregister_hw(sip->epub->hw);
|
||
+ }
|
||
+#endif
|
||
+ atomic_set(&sip->state, SIP_INIT);
|
||
+ } else
|
||
+ esp_dbg(ESP_DBG_ERROR, "%s wrong state %d\n", __func__,
|
||
+ atomic_read(&sip->state));
|
||
+
|
||
+ kfree(sip);
|
||
+}
|
||
+
|
||
+int sip_write_memory(struct esp_sip *sip, u32 addr, u8 * buf, u16 len)
|
||
+{
|
||
+ struct sip_cmd_write_memory *cmd;
|
||
+ struct sip_hdr *chdr;
|
||
+ u16 remains, hdrs, bufsize;
|
||
+ u32 loadaddr;
|
||
+ u8 *src;
|
||
+ int err = 0;
|
||
+ u32 *t = NULL;
|
||
+
|
||
+ if (sip == NULL || sip->rawbuf == NULL) {
|
||
+ ESSERT(sip != NULL);
|
||
+ ESSERT(sip->rawbuf != NULL);
|
||
+ return -EINVAL;
|
||
+ }
|
||
+
|
||
+ memset(sip->rawbuf, 0, SIP_BOOT_BUF_SIZE);
|
||
+
|
||
+ chdr = (struct sip_hdr *) sip->rawbuf;
|
||
+ SIP_HDR_SET_TYPE(chdr->fc[0], SIP_CTRL);
|
||
+ chdr->c_cmdid = SIP_CMD_WRITE_MEMORY;
|
||
+
|
||
+ remains = len;
|
||
+ hdrs =
|
||
+ sizeof(struct sip_hdr) + sizeof(struct sip_cmd_write_memory);
|
||
+
|
||
+ while (remains) {
|
||
+ src = &buf[len - remains];
|
||
+ loadaddr = addr + (len - remains);
|
||
+
|
||
+ if (remains < (SIP_BOOT_BUF_SIZE - hdrs)) {
|
||
+ /* aligned with 4 bytes */
|
||
+ bufsize = roundup(remains, 4);
|
||
+ memset(sip->rawbuf + hdrs, 0, bufsize);
|
||
+ remains = 0;
|
||
+ } else {
|
||
+ bufsize = SIP_BOOT_BUF_SIZE - hdrs;
|
||
+ remains -= bufsize;
|
||
+ }
|
||
+
|
||
+ chdr->len = bufsize + hdrs;
|
||
+ chdr->seq = sip->txseq++;
|
||
+ cmd =
|
||
+ (struct sip_cmd_write_memory *) (sip->rawbuf +
|
||
+ SIP_CTRL_HDR_LEN);
|
||
+ cmd->len = bufsize;
|
||
+ cmd->addr = loadaddr;
|
||
+ memcpy(sip->rawbuf + hdrs, src, bufsize);
|
||
+
|
||
+ t = (u32 *) sip->rawbuf;
|
||
+ esp_dbg(ESP_DBG_TRACE,
|
||
+ "%s t0: 0x%08x t1: 0x%08x t2:0x%08x loadaddr 0x%08x \n",
|
||
+ __func__, t[0], t[1], t[2], loadaddr);
|
||
+
|
||
+ err =
|
||
+ esp_common_write(sip->epub, sip->rawbuf, chdr->len,
|
||
+ ESP_SIF_SYNC);
|
||
+
|
||
+ if (err) {
|
||
+ esp_dbg(ESP_DBG_ERROR, "%s send buffer failed\n",
|
||
+ __func__);
|
||
+ return err;
|
||
+ }
|
||
+ // 1ms is enough, in fact on dell-d430, need not delay at all.
|
||
+ mdelay(1);
|
||
+
|
||
+ }
|
||
+
|
||
+ return err;
|
||
+}
|
||
+
|
||
+int sip_send_cmd(struct esp_sip *sip, int cid, u32 cmdlen, void *cmd)
|
||
+{
|
||
+ struct sip_hdr *chdr;
|
||
+ struct sip_pkt *pkt = NULL;
|
||
+ int ret = 0;
|
||
+
|
||
+ pkt = sip_get_ctrl_buf(sip, SIP_TX_CTRL_BUF);
|
||
+
|
||
+ if (pkt == NULL)
|
||
+ return -ENOMEM;
|
||
+
|
||
+ chdr = (struct sip_hdr *) pkt->buf_begin;
|
||
+ chdr->len = SIP_CTRL_HDR_LEN + cmdlen;
|
||
+ chdr->seq = sip->txseq++;
|
||
+ chdr->c_cmdid = cid;
|
||
+
|
||
+
|
||
+ if (cmd) {
|
||
+ memset(pkt->buf, 0, cmdlen);
|
||
+ memcpy(pkt->buf, (u8 *) cmd, cmdlen);
|
||
+ }
|
||
+
|
||
+ esp_dbg(ESP_DBG_TRACE, "cid %d, len %u, seq %u \n", chdr->c_cmdid,
|
||
+ chdr->len, chdr->seq);
|
||
+
|
||
+ esp_dbg(ESP_DBG_TRACE, "c1 0x%08x c2 0x%08x\n",
|
||
+ *(u32 *) & pkt->buf[0], *(u32 *) & pkt->buf[4]);
|
||
+
|
||
+ ret =
|
||
+ esp_common_write(sip->epub, pkt->buf_begin, chdr->len,
|
||
+ ESP_SIF_SYNC);
|
||
+
|
||
+ if (ret)
|
||
+ esp_dbg(ESP_DBG_ERROR, "%s send cmd %d failed \n",
|
||
+ __func__, cid);
|
||
+
|
||
+ sip_reclaim_ctrl_buf(sip, pkt, SIP_TX_CTRL_BUF);
|
||
+
|
||
+ /*
|
||
+ * Hack here: reset tx/rx seq before target ram code is up...
|
||
+ */
|
||
+ if (cid == SIP_CMD_BOOTUP) {
|
||
+ sip->rxseq = 0;
|
||
+ sip->txseq = 0;
|
||
+ sip->txdataseq = 0;
|
||
+ }
|
||
+
|
||
+ return ret;
|
||
+}
|
||
+
|
||
+struct sk_buff *sip_alloc_ctrl_skbuf(struct esp_sip *sip, u16 len, u32 cid)
|
||
+{
|
||
+ struct sip_hdr *si = NULL;
|
||
+ struct ieee80211_tx_info *ti = NULL;
|
||
+ struct sk_buff *skb = NULL;
|
||
+
|
||
+ ESSERT(len <= sip->tx_blksz);
|
||
+
|
||
+ /* no need to reserve space for net stack */
|
||
+ skb = __dev_alloc_skb(len, GFP_KERNEL);
|
||
+
|
||
+ if (skb == NULL) {
|
||
+ esp_dbg(ESP_DBG_ERROR, "no skb for ctrl !\n");
|
||
+ return NULL;
|
||
+ }
|
||
+
|
||
+ skb->len = len;
|
||
+
|
||
+ ti = IEEE80211_SKB_CB(skb);
|
||
+ /* set tx_info flags to 0xffffffff to indicate sip_ctrl pkt */
|
||
+ ti->flags = 0xffffffff;
|
||
+ si = (struct sip_hdr *) skb->data;
|
||
+ memset(si, 0, sizeof(struct sip_hdr));
|
||
+ SIP_HDR_SET_TYPE(si->fc[0], SIP_CTRL);
|
||
+ si->len = len;
|
||
+ si->c_cmdid = cid;
|
||
+
|
||
+ return skb;
|
||
+}
|
||
+
|
||
+void sip_free_ctrl_skbuff(struct esp_sip *sip, struct sk_buff *skb)
|
||
+{
|
||
+ memset(IEEE80211_SKB_CB(skb), 0, sizeof(struct ieee80211_tx_info));
|
||
+ kfree_skb(skb);
|
||
+}
|
||
+
|
||
+static struct sip_pkt *sip_get_ctrl_buf(struct esp_sip *sip,
|
||
+ SIP_BUF_TYPE bftype)
|
||
+{
|
||
+ struct sip_pkt *pkt = NULL;
|
||
+ struct list_head *bflist;
|
||
+ struct sip_hdr *chdr;
|
||
+
|
||
+ bflist =
|
||
+ (bftype ==
|
||
+ SIP_TX_CTRL_BUF) ? &sip->free_ctrl_txbuf : &sip->
|
||
+ free_ctrl_rxbuf;
|
||
+
|
||
+ spin_lock_bh(&sip->lock);
|
||
+
|
||
+ if (list_empty(bflist)) {
|
||
+ spin_unlock_bh(&sip->lock);
|
||
+ return NULL;
|
||
+ }
|
||
+
|
||
+ pkt = list_first_entry(bflist, struct sip_pkt, list);
|
||
+ list_del(&pkt->list);
|
||
+ spin_unlock_bh(&sip->lock);
|
||
+
|
||
+ if (bftype == SIP_TX_CTRL_BUF) {
|
||
+ chdr = (struct sip_hdr *) pkt->buf_begin;
|
||
+ SIP_HDR_SET_TYPE(chdr->fc[0], SIP_CTRL);
|
||
+ pkt->buf = pkt->buf_begin + SIP_CTRL_HDR_LEN;
|
||
+ } else {
|
||
+ pkt->buf = pkt->buf_begin;
|
||
+ }
|
||
+
|
||
+ return pkt;
|
||
+}
|
||
+
|
||
+static void
|
||
+sip_reclaim_ctrl_buf(struct esp_sip *sip, struct sip_pkt *pkt,
|
||
+ SIP_BUF_TYPE bftype)
|
||
+{
|
||
+ struct list_head *bflist = NULL;
|
||
+
|
||
+ if (bftype == SIP_TX_CTRL_BUF)
|
||
+ bflist = &sip->free_ctrl_txbuf;
|
||
+ else if (bftype == SIP_RX_CTRL_BUF)
|
||
+ bflist = &sip->free_ctrl_rxbuf;
|
||
+ else
|
||
+ return;
|
||
+
|
||
+ pkt->buf = pkt->buf_begin;
|
||
+
|
||
+ spin_lock_bh(&sip->lock);
|
||
+ list_add_tail(&pkt->list, bflist);
|
||
+ spin_unlock_bh(&sip->lock);
|
||
+}
|
||
+
|
||
+int sip_poll_bootup_event(struct esp_sip *sip)
|
||
+{
|
||
+ int ret = 0;
|
||
+
|
||
+ esp_dbg(ESP_DBG_TRACE, "polling bootup event... \n");
|
||
+
|
||
+ if (gl_bootup_cplx)
|
||
+ ret = wait_for_completion_timeout(gl_bootup_cplx, 2 * HZ);
|
||
+
|
||
+ esp_dbg(ESP_DBG_TRACE, "******time remain****** = [%d]\n", ret);
|
||
+ if (ret <= 0) {
|
||
+ esp_dbg(ESP_DBG_ERROR, "bootup event timeout\n");
|
||
+ return -ETIMEDOUT;
|
||
+ }
|
||
+
|
||
+ if (sif_get_ate_config() == 0) {
|
||
+ ret = esp_register_mac80211(sip->epub);
|
||
+ }
|
||
+#ifdef TEST_MODE
|
||
+ ret = test_init_netlink(sip);
|
||
+ if (ret < 0) {
|
||
+ esp_sip_dbg(ESP_DBG_TRACE,
|
||
+ "esp_sdio: failed initializing netlink\n");
|
||
+ return ret;
|
||
+ }
|
||
+#endif
|
||
+
|
||
+ atomic_set(&sip->state, SIP_RUN);
|
||
+ esp_dbg(ESP_DBG_TRACE, "target booted up\n");
|
||
+
|
||
+ return ret;
|
||
+}
|
||
+
|
||
+int sip_poll_resetting_event(struct esp_sip *sip)
|
||
+{
|
||
+ int ret = 0;
|
||
+
|
||
+ esp_dbg(ESP_DBG_TRACE, "polling resetting event... \n");
|
||
+
|
||
+ if (gl_bootup_cplx)
|
||
+ ret = wait_for_completion_timeout(gl_bootup_cplx, 10 * HZ);
|
||
+
|
||
+ esp_dbg(ESP_DBG_TRACE, "******time remain****** = [%d]\n", ret);
|
||
+ if (ret <= 0) {
|
||
+ esp_dbg(ESP_DBG_ERROR, "resetting event timeout\n");
|
||
+ return -ETIMEDOUT;
|
||
+ }
|
||
+
|
||
+ esp_dbg(ESP_DBG_TRACE, "target resetting %d %p\n", ret,
|
||
+ gl_bootup_cplx);
|
||
+
|
||
+ return 0;
|
||
+}
|
||
+
|
||
+
|
||
+#ifdef FPGA_DEBUG
|
||
+
|
||
+/* bogus bootup cmd for FPGA debugging */
|
||
+int sip_send_bootup(struct esp_sip *sip)
|
||
+{
|
||
+ int ret;
|
||
+ struct sip_cmd_bootup bootcmd;
|
||
+
|
||
+ esp_dbg(ESP_DBG_LOG, "sending bootup\n");
|
||
+
|
||
+ bootcmd.boot_addr = 0;
|
||
+ ret =
|
||
+ sip_send_cmd(sip, SIP_CMD_BOOTUP,
|
||
+ sizeof(struct sip_cmd_bootup), &bootcmd);
|
||
+
|
||
+ return ret;
|
||
+}
|
||
+
|
||
+#endif /* FPGA_DEBUG */
|
||
+
|
||
+bool sip_queue_need_stop(struct esp_sip * sip)
|
||
+{
|
||
+ return atomic_read(&sip->tx_data_pkt_queued) >=
|
||
+ SIP_STOP_QUEUE_THRESHOLD || (atomic_read(&sip->tx_credits) < 8
|
||
+ && atomic_read(&sip->
|
||
+ tx_data_pkt_queued)
|
||
+ >=
|
||
+ SIP_STOP_QUEUE_THRESHOLD / 4 * 3);
|
||
+}
|
||
+
|
||
+bool sip_queue_may_resume(struct esp_sip * sip)
|
||
+{
|
||
+ return atomic_read(&sip->epub->txq_stopped)
|
||
+ && !test_bit(ESP_WL_FLAG_STOP_TXQ, &sip->epub->wl.flags)
|
||
+ && ((atomic_read(&sip->tx_credits) >= 16
|
||
+ && atomic_read(&sip->tx_data_pkt_queued) <
|
||
+ SIP_RESUME_QUEUE_THRESHOLD * 2)
|
||
+ || atomic_read(&sip->tx_data_pkt_queued) <
|
||
+ SIP_RESUME_QUEUE_THRESHOLD);
|
||
+}
|
||
+
|
||
+int sip_cmd_enqueue(struct esp_sip *sip, struct sk_buff *skb, int prior)
|
||
+{
|
||
+ if (!sip || !sip->epub) {
|
||
+ esp_dbg(ESP_DBG_ERROR, "func %s, sip->epub->txq is NULL\n",
|
||
+ __func__);
|
||
+ return -EINVAL;
|
||
+ }
|
||
+
|
||
+ if (!skb) {
|
||
+ esp_dbg(ESP_DBG_ERROR, "func %s, skb is NULL\n", __func__);
|
||
+ return -EINVAL;
|
||
+ }
|
||
+
|
||
+ if (prior == ENQUEUE_PRIOR_HEAD)
|
||
+ skb_queue_head(&sip->epub->txq, skb);
|
||
+ else
|
||
+ skb_queue_tail(&sip->epub->txq, skb);
|
||
+
|
||
+ if (sif_get_ate_config() == 0) {
|
||
+ ieee80211_queue_work(sip->epub->hw, &sip->epub->tx_work);
|
||
+ } else {
|
||
+ queue_work(sip->epub->esp_wkq, &sip->epub->tx_work);
|
||
+ }
|
||
+ return 0;
|
||
+}
|
||
+
|
||
+void sip_tx_data_pkt_enqueue(struct esp_pub *epub, struct sk_buff *skb)
|
||
+{
|
||
+ if (!epub || !epub->sip) {
|
||
+ if (!epub)
|
||
+ esp_dbg(ESP_DBG_ERROR, "func %s, epub is NULL\n",
|
||
+ __func__);
|
||
+ else
|
||
+ esp_dbg(ESP_DBG_ERROR,
|
||
+ "func %s, epub->sip is NULL\n", __func__);
|
||
+
|
||
+ return;
|
||
+ }
|
||
+ if (!skb) {
|
||
+ esp_dbg(ESP_DBG_ERROR, "func %s, skb is NULL\n", __func__);
|
||
+ return;
|
||
+ }
|
||
+ skb_queue_tail(&epub->txq, skb);
|
||
+ atomic_inc(&epub->sip->tx_data_pkt_queued);
|
||
+ if (sip_queue_need_stop(epub->sip)) {
|
||
+ if (epub->hw) {
|
||
+ ieee80211_stop_queues(epub->hw);
|
||
+ atomic_set(&epub->txq_stopped, true);
|
||
+ }
|
||
+
|
||
+ }
|
||
+}
|
||
+
|
||
+#ifdef FPGA_TXDATA
|
||
+int sip_send_tx_data(struct esp_sip *sip)
|
||
+{
|
||
+ struct sk_buff *skb = NULL;
|
||
+ struct sip_cmd_bss_info_update *bsscmd;
|
||
+
|
||
+ skb =
|
||
+ sip_alloc_ctrl_skbuf(epub->sip,
|
||
+ sizeof(struct sip_cmd_bss_info_update),
|
||
+ SIP_CMD_BSS_INFO_UPDATE);
|
||
+ if (!skb)
|
||
+ return -EINVAL;
|
||
+
|
||
+ bsscmd =
|
||
+ (struct sip_cmd_bss_info_update *) (skb->data +
|
||
+ sizeof(struct
|
||
+ sip_tx_info));
|
||
+ bsscmd->isassoc = (assoc == true) ? 1 : 0;
|
||
+ memcpy(bsscmd->bssid, bssid, ETH_ALEN);
|
||
+ STRACE_SHOW(epub->sip);
|
||
+ return sip_cmd_enqueue(epub->sip, skb, ENQUEUE_PRIOR_TAIL);
|
||
+}
|
||
+#endif /* FPGA_TXDATA */
|
||
diff --git a/drivers/net/wireless/esp8089/esp_sip.h b/drivers/net/wireless/esp8089/esp_sip.h
|
||
new file mode 100644
|
||
index 000000000..95cc42989
|
||
--- /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 000000000..8b188de79
|
||
--- /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 000000000..ed16d9ca0
|
||
--- /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 000000000..481d98841
|
||
--- /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 000000000..e3e62a83d
|
||
--- /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 000000000..72d13cbfc
|
||
--- /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 000000000..0dd35c82a
|
||
--- /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 000000000..c27d93269
|
||
--- /dev/null
|
||
+++ b/drivers/net/wireless/esp8089/sdio_sif_esp.c
|
||
@@ -0,0 +1,810 @@
|
||
+/*
|
||
+ * Copyright (c) 2010 -2013 Espressif System.
|
||
+ *
|
||
+ * sdio serial i/f driver
|
||
+ * - sdio device control routines
|
||
+ * - sync/async DMA/PIO read/write
|
||
+ *
|
||
+ * This program is free software; you can redistribute it and/or modify
|
||
+ * it under the terms of the GNU General Public License as published by
|
||
+ * the Free Software Foundation; either version 2 of the License, or
|
||
+ * (at your option) any later version.
|
||
+ *
|
||
+ * This program is distributed in the hope that it will be useful,
|
||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
+ * GNU General Public License for more details.
|
||
+ */
|
||
+#include <linux/mmc/card.h>
|
||
+#include <linux/mmc/mmc.h>
|
||
+#include <linux/mmc/core.h>
|
||
+#include <linux/mmc/host.h>
|
||
+#include <linux/mmc/sdio_func.h>
|
||
+#include <linux/mmc/sdio_ids.h>
|
||
+#include <linux/mmc/sdio.h>
|
||
+#include <linux/mmc/sd.h>
|
||
+#include <linux/module.h>
|
||
+#include <net/mac80211.h>
|
||
+#include <linux/time.h>
|
||
+#include <linux/pm.h>
|
||
+
|
||
+#include "esp_pub.h"
|
||
+#include "esp_sif.h"
|
||
+#include "esp_sip.h"
|
||
+#include "esp_debug.h"
|
||
+#include "slc_host_register.h"
|
||
+#include "esp_version.h"
|
||
+#include "esp_ctrl.h"
|
||
+#include "esp_file.h"
|
||
+#ifdef USE_EXT_GPIO
|
||
+#include "esp_ext.h"
|
||
+#endif /* USE_EXT_GPIO */
|
||
+
|
||
+/*
|
||
+ * HdG: Note:
|
||
+ * 1) MMC_HAS_FORCE_DETECT_CHANGE is a hack which is set by my sunxi-wip
|
||
+ * tree. FIXME replace with a version check once mmc_force_detect_change()
|
||
+ * is added to the mainline kernel.
|
||
+ * 2) This version does NOT implement keep_power, the dts must mark the
|
||
+ * regulators as regulator-always-on and not use mmc-pwrseq for this stub
|
||
+ * to work.
|
||
+ */
|
||
+#ifndef MMC_HAS_FORCE_DETECT_CHANGE
|
||
+void mmc_force_detect_change(struct mmc_host *host, unsigned long delay,
|
||
+ bool keep_power)
|
||
+{
|
||
+ host->caps &= ~MMC_CAP_NONREMOVABLE;
|
||
+ host->caps |= MMC_CAP_NEEDS_POLL;
|
||
+ mmc_detect_change(host, delay);
|
||
+}
|
||
+#endif
|
||
+
|
||
+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);
|
||
+
|
||
+}
|
||
+
|
||
+#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 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;
|
||
+ struct mmc_host *host = func->card->host;
|
||
+
|
||
+ 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) {
|
||
+ request_init_conf(&func->dev);
|
||
+
|
||
+ 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 */
|
||
+ mmc_force_detect_change(host, msecs_to_jiffies(100), true);
|
||
+ }
|
||
+
|
||
+ return err;
|
||
+
|
||
+ _err_off:
|
||
+ esdio_power_off(sctrl);
|
||
+ _err_ext_gpio:
|
||
+#ifdef USE_EXT_GPIO
|
||
+ if (sif_get_ate_config() == 0)
|
||
+ ext_gpio_deinit();
|
||
+ _err_epub:
|
||
+#endif
|
||
+ esp_pub_dealloc_mac80211(epub);
|
||
+ _err_dma:
|
||
+ kfree(sctrl->dma_buffer);
|
||
+ _err_last:
|
||
+ kfree(sctrl);
|
||
+ _err_first_init:
|
||
+ if (epub && epub->sdio_state == ESP_SDIO_STATE_FIRST_INIT) {
|
||
+ esp_dbg(ESP_DBG_ERROR, "first error exit\n");
|
||
+ epub->sdio_state = ESP_SDIO_STATE_FIRST_ERROR_EXIT;
|
||
+ }
|
||
+ return err;
|
||
+ _err_second_init:
|
||
+ epub->sdio_state = ESP_SDIO_STATE_SECOND_ERROR_EXIT;
|
||
+ esp_sdio_remove(func);
|
||
+ return err;
|
||
+}
|
||
+
|
||
+static void esp_sdio_remove(struct sdio_func *func)
|
||
+{
|
||
+ struct esp_sdio_ctrl *sctrl = NULL;
|
||
+ struct esp_pub *epub = NULL;
|
||
+
|
||
+ esp_dbg(ESP_DBG_TRACE, "%s enter\n", __func__);
|
||
+
|
||
+ sctrl = sdio_get_drvdata(func);
|
||
+
|
||
+ if (sctrl == NULL) {
|
||
+ esp_dbg(ESP_DBG_ERROR, "%s no sctrl\n", __func__);
|
||
+ return;
|
||
+ }
|
||
+
|
||
+ do {
|
||
+ epub = sctrl->epub;
|
||
+ if (epub == NULL) {
|
||
+ esp_dbg(ESP_DBG_ERROR, "%s epub null\n", __func__);
|
||
+ break;
|
||
+ }
|
||
+ if (epub->sdio_state != ESP_SDIO_STATE_FIRST_NORMAL_EXIT) {
|
||
+ if (epub->sip) {
|
||
+ sip_detach(epub->sip);
|
||
+ epub->sip = NULL;
|
||
+ esp_dbg(ESP_DBG_TRACE,
|
||
+ "%s sip detached \n", __func__);
|
||
+ }
|
||
+#ifdef USE_EXT_GPIO
|
||
+ if (sif_get_ate_config() == 0)
|
||
+ ext_gpio_deinit();
|
||
+#endif
|
||
+ } else {
|
||
+ //sif_disable_target_interrupt(epub);
|
||
+ atomic_set(&epub->sip->state, SIP_STOP);
|
||
+ sif_disable_irq(epub);
|
||
+ }
|
||
+
|
||
+ if (epub->sdio_state != ESP_SDIO_STATE_FIRST_NORMAL_EXIT) {
|
||
+ esp_pub_dealloc_mac80211(epub);
|
||
+ esp_dbg(ESP_DBG_TRACE, "%s dealloc mac80211 \n",
|
||
+ __func__);
|
||
+
|
||
+ if (sctrl->dma_buffer) {
|
||
+ kfree(sctrl->dma_buffer);
|
||
+ sctrl->dma_buffer = NULL;
|
||
+ esp_dbg(ESP_DBG_TRACE,
|
||
+ "%s free dma_buffer \n", __func__);
|
||
+ }
|
||
+
|
||
+ kfree(sctrl);
|
||
+ }
|
||
+
|
||
+ } while (0);
|
||
+
|
||
+ sdio_set_drvdata(func, NULL);
|
||
+
|
||
+ esp_dbg(ESP_DBG_TRACE, "eagle sdio remove complete\n");
|
||
+}
|
||
+
|
||
+MODULE_DEVICE_TABLE(sdio, esp_sdio_devices);
|
||
+
|
||
+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,},
|
||
+};
|
||
+
|
||
+static int /*__init*/ esp_sdio_init(void)
|
||
+{
|
||
+ esp_debugfs_init();
|
||
+
|
||
+ 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_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 000000000..d46e87589
|
||
--- /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 000000000..2cdb2c856
|
||
--- /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
|