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