diff --git a/config/kernel/linux-rk322x-current.config b/config/kernel/linux-rk322x-current.config index c7492801e..53ebf4ab1 100644 --- a/config/kernel/linux-rk322x-current.config +++ b/config/kernel/linux-rk322x-current.config @@ -1,6 +1,6 @@ # # Automatically generated file; DO NOT EDIT. -# Linux/arm 5.15.63 Kernel Configuration +# Linux/arm 5.15.71 Kernel Configuration # CONFIG_CC_VERSION_TEXT="arm-linux-gnueabihf-gcc (GNU Toolchain for the A-profile Architecture 8.3-2019.03 (arm-rel-8.36)) 8.3.0" CONFIG_CC_IS_GCC=y @@ -2457,6 +2457,8 @@ CONFIG_USB_ZD1201=m CONFIG_ZD1211RW=m # CONFIG_ZD1211RW_DEBUG is not set CONFIG_WLAN_VENDOR_QUANTENNA=y +CONFIG_ESP8089=m +CONFIG_ESP8089_DEBUG_FS=y # CONFIG_MAC80211_HWSIM is not set CONFIG_USB_NET_RNDIS_WLAN=y CONFIG_VIRT_WIFI=m diff --git a/config/kernel/linux-rk322x-edge.config b/config/kernel/linux-rk322x-edge.config index 75476f43c..92f7ca128 100644 --- a/config/kernel/linux-rk322x-edge.config +++ b/config/kernel/linux-rk322x-edge.config @@ -1,6 +1,6 @@ # # Automatically generated file; DO NOT EDIT. -# Linux/arm 5.19.4 Kernel Configuration +# Linux/arm 5.19.11 Kernel Configuration # CONFIG_CC_VERSION_TEXT="arm-linux-gnueabihf-gcc (GNU Toolchain for the A-profile Architecture 8.3-2019.03 (arm-rel-8.36)) 8.3.0" CONFIG_CC_IS_GCC=y @@ -2507,6 +2507,8 @@ CONFIG_USB_ZD1201=m CONFIG_ZD1211RW=m # CONFIG_ZD1211RW_DEBUG is not set CONFIG_WLAN_VENDOR_QUANTENNA=y +CONFIG_ESP8089=m +CONFIG_ESP8089_DEBUG_FS=y # CONFIG_MAC80211_HWSIM is not set CONFIG_USB_NET_RNDIS_WLAN=y CONFIG_VIRT_WIFI=m @@ -4686,10 +4688,10 @@ CONFIG_HDMI=y # CONFIG_DUMMY_CONSOLE=y CONFIG_FRAMEBUFFER_CONSOLE=y -CONFIG_FRAMEBUFFER_CONSOLE_LEGACY_ACCELERATION=y CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y # CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER is not set +# CONFIG_BOOTSPLASH is not set # end of Console display driver support CONFIG_LOGO=y diff --git a/config/sources/families/rk322x.conf b/config/sources/families/rk322x.conf index 675da18e6..b9307644d 100644 --- a/config/sources/families/rk322x.conf +++ b/config/sources/families/rk322x.conf @@ -153,9 +153,11 @@ family_tweaks_bsp() cp $SRC/packages/bsp/rockchip/asound.conf $destination/etc/ cp $SRC/packages/bsp/rockchip/89-pulseaudio-usb.rules $destination/etc/udev/rules.d - # esp8089 wifi driver modprobe default configuration options + # esp8089 wifi driver modprobe default configuration options, only for legacy kernel mkdir -p $destination/etc/modprobe.d - cp $SRC/packages/bsp/rk322x/esp8089.conf $destination/etc/modprobe.d/esp8089.conf + if [[ "$BRANCH" = "legacy" ]]; then + cp $SRC/packages/bsp/rk322x/esp8089.conf $destination/etc/modprobe.d/esp8089.conf + fi # Board selection script install -m 755 $SRC/packages/bsp/rk322x/rk322x-config $destination/usr/sbin diff --git a/packages/bsp/rk322x/rk322x-config b/packages/bsp/rk322x/rk322x-config index 43fbc4578..3ab510183 100755 --- a/packages/bsp/rk322x/rk322x-config +++ b/packages/bsp/rk322x/rk322x-config @@ -91,14 +91,13 @@ DT_LED_OVERLAYS+=(["led-conf2"]="R329q, MXQ-RK3229") DT_LED_OVERLAYS+=(["led-conf3"]="R28-MXQ") DT_LED_OVERLAYS+=(["led-conf4"]="T066/T066-V2") DT_LED_OVERLAYS+=(["led-conf5"]="AEMS IPB900") -DT_LED_OVERLAYS+=(["led-conf6"]="MXQ_PRO") +DT_LED_OVERLAYS+=(["led-conf6"]="MXQPRO_V72/V73)") DT_LED_OVERLAYS+=(["led-conf7"]="R29_MXQ") DT_LED_OVERLAYS_ORDER=("led-conf-default" "led-conf1" "led-conf2" "led-conf3" "led-conf4" "led-conf5" "led-conf6" "led-conf7") DT_CPU_OVERLAYS+=(["cpu-hs"]="RK3228B or RK3229") -DT_WIFI_OVERLAYS+=(["6666:1111"]="wlan-esp8089") DT_WIFI_OVERLAYS+=(["024c:b703"]="bt-8723cs") DT_DDR_OVERLAYS+=(["default"]="Use DDR2/DDR3 default") diff --git a/patch/kernel/archive/rk322x-5.15/general-add-overlays.patch b/patch/kernel/archive/rk322x-5.15/general-add-overlays.patch index 380b4c20f..5ee7df856 100644 --- a/patch/kernel/archive/rk322x-5.15/general-add-overlays.patch +++ b/patch/kernel/archive/rk322x-5.15/general-add-overlays.patch @@ -1,37 +1,37 @@ -From 00fdb8cff230e2d6592e7eddb661e86a680154d9 Mon Sep 17 00:00:00 2001 +From 700cbde2890adf66b668b2edb941b773d7bf2254 Mon Sep 17 00:00:00 2001 From: Paolo Sabatino -Date: Sun, 28 Aug 2022 14:05:25 +0000 -Subject: [PATCH] add rk322x overlays +Date: Sun, 2 Oct 2022 11:00:19 +0000 +Subject: [PATCH] rk322x device tree overlays --- arch/arm/boot/dts/overlay/Makefile | 39 ++++++ - .../boot/dts/overlay/README.rk322x-overlays | 90 ++++++++++++++ + .../boot/dts/overlay/README.rk322x-overlays | 90 +++++++++++++ .../arm/boot/dts/overlay/rk322x-bt-8723cs.dts | 19 +++ - .../arm/boot/dts/overlay/rk322x-cpu-hs-lv.dts | 68 +++++++++++ - arch/arm/boot/dts/overlay/rk322x-cpu-hs.dts | 28 +++++ + .../arm/boot/dts/overlay/rk322x-cpu-hs-lv.dts | 68 ++++++++++ + arch/arm/boot/dts/overlay/rk322x-cpu-hs.dts | 28 ++++ .../boot/dts/overlay/rk322x-cpu-stability.dts | 52 ++++++++ - arch/arm/boot/dts/overlay/rk322x-ddr3-330.dts | 28 +++++ - arch/arm/boot/dts/overlay/rk322x-ddr3-528.dts | 28 +++++ - arch/arm/boot/dts/overlay/rk322x-ddr3-660.dts | 28 +++++ - arch/arm/boot/dts/overlay/rk322x-ddr3-800.dts | 28 +++++ - .../dts/overlay/rk322x-emmc-ddr-ph180.dts | 14 +++ - .../boot/dts/overlay/rk322x-emmc-ddr-ph45.dts | 14 +++ + arch/arm/boot/dts/overlay/rk322x-ddr3-330.dts | 28 ++++ + arch/arm/boot/dts/overlay/rk322x-ddr3-528.dts | 28 ++++ + arch/arm/boot/dts/overlay/rk322x-ddr3-660.dts | 28 ++++ + arch/arm/boot/dts/overlay/rk322x-ddr3-800.dts | 28 ++++ + .../dts/overlay/rk322x-emmc-ddr-ph180.dts | 14 ++ + .../boot/dts/overlay/rk322x-emmc-ddr-ph45.dts | 14 ++ .../boot/dts/overlay/rk322x-emmc-hs200.dts | 13 ++ - .../arm/boot/dts/overlay/rk322x-emmc-pins.dts | 22 ++++ + .../arm/boot/dts/overlay/rk322x-emmc-pins.dts | 22 +++ arch/arm/boot/dts/overlay/rk322x-emmc.dts | 20 +++ .../arm/boot/dts/overlay/rk322x-fixup.scr-cmd | 4 + - .../dts/overlay/rk322x-led-conf-default.dts | 22 ++++ - .../arm/boot/dts/overlay/rk322x-led-conf1.dts | 64 ++++++++++ - .../arm/boot/dts/overlay/rk322x-led-conf2.dts | 64 ++++++++++ - .../arm/boot/dts/overlay/rk322x-led-conf3.dts | 64 ++++++++++ - .../arm/boot/dts/overlay/rk322x-led-conf4.dts | 81 ++++++++++++ - .../arm/boot/dts/overlay/rk322x-led-conf5.dts | 97 +++++++++++++++ - .../arm/boot/dts/overlay/rk322x-led-conf6.dts | 115 ++++++++++++++++++ - .../arm/boot/dts/overlay/rk322x-led-conf7.dts | 106 ++++++++++++++++ - arch/arm/boot/dts/overlay/rk322x-nand.dts | 22 ++++ + .../dts/overlay/rk322x-led-conf-default.dts | 22 +++ + .../arm/boot/dts/overlay/rk322x-led-conf1.dts | 64 +++++++++ + .../arm/boot/dts/overlay/rk322x-led-conf2.dts | 64 +++++++++ + .../arm/boot/dts/overlay/rk322x-led-conf3.dts | 64 +++++++++ + .../arm/boot/dts/overlay/rk322x-led-conf4.dts | 96 ++++++++++++++ + .../arm/boot/dts/overlay/rk322x-led-conf5.dts | 97 ++++++++++++++ + .../arm/boot/dts/overlay/rk322x-led-conf6.dts | 125 ++++++++++++++++++ + .../arm/boot/dts/overlay/rk322x-led-conf7.dts | 106 +++++++++++++++ + arch/arm/boot/dts/overlay/rk322x-nand.dts | 22 +++ .../dts/overlay/rk322x-usb-otg-peripheral.dts | 11 ++ .../dts/overlay/rk322x-wlan-alt-wiring.dts | 67 ++++++++++ - 27 files changed, 1208 insertions(+) + 27 files changed, 1233 insertions(+) create mode 100755 arch/arm/boot/dts/overlay/Makefile create mode 100755 arch/arm/boot/dts/overlay/README.rk322x-overlays create mode 100644 arch/arm/boot/dts/overlay/rk322x-bt-8723cs.dts @@ -891,10 +891,10 @@ index 000000000000..03c99ef8b6ad +}; diff --git a/arch/arm/boot/dts/overlay/rk322x-led-conf4.dts b/arch/arm/boot/dts/overlay/rk322x-led-conf4.dts new file mode 100644 -index 000000000000..0ebd846ebefb +index 000000000000..37dbe3610f28 --- /dev/null +++ b/arch/arm/boot/dts/overlay/rk322x-led-conf4.dts -@@ -0,0 +1,81 @@ +@@ -0,0 +1,96 @@ +/dts-v1/; +/plugin/; + @@ -975,6 +975,21 @@ index 000000000000..0ebd846ebefb + + }; + ++ fragment@5 { ++ target = <&sdio>; ++ __overlay__ { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ wifi@1 { ++ compatible = "esp,esp8089"; ++ reg = <1>; ++ esp,crystal-26M-en = <0>; ++ }; ++ ++ }; ++ }; ++ +}; diff --git a/arch/arm/boot/dts/overlay/rk322x-led-conf5.dts b/arch/arm/boot/dts/overlay/rk322x-led-conf5.dts new file mode 100755 @@ -1081,10 +1096,10 @@ index 000000000000..f74687eedc9e +}; diff --git a/arch/arm/boot/dts/overlay/rk322x-led-conf6.dts b/arch/arm/boot/dts/overlay/rk322x-led-conf6.dts new file mode 100644 -index 000000000000..045ab24932c4 +index 000000000000..92cc548b0fe3 --- /dev/null +++ b/arch/arm/boot/dts/overlay/rk322x-led-conf6.dts -@@ -0,0 +1,115 @@ +@@ -0,0 +1,125 @@ +/dts-v1/; +/plugin/; + @@ -1186,16 +1201,26 @@ index 000000000000..045ab24932c4 + }; + + fragment@6 { -+ target = <&sdio>; ++ target = <&sdio_pwrseq>; + __overlay__ { -+ max-frequency = <37500000>; ++ post-power-on-delay-ms = <300>; ++ power-off-delay-us = <200000>; ++ reset-gpios = <&gpio2 RK_PD2 GPIO_ACTIVE_LOW>; + }; + }; + + fragment@7 { -+ target = <&sdio_pwrseq>; ++ target = <&sdio>; + __overlay__ { -+ reset-gpios = <&gpio2 RK_PD2 GPIO_ACTIVE_HIGH>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ wifi@1 { ++ compatible = "esp,esp8089"; ++ reg = <1>; ++ esp,crystal-26M-en = <1>; ++ }; ++ + }; + }; + diff --git a/patch/kernel/archive/rk322x-5.15/wifi-4004-esp8089-kernel-driver.patch b/patch/kernel/archive/rk322x-5.15/wifi-4004-esp8089-kernel-driver.patch new file mode 100644 index 000000000..f8049acf0 --- /dev/null +++ b/patch/kernel/archive/rk322x-5.15/wifi-4004-esp8089-kernel-driver.patch @@ -0,0 +1,10917 @@ +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 80b32449978..ee8b47e953c 100644 +--- a/drivers/net/wireless/Makefile ++++ b/drivers/net/wireless/Makefile +@@ -20,6 +20,7 @@ obj-$(CONFIG_WLAN_VENDOR_ST) += st/ + obj-$(CONFIG_WLAN_VENDOR_TI) += ti/ + obj-$(CONFIG_WLAN_VENDOR_ZYDAS) += zydas/ + obj-$(CONFIG_WLAN_VENDOR_QUANTENNA) += quantenna/ ++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->bss_conf.aid; ++ else ++ setstacmd->aid = sta->aid; ++ memcpy(setstacmd->mac, sta->addr, ETH_ALEN); ++ if (set) { ++ if (sta->ht_cap.ht_supported) { ++ if (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20) ++ setstacmd->phymode = ++ ESP_IEEE80211_T_HT20_S; ++ else ++ setstacmd->phymode = ++ ESP_IEEE80211_T_HT20_L; ++ setstacmd->ampdu_factor = sta->ht_cap.ampdu_factor; ++ setstacmd->ampdu_density = ++ sta->ht_cap.ampdu_density; ++ } else { ++ if (sta->supp_rates[NL80211_BAND_2GHZ] & (~(u32) ++ CONF_HW_BIT_RATE_11B_MASK)) ++ { ++ setstacmd->phymode = ESP_IEEE80211_T_OFDM; ++ } else { ++ setstacmd->phymode = ESP_IEEE80211_T_CCK; ++ } ++ } ++ } ++ return sip_cmd_enqueue(epub->sip, skb, ENQUEUE_PRIOR_TAIL); ++} ++ ++int sip_send_recalc_credit(struct esp_pub *epub) ++{ ++ struct sk_buff *skb = NULL; ++ ++ skb = ++ sip_alloc_ctrl_skbuf(epub->sip, 0 + sizeof(struct sip_hdr), ++ SIP_CMD_RECALC_CREDIT); ++ if (!skb) ++ return -ENOMEM; ++ ++ return sip_cmd_enqueue(epub->sip, skb, ENQUEUE_PRIOR_HEAD); ++} ++ ++int sip_cmd(struct esp_pub *epub, enum sip_cmd_id cmd_id, u8 * cmd_buf, ++ u8 cmd_len) ++{ ++ struct sk_buff *skb = NULL; ++ ++ skb = ++ sip_alloc_ctrl_skbuf(epub->sip, ++ cmd_len + sizeof(struct sip_hdr), cmd_id); ++ if (!skb) ++ return -ENOMEM; ++ ++ memcpy(skb->data + sizeof(struct sip_hdr), cmd_buf, cmd_len); ++ ++ return sip_cmd_enqueue(epub->sip, skb, ENQUEUE_PRIOR_TAIL); ++} +diff --git a/drivers/net/wireless/esp8089/esp_ctrl.h b/drivers/net/wireless/esp8089/esp_ctrl.h +new file mode 100644 +index 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,259 @@ ++/* ++ * 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 ++ ++#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); ++ ++ 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, ++ u32 changed) ++{ ++ struct esp_pub *epub = (struct esp_pub *) hw->priv; ++ struct esp_vif *evif = (struct esp_vif *) vif->drv_priv; ++ ++ // ieee80211_bss_conf(include/net/mac80211.h) is included in ieee80211_sub_if_data(net/mac80211/ieee80211_i.h) , does bssid=ieee80211_if_ap's ssid ? ++ // in 2.6.27, ieee80211_sub_if_data has ieee80211_bss_conf while in 2.6.32 ieee80211_sub_if_data don't have ieee80211_bss_conf ++ // in 2.6.27, ieee80211_bss_conf->enable_beacon don't exist, does it mean it support beacon always? ++ // ESP_IEEE80211_DBG(ESP_DBG_OP, " %s enter: vif addr %pM, changed %x, assoc %x, bssid %pM\n", __func__, vif->addr, changed, info->assoc, info->bssid); ++ // sdata->u.sta.bssid ++ ++ ESP_IEEE80211_DBG(ESP_DBG_OP, ++ " %s enter: changed %x, assoc %x, bssid %pM\n", ++ __func__, changed, info->assoc, info->bssid); ++ ++ if (vif->type == NL80211_IFTYPE_STATION) { ++ if ((changed & BSS_CHANGED_BSSID) || ++ ((changed & BSS_CHANGED_ASSOC) && (info->assoc))) { ++ ESP_IEEE80211_DBG(ESP_DBG_TRACE, ++ " %s STA change bssid or assoc\n", ++ __func__); ++ evif->beacon_interval = info->aid; ++ memcpy(epub->wl.bssid, (u8 *) info->bssid, ++ ETH_ALEN); ++ sip_send_bss_info_update(epub, evif, ++ (u8 *) info->bssid, ++ info->assoc); ++ } else if ((changed & BSS_CHANGED_ASSOC) && (!info->assoc)) { ++ ESP_IEEE80211_DBG(ESP_DBG_TRACE, ++ " %s STA change disassoc\n", ++ __func__); ++ evif->beacon_interval = 0; ++ memset(epub->wl.bssid, 0, ETH_ALEN); ++ sip_send_bss_info_update(epub, evif, ++ (u8 *) info->bssid, ++ info->assoc); ++ } else { ++ ESP_IEEE80211_DBG(ESP_DBG_TRACE, ++ "%s wrong mode of STA mode\n", ++ __func__); ++ } ++ } else if (vif->type == NL80211_IFTYPE_AP) { ++ if ((changed & BSS_CHANGED_BEACON_ENABLED) || ++ (changed & BSS_CHANGED_BEACON_INT)) { ++ ESP_IEEE80211_DBG(ESP_DBG_TRACE, ++ " %s AP change enable %d, interval is %d, bssid %pM\n", ++ __func__, info->enable_beacon, ++ info->beacon_int, info->bssid); ++ if (info->enable_beacon && evif->ap_up != true) { ++ evif->beacon_interval = info->beacon_int; ++ init_beacon_timer(vif); ++ sip_send_bss_info_update(epub, evif, ++ (u8 *) info-> ++ bssid, 2); ++ evif->ap_up = true; ++ } else if (!info->enable_beacon && evif->ap_up && ++ !(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL) ++ ) { ++ ESP_IEEE80211_DBG(ESP_DBG_TRACE, ++ " %s AP disable beacon, interval is %d\n", ++ __func__, ++ info->beacon_int); ++ evif->beacon_interval = 0; ++ del_timer_sync(&evif->beacon_timer); ++ sip_send_bss_info_update(epub, evif, ++ (u8 *) info-> ++ bssid, 2); ++ evif->ap_up = false; ++ } ++ } ++ } else { ++ ESP_IEEE80211_DBG(ESP_DBG_ERROR, ++ "%s op mode unspecified\n", __func__); ++ } ++} ++ ++ ++static u64 esp_op_prepare_multicast(struct ieee80211_hw *hw, ++ struct netdev_hw_addr_list *mc_list) ++{ ++ ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s enter \n", __func__); ++ ++ return 0; ++} ++ ++static void esp_op_configure_filter(struct ieee80211_hw *hw, ++ unsigned int changed_flags, ++ unsigned int *total_flags, ++ u64 multicast) ++{ ++ struct esp_pub *epub = (struct esp_pub *) hw->priv; ++ ++ ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s enter \n", __func__); ++ ++ epub->rx_filter = 0; ++ ++ if (*total_flags & FIF_ALLMULTI) ++ epub->rx_filter |= FIF_ALLMULTI; ++ ++ *total_flags = epub->rx_filter; ++} ++ ++static int esp_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, ++ struct ieee80211_vif *vif, ++ struct ieee80211_sta *sta, ++ struct ieee80211_key_conf *key) ++{ ++ u8 i; ++ int ret; ++ struct esp_pub *epub = (struct esp_pub *) hw->priv; ++ struct esp_vif *evif = (struct esp_vif *) vif->drv_priv; ++ u8 ifidx = evif->index; ++ u8 *peer_addr, isvalid; ++ ++ ESP_IEEE80211_DBG(ESP_DBG_OP, ++ "%s enter, flags = %x keyindx = %x cmd = %x mac = %pM cipher = %x\n", ++ __func__, key->flags, key->keyidx, cmd, ++ vif->addr, key->cipher); ++ ++ key->flags = key->flags | IEEE80211_KEY_FLAG_GENERATE_IV; ++ ++ if (sta) { ++ if (memcmp(sta->addr, epub->wl.bssid, ETH_ALEN)) ++ peer_addr = sta->addr; ++ else ++ peer_addr = epub->wl.bssid; ++ } else { ++ peer_addr = epub->wl.bssid; ++ } ++ isvalid = (cmd == SET_KEY) ? 1 : 0; ++ ++ if ((key->flags & IEEE80211_KEY_FLAG_PAIRWISE) ++ || (key->cipher == WLAN_CIPHER_SUITE_WEP40 ++ || key->cipher == WLAN_CIPHER_SUITE_WEP104)) { ++ if (isvalid) { ++ for (i = 0; i < 19; i++) { ++ if (epub->hi_map[i].flag == 0) { ++ epub->hi_map[i].flag = 1; ++ key->hw_key_idx = i + 6; ++ memcpy(epub->hi_map[i].mac, ++ peer_addr, ETH_ALEN); ++ break; ++ } ++ } ++ } else { ++ u8 index = key->hw_key_idx - 6; ++ epub->hi_map[index].flag = 0; ++ memset(epub->hi_map[index].mac, 0, ETH_ALEN); ++ } ++ } else { ++ if (isvalid) { ++ for (i = 0; i < 2; i++) ++ if (epub->low_map[ifidx][i].flag == 0) { ++ epub->low_map[ifidx][i].flag = 1; ++ key->hw_key_idx = ++ i + ifidx * 2 + 2; ++ memcpy(epub->low_map[ifidx][i].mac, ++ peer_addr, ETH_ALEN); ++ break; ++ } ++ } else { ++ u8 index = key->hw_key_idx - 2 - ifidx * 2; ++ epub->low_map[ifidx][index].flag = 0; ++ memset(epub->low_map[ifidx][index].mac, 0, ++ ETH_ALEN); ++ } ++ //key->hw_key_idx = key->keyidx + ifidx * 2 + 1; ++ } ++ ++ if (key->hw_key_idx >= 6) { ++ /*send sub_scan task to target */ ++ //epub->wl.ptk = (cmd==SET_KEY) ? key : NULL; ++ if (isvalid) ++ atomic_inc(&epub->wl.ptk_cnt); ++ else ++ atomic_dec(&epub->wl.ptk_cnt); ++ if (key->cipher == WLAN_CIPHER_SUITE_WEP40 ++ || key->cipher == WLAN_CIPHER_SUITE_WEP104) { ++ if (isvalid) ++ atomic_inc(&epub->wl.gtk_cnt); ++ else ++ atomic_dec(&epub->wl.gtk_cnt); ++ } ++ } else { ++ /*send sub_scan task to target */ ++ if (isvalid) ++ atomic_inc(&epub->wl.gtk_cnt); ++ else ++ atomic_dec(&epub->wl.gtk_cnt); ++ ++ if ((key->cipher == WLAN_CIPHER_SUITE_WEP40 ++ || key->cipher == WLAN_CIPHER_SUITE_WEP104)) { ++ if (isvalid) ++ atomic_inc(&epub->wl.ptk_cnt); ++ else ++ atomic_dec(&epub->wl.ptk_cnt); ++ //epub->wl.ptk = (cmd==SET_KEY) ? key : NULL; ++ } ++ } ++ ++ ret = sip_send_setkey(epub, ifidx, peer_addr, key, isvalid); ++ ++ if ((key->cipher == WLAN_CIPHER_SUITE_TKIP ++ || key->cipher == WLAN_CIPHER_SUITE_TKIP)) { ++ if (ret == 0) ++ atomic_set(&epub->wl.tkip_key_set, 1); ++ } ++ ++ ESP_IEEE80211_DBG(ESP_DBG_OP, "%s exit\n", __func__); ++ return ret; ++} ++ ++static void esp_op_update_tkip_key(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, ++ struct ieee80211_key_conf *conf, ++ struct ieee80211_sta *sta, ++ u32 iv32, u16 * phase1key) ++{ ++ ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s enter \n", __func__); ++ ++} ++ ++void hw_scan_done(struct esp_pub *epub, bool aborted) ++{ ++ cancel_delayed_work_sync(&epub->scan_timeout_work); ++ ++ ESSERT(epub->wl.scan_req != NULL); ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0)) ++ { ++ struct cfg80211_scan_info info = { ++ .aborted = aborted, ++ }; ++ ++ ieee80211_scan_completed(epub->hw, &info); ++ } ++#else ++ ieee80211_scan_completed(epub->hw, aborted); ++#endif ++ if (test_and_clear_bit(ESP_WL_FLAG_STOP_TXQ, &epub->wl.flags)) { ++ sip_trigger_txq_process(epub->sip); ++ } ++} ++ ++static void hw_scan_timeout_report(struct work_struct *work) ++{ ++ struct esp_pub *epub = ++ container_of(work, struct esp_pub, scan_timeout_work.work); ++ bool aborted; ++ ++ ESP_IEEE80211_DBG(ESP_DBG_TRACE, "eagle hw scan done\n"); ++ ++ if (test_and_clear_bit(ESP_WL_FLAG_STOP_TXQ, &epub->wl.flags)) { ++ sip_trigger_txq_process(epub->sip); ++ } ++ /*check if normally complete or aborted like timeout/hw error */ ++ aborted = (epub->wl.scan_req) ? true : false; ++ ++ if (aborted == true) { ++ epub->wl.scan_req = NULL; ++ } ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0)) ++ { ++ struct cfg80211_scan_info info = { ++ .aborted = aborted, ++ }; ++ ++ ieee80211_scan_completed(epub->hw, &info); ++ } ++#else ++ ieee80211_scan_completed(epub->hw, aborted); ++#endif ++} ++ ++static int esp_op_set_rts_threshold(struct ieee80211_hw *hw, u32 value) ++{ ++ ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s enter \n", __func__); ++ ++ return 0; ++} ++ ++static int esp_node_attach(struct ieee80211_hw *hw, u8 ifidx, ++ struct ieee80211_sta *sta) ++{ ++ struct esp_pub *epub = (struct esp_pub *) hw->priv; ++ struct esp_node *node; ++ u8 tidno; ++ struct esp_tx_tid *tid; ++ int i; ++ ++ spin_lock_bh(&epub->tx_ampdu_lock); ++ ++ if (hweight32(epub->enodes_maps[ifidx]) < ESP_PUB_MAX_STA ++ && (i = ffz(epub->enodes_map)) < ESP_PUB_MAX_STA + 1) { ++ epub->enodes_map |= (1 << i); ++ epub->enodes_maps[ifidx] |= (1 << i); ++ node = (struct esp_node *) sta->drv_priv; ++ epub->enodes[i] = node; ++ node->sta = sta; ++ node->ifidx = ifidx; ++ node->index = i; ++ ++ for (tidno = 0, tid = &node->tid[tidno]; ++ tidno < WME_NUM_TID; tidno++) { ++ tid->ssn = 0; ++ tid->cnt = 0; ++ tid->state = ESP_TID_STATE_INIT; ++ } ++ ++ ++ } else { ++ i = -1; ++ } ++ ++ spin_unlock_bh(&epub->tx_ampdu_lock); ++ return i; ++} ++ ++static int esp_node_detach(struct ieee80211_hw *hw, u8 ifidx, ++ struct ieee80211_sta *sta) ++{ ++ struct esp_pub *epub = (struct esp_pub *) hw->priv; ++ u32 map; ++ int i; ++ struct esp_node *node = NULL; ++ ++ spin_lock_bh(&epub->tx_ampdu_lock); ++ map = epub->enodes_maps[ifidx]; ++ while (map != 0) { ++ i = ffs(map) - 1; ++ if (epub->enodes[i]->sta == sta) { ++ epub->enodes[i]->sta = NULL; ++ node = epub->enodes[i]; ++ epub->enodes[i] = NULL; ++ epub->enodes_map &= ~(1 << i); ++ epub->enodes_maps[ifidx] &= ~(1 << i); ++ ++ spin_unlock_bh(&epub->tx_ampdu_lock); ++ return i; ++ } ++ map &= ~(1 << i); ++ } ++ ++ spin_unlock_bh(&epub->tx_ampdu_lock); ++ return -1; ++} ++ ++struct esp_node *esp_get_node_by_addr(struct esp_pub *epub, ++ const u8 * addr) ++{ ++ int i; ++ u32 map; ++ struct esp_node *node = NULL; ++ if (addr == NULL) ++ return NULL; ++ spin_lock_bh(&epub->tx_ampdu_lock); ++ map = epub->enodes_map; ++ while (map != 0) { ++ i = ffs(map) - 1; ++ if (i < 0) { ++ spin_unlock_bh(&epub->tx_ampdu_lock); ++ return NULL; ++ } ++ map &= ~(1 << i); ++ if (memcmp(epub->enodes[i]->sta->addr, addr, ETH_ALEN) == ++ 0) { ++ node = epub->enodes[i]; ++ break; ++ } ++ } ++ ++ spin_unlock_bh(&epub->tx_ampdu_lock); ++ return node; ++} ++ ++struct esp_node *esp_get_node_by_index(struct esp_pub *epub, u8 index) ++{ ++ u32 map; ++ struct esp_node *node = NULL; ++ ++ if (epub == NULL) ++ return NULL; ++ ++ spin_lock_bh(&epub->tx_ampdu_lock); ++ map = epub->enodes_map; ++ if (map & BIT(index)) { ++ node = epub->enodes[index]; ++ } else { ++ spin_unlock_bh(&epub->tx_ampdu_lock); ++ return NULL; ++ } ++ ++ spin_unlock_bh(&epub->tx_ampdu_lock); ++ return node; ++} ++ ++int esp_get_empty_rxampdu(struct esp_pub *epub, const u8 * addr, u8 tid) ++{ ++ int index = -1; ++ if (addr == NULL) ++ return index; ++ spin_lock_bh(&epub->rx_ampdu_lock); ++ if ((index = ffz(epub->rxampdu_map)) < ESP_PUB_MAX_RXAMPDU) { ++ epub->rxampdu_map |= BIT(index); ++ epub->rxampdu_node[index] = ++ esp_get_node_by_addr(epub, addr); ++ epub->rxampdu_tid[index] = tid; ++ } else { ++ index = -1; ++ } ++ spin_unlock_bh(&epub->rx_ampdu_lock); ++ return index; ++} ++ ++int esp_get_exist_rxampdu(struct esp_pub *epub, const u8 * addr, u8 tid) ++{ ++ u8 map; ++ int index = -1; ++ int i; ++ if (addr == NULL) ++ return index; ++ spin_lock_bh(&epub->rx_ampdu_lock); ++ map = epub->rxampdu_map; ++ while (map != 0) { ++ i = ffs(map) - 1; ++ if (i < 0) { ++ spin_unlock_bh(&epub->rx_ampdu_lock); ++ return index; ++ } ++ map &= ~BIT(i); ++ if (epub->rxampdu_tid[i] == tid && ++ memcmp(epub->rxampdu_node[i]->sta->addr, addr, ++ ETH_ALEN) == 0) { ++ index = i; ++ break; ++ } ++ } ++ ++ epub->rxampdu_map &= ~BIT(index); ++ spin_unlock_bh(&epub->rx_ampdu_lock); ++ return index; ++ ++} ++ ++static int esp_op_sta_add(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, ++ struct ieee80211_sta *sta) ++{ ++ struct esp_pub *epub = (struct esp_pub *) hw->priv; ++ struct esp_vif *evif = (struct esp_vif *) vif->drv_priv; ++ int index; ++ ESP_IEEE80211_DBG(ESP_DBG_OP, ++ "%s enter, vif addr %pM, sta addr %pM\n", ++ __func__, vif->addr, sta->addr); ++ index = esp_node_attach(hw, evif->index, sta); ++ ++ if (index < 0) ++ return -1; ++ sip_send_set_sta(epub, evif->index, 1, sta, vif, (u8) index); ++ return 0; ++} ++ ++static int esp_op_sta_remove(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, ++ struct ieee80211_sta *sta) ++{ ++ struct esp_pub *epub = (struct esp_pub *) hw->priv; ++ struct esp_vif *evif = (struct esp_vif *) vif->drv_priv; ++ int index; ++ ++ ESP_IEEE80211_DBG(ESP_DBG_OP, ++ "%s enter, vif addr %pM, sta addr %pM\n", ++ __func__, vif->addr, sta->addr); ++ ++ //remove a connect in target ++ index = esp_node_detach(hw, evif->index, sta); ++ sip_send_set_sta(epub, evif->index, 0, sta, vif, (u8) index); ++ ++ return 0; ++} ++ ++ ++static void esp_op_sta_notify(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, ++ enum sta_notify_cmd cmd, ++ struct ieee80211_sta *sta) ++{ ++ ++ ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s enter \n", __func__); ++ ++ switch (cmd) { ++ case STA_NOTIFY_SLEEP: ++ break; ++ ++ case STA_NOTIFY_AWAKE: ++ break; ++ ++ default: ++ break; ++ } ++} ++ ++ ++static int esp_op_conf_tx(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, ++ u16 queue, ++ const struct ieee80211_tx_queue_params *params) ++{ ++ struct esp_pub *epub = (struct esp_pub *) hw->priv; ++ ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s enter \n", __func__); ++ return sip_send_wmm_params(epub, queue, params); ++} ++ ++static u64 esp_op_get_tsf(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif) ++{ ++ ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s enter \n", __func__); ++ ++ return 0; ++} ++ ++static void esp_op_set_tsf(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, u64 tsf) ++{ ++ ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s enter \n", __func__); ++} ++ ++static void esp_op_reset_tsf(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif) ++{ ++ ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s enter \n", __func__); ++ ++} ++ ++static void esp_op_rfkill_poll(struct ieee80211_hw *hw) ++{ ++ struct esp_pub *epub = (struct esp_pub *) hw->priv; ++ ++ ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s enter \n", __func__); ++ ++ wiphy_rfkill_set_hw_state(hw->wiphy, ++ test_bit(ESP_WL_FLAG_RFKILL, ++ &epub->wl. ++ flags) ? true : false); ++} ++ ++#ifdef HW_SCAN ++static int esp_op_hw_scan(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, ++ struct cfg80211_scan_request *req) ++{ ++ struct esp_pub *epub = (struct esp_pub *) hw->priv; ++ int i, ret; ++ bool scan_often = true; ++ ++ ESP_IEEE80211_DBG(ESP_DBG_OP, "%s\n", __func__); ++ ++ ESP_IEEE80211_DBG(ESP_DBG_TRACE, "scan, %d\n", req->n_ssids); ++ ESP_IEEE80211_DBG(ESP_DBG_TRACE, "scan, len 1:%d,ssid 1:%s\n", ++ req->ssids->ssid_len, ++ req->ssids->ssid_len == ++ 0 ? "" : (char *) req->ssids->ssid); ++ if (req->n_ssids > 1) ++ ESP_IEEE80211_DBG(ESP_DBG_TRACE, ++ "scan, len 2:%d,ssid 2:%s\n", ++ (req->ssids + 1)->ssid_len, ++ (req->ssids + 1)->ssid_len == ++ 0 ? "" : (char *) (req->ssids + ++ 1)->ssid); ++ ++ /*scan_request is keep allocate untill scan_done,record it ++ to split request into multi sdio_cmd */ ++ if (atomic_read(&epub->wl.off)) { ++ esp_dbg(ESP_DBG_ERROR, "%s scan but wl off \n", __func__); ++ return -EPERM; ++ } ++ ++ if (req->n_ssids > 1) { ++ struct cfg80211_ssid *ssid2 = req->ssids + 1; ++ if ((req->ssids->ssid_len > 0 && ssid2->ssid_len > 0) ++ || req->n_ssids > 2) { ++ ESP_IEEE80211_DBG(ESP_DBG_ERROR, ++ "scan ssid num: %d, ssid1:%s, ssid2:%s,not support\n", ++ req->n_ssids, ++ req->ssids->ssid_len == ++ 0 ? "" : (char *) req->ssids-> ++ ssid, ++ ssid2->ssid_len == ++ 0 ? "" : (char *) ssid2->ssid); ++ return -EINVAL; ++ } ++ } ++ ++ epub->wl.scan_req = req; ++ ++ for (i = 0; i < req->n_channels; i++) ++ ESP_IEEE80211_DBG(ESP_DBG_TRACE, "eagle hw_scan freq %d\n", ++ req->channels[i]->center_freq); ++#if 0 ++ for (i = 0; i < req->n_ssids; i++) { ++ if (req->ssids->ssid_len > 0) { ++ req->ssids->ssid[req->ssids->ssid_len] = '\0'; ++ ESP_IEEE80211_DBG(ESP_DBG_TRACE, ++ "scan_ssid %d:%s\n", i, ++ req->ssids->ssid); ++ } ++ } ++#endif ++ ++ /*in connect state, suspend tx data */ ++ if (epub->sip->support_bgscan && ++ test_bit(ESP_WL_FLAG_CONNECT, &epub->wl.flags) && ++ req->n_channels > 0) { ++ ++ scan_often = epub->scan_permit_valid ++ && time_before(jiffies, epub->scan_permit); ++ epub->scan_permit_valid = true; ++ ++ if (!scan_often) { ++/* epub->scan_permit = jiffies + msecs_to_jiffies(900); ++ set_bit(ESP_WL_FLAG_STOP_TXQ, &epub->wl.flags); ++ if (atomic_read(&epub->txq_stopped) == false) { ++ atomic_set(&epub->txq_stopped, true); ++ ieee80211_stop_queues(hw); ++ } ++*/ ++ } else { ++ ESP_IEEE80211_DBG(ESP_DBG_LOG, "scan too often\n"); ++ return -EACCES; ++ } ++ } else { ++ scan_often = false; ++ } ++ ++ /*send sub_scan task to target */ ++ ret = sip_send_scan(epub); ++ ++ if (ret) { ++ ESP_IEEE80211_DBG(ESP_DBG_ERROR, ++ "fail to send scan_cmd\n"); ++ return ret; ++ } else { ++ if (!scan_often) { ++ epub->scan_permit = ++ jiffies + msecs_to_jiffies(900); ++ set_bit(ESP_WL_FLAG_STOP_TXQ, &epub->wl.flags); ++ if (atomic_read(&epub->txq_stopped) == false) { ++ atomic_set(&epub->txq_stopped, true); ++ ieee80211_stop_queues(hw); ++ } ++ /*force scan complete in case target fail to report in time */ ++ ieee80211_queue_delayed_work(hw, ++ &epub-> ++ scan_timeout_work, ++ req->n_channels * HZ / ++ 4); ++ } ++ } ++ ++ return 0; ++} ++ ++static int esp_op_remain_on_channel(struct ieee80211_hw *hw, ++ struct ieee80211_channel *chan, ++ enum nl80211_channel_type channel_type, ++ int duration) ++{ ++ struct esp_pub *epub = (struct esp_pub *) hw->priv; ++ ++ ESP_IEEE80211_DBG(ESP_DBG_OP, ++ "%s enter, center_freq = %d duration = %d\n", ++ __func__, chan->center_freq, duration); ++ sip_send_roc(epub, chan->center_freq, duration); ++ return 0; ++} ++ ++static int esp_op_cancel_remain_on_channel(struct ieee80211_hw *hw) ++{ ++ struct esp_pub *epub = (struct esp_pub *) hw->priv; ++ ++ ESP_IEEE80211_DBG(ESP_DBG_OP, "%s enter \n", __func__); ++ epub->roc_flags = 0; // to disable roc state ++ sip_send_roc(epub, 0, 0); ++ return 0; ++} ++#endif ++ ++void esp_rocdone_process(struct ieee80211_hw *hw, ++ struct sip_evt_roc *report) ++{ ++ struct esp_pub *epub = (struct esp_pub *) hw->priv; ++ ++ ESP_IEEE80211_DBG(ESP_DBG_OP, "%s enter, state = %d is_ok = %d\n", ++ __func__, report->state, report->is_ok); ++ ++ //roc process begin ++ if ((report->state == 1) && (report->is_ok == 1)) { ++ epub->roc_flags = 1; //flags in roc state, to fix channel, not change ++ ieee80211_ready_on_channel(hw); ++ } else if ((report->state == 0) && (report->is_ok == 1)) //roc process timeout ++ { ++ epub->roc_flags = 0; // to disable roc state ++ ieee80211_remain_on_channel_expired(hw); ++ } ++} ++ ++static int esp_op_set_bitrate_mask(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, ++ const struct cfg80211_bitrate_mask ++ *mask) ++{ ++ ESP_IEEE80211_DBG(ESP_DBG_OP, "%s enter \n", __func__); ++ ESP_IEEE80211_DBG(ESP_DBG_OP, "%s vif->macaddr[%pM], mask[%d]\n", ++ __func__, vif->addr, mask->control[0].legacy); ++ ++ return 0; ++} ++ ++void esp_op_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, ++ u32 queues, bool drop) ++{ ++ ++ ESP_IEEE80211_DBG(ESP_DBG_OP, "%s enter \n", __func__); ++ do { ++ ++ struct esp_pub *epub = (struct esp_pub *) hw->priv; ++ unsigned long time = jiffies + msecs_to_jiffies(15); ++ while (atomic_read(&epub->sip->tx_data_pkt_queued)) { ++ if (!time_before(jiffies, time)) { ++ break; ++ } ++ if (sif_get_ate_config() == 0) { ++ ieee80211_queue_work(epub->hw, ++ &epub->tx_work); ++ } else { ++ queue_work(epub->esp_wkq, &epub->tx_work); ++ } ++ //sip_txq_process(epub); ++ } ++ mdelay(10); ++ ++ } while (0); ++} ++ ++static int esp_op_ampdu_action(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, ++ struct ieee80211_ampdu_params *params) ++{ ++ int ret = -EOPNOTSUPP; ++ enum ieee80211_ampdu_mlme_action action = params->action; ++ struct ieee80211_sta *sta = params->sta; ++ u16 tid = params->tid; ++ u16 *ssn = ¶ms->ssn; ++ u8 buf_size = params->buf_size; ++ struct esp_pub *epub = (struct esp_pub *) hw->priv; ++ struct esp_node *node = (struct esp_node *) sta->drv_priv; ++ struct esp_tx_tid *tid_info = &node->tid[tid]; ++ ++ ESP_IEEE80211_DBG(ESP_DBG_OP, "%s enter \n", __func__); ++ switch (action) { ++ case IEEE80211_AMPDU_TX_START: ++ if (mod_support_no_txampdu() || ++ cfg80211_get_chandef_type(&epub->hw->conf.chandef) == ++ NL80211_CHAN_NO_HT || !sta->ht_cap.ht_supported) ++ return ret; ++ ++ //if (vif->p2p || vif->type != NL80211_IFTYPE_STATION) ++ // return ret; ++ ++ ESP_IEEE80211_DBG(ESP_DBG_TRACE, ++ "%s TX START, addr:%pM,tid:%u,state:%d\n", ++ __func__, sta->addr, tid, ++ tid_info->state); ++ spin_lock_bh(&epub->tx_ampdu_lock); ++ ESSERT(tid_info->state == ESP_TID_STATE_TRIGGER); ++ *ssn = tid_info->ssn; ++ tid_info->state = ESP_TID_STATE_PROGRESS; ++ ++ ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); ++ spin_unlock_bh(&epub->tx_ampdu_lock); ++ ret = 0; ++ break; ++ case IEEE80211_AMPDU_TX_STOP_CONT: ++ ESP_IEEE80211_DBG(ESP_DBG_TRACE, ++ "%s TX STOP, addr:%pM,tid:%u,state:%d\n", ++ __func__, sta->addr, tid, ++ tid_info->state); ++ spin_lock_bh(&epub->tx_ampdu_lock); ++ if (tid_info->state == ESP_TID_STATE_WAIT_STOP) ++ tid_info->state = ESP_TID_STATE_STOP; ++ else ++ tid_info->state = ESP_TID_STATE_INIT; ++ ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); ++ spin_unlock_bh(&epub->tx_ampdu_lock); ++ ret = ++ sip_send_ampdu_action(epub, SIP_AMPDU_TX_STOP, ++ sta->addr, tid, node->ifidx, 0); ++ break; ++ case IEEE80211_AMPDU_TX_STOP_FLUSH: ++ case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: ++ if (tid_info->state == ESP_TID_STATE_WAIT_STOP) ++ tid_info->state = ESP_TID_STATE_STOP; ++ else ++ tid_info->state = ESP_TID_STATE_INIT; ++ ret = ++ sip_send_ampdu_action(epub, SIP_AMPDU_TX_STOP, ++ sta->addr, tid, node->ifidx, 0); ++ break; ++ case IEEE80211_AMPDU_TX_OPERATIONAL: ++ ESP_IEEE80211_DBG(ESP_DBG_TRACE, ++ "%s TX OPERATION, addr:%pM,tid:%u,state:%d\n", ++ __func__, sta->addr, tid, ++ tid_info->state); ++ spin_lock_bh(&epub->tx_ampdu_lock); ++ ++ if (tid_info->state != ESP_TID_STATE_PROGRESS) { ++ if (tid_info->state == ESP_TID_STATE_INIT) { ++ printk(KERN_ERR "%s WIFI RESET, IGNORE\n", ++ __func__); ++ spin_unlock_bh(&epub->tx_ampdu_lock); ++ return -ENETRESET; ++ } else { ++ ESSERT(0); ++ } ++ } ++ ++ tid_info->state = ESP_TID_STATE_OPERATIONAL; ++ spin_unlock_bh(&epub->tx_ampdu_lock); ++ ret = ++ sip_send_ampdu_action(epub, SIP_AMPDU_TX_OPERATIONAL, ++ sta->addr, tid, node->ifidx, ++ buf_size); ++ break; ++ case IEEE80211_AMPDU_RX_START: ++ if (mod_support_no_rxampdu() || ++ cfg80211_get_chandef_type(&epub->hw->conf.chandef) == ++ NL80211_CHAN_NO_HT || !sta->ht_cap.ht_supported) ++ return ret; ++ ++ if ((vif->p2p && false) ++ || (vif->type != NL80211_IFTYPE_STATION && false) ++ ) ++ return ret; ++ ESP_IEEE80211_DBG(ESP_DBG_TRACE, ++ "%s RX START %pM tid %u %u\n", __func__, ++ sta->addr, tid, *ssn); ++ ret = ++ sip_send_ampdu_action(epub, SIP_AMPDU_RX_START, ++ sta->addr, tid, *ssn, 64); ++ break; ++ case IEEE80211_AMPDU_RX_STOP: ++ ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s RX STOP %pM tid %u\n", ++ __func__, sta->addr, tid); ++ ret = ++ sip_send_ampdu_action(epub, SIP_AMPDU_RX_STOP, ++ sta->addr, tid, 0, 0); ++ break; ++ default: ++ break; ++ } ++ return ret; ++} ++ ++static void esp_tx_work(struct work_struct *work) ++{ ++ struct esp_pub *epub = container_of(work, struct esp_pub, tx_work); ++ ++ mutex_lock(&epub->tx_mtx); ++ sip_txq_process(epub); ++ mutex_unlock(&epub->tx_mtx); ++} ++ ++static const struct ieee80211_ops esp_mac80211_ops = { ++ .tx = esp_op_tx, ++ .start = esp_op_start, ++ .stop = esp_op_stop, ++#ifdef CONFIG_PM ++ .suspend = esp_op_suspend, ++ .resume = esp_op_resume, ++#endif ++ .add_interface = esp_op_add_interface, ++ .remove_interface = esp_op_remove_interface, ++ .config = esp_op_config, ++ ++ .bss_info_changed = esp_op_bss_info_changed, ++ .prepare_multicast = esp_op_prepare_multicast, ++ .configure_filter = esp_op_configure_filter, ++ .set_key = esp_op_set_key, ++ .update_tkip_key = esp_op_update_tkip_key, ++ //.sched_scan_start = esp_op_sched_scan_start, ++ //.sched_scan_stop = esp_op_sched_scan_stop, ++ .set_rts_threshold = esp_op_set_rts_threshold, ++ .sta_notify = esp_op_sta_notify, ++ .conf_tx = esp_op_conf_tx, ++ .change_interface = esp_op_change_interface, ++ .get_tsf = esp_op_get_tsf, ++ .set_tsf = esp_op_set_tsf, ++ .reset_tsf = esp_op_reset_tsf, ++ .rfkill_poll = esp_op_rfkill_poll, ++#ifdef HW_SCAN ++ .hw_scan = esp_op_hw_scan, ++ .remain_on_channel = esp_op_remain_on_channel, ++ .cancel_remain_on_channel = esp_op_cancel_remain_on_channel, ++#endif ++ .ampdu_action = esp_op_ampdu_action, ++ //.get_survey = esp_op_get_survey, ++ .sta_add = esp_op_sta_add, ++ .sta_remove = esp_op_sta_remove, ++#ifdef CONFIG_NL80211_TESTMODE ++ //CFG80211_TESTMODE_CMD(esp_op_tm_cmd) ++#endif ++ .set_bitrate_mask = esp_op_set_bitrate_mask, ++ .flush = esp_op_flush, ++}; ++ ++struct esp_pub *esp_pub_alloc_mac80211(struct device *dev) ++{ ++ struct ieee80211_hw *hw; ++ struct esp_pub *epub; ++ int ret = 0; ++ ++ hw = ieee80211_alloc_hw(sizeof(struct esp_pub), &esp_mac80211_ops); ++ ++ if (hw == NULL) { ++ esp_dbg(ESP_DBG_ERROR, "ieee80211 can't alloc hw!\n"); ++ ret = -ENOMEM; ++ return ERR_PTR(ret); ++ } ++ hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; ++ ++ epub = hw->priv; ++ memset(epub, 0, sizeof(*epub)); ++ epub->hw = hw; ++ SET_IEEE80211_DEV(hw, dev); ++ epub->dev = dev; ++ ++ skb_queue_head_init(&epub->txq); ++ skb_queue_head_init(&epub->txdoneq); ++ skb_queue_head_init(&epub->rxq); ++ ++ spin_lock_init(&epub->tx_ampdu_lock); ++ spin_lock_init(&epub->rx_ampdu_lock); ++ spin_lock_init(&epub->tx_lock); ++ mutex_init(&epub->tx_mtx); ++ spin_lock_init(&epub->rx_lock); ++ ++ INIT_WORK(&epub->tx_work, esp_tx_work); ++ ++ //epub->esp_wkq = create_freezable_workqueue("esp_wkq"); ++ epub->esp_wkq = create_singlethread_workqueue("esp_wkq"); ++ ++ if (epub->esp_wkq == NULL) { ++ ret = -ENOMEM; ++ return ERR_PTR(ret); ++ } ++ epub->scan_permit_valid = false; ++ INIT_DELAYED_WORK(&epub->scan_timeout_work, ++ hw_scan_timeout_report); ++ ++ return epub; ++} ++ ++ ++int esp_pub_dealloc_mac80211(struct esp_pub *epub) ++{ ++ set_bit(ESP_WL_FLAG_RFKILL, &epub->wl.flags); ++ ++ destroy_workqueue(epub->esp_wkq); ++ mutex_destroy(&epub->tx_mtx); ++ ++#ifdef ESP_NO_MAC80211 ++ free_netdev(epub->net_dev); ++ wiphy_free(epub->wdev->wiphy); ++ kfree(epub->wdev); ++#else ++ if (epub->hw) { ++ ieee80211_free_hw(epub->hw); ++ } ++#endif ++ ++ return 0; ++} ++ ++#if 0 ++static int esp_reg_notifier(struct wiphy *wiphy, ++ struct regulatory_request *request) ++{ ++ struct ieee80211_supported_band *sband; ++ struct ieee80211_channel *ch; ++ int i; ++ ++ ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s enter %d\n", __func__, ++ request->initiator); ++ ++ //TBD ++} ++#endif ++ ++/* 2G band channels */ ++static struct ieee80211_channel esp_channels_2ghz[] = { ++ {.hw_value = 1,.center_freq = 2412,.max_power = 25}, ++ {.hw_value = 2,.center_freq = 2417,.max_power = 25}, ++ {.hw_value = 3,.center_freq = 2422,.max_power = 25}, ++ {.hw_value = 4,.center_freq = 2427,.max_power = 25}, ++ {.hw_value = 5,.center_freq = 2432,.max_power = 25}, ++ {.hw_value = 6,.center_freq = 2437,.max_power = 25}, ++ {.hw_value = 7,.center_freq = 2442,.max_power = 25}, ++ {.hw_value = 8,.center_freq = 2447,.max_power = 25}, ++ {.hw_value = 9,.center_freq = 2452,.max_power = 25}, ++ {.hw_value = 10,.center_freq = 2457,.max_power = 25}, ++ {.hw_value = 11,.center_freq = 2462,.max_power = 25}, ++ {.hw_value = 12,.center_freq = 2467,.max_power = 25}, ++ {.hw_value = 13,.center_freq = 2472,.max_power = 25}, ++ //{ .hw_value = 14, .center_freq = 2484, .max_power = 25 }, ++}; ++ ++/* 11G rate */ ++static struct ieee80211_rate esp_rates_2ghz[] = { ++ { ++ .bitrate = 10, ++ .hw_value = CONF_HW_BIT_RATE_1MBPS, ++ .hw_value_short = CONF_HW_BIT_RATE_1MBPS, ++ }, ++ { ++ .bitrate = 20, ++ .hw_value = CONF_HW_BIT_RATE_2MBPS, ++ .hw_value_short = CONF_HW_BIT_RATE_2MBPS, ++ .flags = IEEE80211_RATE_SHORT_PREAMBLE}, ++ { ++ .bitrate = 55, ++ .hw_value = CONF_HW_BIT_RATE_5_5MBPS, ++ .hw_value_short = CONF_HW_BIT_RATE_5_5MBPS, ++ .flags = IEEE80211_RATE_SHORT_PREAMBLE}, ++ { ++ .bitrate = 110, ++ .hw_value = CONF_HW_BIT_RATE_11MBPS, ++ .hw_value_short = CONF_HW_BIT_RATE_11MBPS, ++ .flags = IEEE80211_RATE_SHORT_PREAMBLE}, ++ { ++ .bitrate = 60, ++ .hw_value = CONF_HW_BIT_RATE_6MBPS, ++ .hw_value_short = CONF_HW_BIT_RATE_6MBPS, ++ }, ++ { ++ .bitrate = 90, ++ .hw_value = CONF_HW_BIT_RATE_9MBPS, ++ .hw_value_short = CONF_HW_BIT_RATE_9MBPS, ++ }, ++ { ++ .bitrate = 120, ++ .hw_value = CONF_HW_BIT_RATE_12MBPS, ++ .hw_value_short = CONF_HW_BIT_RATE_12MBPS, ++ }, ++ { ++ .bitrate = 180, ++ .hw_value = CONF_HW_BIT_RATE_18MBPS, ++ .hw_value_short = CONF_HW_BIT_RATE_18MBPS, ++ }, ++ { ++ .bitrate = 240, ++ .hw_value = CONF_HW_BIT_RATE_24MBPS, ++ .hw_value_short = CONF_HW_BIT_RATE_24MBPS, ++ }, ++ { ++ .bitrate = 360, ++ .hw_value = CONF_HW_BIT_RATE_36MBPS, ++ .hw_value_short = CONF_HW_BIT_RATE_36MBPS, ++ }, ++ { ++ .bitrate = 480, ++ .hw_value = CONF_HW_BIT_RATE_48MBPS, ++ .hw_value_short = CONF_HW_BIT_RATE_48MBPS, ++ }, ++ { ++ .bitrate = 540, ++ .hw_value = CONF_HW_BIT_RATE_54MBPS, ++ .hw_value_short = CONF_HW_BIT_RATE_54MBPS, ++ }, ++}; ++ ++static void esp_pub_init_mac80211(struct esp_pub *epub) ++{ ++ struct ieee80211_hw *hw = epub->hw; ++ ++ static const u32 cipher_suites[] = { ++ WLAN_CIPHER_SUITE_WEP40, ++ WLAN_CIPHER_SUITE_WEP104, ++ WLAN_CIPHER_SUITE_TKIP, ++ WLAN_CIPHER_SUITE_CCMP, ++ }; ++ ++ hw->max_listen_interval = 10; ++ ++ ieee80211_hw_set(hw, SIGNAL_DBM); ++ ieee80211_hw_set(hw, HAS_RATE_CONTROL); ++ ieee80211_hw_set(hw, SUPPORTS_PS); ++ ieee80211_hw_set(hw, AMPDU_AGGREGATION); ++ ieee80211_hw_set(hw, HOST_BROADCAST_PS_BUFFERING); ++ //IEEE80211_HW_PS_NULLFUNC_STACK | ++ //IEEE80211_HW_CONNECTION_MONITOR | ++ //IEEE80211_HW_BEACON_FILTER | ++ //IEEE80211_HW_AMPDU_AGGREGATION | ++ //IEEE80211_HW_REPORTS_TX_ACK_STATUS; ++ hw->max_rx_aggregation_subframes = 0x40; ++ hw->max_tx_aggregation_subframes = 0x40; ++ ++ hw->wiphy->cipher_suites = cipher_suites; ++ hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); ++ hw->wiphy->max_scan_ie_len = ++ epub->sip->tx_blksz - sizeof(struct sip_hdr) - ++ sizeof(struct sip_cmd_scan); ++ ++ /* ONLY station for now, support P2P soon... */ ++ hw->wiphy->interface_modes = ++ BIT(NL80211_IFTYPE_P2P_GO) | ++ BIT(NL80211_IFTYPE_P2P_CLIENT) | ++ BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP); ++ ++ hw->wiphy->max_scan_ssids = 2; ++ //hw->wiphy->max_sched_scan_ssids = 16; ++ //hw->wiphy->max_match_sets = 16; ++ ++ hw->wiphy->max_remain_on_channel_duration = 5000; ++ ++ atomic_set(&epub->wl.off, 1); ++ ++ epub->wl.sbands[NL80211_BAND_2GHZ].band = NL80211_BAND_2GHZ; ++ epub->wl.sbands[NL80211_BAND_2GHZ].channels = esp_channels_2ghz; ++ epub->wl.sbands[NL80211_BAND_2GHZ].bitrates = esp_rates_2ghz; ++ epub->wl.sbands[NL80211_BAND_2GHZ].n_channels = ++ ARRAY_SIZE(esp_channels_2ghz); ++ epub->wl.sbands[NL80211_BAND_2GHZ].n_bitrates = ++ ARRAY_SIZE(esp_rates_2ghz); ++ /*add to support 11n */ ++ epub->wl.sbands[NL80211_BAND_2GHZ].ht_cap.ht_supported = true; ++ epub->wl.sbands[NL80211_BAND_2GHZ].ht_cap.cap = 0x116C; //IEEE80211_HT_CAP_RX_STBC; //IEEE80211_HT_CAP_SGI_20; ++ epub->wl.sbands[NL80211_BAND_2GHZ].ht_cap.ampdu_factor = ++ IEEE80211_HT_MAX_AMPDU_16K; ++ epub->wl.sbands[NL80211_BAND_2GHZ].ht_cap.ampdu_density = ++ IEEE80211_HT_MPDU_DENSITY_NONE; ++ memset(&epub->wl.sbands[NL80211_BAND_2GHZ].ht_cap.mcs, 0, ++ sizeof(epub->wl.sbands[NL80211_BAND_2GHZ].ht_cap.mcs)); ++ epub->wl.sbands[NL80211_BAND_2GHZ].ht_cap.mcs.rx_mask[0] = 0xff; ++ //epub->wl.sbands[NL80211_BAND_2GHZ].ht_cap.mcs.rx_highest = 7; ++ //epub->wl.sbands[NL80211_BAND_2GHZ].ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; ++ ++ /* BAND_5GHZ TBD */ ++ ++ hw->wiphy->bands[NL80211_BAND_2GHZ] = ++ &epub->wl.sbands[NL80211_BAND_2GHZ]; ++ /* BAND_5GHZ TBD */ ++ ++ /*no fragment */ ++ hw->wiphy->frag_threshold = IEEE80211_MAX_FRAG_THRESHOLD; ++ ++ /* handle AC queue in f/w */ ++ hw->queues = 4; ++ hw->max_rates = 4; ++ //hw->wiphy->reg_notifier = esp_reg_notify; ++ ++ hw->vif_data_size = sizeof(struct esp_vif); ++ hw->sta_data_size = sizeof(struct esp_node); ++ ++ //hw->max_rx_aggregation_subframes = 8; ++} ++ ++int esp_register_mac80211(struct esp_pub *epub) ++{ ++ int ret = 0; ++ u8 *wlan_addr; ++ u8 *p2p_addr; ++ int idx; ++ ++ esp_pub_init_mac80211(epub); ++ ++ epub->hw->wiphy->addresses = (struct mac_address *) esp_mac_addr; ++ memcpy(&epub->hw->wiphy->addresses[0], epub->mac_addr, ETH_ALEN); ++ memcpy(&epub->hw->wiphy->addresses[1], epub->mac_addr, ETH_ALEN); ++ wlan_addr = (u8 *) & epub->hw->wiphy->addresses[0]; ++ p2p_addr = (u8 *) & epub->hw->wiphy->addresses[1]; ++ ++ for (idx = 0; idx < 64; idx++) { ++ p2p_addr[0] = wlan_addr[0] | 0x02; ++ p2p_addr[0] ^= idx << 2; ++ if (strncmp(p2p_addr, wlan_addr, 6) != 0) ++ break; ++ } ++ ++ epub->hw->wiphy->n_addresses = 2; ++ ++ ret = ieee80211_register_hw(epub->hw); ++ ++ if (ret < 0) { ++ ESP_IEEE80211_DBG(ESP_DBG_ERROR, ++ "unable to register mac80211 hw: %d\n", ++ ret); ++ return ret; ++ } else { ++#ifdef MAC80211_NO_CHANGE ++ rtnl_lock(); ++ if (epub->hw->wiphy->interface_modes & ++ (BIT(NL80211_IFTYPE_P2P_GO) | ++ BIT(NL80211_IFTYPE_P2P_CLIENT))) { ++ ret = ++ ieee80211_if_add(hw_to_local(epub->hw), ++ "p2p%d", NULL, ++ NL80211_IFTYPE_STATION, NULL); ++ if (ret) ++ wiphy_warn(epub->hw->wiphy, ++ "Failed to add default virtual iface\n"); ++ } ++ ++ rtnl_unlock(); ++#endif ++ } ++ ++ set_bit(ESP_WL_FLAG_HW_REGISTERED, &epub->wl.flags); ++ ++ return ret; ++} ++ ++static u8 getaddr_index(u8 * addr, struct esp_pub *epub) ++{ ++ int i; ++ for (i = 0; i < ESP_PUB_MAX_VIF; i++) ++ if (memcmp ++ (addr, (u8 *) & epub->hw->wiphy->addresses[i], ++ ETH_ALEN) == 0) ++ return i; ++ return ESP_PUB_MAX_VIF; ++} +diff --git a/drivers/net/wireless/esp8089/esp_mac80211.h b/drivers/net/wireless/esp8089/esp_mac80211.h +new file mode 100644 +index 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 4 //for one interface ++#define ESP_PUB_MAX_RXAMPDU 8 //for all interfaces ++ ++enum { ++ ESP_PM_OFF = 0, ++ ESP_PM_TURNING_ON, ++ ESP_PM_ON, ++ ESP_PM_TURNING_OFF, /* Do NOT change the order */ ++}; ++ ++struct esp_ps { ++ u32 dtim_period; ++ u32 max_sleep_period; ++ unsigned long last_config_time; ++ atomic_t state; ++ bool nulldata_pm_on; ++}; ++ ++struct esp_mac_prefix { ++ u8 mac_index; ++ u8 mac_addr_prefix[3]; ++}; ++ ++struct esp_pub { ++ struct device *dev; ++#ifdef ESP_NO_MAC80211 ++ struct net_device *net_dev; ++ struct wireless_dev *wdev; ++ struct net_device_stats *net_stats; ++#else ++ struct ieee80211_hw *hw; ++ struct ieee80211_vif *vif; ++ u8 vif_slot; ++#endif /* ESP_MAC80211 */ ++ ++ void *sif; /* serial interface control block, e.g. sdio */ ++ enum esp_sdio_state sdio_state; ++ struct esp_sip *sip; ++ struct esp_wl wl; ++ struct esp_hw_idx_map hi_map[19]; ++ struct esp_hw_idx_map low_map[ESP_PUB_MAX_VIF][2]; ++ //u32 flags; //flags to represent rfkill switch,start ++ u8 roc_flags; //0: not in remain on channel state, 1: in roc state ++ ++ struct work_struct tx_work; /* attach to ieee80211 workqueue */ ++ /* latest mac80211 has multiple tx queue, but we stick with single queue now */ ++ spinlock_t rx_lock; ++ spinlock_t tx_ampdu_lock; ++ spinlock_t rx_ampdu_lock; ++ spinlock_t tx_lock; ++ struct mutex tx_mtx; ++ struct sk_buff_head txq; ++ atomic_t txq_stopped; ++ ++ struct work_struct sendup_work; /* attach to ieee80211 workqueue */ ++ struct sk_buff_head txdoneq; ++ struct sk_buff_head rxq; ++ ++ struct workqueue_struct *esp_wkq; ++ ++ //u8 bssid[ETH_ALEN]; ++ u8 mac_addr[ETH_ALEN]; ++ ++ u32 rx_filter; ++ unsigned long scan_permit; ++ bool scan_permit_valid; ++ struct delayed_work scan_timeout_work; ++ u32 enodes_map; ++ u8 rxampdu_map; ++ u32 enodes_maps[ESP_PUB_MAX_VIF]; ++ struct esp_node *enodes[ESP_PUB_MAX_STA + 1]; ++ struct esp_node *rxampdu_node[ESP_PUB_MAX_RXAMPDU]; ++ u8 rxampdu_tid[ESP_PUB_MAX_RXAMPDU]; ++ struct esp_ps ps; ++ int enable_int; ++ int wait_reset; ++}; ++ ++typedef struct esp_pub esp_pub_t; ++ ++struct esp_pub *esp_pub_alloc_mac80211(struct device *dev); ++int esp_pub_dealloc_mac80211(struct esp_pub *epub); ++int esp_register_mac80211(struct esp_pub *epub); ++ ++int esp_pub_init_all(struct esp_pub *epub); ++ ++char *mod_eagle_path_get(void); ++ ++void esp_dsr(struct esp_pub *epub); ++void hw_scan_done(struct esp_pub *epub, bool aborted); ++void esp_rocdone_process(struct ieee80211_hw *hw, ++ struct sip_evt_roc *report); ++ ++void esp_ps_config(struct esp_pub *epub, struct esp_ps *ps, bool on); ++ ++struct esp_node *esp_get_node_by_addr(struct esp_pub *epub, ++ const u8 * addr); ++struct esp_node *esp_get_node_by_index(struct esp_pub *epub, u8 index); ++int esp_get_empty_rxampdu(struct esp_pub *epub, const u8 * addr, u8 tid); ++int esp_get_exist_rxampdu(struct esp_pub *epub, const u8 * addr, u8 tid); ++ ++#ifdef TEST_MODE ++int test_init_netlink(struct esp_sip *sip); ++void test_exit_netlink(void); ++void esp_test_cmd_event(u32 cmd_type, char *reply_info); ++void esp_test_init(struct esp_pub *epub); ++#endif ++#endif /* _ESP_PUB_H_ */ +diff --git a/drivers/net/wireless/esp8089/esp_sif.h b/drivers/net/wireless/esp8089/esp_sif.h +new file mode 100644 +index 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->host); ++ 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 + + diff --git a/patch/kernel/archive/rk322x-5.19/general-add-overlays.patch b/patch/kernel/archive/rk322x-5.19/general-add-overlays.patch index 380b4c20f..5ee7df856 100644 --- a/patch/kernel/archive/rk322x-5.19/general-add-overlays.patch +++ b/patch/kernel/archive/rk322x-5.19/general-add-overlays.patch @@ -1,37 +1,37 @@ -From 00fdb8cff230e2d6592e7eddb661e86a680154d9 Mon Sep 17 00:00:00 2001 +From 700cbde2890adf66b668b2edb941b773d7bf2254 Mon Sep 17 00:00:00 2001 From: Paolo Sabatino -Date: Sun, 28 Aug 2022 14:05:25 +0000 -Subject: [PATCH] add rk322x overlays +Date: Sun, 2 Oct 2022 11:00:19 +0000 +Subject: [PATCH] rk322x device tree overlays --- arch/arm/boot/dts/overlay/Makefile | 39 ++++++ - .../boot/dts/overlay/README.rk322x-overlays | 90 ++++++++++++++ + .../boot/dts/overlay/README.rk322x-overlays | 90 +++++++++++++ .../arm/boot/dts/overlay/rk322x-bt-8723cs.dts | 19 +++ - .../arm/boot/dts/overlay/rk322x-cpu-hs-lv.dts | 68 +++++++++++ - arch/arm/boot/dts/overlay/rk322x-cpu-hs.dts | 28 +++++ + .../arm/boot/dts/overlay/rk322x-cpu-hs-lv.dts | 68 ++++++++++ + arch/arm/boot/dts/overlay/rk322x-cpu-hs.dts | 28 ++++ .../boot/dts/overlay/rk322x-cpu-stability.dts | 52 ++++++++ - arch/arm/boot/dts/overlay/rk322x-ddr3-330.dts | 28 +++++ - arch/arm/boot/dts/overlay/rk322x-ddr3-528.dts | 28 +++++ - arch/arm/boot/dts/overlay/rk322x-ddr3-660.dts | 28 +++++ - arch/arm/boot/dts/overlay/rk322x-ddr3-800.dts | 28 +++++ - .../dts/overlay/rk322x-emmc-ddr-ph180.dts | 14 +++ - .../boot/dts/overlay/rk322x-emmc-ddr-ph45.dts | 14 +++ + arch/arm/boot/dts/overlay/rk322x-ddr3-330.dts | 28 ++++ + arch/arm/boot/dts/overlay/rk322x-ddr3-528.dts | 28 ++++ + arch/arm/boot/dts/overlay/rk322x-ddr3-660.dts | 28 ++++ + arch/arm/boot/dts/overlay/rk322x-ddr3-800.dts | 28 ++++ + .../dts/overlay/rk322x-emmc-ddr-ph180.dts | 14 ++ + .../boot/dts/overlay/rk322x-emmc-ddr-ph45.dts | 14 ++ .../boot/dts/overlay/rk322x-emmc-hs200.dts | 13 ++ - .../arm/boot/dts/overlay/rk322x-emmc-pins.dts | 22 ++++ + .../arm/boot/dts/overlay/rk322x-emmc-pins.dts | 22 +++ arch/arm/boot/dts/overlay/rk322x-emmc.dts | 20 +++ .../arm/boot/dts/overlay/rk322x-fixup.scr-cmd | 4 + - .../dts/overlay/rk322x-led-conf-default.dts | 22 ++++ - .../arm/boot/dts/overlay/rk322x-led-conf1.dts | 64 ++++++++++ - .../arm/boot/dts/overlay/rk322x-led-conf2.dts | 64 ++++++++++ - .../arm/boot/dts/overlay/rk322x-led-conf3.dts | 64 ++++++++++ - .../arm/boot/dts/overlay/rk322x-led-conf4.dts | 81 ++++++++++++ - .../arm/boot/dts/overlay/rk322x-led-conf5.dts | 97 +++++++++++++++ - .../arm/boot/dts/overlay/rk322x-led-conf6.dts | 115 ++++++++++++++++++ - .../arm/boot/dts/overlay/rk322x-led-conf7.dts | 106 ++++++++++++++++ - arch/arm/boot/dts/overlay/rk322x-nand.dts | 22 ++++ + .../dts/overlay/rk322x-led-conf-default.dts | 22 +++ + .../arm/boot/dts/overlay/rk322x-led-conf1.dts | 64 +++++++++ + .../arm/boot/dts/overlay/rk322x-led-conf2.dts | 64 +++++++++ + .../arm/boot/dts/overlay/rk322x-led-conf3.dts | 64 +++++++++ + .../arm/boot/dts/overlay/rk322x-led-conf4.dts | 96 ++++++++++++++ + .../arm/boot/dts/overlay/rk322x-led-conf5.dts | 97 ++++++++++++++ + .../arm/boot/dts/overlay/rk322x-led-conf6.dts | 125 ++++++++++++++++++ + .../arm/boot/dts/overlay/rk322x-led-conf7.dts | 106 +++++++++++++++ + arch/arm/boot/dts/overlay/rk322x-nand.dts | 22 +++ .../dts/overlay/rk322x-usb-otg-peripheral.dts | 11 ++ .../dts/overlay/rk322x-wlan-alt-wiring.dts | 67 ++++++++++ - 27 files changed, 1208 insertions(+) + 27 files changed, 1233 insertions(+) create mode 100755 arch/arm/boot/dts/overlay/Makefile create mode 100755 arch/arm/boot/dts/overlay/README.rk322x-overlays create mode 100644 arch/arm/boot/dts/overlay/rk322x-bt-8723cs.dts @@ -891,10 +891,10 @@ index 000000000000..03c99ef8b6ad +}; diff --git a/arch/arm/boot/dts/overlay/rk322x-led-conf4.dts b/arch/arm/boot/dts/overlay/rk322x-led-conf4.dts new file mode 100644 -index 000000000000..0ebd846ebefb +index 000000000000..37dbe3610f28 --- /dev/null +++ b/arch/arm/boot/dts/overlay/rk322x-led-conf4.dts -@@ -0,0 +1,81 @@ +@@ -0,0 +1,96 @@ +/dts-v1/; +/plugin/; + @@ -975,6 +975,21 @@ index 000000000000..0ebd846ebefb + + }; + ++ fragment@5 { ++ target = <&sdio>; ++ __overlay__ { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ wifi@1 { ++ compatible = "esp,esp8089"; ++ reg = <1>; ++ esp,crystal-26M-en = <0>; ++ }; ++ ++ }; ++ }; ++ +}; diff --git a/arch/arm/boot/dts/overlay/rk322x-led-conf5.dts b/arch/arm/boot/dts/overlay/rk322x-led-conf5.dts new file mode 100755 @@ -1081,10 +1096,10 @@ index 000000000000..f74687eedc9e +}; diff --git a/arch/arm/boot/dts/overlay/rk322x-led-conf6.dts b/arch/arm/boot/dts/overlay/rk322x-led-conf6.dts new file mode 100644 -index 000000000000..045ab24932c4 +index 000000000000..92cc548b0fe3 --- /dev/null +++ b/arch/arm/boot/dts/overlay/rk322x-led-conf6.dts -@@ -0,0 +1,115 @@ +@@ -0,0 +1,125 @@ +/dts-v1/; +/plugin/; + @@ -1186,16 +1201,26 @@ index 000000000000..045ab24932c4 + }; + + fragment@6 { -+ target = <&sdio>; ++ target = <&sdio_pwrseq>; + __overlay__ { -+ max-frequency = <37500000>; ++ post-power-on-delay-ms = <300>; ++ power-off-delay-us = <200000>; ++ reset-gpios = <&gpio2 RK_PD2 GPIO_ACTIVE_LOW>; + }; + }; + + fragment@7 { -+ target = <&sdio_pwrseq>; ++ target = <&sdio>; + __overlay__ { -+ reset-gpios = <&gpio2 RK_PD2 GPIO_ACTIVE_HIGH>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ wifi@1 { ++ compatible = "esp,esp8089"; ++ reg = <1>; ++ esp,crystal-26M-en = <1>; ++ }; ++ + }; + }; + diff --git a/patch/kernel/archive/rk322x-5.19/wifi-4004-esp8089-kernel-driver.patch b/patch/kernel/archive/rk322x-5.19/wifi-4004-esp8089-kernel-driver.patch new file mode 100644 index 000000000..d45189a1b --- /dev/null +++ b/patch/kernel/archive/rk322x-5.19/wifi-4004-esp8089-kernel-driver.patch @@ -0,0 +1,10915 @@ +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->bss_conf.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); ++ ++ 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, ++ u32 changed) ++{ ++ struct esp_pub *epub = (struct esp_pub *) hw->priv; ++ struct esp_vif *evif = (struct esp_vif *) vif->drv_priv; ++ ++ // ieee80211_bss_conf(include/net/mac80211.h) is included in ieee80211_sub_if_data(net/mac80211/ieee80211_i.h) , does bssid=ieee80211_if_ap's ssid ? ++ // in 2.6.27, ieee80211_sub_if_data has ieee80211_bss_conf while in 2.6.32 ieee80211_sub_if_data don't have ieee80211_bss_conf ++ // in 2.6.27, ieee80211_bss_conf->enable_beacon don't exist, does it mean it support beacon always? ++ // ESP_IEEE80211_DBG(ESP_DBG_OP, " %s enter: vif addr %pM, changed %x, assoc %x, bssid %pM\n", __func__, vif->addr, changed, info->assoc, info->bssid); ++ // sdata->u.sta.bssid ++ ++ ESP_IEEE80211_DBG(ESP_DBG_OP, ++ " %s enter: changed %x, assoc %x, bssid %pM\n", ++ __func__, changed, info->assoc, info->bssid); ++ ++ if (vif->type == NL80211_IFTYPE_STATION) { ++ if ((changed & BSS_CHANGED_BSSID) || ++ ((changed & BSS_CHANGED_ASSOC) && (info->assoc))) { ++ ESP_IEEE80211_DBG(ESP_DBG_TRACE, ++ " %s STA change bssid or assoc\n", ++ __func__); ++ evif->beacon_interval = info->aid; ++ memcpy(epub->wl.bssid, (u8 *) info->bssid, ++ ETH_ALEN); ++ sip_send_bss_info_update(epub, evif, ++ (u8 *) info->bssid, ++ info->assoc); ++ } else if ((changed & BSS_CHANGED_ASSOC) && (!info->assoc)) { ++ ESP_IEEE80211_DBG(ESP_DBG_TRACE, ++ " %s STA change disassoc\n", ++ __func__); ++ evif->beacon_interval = 0; ++ memset(epub->wl.bssid, 0, ETH_ALEN); ++ sip_send_bss_info_update(epub, evif, ++ (u8 *) info->bssid, ++ info->assoc); ++ } else { ++ ESP_IEEE80211_DBG(ESP_DBG_TRACE, ++ "%s wrong mode of STA mode\n", ++ __func__); ++ } ++ } else if (vif->type == NL80211_IFTYPE_AP) { ++ if ((changed & BSS_CHANGED_BEACON_ENABLED) || ++ (changed & BSS_CHANGED_BEACON_INT)) { ++ ESP_IEEE80211_DBG(ESP_DBG_TRACE, ++ " %s AP change enable %d, interval is %d, bssid %pM\n", ++ __func__, info->enable_beacon, ++ info->beacon_int, info->bssid); ++ if (info->enable_beacon && evif->ap_up != true) { ++ evif->beacon_interval = info->beacon_int; ++ init_beacon_timer(vif); ++ sip_send_bss_info_update(epub, evif, ++ (u8 *) info-> ++ bssid, 2); ++ evif->ap_up = true; ++ } else if (!info->enable_beacon && evif->ap_up && ++ !(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL) ++ ) { ++ ESP_IEEE80211_DBG(ESP_DBG_TRACE, ++ " %s AP disable beacon, interval is %d\n", ++ __func__, ++ info->beacon_int); ++ evif->beacon_interval = 0; ++ del_timer_sync(&evif->beacon_timer); ++ sip_send_bss_info_update(epub, evif, ++ (u8 *) info-> ++ bssid, 2); ++ evif->ap_up = false; ++ } ++ } ++ } else { ++ ESP_IEEE80211_DBG(ESP_DBG_ERROR, ++ "%s op mode unspecified\n", __func__); ++ } ++} ++ ++ ++static u64 esp_op_prepare_multicast(struct ieee80211_hw *hw, ++ struct netdev_hw_addr_list *mc_list) ++{ ++ ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s enter \n", __func__); ++ ++ return 0; ++} ++ ++static void esp_op_configure_filter(struct ieee80211_hw *hw, ++ unsigned int changed_flags, ++ unsigned int *total_flags, ++ u64 multicast) ++{ ++ struct esp_pub *epub = (struct esp_pub *) hw->priv; ++ ++ ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s enter \n", __func__); ++ ++ epub->rx_filter = 0; ++ ++ if (*total_flags & FIF_ALLMULTI) ++ epub->rx_filter |= FIF_ALLMULTI; ++ ++ *total_flags = epub->rx_filter; ++} ++ ++static int esp_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, ++ struct ieee80211_vif *vif, ++ struct ieee80211_sta *sta, ++ struct ieee80211_key_conf *key) ++{ ++ u8 i; ++ int ret; ++ struct esp_pub *epub = (struct esp_pub *) hw->priv; ++ struct esp_vif *evif = (struct esp_vif *) vif->drv_priv; ++ u8 ifidx = evif->index; ++ u8 *peer_addr, isvalid; ++ ++ ESP_IEEE80211_DBG(ESP_DBG_OP, ++ "%s enter, flags = %x keyindx = %x cmd = %x mac = %pM cipher = %x\n", ++ __func__, key->flags, key->keyidx, cmd, ++ vif->addr, key->cipher); ++ ++ key->flags = key->flags | IEEE80211_KEY_FLAG_GENERATE_IV; ++ ++ if (sta) { ++ if (memcmp(sta->addr, epub->wl.bssid, ETH_ALEN)) ++ peer_addr = sta->addr; ++ else ++ peer_addr = epub->wl.bssid; ++ } else { ++ peer_addr = epub->wl.bssid; ++ } ++ isvalid = (cmd == SET_KEY) ? 1 : 0; ++ ++ if ((key->flags & IEEE80211_KEY_FLAG_PAIRWISE) ++ || (key->cipher == WLAN_CIPHER_SUITE_WEP40 ++ || key->cipher == WLAN_CIPHER_SUITE_WEP104)) { ++ if (isvalid) { ++ for (i = 0; i < 19; i++) { ++ if (epub->hi_map[i].flag == 0) { ++ epub->hi_map[i].flag = 1; ++ key->hw_key_idx = i + 6; ++ memcpy(epub->hi_map[i].mac, ++ peer_addr, ETH_ALEN); ++ break; ++ } ++ } ++ } else { ++ u8 index = key->hw_key_idx - 6; ++ epub->hi_map[index].flag = 0; ++ memset(epub->hi_map[index].mac, 0, ETH_ALEN); ++ } ++ } else { ++ if (isvalid) { ++ for (i = 0; i < 2; i++) ++ if (epub->low_map[ifidx][i].flag == 0) { ++ epub->low_map[ifidx][i].flag = 1; ++ key->hw_key_idx = ++ i + ifidx * 2 + 2; ++ memcpy(epub->low_map[ifidx][i].mac, ++ peer_addr, ETH_ALEN); ++ break; ++ } ++ } else { ++ u8 index = key->hw_key_idx - 2 - ifidx * 2; ++ epub->low_map[ifidx][index].flag = 0; ++ memset(epub->low_map[ifidx][index].mac, 0, ++ ETH_ALEN); ++ } ++ //key->hw_key_idx = key->keyidx + ifidx * 2 + 1; ++ } ++ ++ if (key->hw_key_idx >= 6) { ++ /*send sub_scan task to target */ ++ //epub->wl.ptk = (cmd==SET_KEY) ? key : NULL; ++ if (isvalid) ++ atomic_inc(&epub->wl.ptk_cnt); ++ else ++ atomic_dec(&epub->wl.ptk_cnt); ++ if (key->cipher == WLAN_CIPHER_SUITE_WEP40 ++ || key->cipher == WLAN_CIPHER_SUITE_WEP104) { ++ if (isvalid) ++ atomic_inc(&epub->wl.gtk_cnt); ++ else ++ atomic_dec(&epub->wl.gtk_cnt); ++ } ++ } else { ++ /*send sub_scan task to target */ ++ if (isvalid) ++ atomic_inc(&epub->wl.gtk_cnt); ++ else ++ atomic_dec(&epub->wl.gtk_cnt); ++ ++ if ((key->cipher == WLAN_CIPHER_SUITE_WEP40 ++ || key->cipher == WLAN_CIPHER_SUITE_WEP104)) { ++ if (isvalid) ++ atomic_inc(&epub->wl.ptk_cnt); ++ else ++ atomic_dec(&epub->wl.ptk_cnt); ++ //epub->wl.ptk = (cmd==SET_KEY) ? key : NULL; ++ } ++ } ++ ++ ret = sip_send_setkey(epub, ifidx, peer_addr, key, isvalid); ++ ++ if ((key->cipher == WLAN_CIPHER_SUITE_TKIP ++ || key->cipher == WLAN_CIPHER_SUITE_TKIP)) { ++ if (ret == 0) ++ atomic_set(&epub->wl.tkip_key_set, 1); ++ } ++ ++ ESP_IEEE80211_DBG(ESP_DBG_OP, "%s exit\n", __func__); ++ return ret; ++} ++ ++static void esp_op_update_tkip_key(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, ++ struct ieee80211_key_conf *conf, ++ struct ieee80211_sta *sta, ++ u32 iv32, u16 * phase1key) ++{ ++ ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s enter \n", __func__); ++ ++} ++ ++void hw_scan_done(struct esp_pub *epub, bool aborted) ++{ ++ cancel_delayed_work_sync(&epub->scan_timeout_work); ++ ++ ESSERT(epub->wl.scan_req != NULL); ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0)) ++ { ++ struct cfg80211_scan_info info = { ++ .aborted = aborted, ++ }; ++ ++ ieee80211_scan_completed(epub->hw, &info); ++ } ++#else ++ ieee80211_scan_completed(epub->hw, aborted); ++#endif ++ if (test_and_clear_bit(ESP_WL_FLAG_STOP_TXQ, &epub->wl.flags)) { ++ sip_trigger_txq_process(epub->sip); ++ } ++} ++ ++static void hw_scan_timeout_report(struct work_struct *work) ++{ ++ struct esp_pub *epub = ++ container_of(work, struct esp_pub, scan_timeout_work.work); ++ bool aborted; ++ ++ ESP_IEEE80211_DBG(ESP_DBG_TRACE, "eagle hw scan done\n"); ++ ++ if (test_and_clear_bit(ESP_WL_FLAG_STOP_TXQ, &epub->wl.flags)) { ++ sip_trigger_txq_process(epub->sip); ++ } ++ /*check if normally complete or aborted like timeout/hw error */ ++ aborted = (epub->wl.scan_req) ? true : false; ++ ++ if (aborted == true) { ++ epub->wl.scan_req = NULL; ++ } ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0)) ++ { ++ struct cfg80211_scan_info info = { ++ .aborted = aborted, ++ }; ++ ++ ieee80211_scan_completed(epub->hw, &info); ++ } ++#else ++ ieee80211_scan_completed(epub->hw, aborted); ++#endif ++} ++ ++static int esp_op_set_rts_threshold(struct ieee80211_hw *hw, u32 value) ++{ ++ ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s enter \n", __func__); ++ ++ return 0; ++} ++ ++static int esp_node_attach(struct ieee80211_hw *hw, u8 ifidx, ++ struct ieee80211_sta *sta) ++{ ++ struct esp_pub *epub = (struct esp_pub *) hw->priv; ++ struct esp_node *node; ++ u8 tidno; ++ struct esp_tx_tid *tid; ++ int i; ++ ++ spin_lock_bh(&epub->tx_ampdu_lock); ++ ++ if (hweight32(epub->enodes_maps[ifidx]) < ESP_PUB_MAX_STA ++ && (i = ffz(epub->enodes_map)) < ESP_PUB_MAX_STA + 1) { ++ epub->enodes_map |= (1 << i); ++ epub->enodes_maps[ifidx] |= (1 << i); ++ node = (struct esp_node *) sta->drv_priv; ++ epub->enodes[i] = node; ++ node->sta = sta; ++ node->ifidx = ifidx; ++ node->index = i; ++ ++ for (tidno = 0, tid = &node->tid[tidno]; ++ tidno < WME_NUM_TID; tidno++) { ++ tid->ssn = 0; ++ tid->cnt = 0; ++ tid->state = ESP_TID_STATE_INIT; ++ } ++ ++ ++ } else { ++ i = -1; ++ } ++ ++ spin_unlock_bh(&epub->tx_ampdu_lock); ++ return i; ++} ++ ++static int esp_node_detach(struct ieee80211_hw *hw, u8 ifidx, ++ struct ieee80211_sta *sta) ++{ ++ struct esp_pub *epub = (struct esp_pub *) hw->priv; ++ u32 map; ++ int i; ++ struct esp_node *node = NULL; ++ ++ spin_lock_bh(&epub->tx_ampdu_lock); ++ map = epub->enodes_maps[ifidx]; ++ while (map != 0) { ++ i = ffs(map) - 1; ++ if (epub->enodes[i]->sta == sta) { ++ epub->enodes[i]->sta = NULL; ++ node = epub->enodes[i]; ++ epub->enodes[i] = NULL; ++ epub->enodes_map &= ~(1 << i); ++ epub->enodes_maps[ifidx] &= ~(1 << i); ++ ++ spin_unlock_bh(&epub->tx_ampdu_lock); ++ return i; ++ } ++ map &= ~(1 << i); ++ } ++ ++ spin_unlock_bh(&epub->tx_ampdu_lock); ++ return -1; ++} ++ ++struct esp_node *esp_get_node_by_addr(struct esp_pub *epub, ++ const u8 * addr) ++{ ++ int i; ++ u32 map; ++ struct esp_node *node = NULL; ++ if (addr == NULL) ++ return NULL; ++ spin_lock_bh(&epub->tx_ampdu_lock); ++ map = epub->enodes_map; ++ while (map != 0) { ++ i = ffs(map) - 1; ++ if (i < 0) { ++ spin_unlock_bh(&epub->tx_ampdu_lock); ++ return NULL; ++ } ++ map &= ~(1 << i); ++ if (memcmp(epub->enodes[i]->sta->addr, addr, ETH_ALEN) == ++ 0) { ++ node = epub->enodes[i]; ++ break; ++ } ++ } ++ ++ spin_unlock_bh(&epub->tx_ampdu_lock); ++ return node; ++} ++ ++struct esp_node *esp_get_node_by_index(struct esp_pub *epub, u8 index) ++{ ++ u32 map; ++ struct esp_node *node = NULL; ++ ++ if (epub == NULL) ++ return NULL; ++ ++ spin_lock_bh(&epub->tx_ampdu_lock); ++ map = epub->enodes_map; ++ if (map & BIT(index)) { ++ node = epub->enodes[index]; ++ } else { ++ spin_unlock_bh(&epub->tx_ampdu_lock); ++ return NULL; ++ } ++ ++ spin_unlock_bh(&epub->tx_ampdu_lock); ++ return node; ++} ++ ++int esp_get_empty_rxampdu(struct esp_pub *epub, const u8 * addr, u8 tid) ++{ ++ int index = -1; ++ if (addr == NULL) ++ return index; ++ spin_lock_bh(&epub->rx_ampdu_lock); ++ if ((index = ffz(epub->rxampdu_map)) < ESP_PUB_MAX_RXAMPDU) { ++ epub->rxampdu_map |= BIT(index); ++ epub->rxampdu_node[index] = ++ esp_get_node_by_addr(epub, addr); ++ epub->rxampdu_tid[index] = tid; ++ } else { ++ index = -1; ++ } ++ spin_unlock_bh(&epub->rx_ampdu_lock); ++ return index; ++} ++ ++int esp_get_exist_rxampdu(struct esp_pub *epub, const u8 * addr, u8 tid) ++{ ++ u8 map; ++ int index = -1; ++ int i; ++ if (addr == NULL) ++ return index; ++ spin_lock_bh(&epub->rx_ampdu_lock); ++ map = epub->rxampdu_map; ++ while (map != 0) { ++ i = ffs(map) - 1; ++ if (i < 0) { ++ spin_unlock_bh(&epub->rx_ampdu_lock); ++ return index; ++ } ++ map &= ~BIT(i); ++ if (epub->rxampdu_tid[i] == tid && ++ memcmp(epub->rxampdu_node[i]->sta->addr, addr, ++ ETH_ALEN) == 0) { ++ index = i; ++ break; ++ } ++ } ++ ++ epub->rxampdu_map &= ~BIT(index); ++ spin_unlock_bh(&epub->rx_ampdu_lock); ++ return index; ++ ++} ++ ++static int esp_op_sta_add(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, ++ struct ieee80211_sta *sta) ++{ ++ struct esp_pub *epub = (struct esp_pub *) hw->priv; ++ struct esp_vif *evif = (struct esp_vif *) vif->drv_priv; ++ int index; ++ ESP_IEEE80211_DBG(ESP_DBG_OP, ++ "%s enter, vif addr %pM, sta addr %pM\n", ++ __func__, vif->addr, sta->addr); ++ index = esp_node_attach(hw, evif->index, sta); ++ ++ if (index < 0) ++ return -1; ++ sip_send_set_sta(epub, evif->index, 1, sta, vif, (u8) index); ++ return 0; ++} ++ ++static int esp_op_sta_remove(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, ++ struct ieee80211_sta *sta) ++{ ++ struct esp_pub *epub = (struct esp_pub *) hw->priv; ++ struct esp_vif *evif = (struct esp_vif *) vif->drv_priv; ++ int index; ++ ++ ESP_IEEE80211_DBG(ESP_DBG_OP, ++ "%s enter, vif addr %pM, sta addr %pM\n", ++ __func__, vif->addr, sta->addr); ++ ++ //remove a connect in target ++ index = esp_node_detach(hw, evif->index, sta); ++ sip_send_set_sta(epub, evif->index, 0, sta, vif, (u8) index); ++ ++ return 0; ++} ++ ++ ++static void esp_op_sta_notify(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, ++ enum sta_notify_cmd cmd, ++ struct ieee80211_sta *sta) ++{ ++ ++ ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s enter \n", __func__); ++ ++ switch (cmd) { ++ case STA_NOTIFY_SLEEP: ++ break; ++ ++ case STA_NOTIFY_AWAKE: ++ break; ++ ++ default: ++ break; ++ } ++} ++ ++ ++static int esp_op_conf_tx(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, ++ u16 queue, ++ const struct ieee80211_tx_queue_params *params) ++{ ++ struct esp_pub *epub = (struct esp_pub *) hw->priv; ++ ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s enter \n", __func__); ++ return sip_send_wmm_params(epub, queue, params); ++} ++ ++static u64 esp_op_get_tsf(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif) ++{ ++ ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s enter \n", __func__); ++ ++ return 0; ++} ++ ++static void esp_op_set_tsf(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, u64 tsf) ++{ ++ ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s enter \n", __func__); ++} ++ ++static void esp_op_reset_tsf(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif) ++{ ++ ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s enter \n", __func__); ++ ++} ++ ++static void esp_op_rfkill_poll(struct ieee80211_hw *hw) ++{ ++ struct esp_pub *epub = (struct esp_pub *) hw->priv; ++ ++ ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s enter \n", __func__); ++ ++ wiphy_rfkill_set_hw_state(hw->wiphy, ++ test_bit(ESP_WL_FLAG_RFKILL, ++ &epub->wl. ++ flags) ? true : false); ++} ++ ++#ifdef HW_SCAN ++static int esp_op_hw_scan(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, ++ struct cfg80211_scan_request *req) ++{ ++ struct esp_pub *epub = (struct esp_pub *) hw->priv; ++ int i, ret; ++ bool scan_often = true; ++ ++ ESP_IEEE80211_DBG(ESP_DBG_OP, "%s\n", __func__); ++ ++ ESP_IEEE80211_DBG(ESP_DBG_TRACE, "scan, %d\n", req->n_ssids); ++ ESP_IEEE80211_DBG(ESP_DBG_TRACE, "scan, len 1:%d,ssid 1:%s\n", ++ req->ssids->ssid_len, ++ req->ssids->ssid_len == ++ 0 ? "" : (char *) req->ssids->ssid); ++ if (req->n_ssids > 1) ++ ESP_IEEE80211_DBG(ESP_DBG_TRACE, ++ "scan, len 2:%d,ssid 2:%s\n", ++ (req->ssids + 1)->ssid_len, ++ (req->ssids + 1)->ssid_len == ++ 0 ? "" : (char *) (req->ssids + ++ 1)->ssid); ++ ++ /*scan_request is keep allocate untill scan_done,record it ++ to split request into multi sdio_cmd */ ++ if (atomic_read(&epub->wl.off)) { ++ esp_dbg(ESP_DBG_ERROR, "%s scan but wl off \n", __func__); ++ return -EPERM; ++ } ++ ++ if (req->n_ssids > 1) { ++ struct cfg80211_ssid *ssid2 = req->ssids + 1; ++ if ((req->ssids->ssid_len > 0 && ssid2->ssid_len > 0) ++ || req->n_ssids > 2) { ++ ESP_IEEE80211_DBG(ESP_DBG_ERROR, ++ "scan ssid num: %d, ssid1:%s, ssid2:%s,not support\n", ++ req->n_ssids, ++ req->ssids->ssid_len == ++ 0 ? "" : (char *) req->ssids-> ++ ssid, ++ ssid2->ssid_len == ++ 0 ? "" : (char *) ssid2->ssid); ++ return -EINVAL; ++ } ++ } ++ ++ epub->wl.scan_req = req; ++ ++ for (i = 0; i < req->n_channels; i++) ++ ESP_IEEE80211_DBG(ESP_DBG_TRACE, "eagle hw_scan freq %d\n", ++ req->channels[i]->center_freq); ++#if 0 ++ for (i = 0; i < req->n_ssids; i++) { ++ if (req->ssids->ssid_len > 0) { ++ req->ssids->ssid[req->ssids->ssid_len] = '\0'; ++ ESP_IEEE80211_DBG(ESP_DBG_TRACE, ++ "scan_ssid %d:%s\n", i, ++ req->ssids->ssid); ++ } ++ } ++#endif ++ ++ /*in connect state, suspend tx data */ ++ if (epub->sip->support_bgscan && ++ test_bit(ESP_WL_FLAG_CONNECT, &epub->wl.flags) && ++ req->n_channels > 0) { ++ ++ scan_often = epub->scan_permit_valid ++ && time_before(jiffies, epub->scan_permit); ++ epub->scan_permit_valid = true; ++ ++ if (!scan_often) { ++/* epub->scan_permit = jiffies + msecs_to_jiffies(900); ++ set_bit(ESP_WL_FLAG_STOP_TXQ, &epub->wl.flags); ++ if (atomic_read(&epub->txq_stopped) == false) { ++ atomic_set(&epub->txq_stopped, true); ++ ieee80211_stop_queues(hw); ++ } ++*/ ++ } else { ++ ESP_IEEE80211_DBG(ESP_DBG_LOG, "scan too often\n"); ++ return -EACCES; ++ } ++ } else { ++ scan_often = false; ++ } ++ ++ /*send sub_scan task to target */ ++ ret = sip_send_scan(epub); ++ ++ if (ret) { ++ ESP_IEEE80211_DBG(ESP_DBG_ERROR, ++ "fail to send scan_cmd\n"); ++ return ret; ++ } else { ++ if (!scan_often) { ++ epub->scan_permit = ++ jiffies + msecs_to_jiffies(900); ++ set_bit(ESP_WL_FLAG_STOP_TXQ, &epub->wl.flags); ++ if (atomic_read(&epub->txq_stopped) == false) { ++ atomic_set(&epub->txq_stopped, true); ++ ieee80211_stop_queues(hw); ++ } ++ /*force scan complete in case target fail to report in time */ ++ ieee80211_queue_delayed_work(hw, ++ &epub-> ++ scan_timeout_work, ++ req->n_channels * HZ / ++ 4); ++ } ++ } ++ ++ return 0; ++} ++ ++static int esp_op_remain_on_channel(struct ieee80211_hw *hw, ++ struct ieee80211_channel *chan, ++ enum nl80211_channel_type channel_type, ++ int duration) ++{ ++ struct esp_pub *epub = (struct esp_pub *) hw->priv; ++ ++ ESP_IEEE80211_DBG(ESP_DBG_OP, ++ "%s enter, center_freq = %d duration = %d\n", ++ __func__, chan->center_freq, duration); ++ sip_send_roc(epub, chan->center_freq, duration); ++ return 0; ++} ++ ++static int esp_op_cancel_remain_on_channel(struct ieee80211_hw *hw) ++{ ++ struct esp_pub *epub = (struct esp_pub *) hw->priv; ++ ++ ESP_IEEE80211_DBG(ESP_DBG_OP, "%s enter \n", __func__); ++ epub->roc_flags = 0; // to disable roc state ++ sip_send_roc(epub, 0, 0); ++ return 0; ++} ++#endif ++ ++void esp_rocdone_process(struct ieee80211_hw *hw, ++ struct sip_evt_roc *report) ++{ ++ struct esp_pub *epub = (struct esp_pub *) hw->priv; ++ ++ ESP_IEEE80211_DBG(ESP_DBG_OP, "%s enter, state = %d is_ok = %d\n", ++ __func__, report->state, report->is_ok); ++ ++ //roc process begin ++ if ((report->state == 1) && (report->is_ok == 1)) { ++ epub->roc_flags = 1; //flags in roc state, to fix channel, not change ++ ieee80211_ready_on_channel(hw); ++ } else if ((report->state == 0) && (report->is_ok == 1)) //roc process timeout ++ { ++ epub->roc_flags = 0; // to disable roc state ++ ieee80211_remain_on_channel_expired(hw); ++ } ++} ++ ++static int esp_op_set_bitrate_mask(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, ++ const struct cfg80211_bitrate_mask ++ *mask) ++{ ++ ESP_IEEE80211_DBG(ESP_DBG_OP, "%s enter \n", __func__); ++ ESP_IEEE80211_DBG(ESP_DBG_OP, "%s vif->macaddr[%pM], mask[%d]\n", ++ __func__, vif->addr, mask->control[0].legacy); ++ ++ return 0; ++} ++ ++void esp_op_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, ++ u32 queues, bool drop) ++{ ++ ++ ESP_IEEE80211_DBG(ESP_DBG_OP, "%s enter \n", __func__); ++ do { ++ ++ struct esp_pub *epub = (struct esp_pub *) hw->priv; ++ unsigned long time = jiffies + msecs_to_jiffies(15); ++ while (atomic_read(&epub->sip->tx_data_pkt_queued)) { ++ if (!time_before(jiffies, time)) { ++ break; ++ } ++ if (sif_get_ate_config() == 0) { ++ ieee80211_queue_work(epub->hw, ++ &epub->tx_work); ++ } else { ++ queue_work(epub->esp_wkq, &epub->tx_work); ++ } ++ //sip_txq_process(epub); ++ } ++ mdelay(10); ++ ++ } while (0); ++} ++ ++static int esp_op_ampdu_action(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, ++ struct ieee80211_ampdu_params *params) ++{ ++ int ret = -EOPNOTSUPP; ++ enum ieee80211_ampdu_mlme_action action = params->action; ++ struct ieee80211_sta *sta = params->sta; ++ u16 tid = params->tid; ++ u16 *ssn = ¶ms->ssn; ++ u8 buf_size = params->buf_size; ++ struct esp_pub *epub = (struct esp_pub *) hw->priv; ++ struct esp_node *node = (struct esp_node *) sta->drv_priv; ++ struct esp_tx_tid *tid_info = &node->tid[tid]; ++ ++ ESP_IEEE80211_DBG(ESP_DBG_OP, "%s enter \n", __func__); ++ switch (action) { ++ case IEEE80211_AMPDU_TX_START: ++ if (mod_support_no_txampdu() || ++ cfg80211_get_chandef_type(&epub->hw->conf.chandef) == ++ NL80211_CHAN_NO_HT || !sta->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 4 //for one interface ++#define ESP_PUB_MAX_RXAMPDU 8 //for all interfaces ++ ++enum { ++ ESP_PM_OFF = 0, ++ ESP_PM_TURNING_ON, ++ ESP_PM_ON, ++ ESP_PM_TURNING_OFF, /* Do NOT change the order */ ++}; ++ ++struct esp_ps { ++ u32 dtim_period; ++ u32 max_sleep_period; ++ unsigned long last_config_time; ++ atomic_t state; ++ bool nulldata_pm_on; ++}; ++ ++struct esp_mac_prefix { ++ u8 mac_index; ++ u8 mac_addr_prefix[3]; ++}; ++ ++struct esp_pub { ++ struct device *dev; ++#ifdef ESP_NO_MAC80211 ++ struct net_device *net_dev; ++ struct wireless_dev *wdev; ++ struct net_device_stats *net_stats; ++#else ++ struct ieee80211_hw *hw; ++ struct ieee80211_vif *vif; ++ u8 vif_slot; ++#endif /* ESP_MAC80211 */ ++ ++ void *sif; /* serial interface control block, e.g. sdio */ ++ enum esp_sdio_state sdio_state; ++ struct esp_sip *sip; ++ struct esp_wl wl; ++ struct esp_hw_idx_map hi_map[19]; ++ struct esp_hw_idx_map low_map[ESP_PUB_MAX_VIF][2]; ++ //u32 flags; //flags to represent rfkill switch,start ++ u8 roc_flags; //0: not in remain on channel state, 1: in roc state ++ ++ struct work_struct tx_work; /* attach to ieee80211 workqueue */ ++ /* latest mac80211 has multiple tx queue, but we stick with single queue now */ ++ spinlock_t rx_lock; ++ spinlock_t tx_ampdu_lock; ++ spinlock_t rx_ampdu_lock; ++ spinlock_t tx_lock; ++ struct mutex tx_mtx; ++ struct sk_buff_head txq; ++ atomic_t txq_stopped; ++ ++ struct work_struct sendup_work; /* attach to ieee80211 workqueue */ ++ struct sk_buff_head txdoneq; ++ struct sk_buff_head rxq; ++ ++ struct workqueue_struct *esp_wkq; ++ ++ //u8 bssid[ETH_ALEN]; ++ u8 mac_addr[ETH_ALEN]; ++ ++ u32 rx_filter; ++ unsigned long scan_permit; ++ bool scan_permit_valid; ++ struct delayed_work scan_timeout_work; ++ u32 enodes_map; ++ u8 rxampdu_map; ++ u32 enodes_maps[ESP_PUB_MAX_VIF]; ++ struct esp_node *enodes[ESP_PUB_MAX_STA + 1]; ++ struct esp_node *rxampdu_node[ESP_PUB_MAX_RXAMPDU]; ++ u8 rxampdu_tid[ESP_PUB_MAX_RXAMPDU]; ++ struct esp_ps ps; ++ int enable_int; ++ int wait_reset; ++}; ++ ++typedef struct esp_pub esp_pub_t; ++ ++struct esp_pub *esp_pub_alloc_mac80211(struct device *dev); ++int esp_pub_dealloc_mac80211(struct esp_pub *epub); ++int esp_register_mac80211(struct esp_pub *epub); ++ ++int esp_pub_init_all(struct esp_pub *epub); ++ ++char *mod_eagle_path_get(void); ++ ++void esp_dsr(struct esp_pub *epub); ++void hw_scan_done(struct esp_pub *epub, bool aborted); ++void esp_rocdone_process(struct ieee80211_hw *hw, ++ struct sip_evt_roc *report); ++ ++void esp_ps_config(struct esp_pub *epub, struct esp_ps *ps, bool on); ++ ++struct esp_node *esp_get_node_by_addr(struct esp_pub *epub, ++ const u8 * addr); ++struct esp_node *esp_get_node_by_index(struct esp_pub *epub, u8 index); ++int esp_get_empty_rxampdu(struct esp_pub *epub, const u8 * addr, u8 tid); ++int esp_get_exist_rxampdu(struct esp_pub *epub, const u8 * addr, u8 tid); ++ ++#ifdef TEST_MODE ++int test_init_netlink(struct esp_sip *sip); ++void test_exit_netlink(void); ++void esp_test_cmd_event(u32 cmd_type, char *reply_info); ++void esp_test_init(struct esp_pub *epub); ++#endif ++#endif /* _ESP_PUB_H_ */ +diff --git a/drivers/net/wireless/esp8089/esp_sif.h b/drivers/net/wireless/esp8089/esp_sif.h +new file mode 100644 +index 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 +