Project import generated by Copybara.
NOKEYCHECK=True
GitOrigin-RevId: 877e660bb66790bc30a4ccf27473430b3260818f
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..43a3d08
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,21 @@
+Kconfig.kernel
+Kconfig.versions
+.kernel_config_md5
+.config
+.config.old
+*~
+*.o
+*.d
+.tmp_*
+*.ko
+*.cmd
+*.tmp
+*.ver
+modules.order
+backport-include/backport/autoconf.h
+modules
+kconf/mconf
+kconf/conf
+.tmp_versions
+Module.symvers
+*.mod.c
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..cd3ced9
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,189 @@
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := brcmfmac.ko
+LOCAL_MDOULE_TAGS := optional
+LOCAL_MODULE_DEBUG_ENABLE := true
+LOCAL_MODULE_PATH := $(TARGET_OUT)/lib/modules
+LOCAL_MODULE_CLASS := DLKM
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+ifeq ($(TARGET_PRODUCT),antares)
+
+LOCAL_DIR := device/nest/antares
+include device/nest/antares/kernel.mk
+
+kbuild_kernel_offset_to_root := ../../../../../
+kbuild_kernel_image := $(KERNEL_IMAGE)
+kbuild_kernel_src := $(TARGET_KERNEL_SRC)
+kbuild_cflags := $(if $(KERNEL_CFLAGS),KCFLAGS=$(strip $(KERNEL_CFLAGS)))
+kbuild_aflags := $(if $(KERNEL_AFLAGS),KAFLAGS=$(strip $(KERNEL_AFLAGS)))
+sign_file_path := perl $(kbuild_kernel_src)/scripts/sign-file
+sign_sec_key := $(KERNEL_OUT)/signing_key.priv
+sign_pub_key := $(KERNEL_OUT)/signing_key.x509
+
+else ifeq ($(TARGET_PRODUCT),cygnus)
+
+KERNEL_DEFCONFIG ?= $(if $(filter $(TARGET_BUILD_VARIANT),user), msmcortex-perf_defconfig, msmcortex_defconfig)
+include kernel/AndroidKernel.mk
+
+kbuild_kernel_offset_to_root := ../
+kbuild_kernel_image := $(TARGET_PREBUILT_KERNEL)
+kbuild_kernel_src := $(TARGET_KERNEL_SOURCE)
+kbuild_cflags := $(strip $(KERNEL_CFLAGS))
+kbuild_aflags := $(strip $(KERNEL_AFLAGS))
+sign_file_path := perl $(kbuild_kernel_src)/scripts/sign-file
+sign_sec_key := $(KERNEL_OUT)/signing_key.priv
+sign_pub_key := $(KERNEL_OUT)/signing_key.x509
+
+else ifeq ($(TARGET_PRODUCT),iot_cygnus)
+
+include device/qcom/msm8x53/AndroidBoard_common.mk
+include device/google/iot/kernel.mk
+
+kbuild_kernel_offset_to_root := ../../../../../
+kbuild_kernel_image := $(KERNEL_BIN)
+kbuild_kernel_src := $(TARGET_KERNEL_SRC)
+kbuild_cflags := $(if $(KERNEL_CFLAGS),KCFLAGS=$(strip $(KERNEL_CFLAGS)))
+kbuild_aflags := $(if $(KERNEL_AFLAGS),KAFLAGS=$(strip $(KERNEL_AFLAGS)))
+sign_file_path := $(KERNEL_OUT)/scripts/sign-file
+sign_sec_key = $(KERNEL_OUT)/$(shell cat $(KERNEL_OUT)/.config | grep CONFIG_MODULE_SIG_KEY | cut -d'=' -f2 | sed 's/\"//g')
+sign_pub_key := $(KERNEL_OUT)/certs/signing_key.x509
+
+endif
+
+kbuild_defconfig := defconfig-brcmfmac
+
+KBUILD_OPTIONS := \
+ KLIB_BUILD=../../../$(KERNEL_OUT)
+
+KBUILD_TARGET := $(strip \
+ $(subst .,_, \
+ $(subst -,_, \
+ $(subst :,_, \
+ $(subst /,_,$(LOCAL_PATH))))))
+
+# Intermediate directory where the kernel modules are created
+# by the kernel build system. Ideally this would be the same
+# directory as LOCAL_BUILT_MODULE, but because we're using
+# relative paths for both O= and M=, we don't have much choice
+KBUILD_OUT_DIR := $(KERNEL_OUT)/$(kbuild_kernel_offset_to_root)$(LOCAL_PATH)
+
+# Path to the intermediate location where the kernel build
+# system creates the kernel module.
+
+KBUILD_MODULE := \
+ $(KBUILD_OUT_DIR)/compat/compat.ko \
+ $(KBUILD_OUT_DIR)/net/wireless/cfg80211.ko \
+ $(KBUILD_OUT_DIR)/drivers/net/wireless/broadcom/brcm80211/brcmutil/brcmutil.ko \
+ $(KBUILD_OUT_DIR)/drivers/net/wireless/broadcom/brcm80211/brcmfmac/$(LOCAL_MODULE)
+
+# Since we only invoke the kernel build system once per directory,
+# each kernel module must depend on the same target.
+$(KBUILD_MODULE): kbuild_out := $(KBUILD_OUT_DIR)/$(LOCAL_MODULE_KBUILD_NAME)
+$(KBUILD_MODULE): $(KBUILD_TARGET) | $(ACP)
+ifneq "$(LOCAL_MODULE_KBUILD_NAME)" ""
+ mv -f $(kbuild_out) $@
+endif
+ifneq "$(LOCAL_MODULE_DEBUG_ENABLE)" ""
+ mkdir -p $(dir $(TARGET_OUT_INTERMEDIATES)/DLKM/$(notdir $@)_intermediates/$(notdir $@))
+ cp $@ $@.unstripped
+ $(TARGET_STRIP) --strip-debug $@
+ cp $@ $@.stripped
+endif
+ @sh -c "\
+ KMOD_SIG_ALL=`cat $(KERNEL_OUT)/.config | grep CONFIG_MODULE_SIG_ALL | cut -d'=' -f2`; \
+ KMOD_SIG_HASH=`cat $(KERNEL_OUT)/.config | grep CONFIG_MODULE_SIG_HASH | cut -d'=' -f2 | sed 's/\"//g'`; \
+ if [ \"\$$KMOD_SIG_ALL\" = \"y\" ] && [ -n \"\$$KMOD_SIG_HASH\" ]; then \
+ echo \"Signing kernel module: \" `basename $@`; \
+ MODSECKEY=$(sign_sec_key); \
+ MODPUBKEY=$(sign_pub_key); \
+ cp $@ $@.unsigned; \
+ $(sign_file_path) \$$KMOD_SIG_HASH \$$MODSECKEY \$$MODPUBKEY $@; \
+ fi; \
+ "
+ cp $@ $(TARGET_OUT_INTERMEDIATES)/DLKM/$(notdir $@)_intermediates/$(notdir $@)
+
+$(LOCAL_BUILT_MODULE): $(KBUILD_MODULE)
+
+# This should really be cleared in build/core/clear-vars.mk, but for
+# the time being, we need to clear it ourselves
+LOCAL_MODULE_KBUILD_NAME :=
+LOCAL_MODULE_DEBUG_ENABLE :=
+
+# Ensure the kernel module created by the kernel build system, as
+# well as all the other intermediate files, are removed during a clean.
+$(cleantarget): PRIVATE_CLEAN_FILES := $(PRIVATE_CLEAN_FILES) $(KBUILD_OUT_DIR)
+
+
+# Since this file will be included more than once for directories
+# with more than one kernel module, the shared KBUILD_TARGET rule should
+# only be defined once to avoid "overriding commands ..." warnings.
+ifndef $(KBUILD_TARGET)_RULE
+$(KBUILD_TARGET)_RULE := 1
+
+# Kernel modules have to be built after:
+# * the kernel config has been created
+# * host executables, like scripts/basic/fixdep, have been built
+# (otherwise parallel invocations of the kernel build system will
+# fail as they all try to compile these executables at the same time)
+# * a full kernel build (to make module versioning work)
+#
+# For these reasons, kernel modules are dependent on
+# TARGET_PREBUILT_INT_KERNEL which will ensure all of the above.
+#
+# NOTE: Due to a bug in the kernel build system when using a Kbuild file
+# and relative paths for both O= and M=, the Kbuild file must
+# be copied to the output directory.
+#
+# NOTE: The following paths are equivalent:
+# $(KBUILD_OUT_DIR)
+# $(KERNEL_OUT)/$(kbuild_kernel_offset_to_root)$(LOCAL_PATH)
+.PHONY: $(KBUILD_TARGET)
+$(KBUILD_TARGET): local_path := $(LOCAL_PATH)
+$(KBUILD_TARGET): kbuild_out_dir := $(KBUILD_OUT_DIR)
+$(KBUILD_TARGET): kbuild_options := $(KBUILD_OPTIONS)
+$(KBUILD_TARGET): $(kbuild_kernel_image)
+ @mkdir -p $(kbuild_out_dir)
+ $(MAKE) -C $(local_path) ARCH=$(KERNEL_ARCH) CROSS_COMPILE=$(KERNEL_CROSS_COMPILE) $(kbuild_cflags) $(kbuild_aflags) $(kbuild_options) $(kbuild_defconfig)
+ $(MAKE) -C $(local_path) ARCH=$(KERNEL_ARCH) CROSS_COMPILE=$(KERNEL_CROSS_COMPILE) $(kbuild_cflags) $(kbuild_aflags) $(kbuild_options) backport-include/backport/autoconf.h
+ $(MAKE) -C $(kbuild_kernel_src) M=$(kbuild_kernel_offset_to_root)$(local_path) O=$(kbuild_kernel_offset_to_root)$(KERNEL_OUT) ARCH=$(KERNEL_ARCH) CROSS_COMPILE=$(KERNEL_CROSS_COMPILE) $(kbuild_cflags) $(kbuild_aflags) $(kbuild_options) BACKPORT_DIR=$(shell pwd)/$(local_path) ANDROID_BUILD=y modules
+
+endif
+
+# Once the KBUILD_OPTIONS variable has been used for the target
+# that's specific to the LOCAL_PATH, clear it. If this isn't done,
+# then every kernel module would need to explicitly set KBUILD_OPTIONS,
+# or the variable would have to be cleared in 'include $(CLEAR_VARS)'
+# which would require a change to build/core.
+KBUILD_OPTIONS :=
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := brcmutil.ko
+LOCAL_MDOULE_TAGS := optional
+LOCAL_MODULE_DEBUG_ENABLE := true
+LOCAL_MODULE_PATH := $(TARGET_OUT)/lib/modules
+LOCAL_MODULE_CLASS := DLKM
+include $(BUILD_SYSTEM)/base_rules.mk
+$(LOCAL_BUILT_MODULE) : $(TARGET_OUT_INTERMEDIATES)/DLKM/brcmfmac.ko_intermediates/brcmfmac.ko
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := compat.ko
+LOCAL_MDOULE_TAGS := optional
+LOCAL_MODULE_DEBUG_ENABLE := true
+LOCAL_MODULE_PATH := $(TARGET_OUT)/lib/modules
+LOCAL_MODULE_CLASS := DLKM
+LOCAL_ADDITIONAL_DEPENDENCIES := brcmfmac.ko
+include $(BUILD_SYSTEM)/base_rules.mk
+$(LOCAL_BUILT_MODULE) : $(TARGET_OUT_INTERMEDIATES)/DLKM/brcmfmac.ko_intermediates/brcmfmac.ko
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := cfg80211.ko
+LOCAL_MDOULE_TAGS := optional
+LOCAL_MODULE_DEBUG_ENABLE := true
+LOCAL_MODULE_PATH := $(TARGET_OUT)/lib/modules
+LOCAL_MODULE_CLASS := DLKM
+LOCAL_ADDITIONAL_DEPENDENCIES := brcmfmac.ko
+include $(BUILD_SYSTEM)/base_rules.mk
+$(LOCAL_BUILT_MODULE) : $(TARGET_OUT_INTERMEDIATES)/DLKM/brcmfmac.ko_intermediates/brcmfmac.ko
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..ca442d3
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,356 @@
+
+ NOTE! This copyright does *not* cover user programs that use kernel
+ services by normal system calls - this is merely considered normal use
+ of the kernel, and does *not* fall under the heading of "derived work".
+ Also note that the GPL below is copyrighted by the Free Software
+ Foundation, but the instance of code that it refers to (the Linux
+ kernel) is copyrighted by me and others who actually wrote it.
+
+ Also note that the only valid version of the GPL as far as the kernel
+ is concerned is _this_ particular version of the license (ie v2, not
+ v2.2 or v3.x or whatever), unless explicitly otherwise stated.
+
+ Linus Torvalds
+
+----------------------------------------
+
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin St, 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 Library 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.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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 St, 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 Library General
+Public License instead of this License.
diff --git a/Kconfig b/Kconfig
new file mode 100644
index 0000000..f9e447b
--- /dev/null
+++ b/Kconfig
@@ -0,0 +1,28 @@
+mainmenu "Backports from $BACKPORTED_KERNEL_NAME $BACKPORTED_KERNEL_VERSION (backports $BACKPORTS_VERSION)"
+
+config BACKPORT_DIR
+ string
+ option env="BACKPORT_DIR"
+config BACKPORTS_VERSION
+ string
+ option env="BACKPORTS_VERSION"
+config BACKPORTED_KERNEL_VERSION
+ string
+ option env="BACKPORTED_KERNEL_VERSION"
+config BACKPORTED_KERNEL_NAME
+ string
+ option env="BACKPORTED_KERNEL_NAME"
+config BACKPORTED_DEBUG_INFO
+ bool "Compile the package with debug info"
+ default n
+
+# Packaging hacks
+source "$BACKPORT_DIR/Kconfig.package.hacks"
+
+# Code we backport
+source "$BACKPORT_DIR/Kconfig.sources"
+
+# these will be generated
+source "$BACKPORT_DIR/Kconfig.kernel"
+source "$BACKPORT_DIR/Kconfig.versions"
+source "$BACKPORT_DIR/Kconfig.local"
diff --git a/Kconfig.local b/Kconfig.local
new file mode 100644
index 0000000..5edac0d
--- /dev/null
+++ b/Kconfig.local
@@ -0,0 +1,180 @@
+config BACKPORTED_BACKPORT_DIR
+ tristate
+ default BACKPORT_DIR
+config BACKPORTED_BACKPORTS_VERSION
+ tristate
+ default BACKPORTS_VERSION
+config BACKPORTED_BACKPORTED_KERNEL_VERSION
+ tristate
+ default BACKPORTED_KERNEL_VERSION
+config BACKPORTED_BACKPORTED_KERNEL_NAME
+ tristate
+ default BACKPORTED_KERNEL_NAME
+config BACKPORTED_WIRELESS
+ tristate
+ default WIRELESS
+config BACKPORTED_NET_CORE
+ tristate
+ default NET_CORE
+config BACKPORTED_EXPERT
+ tristate
+ default EXPERT
+config BACKPORTED_BP_MODULES
+ tristate
+ default BP_MODULES
+config BACKPORTED_BPAUTO_BUILD_CORDIC
+ tristate
+ default BPAUTO_BUILD_CORDIC
+config BACKPORTED_BPAUTO_CORDIC
+ tristate
+ default BPAUTO_CORDIC
+config BACKPORTED_BPAUTO_MII
+ tristate
+ default BPAUTO_MII
+config BACKPORTED_BPAUTO_BUILD_LEDS
+ tristate
+ default BPAUTO_BUILD_LEDS
+config BACKPORTED_BPAUTO_NEW_LEDS
+ tristate
+ default BPAUTO_NEW_LEDS
+config BACKPORTED_BPAUTO_LEDS_CLASS
+ tristate
+ default BPAUTO_LEDS_CLASS
+config BACKPORTED_BPAUTO_LEDS_TRIGGERS
+ tristate
+ default BPAUTO_LEDS_TRIGGERS
+config BACKPORTED_BPAUTO_USERSEL_BUILD_ALL
+ tristate
+ default BPAUTO_USERSEL_BUILD_ALL
+config BACKPORTED_BPAUTO_CRYPTO_CCM
+ tristate
+ default BPAUTO_CRYPTO_CCM
+config BACKPORTED_BPAUTO_BUILD_CRYPTO_CCM
+ tristate
+ default BPAUTO_BUILD_CRYPTO_CCM
+config BACKPORTED_BPAUTO_CRYPTO_SKCIPHER
+ tristate
+ default BPAUTO_CRYPTO_SKCIPHER
+config BACKPORTED_BPAUTO_WANT_DEV_COREDUMP
+ tristate
+ default BPAUTO_WANT_DEV_COREDUMP
+config BACKPORTED_BPAUTO_BUILD_WANT_DEV_COREDUMP
+ tristate
+ default BPAUTO_BUILD_WANT_DEV_COREDUMP
+config BACKPORTED_BPAUTO_RHASHTABLE
+ tristate
+ default BPAUTO_RHASHTABLE
+config BACKPORTED_BPAUTO_BUILD_HDMI
+ tristate
+ default BPAUTO_BUILD_HDMI
+config BACKPORTED_BPAUTO_HDMI
+ tristate
+ default BPAUTO_HDMI
+config BACKPORTED_BPAUTO_FRAME_VECTOR
+ tristate
+ default BPAUTO_FRAME_VECTOR
+config BACKPORTED_BPAUTO_BUILD_FRAME_VECTOR
+ tristate
+ default BPAUTO_BUILD_FRAME_VECTOR
+config BACKPORTED_CFG80211
+ tristate
+ default CFG80211
+config BACKPORTED_NL80211_TESTMODE
+ tristate
+ default NL80211_TESTMODE
+config BACKPORTED_CFG80211_DEVELOPER_WARNINGS
+ tristate
+ default CFG80211_DEVELOPER_WARNINGS
+config BACKPORTED_CFG80211_CERTIFICATION_ONUS
+ tristate
+ default CFG80211_CERTIFICATION_ONUS
+config BACKPORTED_CFG80211_REG_CELLULAR_HINTS
+ tristate
+ default CFG80211_REG_CELLULAR_HINTS
+config BACKPORTED_CFG80211_REG_RELAX_NO_IR
+ tristate
+ default CFG80211_REG_RELAX_NO_IR
+config BACKPORTED_CFG80211_DEFAULT_PS
+ tristate
+ default CFG80211_DEFAULT_PS
+config BACKPORTED_CFG80211_DEBUGFS
+ tristate
+ default CFG80211_DEBUGFS
+config BACKPORTED_CFG80211_INTERNAL_REGDB
+ tristate
+ default CFG80211_INTERNAL_REGDB
+config BACKPORTED_CFG80211_CRDA_SUPPORT
+ tristate
+ default CFG80211_CRDA_SUPPORT
+config BACKPORTED_CFG80211_WEXT
+ tristate
+ default CFG80211_WEXT
+config BACKPORTED_CFG80211_WEXT_EXPORT
+ tristate
+ default CFG80211_WEXT_EXPORT
+config BACKPORTED_LIB80211
+ tristate
+ default LIB80211
+config BACKPORTED_LIB80211_CRYPT_WEP
+ tristate
+ default LIB80211_CRYPT_WEP
+config BACKPORTED_LIB80211_CRYPT_CCMP
+ tristate
+ default LIB80211_CRYPT_CCMP
+config BACKPORTED_LIB80211_CRYPT_TKIP
+ tristate
+ default LIB80211_CRYPT_TKIP
+config BACKPORTED_LIB80211_DEBUG
+ tristate
+ default LIB80211_DEBUG
+config BACKPORTED_WLAN
+ tristate
+ default WLAN
+config BACKPORTED_WIRELESS_WDS
+ tristate
+ default WIRELESS_WDS
+config BACKPORTED_PCMCIA_RAYCS
+ tristate
+ default PCMCIA_RAYCS
+config BACKPORTED_PCMCIA_WL3501
+ tristate
+ default PCMCIA_WL3501
+config BACKPORTED_MAC80211_HWSIM
+ tristate
+ default MAC80211_HWSIM
+config BACKPORTED_USB_NET_RNDIS_WLAN
+ tristate
+ default USB_NET_RNDIS_WLAN
+config BACKPORTED_WLAN_VENDOR_BROADCOM
+ tristate
+ default WLAN_VENDOR_BROADCOM
+config BACKPORTED_BRCMUTIL
+ tristate
+ default BRCMUTIL
+config BACKPORTED_BRCMSMAC
+ tristate
+ default BRCMSMAC
+config BACKPORTED_BRCMFMAC
+ tristate
+ default BRCMFMAC
+config BACKPORTED_BRCMFMAC_PROTO_BCDC
+ tristate
+ default BRCMFMAC_PROTO_BCDC
+config BACKPORTED_BRCMFMAC_PROTO_MSGBUF
+ tristate
+ default BRCMFMAC_PROTO_MSGBUF
+config BACKPORTED_BRCMFMAC_SDIO
+ tristate
+ default BRCMFMAC_SDIO
+config BACKPORTED_BRCMFMAC_USB
+ tristate
+ default BRCMFMAC_USB
+config BACKPORTED_BRCMFMAC_PCIE
+ tristate
+ default BRCMFMAC_PCIE
+config BACKPORTED_BRCM_TRACING
+ tristate
+ default BRCM_TRACING
+config BACKPORTED_BRCMDBG
+ tristate
+ default BRCMDBG
diff --git a/Kconfig.package.hacks b/Kconfig.package.hacks
new file mode 100644
index 0000000..6a429dd
--- /dev/null
+++ b/Kconfig.package.hacks
@@ -0,0 +1,8 @@
+# some hacks for when we use backports to generate a package
+# to build modules out of tree.
+config WIRELESS
+ def_bool y
+config NET_CORE
+ def_bool y
+config EXPERT
+ def_bool y
diff --git a/Kconfig.sources b/Kconfig.sources
new file mode 100644
index 0000000..d9ce89f
--- /dev/null
+++ b/Kconfig.sources
@@ -0,0 +1,24 @@
+# this has the configuration for the backport code
+source "$BACKPORT_DIR/compat/Kconfig"
+
+# these are copied from the kernel
+source "$BACKPORT_DIR/net/wireless/Kconfig"
+#source "$BACKPORT_DIR/net/mac80211/Kconfig"
+#source "$BACKPORT_DIR/net/bluetooth/Kconfig"
+source "$BACKPORT_DIR/drivers/net/wireless/Kconfig"
+#source "$BACKPORT_DIR/drivers/net/ethernet/Kconfig"
+#source "$BACKPORT_DIR/drivers/net/usb/Kconfig"
+
+#source "$BACKPORT_DIR/drivers/ssb/Kconfig"
+#source "$BACKPORT_DIR/drivers/bcma/Kconfig"
+
+#source "$BACKPORT_DIR/net/nfc/Kconfig"
+
+#source "$BACKPORT_DIR/drivers/media/Kconfig"
+
+#source "$BACKPORT_DIR/net/6lowpan/Kconfig"
+#source "$BACKPORT_DIR/net/ieee802154/Kconfig"
+#source "$BACKPORT_DIR/net/mac802154/Kconfig"
+#source "$BACKPORT_DIR/drivers/net/ieee802154/Kconfig"
+
+#source "$BACKPORT_DIR/drivers/usb/class/Kconfig"
diff --git a/MAINTAINERS b/MAINTAINERS
new file mode 100644
index 0000000..767e9d2
--- /dev/null
+++ b/MAINTAINERS
@@ -0,0 +1,14196 @@
+
+
+ List of maintainers and how to submit kernel changes
+
+Please try to follow the guidelines below. This will make things
+easier on the maintainers. Not all of these guidelines matter for every
+trivial patch so apply some common sense.
+
+1. Always _test_ your changes, however small, on at least 4 or
+ 5 people, preferably many more.
+
+2. Try to release a few ALPHA test versions to the net. Announce
+ them onto the kernel channel and await results. This is especially
+ important for device drivers, because often that's the only way
+ you will find things like the fact version 3 firmware needs
+ a magic fix you didn't know about, or some clown changed the
+ chips on a board and not its name. (Don't laugh! Look at the
+ SMC etherpower for that.)
+
+3. Make sure your changes compile correctly in multiple
+ configurations. In particular check that changes work both as a
+ module and built into the kernel.
+
+4. When you are happy with a change make it generally available for
+ testing and await feedback.
+
+5. Make a patch available to the relevant maintainer in the list. Use
+ 'diff -u' to make the patch easy to merge. Be prepared to get your
+ changes sent back with seemingly silly requests about formatting
+ and variable names. These aren't as silly as they seem. One
+ job the maintainers (and especially Linus) do is to keep things
+ looking the same. Sometimes this means that the clever hack in
+ your driver to get around a problem actually needs to become a
+ generalized kernel feature ready for next time.
+
+ PLEASE check your patch with the automated style checker
+ (scripts/checkpatch.pl) to catch trivial style violations.
+ See Documentation/process/coding-style.rst for guidance here.
+
+ PLEASE CC: the maintainers and mailing lists that are generated
+ by scripts/get_maintainer.pl. The results returned by the
+ script will be best if you have git installed and are making
+ your changes in a branch derived from Linus' latest git tree.
+ See Documentation/process/submitting-patches.rst for details.
+
+ PLEASE try to include any credit lines you want added with the
+ patch. It avoids people being missed off by mistake and makes
+ it easier to know who wants adding and who doesn't.
+
+ PLEASE document known bugs. If it doesn't work for everything
+ or does something very odd once a month document it.
+
+ PLEASE remember that submissions must be made under the terms
+ of the Linux Foundation certificate of contribution and should
+ include a Signed-off-by: line. The current version of this
+ "Developer's Certificate of Origin" (DCO) is listed in the file
+ Documentation/process/submitting-patches.rst.
+
+6. Make sure you have the right to send any changes you make. If you
+ do changes at work you may find your employer owns the patch
+ not you.
+
+7. When sending security related changes or reports to a maintainer
+ please Cc: security@kernel.org, especially if the maintainer
+ does not respond.
+
+8. Happy hacking.
+
+Descriptions of section entries:
+
+ P: Person (obsolete)
+ M: Mail patches to: FullName <address@domain>
+ R: Designated reviewer: FullName <address@domain>
+ These reviewers should be CCed on patches.
+ L: Mailing list that is relevant to this area
+ W: Web-page with status/info
+ B: URI for where to file bugs. A web-page with detailed bug
+ filing info, a direct bug tracker link, or a mailto: URI.
+ C: URI for chat protocol, server and channel where developers
+ usually hang out, for example irc://server/channel.
+ Q: Patchwork web based patch tracking system site
+ T: SCM tree type and location.
+ Type is one of: git, hg, quilt, stgit, topgit
+ S: Status, one of the following:
+ Supported: Someone is actually paid to look after this.
+ Maintained: Someone actually looks after it.
+ Odd Fixes: It has a maintainer but they don't have time to do
+ much other than throw the odd patch in. See below..
+ Orphan: No current maintainer [but maybe you could take the
+ role as you write your new code].
+ Obsolete: Old code. Something tagged obsolete generally means
+ it has been replaced by a better system and you
+ should be using that.
+ F: Files and directories with wildcard patterns.
+ A trailing slash includes all files and subdirectory files.
+ F: drivers/net/ all files in and below drivers/net
+ F: drivers/net/* all files in drivers/net, but not below
+ F: */net/* all files in "any top level directory"/net
+ One pattern per line. Multiple F: lines acceptable.
+ N: Files and directories with regex patterns.
+ N: [^a-z]tegra all files whose path contains the word tegra
+ One pattern per line. Multiple N: lines acceptable.
+ scripts/get_maintainer.pl has different behavior for files that
+ match F: pattern and matches of N: patterns. By default,
+ get_maintainer will not look at git log history when an F: pattern
+ match occurs. When an N: match occurs, git log history is used
+ to also notify the people that have git commit signatures.
+ X: Files and directories that are NOT maintained, same rules as F:
+ Files exclusions are tested before file matches.
+ Can be useful for excluding a specific subdirectory, for instance:
+ F: net/
+ X: net/ipv6/
+ matches all files in and below net excluding net/ipv6/
+ K: Keyword perl extended regex pattern to match content in a
+ patch or file. For instance:
+ K: of_get_profile
+ matches patches or files that contain "of_get_profile"
+ K: \b(printk|pr_(info|err))\b
+ matches patches or files that contain one or more of the words
+ printk, pr_info or pr_err
+ One regex pattern per line. Multiple K: lines acceptable.
+
+Note: For the hard of thinking, this list is meant to remain in alphabetical
+order. If you could add yourselves to it in alphabetical order that would be
+so much easier [Ed]
+
+Maintainers List (try to look for most precise areas first)
+
+ -----------------------------------
+
+3C59X NETWORK DRIVER
+M: Steffen Klassert <klassert@mathematik.tu-chemnitz.de>
+L: netdev@vger.kernel.org
+S: Maintained
+F: Documentation/networking/vortex.txt
+F: drivers/net/ethernet/3com/3c59x.c
+
+3CR990 NETWORK DRIVER
+M: David Dillow <dave@thedillows.org>
+L: netdev@vger.kernel.org
+S: Maintained
+F: drivers/net/ethernet/3com/typhoon*
+
+3WARE SAS/SATA-RAID SCSI DRIVERS (3W-XXXX, 3W-9XXX, 3W-SAS)
+M: Adam Radford <aradford@gmail.com>
+L: linux-scsi@vger.kernel.org
+W: http://www.lsi.com
+S: Supported
+F: drivers/scsi/3w-*
+
+53C700 AND 53C700-66 SCSI DRIVER
+M: "James E.J. Bottomley" <James.Bottomley@HansenPartnership.com>
+L: linux-scsi@vger.kernel.org
+S: Maintained
+F: drivers/scsi/53c700*
+
+6LOWPAN GENERIC (BTLE/IEEE 802.15.4)
+M: Alexander Aring <aar@pengutronix.de>
+M: Jukka Rissanen <jukka.rissanen@linux.intel.com>
+L: linux-bluetooth@vger.kernel.org
+L: linux-wpan@vger.kernel.org
+S: Maintained
+F: net/6lowpan/
+F: include/net/6lowpan.h
+F: Documentation/networking/6lowpan.txt
+
+6PACK NETWORK DRIVER FOR AX.25
+M: Andreas Koensgen <ajk@comnets.uni-bremen.de>
+L: linux-hams@vger.kernel.org
+S: Maintained
+F: drivers/net/hamradio/6pack.c
+
+8169 10/100/1000 GIGABIT ETHERNET DRIVER
+M: Realtek linux nic maintainers <nic_swsd@realtek.com>
+L: netdev@vger.kernel.org
+S: Maintained
+F: drivers/net/ethernet/realtek/r8169.c
+
+8250/16?50 (AND CLONE UARTS) SERIAL DRIVER
+M: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+L: linux-serial@vger.kernel.org
+S: Maintained
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty.git
+F: drivers/tty/serial/8250*
+F: include/linux/serial_8250.h
+
+8390 NETWORK DRIVERS [WD80x3/SMC-ELITE, SMC-ULTRA, NE2000, 3C503, etc.]
+L: netdev@vger.kernel.org
+S: Orphan / Obsolete
+F: drivers/net/ethernet/8390/
+
+9P FILE SYSTEM
+M: Eric Van Hensbergen <ericvh@gmail.com>
+M: Ron Minnich <rminnich@sandia.gov>
+M: Latchesar Ionkov <lucho@ionkov.net>
+L: v9fs-developer@lists.sourceforge.net
+W: http://swik.net/v9fs
+Q: http://patchwork.kernel.org/project/v9fs-devel/list/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/ericvh/v9fs.git
+S: Maintained
+F: Documentation/filesystems/9p.txt
+F: fs/9p/
+F: net/9p/
+F: include/net/9p/
+F: include/uapi/linux/virtio_9p.h
+F: include/trace/events/9p.h
+
+
+A8293 MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+W: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/media/dvb-frontends/a8293*
+
+AACRAID SCSI RAID DRIVER
+M: Adaptec OEM Raid Solutions <aacraid@microsemi.com>
+L: linux-scsi@vger.kernel.org
+W: http://www.adaptec.com/
+S: Supported
+F: Documentation/scsi/aacraid.txt
+F: drivers/scsi/aacraid/
+
+ABI/API
+L: linux-api@vger.kernel.org
+F: include/linux/syscalls.h
+F: kernel/sys_ni.c
+
+ABIT UGURU 1,2 HARDWARE MONITOR DRIVER
+M: Hans de Goede <hdegoede@redhat.com>
+L: linux-hwmon@vger.kernel.org
+S: Maintained
+F: drivers/hwmon/abituguru.c
+
+ABIT UGURU 3 HARDWARE MONITOR DRIVER
+M: Alistair John Strachan <alistair@devzero.co.uk>
+L: linux-hwmon@vger.kernel.org
+S: Maintained
+F: drivers/hwmon/abituguru3.c
+
+ACCES 104-DIO-48E GPIO DRIVER
+M: William Breathitt Gray <vilhelm.gray@gmail.com>
+L: linux-gpio@vger.kernel.org
+S: Maintained
+F: drivers/gpio/gpio-104-dio-48e.c
+
+ACCES 104-IDI-48 GPIO DRIVER
+M: "William Breathitt Gray" <vilhelm.gray@gmail.com>
+L: linux-gpio@vger.kernel.org
+S: Maintained
+F: drivers/gpio/gpio-104-idi-48.c
+
+ACCES 104-IDIO-16 GPIO DRIVER
+M: "William Breathitt Gray" <vilhelm.gray@gmail.com>
+L: linux-gpio@vger.kernel.org
+S: Maintained
+F: drivers/gpio/gpio-104-idio-16.c
+
+ACCES 104-QUAD-8 IIO DRIVER
+M: William Breathitt Gray <vilhelm.gray@gmail.com>
+L: linux-iio@vger.kernel.org
+S: Maintained
+F: drivers/iio/counter/104-quad-8.c
+
+ACCES PCI-IDIO-16 GPIO DRIVER
+M: William Breathitt Gray <vilhelm.gray@gmail.com>
+L: linux-gpio@vger.kernel.org
+S: Maintained
+F: drivers/gpio/gpio-pci-idio-16.c
+
+ACENIC DRIVER
+M: Jes Sorensen <jes@trained-monkey.org>
+L: linux-acenic@sunsite.dk
+S: Maintained
+F: drivers/net/ethernet/alteon/acenic*
+
+ACER ASPIRE ONE TEMPERATURE AND FAN DRIVER
+M: Peter Feuerer <peter@piie.net>
+L: platform-driver-x86@vger.kernel.org
+W: http://piie.net/?section=acerhdf
+S: Maintained
+F: drivers/platform/x86/acerhdf.c
+
+ACER WMI LAPTOP EXTRAS
+M: "Lee, Chun-Yi" <jlee@suse.com>
+L: platform-driver-x86@vger.kernel.org
+S: Maintained
+F: drivers/platform/x86/acer-wmi.c
+
+ACPI
+M: "Rafael J. Wysocki" <rjw@rjwysocki.net>
+M: Len Brown <lenb@kernel.org>
+L: linux-acpi@vger.kernel.org
+W: https://01.org/linux-acpi
+Q: https://patchwork.kernel.org/project/linux-acpi/list/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm
+B: https://bugzilla.kernel.org
+S: Supported
+F: drivers/acpi/
+F: drivers/pnp/pnpacpi/
+F: include/linux/acpi.h
+F: include/acpi/
+F: Documentation/acpi/
+F: Documentation/ABI/testing/sysfs-bus-acpi
+F: Documentation/ABI/testing/configfs-acpi
+F: drivers/pci/*acpi*
+F: drivers/pci/*/*acpi*
+F: drivers/pci/*/*/*acpi*
+F: tools/power/acpi/
+
+ACPI COMPONENT ARCHITECTURE (ACPICA)
+M: Robert Moore <robert.moore@intel.com>
+M: Lv Zheng <lv.zheng@intel.com>
+M: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>
+L: linux-acpi@vger.kernel.org
+L: devel@acpica.org
+W: https://acpica.org/
+W: https://github.com/acpica/acpica/
+Q: https://patchwork.kernel.org/project/linux-acpi/list/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm
+B: https://bugzilla.kernel.org
+B: https://bugs.acpica.org
+S: Supported
+F: drivers/acpi/acpica/
+F: include/acpi/
+F: tools/power/acpi/
+
+ACPI FAN DRIVER
+M: Zhang Rui <rui.zhang@intel.com>
+L: linux-acpi@vger.kernel.org
+W: https://01.org/linux-acpi
+B: https://bugzilla.kernel.org
+S: Supported
+F: drivers/acpi/fan.c
+
+ACPI FOR ARM64 (ACPI/arm64)
+M: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
+M: Hanjun Guo <hanjun.guo@linaro.org>
+M: Sudeep Holla <sudeep.holla@arm.com>
+L: linux-acpi@vger.kernel.org
+S: Maintained
+F: drivers/acpi/arm64
+
+ACPI THERMAL DRIVER
+M: Zhang Rui <rui.zhang@intel.com>
+L: linux-acpi@vger.kernel.org
+W: https://01.org/linux-acpi
+B: https://bugzilla.kernel.org
+S: Supported
+F: drivers/acpi/*thermal*
+
+ACPI VIDEO DRIVER
+M: Zhang Rui <rui.zhang@intel.com>
+L: linux-acpi@vger.kernel.org
+W: https://01.org/linux-acpi
+B: https://bugzilla.kernel.org
+S: Supported
+F: drivers/acpi/acpi_video.c
+
+ACPI WMI DRIVER
+L: platform-driver-x86@vger.kernel.org
+S: Orphan
+F: drivers/platform/x86/wmi.c
+
+AD1889 ALSA SOUND DRIVER
+M: Thibaut Varene <T-Bone@parisc-linux.org>
+W: http://wiki.parisc-linux.org/AD1889
+L: linux-parisc@vger.kernel.org
+S: Maintained
+F: sound/pci/ad1889.*
+
+AD525X ANALOG DEVICES DIGITAL POTENTIOMETERS DRIVER
+M: Michael Hennerich <michael.hennerich@analog.com>
+W: http://wiki.analog.com/AD5254
+W: http://ez.analog.com/community/linux-device-drivers
+S: Supported
+F: drivers/misc/ad525x_dpot.c
+
+AD5398 CURRENT REGULATOR DRIVER (AD5398/AD5821)
+M: Michael Hennerich <michael.hennerich@analog.com>
+W: http://wiki.analog.com/AD5398
+W: http://ez.analog.com/community/linux-device-drivers
+S: Supported
+F: drivers/regulator/ad5398.c
+
+AD714X CAPACITANCE TOUCH SENSOR DRIVER (AD7142/3/7/8/7A)
+M: Michael Hennerich <michael.hennerich@analog.com>
+W: http://wiki.analog.com/AD7142
+W: http://ez.analog.com/community/linux-device-drivers
+S: Supported
+F: drivers/input/misc/ad714x.c
+
+AD7877 TOUCHSCREEN DRIVER
+M: Michael Hennerich <michael.hennerich@analog.com>
+W: http://wiki.analog.com/AD7877
+W: http://ez.analog.com/community/linux-device-drivers
+S: Supported
+F: drivers/input/touchscreen/ad7877.c
+
+AD7879 TOUCHSCREEN DRIVER (AD7879/AD7889)
+M: Michael Hennerich <michael.hennerich@analog.com>
+W: http://wiki.analog.com/AD7879
+W: http://ez.analog.com/community/linux-device-drivers
+S: Supported
+F: drivers/input/touchscreen/ad7879.c
+
+ADDRESS SPACE LAYOUT RANDOMIZATION (ASLR)
+M: Jiri Kosina <jikos@kernel.org>
+S: Maintained
+
+ADF7242 IEEE 802.15.4 RADIO DRIVER
+M: Michael Hennerich <michael.hennerich@analog.com>
+W: https://wiki.analog.com/ADF7242
+W: http://ez.analog.com/community/linux-device-drivers
+L: linux-wpan@vger.kernel.org
+S: Supported
+F: drivers/net/ieee802154/adf7242.c
+F: Documentation/devicetree/bindings/net/ieee802154/adf7242.txt
+
+ADM1025 HARDWARE MONITOR DRIVER
+M: Jean Delvare <jdelvare@suse.com>
+L: linux-hwmon@vger.kernel.org
+S: Maintained
+F: Documentation/hwmon/adm1025
+F: drivers/hwmon/adm1025.c
+
+ADM1029 HARDWARE MONITOR DRIVER
+M: Corentin Labbe <clabbe.montjoie@gmail.com>
+L: linux-hwmon@vger.kernel.org
+S: Maintained
+F: drivers/hwmon/adm1029.c
+
+ADM8211 WIRELESS DRIVER
+L: linux-wireless@vger.kernel.org
+W: http://wireless.kernel.org/
+S: Orphan
+F: drivers/net/wireless/admtek/adm8211.*
+
+ADP1653 FLASH CONTROLLER DRIVER
+M: Sakari Ailus <sakari.ailus@iki.fi>
+L: linux-media@vger.kernel.org
+S: Maintained
+F: drivers/media/i2c/adp1653.c
+F: include/media/i2c/adp1653.h
+
+ADP5520 BACKLIGHT DRIVER WITH IO EXPANDER (ADP5520/ADP5501)
+M: Michael Hennerich <michael.hennerich@analog.com>
+W: http://wiki.analog.com/ADP5520
+W: http://ez.analog.com/community/linux-device-drivers
+S: Supported
+F: drivers/mfd/adp5520.c
+F: drivers/video/backlight/adp5520_bl.c
+F: drivers/leds/leds-adp5520.c
+F: drivers/gpio/gpio-adp5520.c
+F: drivers/input/keyboard/adp5520-keys.c
+
+ADP5588 QWERTY KEYPAD AND IO EXPANDER DRIVER (ADP5588/ADP5587)
+M: Michael Hennerich <michael.hennerich@analog.com>
+W: http://wiki.analog.com/ADP5588
+W: http://ez.analog.com/community/linux-device-drivers
+S: Supported
+F: drivers/input/keyboard/adp5588-keys.c
+F: drivers/gpio/gpio-adp5588.c
+
+ADP8860 BACKLIGHT DRIVER (ADP8860/ADP8861/ADP8863)
+M: Michael Hennerich <michael.hennerich@analog.com>
+W: http://wiki.analog.com/ADP8860
+W: http://ez.analog.com/community/linux-device-drivers
+S: Supported
+F: drivers/video/backlight/adp8860_bl.c
+
+ADS1015 HARDWARE MONITOR DRIVER
+M: Dirk Eibach <eibach@gdsys.de>
+L: linux-hwmon@vger.kernel.org
+S: Maintained
+F: Documentation/hwmon/ads1015
+F: drivers/hwmon/ads1015.c
+F: include/linux/i2c/ads1015.h
+
+ADT746X FAN DRIVER
+M: Colin Leroy <colin@colino.net>
+S: Maintained
+F: drivers/macintosh/therm_adt746x.c
+
+ADT7475 HARDWARE MONITOR DRIVER
+M: Jean Delvare <jdelvare@suse.com>
+L: linux-hwmon@vger.kernel.org
+S: Maintained
+F: Documentation/hwmon/adt7475
+F: drivers/hwmon/adt7475.c
+
+ADXL34X THREE-AXIS DIGITAL ACCELEROMETER DRIVER (ADXL345/ADXL346)
+M: Michael Hennerich <michael.hennerich@analog.com>
+W: http://wiki.analog.com/ADXL345
+W: http://ez.analog.com/community/linux-device-drivers
+S: Supported
+F: drivers/input/misc/adxl34x.c
+
+ADVANSYS SCSI DRIVER
+M: Matthew Wilcox <matthew@wil.cx>
+M: Hannes Reinecke <hare@suse.com>
+L: linux-scsi@vger.kernel.org
+S: Maintained
+F: Documentation/scsi/advansys.txt
+F: drivers/scsi/advansys.c
+
+AEDSP16 DRIVER
+M: Riccardo Facchetti <fizban@tin.it>
+S: Maintained
+F: sound/oss/aedsp16.c
+
+AF9013 MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+W: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/media/dvb-frontends/af9013*
+
+AF9033 MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+W: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/media/dvb-frontends/af9033*
+
+AFFS FILE SYSTEM
+L: linux-fsdevel@vger.kernel.org
+S: Orphan
+F: Documentation/filesystems/affs.txt
+F: fs/affs/
+
+AFS FILESYSTEM & AF_RXRPC SOCKET DOMAIN
+M: David Howells <dhowells@redhat.com>
+L: linux-afs@lists.infradead.org
+S: Supported
+F: fs/afs/
+F: include/net/af_rxrpc.h
+F: net/rxrpc/af_rxrpc.c
+W: https://www.infradead.org/~dhowells/kafs/
+
+AGPGART DRIVER
+M: David Airlie <airlied@linux.ie>
+T: git git://people.freedesktop.org/~airlied/linux (part of drm maint)
+S: Maintained
+F: drivers/char/agp/
+F: include/linux/agp*
+F: include/uapi/linux/agp*
+
+AHA152X SCSI DRIVER
+M: "Juergen E. Fischer" <fischer@norbit.de>
+L: linux-scsi@vger.kernel.org
+S: Maintained
+F: drivers/scsi/aha152x*
+F: drivers/scsi/pcmcia/aha152x*
+
+AIC7XXX / AIC79XX SCSI DRIVER
+M: Hannes Reinecke <hare@suse.com>
+L: linux-scsi@vger.kernel.org
+S: Maintained
+F: drivers/scsi/aic7xxx/
+
+AIMSLAB FM RADIO RECEIVER DRIVER
+M: Hans Verkuil <hverkuil@xs4all.nl>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+W: https://linuxtv.org
+S: Maintained
+F: drivers/media/radio/radio-aimslab*
+
+AIO
+M: Benjamin LaHaise <bcrl@kvack.org>
+L: linux-aio@kvack.org
+S: Supported
+F: fs/aio.c
+F: include/linux/*aio*.h
+
+AIRSPY MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+W: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/media/usb/airspy/
+
+ALACRITECH GIGABIT ETHERNET DRIVER
+M: Lino Sanfilippo <LinoSanfilippo@gmx.de>
+S: Maintained
+F: drivers/net/ethernet/alacritech/*
+
+ALCATEL SPEEDTOUCH USB DRIVER
+M: Duncan Sands <duncan.sands@free.fr>
+L: linux-usb@vger.kernel.org
+W: http://www.linux-usb.org/SpeedTouch/
+S: Maintained
+F: drivers/usb/atm/speedtch.c
+F: drivers/usb/atm/usbatm.c
+
+ALCHEMY AU1XX0 MMC DRIVER
+M: Manuel Lauss <manuel.lauss@gmail.com>
+S: Maintained
+F: drivers/mmc/host/au1xmmc.c
+
+ALI1563 I2C DRIVER
+M: Rudolf Marek <r.marek@assembler.cz>
+L: linux-i2c@vger.kernel.org
+S: Maintained
+F: Documentation/i2c/busses/i2c-ali1563
+F: drivers/i2c/busses/i2c-ali1563.c
+
+ALLWINNER SECURITY SYSTEM
+M: Corentin Labbe <clabbe.montjoie@gmail.com>
+L: linux-crypto@vger.kernel.org
+S: Maintained
+F: drivers/crypto/sunxi-ss/
+
+ALPHA PORT
+M: Richard Henderson <rth@twiddle.net>
+M: Ivan Kokshaysky <ink@jurassic.park.msu.ru>
+M: Matt Turner <mattst88@gmail.com>
+S: Odd Fixes
+L: linux-alpha@vger.kernel.org
+F: arch/alpha/
+
+ALPS PS/2 TOUCHPAD DRIVER
+R: Pali Rohár <pali.rohar@gmail.com>
+F: drivers/input/mouse/alps.*
+
+ALTERA MAILBOX DRIVER
+M: Ley Foon Tan <lftan@altera.com>
+L: nios2-dev@lists.rocketboards.org (moderated for non-subscribers)
+S: Maintained
+F: drivers/mailbox/mailbox-altera.c
+
+ALTERA PIO DRIVER
+M: Tien Hock Loh <thloh@altera.com>
+L: linux-gpio@vger.kernel.org
+S: Maintained
+F: drivers/gpio/gpio-altera.c
+
+ALTERA SYSTEM RESOURCE DRIVER FOR ARRIA10 DEVKIT
+M: Thor Thayer <thor.thayer@linux.intel.com>
+S: Maintained
+F: drivers/gpio/gpio-altera-a10sr.c
+F: drivers/mfd/altera-a10sr.c
+F: drivers/reset/reset-a10sr.c
+F: include/linux/mfd/altera-a10sr.h
+F: include/dt-bindings/reset/altr,rst-mgr-a10sr.h
+
+ALTERA TRIPLE SPEED ETHERNET DRIVER
+M: Vince Bridgers <vbridger@opensource.altera.com>
+L: netdev@vger.kernel.org
+L: nios2-dev@lists.rocketboards.org (moderated for non-subscribers)
+S: Maintained
+F: drivers/net/ethernet/altera/
+
+ALTERA UART/JTAG UART SERIAL DRIVERS
+M: Tobias Klauser <tklauser@distanz.ch>
+L: linux-serial@vger.kernel.org
+L: nios2-dev@lists.rocketboards.org (moderated for non-subscribers)
+S: Maintained
+F: drivers/tty/serial/altera_uart.c
+F: drivers/tty/serial/altera_jtaguart.c
+F: include/linux/altera_uart.h
+F: include/linux/altera_jtaguart.h
+
+AMAZON ETHERNET DRIVERS
+M: Netanel Belgazal <netanel@annapurnalabs.com>
+R: Saeed Bishara <saeed@annapurnalabs.com>
+R: Zorik Machulsky <zorik@annapurnalabs.com>
+L: netdev@vger.kernel.org
+S: Supported
+F: Documentation/networking/ena.txt
+F: drivers/net/ethernet/amazon/
+
+AMD CRYPTOGRAPHIC COPROCESSOR (CCP) DRIVER
+M: Tom Lendacky <thomas.lendacky@amd.com>
+M: Gary Hook <gary.hook@amd.com>
+L: linux-crypto@vger.kernel.org
+S: Supported
+F: drivers/crypto/ccp/
+F: include/linux/ccp.h
+
+AMD FAM15H PROCESSOR POWER MONITORING DRIVER
+M: Huang Rui <ray.huang@amd.com>
+L: linux-hwmon@vger.kernel.org
+S: Supported
+F: Documentation/hwmon/fam15h_power
+F: drivers/hwmon/fam15h_power.c
+
+AMD GEODE CS5536 USB DEVICE CONTROLLER DRIVER
+L: linux-geode@lists.infradead.org (moderated for non-subscribers)
+S: Orphan
+F: drivers/usb/gadget/udc/amd5536udc.*
+
+AMD GEODE PROCESSOR/CHIPSET SUPPORT
+P: Andres Salomon <dilinger@queued.net>
+L: linux-geode@lists.infradead.org (moderated for non-subscribers)
+W: http://www.amd.com/us-en/ConnectivitySolutions/TechnicalResources/0,,50_2334_2452_11363,00.html
+S: Supported
+F: drivers/char/hw_random/geode-rng.c
+F: drivers/crypto/geode*
+F: drivers/video/fbdev/geode/
+F: arch/x86/include/asm/geode.h
+
+AMD IOMMU (AMD-VI)
+M: Joerg Roedel <joro@8bytes.org>
+L: iommu@lists.linux-foundation.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu.git
+S: Maintained
+F: drivers/iommu/amd_iommu*.[ch]
+F: include/linux/amd-iommu.h
+
+AMD KFD
+M: Oded Gabbay <oded.gabbay@gmail.com>
+L: dri-devel@lists.freedesktop.org
+T: git git://people.freedesktop.org/~gabbayo/linux.git
+S: Supported
+F: drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c
+F: drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h
+F: drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v7.c
+F: drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v8.c
+F: drivers/gpu/drm/amd/amdkfd/
+F: drivers/gpu/drm/amd/include/cik_structs.h
+F: drivers/gpu/drm/amd/include/kgd_kfd_interface.h
+F: drivers/gpu/drm/amd/include/vi_structs.h
+F: drivers/gpu/drm/radeon/radeon_kfd.c
+F: drivers/gpu/drm/radeon/radeon_kfd.h
+F: include/uapi/linux/kfd_ioctl.h
+
+AMD SEATTLE DEVICE TREE SUPPORT
+M: Brijesh Singh <brijeshkumar.singh@amd.com>
+M: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
+M: Tom Lendacky <thomas.lendacky@amd.com>
+S: Supported
+F: arch/arm64/boot/dts/amd/
+
+AMD XGBE DRIVER
+M: Tom Lendacky <thomas.lendacky@amd.com>
+L: netdev@vger.kernel.org
+S: Supported
+F: drivers/net/ethernet/amd/xgbe/
+F: arch/arm64/boot/dts/amd/amd-seattle-xgbe*.dtsi
+
+AMS (Apple Motion Sensor) DRIVER
+M: Michael Hanselmann <linux-kernel@hansmi.ch>
+S: Supported
+F: drivers/macintosh/ams/
+
+ANALOG DEVICES INC AD9389B DRIVER
+M: Hans Verkuil <hans.verkuil@cisco.com>
+L: linux-media@vger.kernel.org
+S: Maintained
+F: drivers/media/i2c/ad9389b*
+
+ANALOG DEVICES INC ADV7180 DRIVER
+M: Lars-Peter Clausen <lars@metafoo.de>
+L: linux-media@vger.kernel.org
+W: http://ez.analog.com/community/linux-device-drivers
+S: Supported
+F: drivers/media/i2c/adv7180.c
+
+ANALOG DEVICES INC ADV7511 DRIVER
+M: Hans Verkuil <hans.verkuil@cisco.com>
+L: linux-media@vger.kernel.org
+S: Maintained
+F: drivers/media/i2c/adv7511*
+
+ANALOG DEVICES INC ADV7604 DRIVER
+M: Hans Verkuil <hans.verkuil@cisco.com>
+L: linux-media@vger.kernel.org
+S: Maintained
+F: drivers/media/i2c/adv7604*
+
+ANALOG DEVICES INC ADV7842 DRIVER
+M: Hans Verkuil <hans.verkuil@cisco.com>
+L: linux-media@vger.kernel.org
+S: Maintained
+F: drivers/media/i2c/adv7842*
+
+ANALOG DEVICES INC ASOC CODEC DRIVERS
+M: Lars-Peter Clausen <lars@metafoo.de>
+L: alsa-devel@alsa-project.org (moderated for non-subscribers)
+W: http://wiki.analog.com/
+W: http://ez.analog.com/community/linux-device-drivers
+S: Supported
+F: sound/soc/codecs/adau*
+F: sound/soc/codecs/adav*
+F: sound/soc/codecs/ad1*
+F: sound/soc/codecs/ad7*
+F: sound/soc/codecs/ssm*
+F: sound/soc/codecs/sigmadsp.*
+
+ANALOG DEVICES INC ASOC DRIVERS
+L: adi-buildroot-devel@lists.sourceforge.net (moderated for non-subscribers)
+L: alsa-devel@alsa-project.org (moderated for non-subscribers)
+W: http://blackfin.uclinux.org/
+S: Supported
+F: sound/soc/blackfin/*
+
+ANALOG DEVICES INC IIO DRIVERS
+M: Lars-Peter Clausen <lars@metafoo.de>
+M: Michael Hennerich <Michael.Hennerich@analog.com>
+W: http://wiki.analog.com/
+W: http://ez.analog.com/community/linux-device-drivers
+S: Supported
+F: drivers/iio/*/ad*
+F: drivers/iio/adc/ltc2497*
+X: drivers/iio/*/adjd*
+F: drivers/staging/iio/*/ad*
+F: drivers/staging/iio/trigger/iio-trig-bfin-timer.c
+
+ANALOG DEVICES INC DMA DRIVERS
+M: Lars-Peter Clausen <lars@metafoo.de>
+W: http://ez.analog.com/community/linux-device-drivers
+S: Supported
+F: drivers/dma/dma-axi-dmac.c
+
+ANDROID CONFIG FRAGMENTS
+M: Rob Herring <robh@kernel.org>
+S: Supported
+F: kernel/configs/android*
+
+ANDROID DRIVERS
+M: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+M: Arve Hjønnevåg <arve@android.com>
+M: Riley Andrews <riandrews@android.com>
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging.git
+L: devel@driverdev.osuosl.org
+S: Supported
+F: drivers/android/
+F: drivers/staging/android/
+
+ANDROID ION DRIVER
+M: Laura Abbott <labbott@redhat.com>
+M: Sumit Semwal <sumit.semwal@linaro.org>
+L: devel@driverdev.osuosl.org
+S: Supported
+F: drivers/staging/android/ion
+F: drivers/staging/android/uapi/ion.h
+F: drivers/staging/android/uapi/ion_test.h
+
+AOA (Apple Onboard Audio) ALSA DRIVER
+M: Johannes Berg <johannes@sipsolutions.net>
+L: linuxppc-dev@lists.ozlabs.org
+L: alsa-devel@alsa-project.org (moderated for non-subscribers)
+S: Maintained
+F: sound/aoa/
+
+APEX EMBEDDED SYSTEMS STX104 IIO DRIVER
+M: William Breathitt Gray <vilhelm.gray@gmail.com>
+L: linux-iio@vger.kernel.org
+S: Maintained
+F: drivers/iio/adc/stx104.c
+
+APM DRIVER
+M: Jiri Kosina <jikos@kernel.org>
+S: Odd fixes
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/jikos/apm.git
+F: arch/x86/kernel/apm_32.c
+F: include/linux/apm_bios.h
+F: include/uapi/linux/apm_bios.h
+F: drivers/char/apm-emulation.c
+
+APPLE BCM5974 MULTITOUCH DRIVER
+M: Henrik Rydberg <rydberg@bitmath.org>
+L: linux-input@vger.kernel.org
+S: Odd fixes
+F: drivers/input/mouse/bcm5974.c
+
+APPLE SMC DRIVER
+M: Henrik Rydberg <rydberg@bitmath.org>
+L: linux-hwmon@vger.kernel.org
+S: Odd fixes
+F: drivers/hwmon/applesmc.c
+
+APPLETALK NETWORK LAYER
+L: netdev@vger.kernel.org
+S: Odd fixes
+F: drivers/net/appletalk/
+F: net/appletalk/
+
+APPLIED MICRO (APM) X-GENE DEVICE TREE SUPPORT
+M: Duc Dang <dhdang@apm.com>
+S: Supported
+F: arch/arm64/boot/dts/apm/
+
+APPLIED MICRO (APM) X-GENE SOC ETHERNET DRIVER
+M: Iyappan Subramanian <isubramanian@apm.com>
+M: Keyur Chudgar <kchudgar@apm.com>
+M: Quan Nguyen <qnguyen@apm.com>
+S: Supported
+F: drivers/net/ethernet/apm/xgene/
+F: drivers/net/phy/mdio-xgene.c
+F: Documentation/devicetree/bindings/net/apm-xgene-enet.txt
+F: Documentation/devicetree/bindings/net/apm-xgene-mdio.txt
+
+APPLIED MICRO (APM) X-GENE SOC ETHERNET (V2) DRIVER
+M: Iyappan Subramanian <isubramanian@apm.com>
+M: Keyur Chudgar <kchudgar@apm.com>
+S: Supported
+F: drivers/net/ethernet/apm/xgene-v2/
+
+APPLIED MICRO (APM) X-GENE SOC PMU
+M: Tai Nguyen <ttnguyen@apm.com>
+S: Supported
+F: drivers/perf/xgene_pmu.c
+F: Documentation/perf/xgene-pmu.txt
+F: Documentation/devicetree/bindings/perf/apm-xgene-pmu.txt
+
+APTINA CAMERA SENSOR PLL
+M: Laurent Pinchart <Laurent.pinchart@ideasonboard.com>
+L: linux-media@vger.kernel.org
+S: Maintained
+F: drivers/media/i2c/aptina-pll.*
+
+ARC FRAMEBUFFER DRIVER
+M: Jaya Kumar <jayalk@intworks.biz>
+S: Maintained
+F: drivers/video/fbdev/arcfb.c
+F: drivers/video/fbdev/core/fb_defio.c
+
+ARCNET NETWORK LAYER
+M: Michael Grzeschik <m.grzeschik@pengutronix.de>
+L: netdev@vger.kernel.org
+S: Maintained
+F: drivers/net/arcnet/
+F: include/uapi/linux/if_arcnet.h
+
+ARC PGU DRM DRIVER
+M: Alexey Brodkin <abrodkin@synopsys.com>
+S: Supported
+F: drivers/gpu/drm/arc/
+F: Documentation/devicetree/bindings/display/snps,arcpgu.txt
+
+ARM ARCHITECTED TIMER DRIVER
+M: Mark Rutland <mark.rutland@arm.com>
+M: Marc Zyngier <marc.zyngier@arm.com>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+F: arch/arm/include/asm/arch_timer.h
+F: arch/arm64/include/asm/arch_timer.h
+F: drivers/clocksource/arm_arch_timer.c
+
+ARM HDLCD DRM DRIVER
+M: Liviu Dudau <liviu.dudau@arm.com>
+S: Supported
+F: drivers/gpu/drm/arm/hdlcd_*
+F: Documentation/devicetree/bindings/display/arm,hdlcd.txt
+
+ARM MALI-DP DRM DRIVER
+M: Liviu Dudau <liviu.dudau@arm.com>
+M: Brian Starkey <brian.starkey@arm.com>
+M: Mali DP Maintainers <malidp@foss.arm.com>
+S: Supported
+F: drivers/gpu/drm/arm/
+F: Documentation/devicetree/bindings/display/arm,malidp.txt
+
+ARM MFM AND FLOPPY DRIVERS
+M: Ian Molton <spyro@f2s.com>
+S: Maintained
+F: arch/arm/lib/floppydma.S
+F: arch/arm/include/asm/floppy.h
+
+ARM PMU PROFILING AND DEBUGGING
+M: Will Deacon <will.deacon@arm.com>
+M: Mark Rutland <mark.rutland@arm.com>
+S: Maintained
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+F: arch/arm*/kernel/perf_*
+F: arch/arm/oprofile/common.c
+F: arch/arm*/kernel/hw_breakpoint.c
+F: arch/arm*/include/asm/hw_breakpoint.h
+F: arch/arm*/include/asm/perf_event.h
+F: drivers/perf/*
+F: include/linux/perf/arm_pmu.h
+F: Documentation/devicetree/bindings/arm/pmu.txt
+F: Documentation/devicetree/bindings/perf/
+
+ARM PORT
+M: Russell King <linux@armlinux.org.uk>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+W: http://www.armlinux.org.uk/
+S: Maintained
+T: git git://git.armlinux.org.uk/~rmk/linux-arm.git
+F: arch/arm/
+
+ARM SUB-ARCHITECTURES
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+F: arch/arm/mach-*/
+F: arch/arm/plat-*/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc.git
+
+ARM PRIMECELL AACI PL041 DRIVER
+M: Russell King <linux@armlinux.org.uk>
+S: Maintained
+F: sound/arm/aaci.*
+
+ARM PRIMECELL CLCD PL110 DRIVER
+M: Russell King <linux@armlinux.org.uk>
+S: Maintained
+F: drivers/video/fbdev/amba-clcd.*
+
+ARM PRIMECELL KMI PL050 DRIVER
+M: Russell King <linux@armlinux.org.uk>
+S: Maintained
+F: drivers/input/serio/ambakmi.*
+F: include/linux/amba/kmi.h
+
+ARM PRIMECELL MMCI PL180/1 DRIVER
+M: Russell King <linux@armlinux.org.uk>
+S: Maintained
+F: drivers/mmc/host/mmci.*
+F: include/linux/amba/mmci.h
+
+ARM PRIMECELL UART PL010 AND PL011 DRIVERS
+M: Russell King <linux@armlinux.org.uk>
+S: Maintained
+F: drivers/tty/serial/amba-pl01*.c
+F: include/linux/amba/serial.h
+
+ARM PRIMECELL BUS SUPPORT
+M: Russell King <linux@armlinux.org.uk>
+S: Maintained
+F: drivers/amba/
+F: include/linux/amba/bus.h
+
+ARM/ADS SPHERE MACHINE SUPPORT
+M: Lennert Buytenhek <kernel@wantstofly.org>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+
+ARM/AFEB9260 MACHINE SUPPORT
+M: Sergey Lapin <slapin@ossfans.org>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+
+ARM/AJECO 1ARM MACHINE SUPPORT
+M: Lennert Buytenhek <kernel@wantstofly.org>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+
+ARM/Allwinner sunXi SoC support
+M: Maxime Ripard <maxime.ripard@free-electrons.com>
+M: Chen-Yu Tsai <wens@csie.org>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+N: sun[x456789]i
+N: sun50i
+F: arch/arm/mach-sunxi/
+F: arch/arm64/boot/dts/allwinner/
+F: drivers/clk/sunxi-ng/
+F: drivers/pinctrl/sunxi/
+F: drivers/soc/sunxi/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/sunxi/linux.git
+
+ARM/Allwinner SoC Clock Support
+M: Emilio López <emilio@elopez.com.ar>
+S: Maintained
+F: drivers/clk/sunxi/
+
+ARM/Amlogic Meson SoC support
+M: Carlo Caione <carlo@caione.org>
+M: Kevin Hilman <khilman@baylibre.com>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+L: linux-amlogic@lists.infradead.org
+W: http://linux-meson.com/
+S: Maintained
+F: arch/arm/mach-meson/
+F: arch/arm/boot/dts/meson*
+F: arch/arm64/boot/dts/amlogic/
+F: drivers/pinctrl/meson/
+F: drivers/mmc/host/meson*
+N: meson
+
+ARM/Amlogic Meson SoC CLOCK FRAMEWORK
+M: Neil Armstrong <narmstrong@baylibre.com>
+M: Jerome Brunet <jbrunet@baylibre.com>
+L: linux-amlogic@lists.infradead.org
+S: Maintained
+F: drivers/clk/meson/
+F: include/dt-bindings/clock/meson*
+F: include/dt-bindings/clock/gxbb*
+F: Documentation/devicetree/bindings/clock/amlogic*
+
+ARM/Annapurna Labs ALPINE ARCHITECTURE
+M: Tsahee Zidenberg <tsahee@annapurnalabs.com>
+M: Antoine Tenart <antoine.tenart@free-electrons.com>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+F: arch/arm/mach-alpine/
+F: arch/arm/boot/dts/alpine*
+F: arch/arm64/boot/dts/al/
+F: drivers/*/*alpine*
+
+ARM/ARTPEC MACHINE SUPPORT
+M: Jesper Nilsson <jesper.nilsson@axis.com>
+M: Lars Persson <lars.persson@axis.com>
+M: Niklas Cassel <niklas.cassel@axis.com>
+S: Maintained
+L: linux-arm-kernel@axis.com
+F: arch/arm/mach-artpec
+F: arch/arm/boot/dts/artpec6*
+F: drivers/clk/axis
+F: drivers/pinctrl/pinctrl-artpec*
+F: Documentation/devicetree/bindings/pinctrl/axis,artpec6-pinctrl.txt
+
+ARM/ASPEED MACHINE SUPPORT
+M: Joel Stanley <joel@jms.id.au>
+S: Maintained
+F: arch/arm/mach-aspeed/
+F: arch/arm/boot/dts/aspeed-*
+F: drivers/*/*aspeed*
+
+ARM/ATMEL AT91RM9200, AT91SAM9 AND SAMA5 SOC SUPPORT
+M: Nicolas Ferre <nicolas.ferre@microchip.com>
+M: Alexandre Belloni <alexandre.belloni@free-electrons.com>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+W: http://www.linux4sam.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/nferre/linux-at91.git
+S: Supported
+F: arch/arm/mach-at91/
+F: include/soc/at91/
+F: arch/arm/boot/dts/at91*.dts
+F: arch/arm/boot/dts/at91*.dtsi
+F: arch/arm/boot/dts/sama*.dts
+F: arch/arm/boot/dts/sama*.dtsi
+F: arch/arm/include/debug/at91.S
+F: drivers/memory/atmel*
+
+ARM/ATMEL AT91 Clock Support
+M: Boris Brezillon <boris.brezillon@free-electrons.com>
+S: Maintained
+F: drivers/clk/at91
+
+ARM/CALXEDA HIGHBANK ARCHITECTURE
+M: Rob Herring <robh@kernel.org>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+F: arch/arm/mach-highbank/
+F: arch/arm/boot/dts/highbank.dts
+F: arch/arm/boot/dts/ecx-*.dts*
+
+ARM/CAVIUM NETWORKS CNS3XXX MACHINE SUPPORT
+M: Krzysztof Halasa <khalasa@piap.pl>
+S: Maintained
+F: arch/arm/mach-cns3xxx/
+
+ARM/CAVIUM THUNDER NETWORK DRIVER
+M: Sunil Goutham <sgoutham@cavium.com>
+M: Robert Richter <rric@kernel.org>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Supported
+F: drivers/net/ethernet/cavium/thunder/
+
+ARM/CIRRUS LOGIC CLPS711X ARM ARCHITECTURE
+M: Alexander Shiyan <shc_work@mail.ru>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Odd Fixes
+N: clps711x
+
+ARM/CIRRUS LOGIC EP93XX ARM ARCHITECTURE
+M: Hartley Sweeten <hsweeten@visionengravers.com>
+M: Alexander Sverdlin <alexander.sverdlin@gmail.com>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+F: arch/arm/mach-ep93xx/
+F: arch/arm/mach-ep93xx/include/mach/
+
+ARM/CIRRUS LOGIC EDB9315A MACHINE SUPPORT
+M: Lennert Buytenhek <kernel@wantstofly.org>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+
+ARM/CLKDEV SUPPORT
+M: Russell King <linux@armlinux.org.uk>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+T: git git://git.armlinux.org.uk/~rmk/linux-arm.git clkdev
+F: arch/arm/include/asm/clkdev.h
+F: drivers/clk/clkdev.c
+
+ARM/COMPULAB CM-X270/EM-X270 and CM-X300 MACHINE SUPPORT
+M: Mike Rapoport <mike@compulab.co.il>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+
+ARM/CONTEC MICRO9 MACHINE SUPPORT
+M: Hubert Feurstein <hubert.feurstein@contec.at>
+S: Maintained
+F: arch/arm/mach-ep93xx/micro9.c
+
+ARM/CORESIGHT FRAMEWORK AND DRIVERS
+M: Mathieu Poirier <mathieu.poirier@linaro.org>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+F: drivers/hwtracing/coresight/*
+F: Documentation/trace/coresight.txt
+F: Documentation/devicetree/bindings/arm/coresight.txt
+F: Documentation/ABI/testing/sysfs-bus-coresight-devices-*
+F: tools/perf/arch/arm/util/pmu.c
+F: tools/perf/arch/arm/util/auxtrace.c
+F: tools/perf/arch/arm/util/cs-etm.c
+F: tools/perf/arch/arm/util/cs-etm.h
+F: tools/perf/util/cs-etm.h
+
+ARM/CORGI MACHINE SUPPORT
+M: Richard Purdie <rpurdie@rpsys.net>
+S: Maintained
+
+ARM/CORTINA SYSTEMS GEMINI ARM ARCHITECTURE
+M: Hans Ulli Kroll <ulli.kroll@googlemail.com>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+T: git git://github.com/ulli-kroll/linux.git
+S: Maintained
+F: arch/arm/mach-gemini/
+F: drivers/rtc/rtc-gemini.c
+
+ARM/CSR SIRFPRIMA2 MACHINE SUPPORT
+M: Barry Song <baohua@kernel.org>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/baohua/linux.git
+S: Maintained
+F: arch/arm/boot/dts/prima2*
+F: arch/arm/mach-prima2/
+F: drivers/clk/sirf/
+F: drivers/clocksource/timer-prima2.c
+F: drivers/clocksource/timer-atlas7.c
+N: [^a-z]sirf
+
+ARM/CONEXANT DIGICOLOR MACHINE SUPPORT
+M: Baruch Siach <baruch@tkos.co.il>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+F: arch/arm/boot/dts/cx92755*
+N: digicolor
+
+ARM/EBSA110 MACHINE SUPPORT
+M: Russell King <linux@armlinux.org.uk>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+W: http://www.armlinux.org.uk/
+S: Maintained
+F: arch/arm/mach-ebsa110/
+F: drivers/net/ethernet/amd/am79c961a.*
+
+ARM/ENERGY MICRO (SILICON LABS) EFM32 SUPPORT
+M: Uwe Kleine-König <kernel@pengutronix.de>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+N: efm32
+
+ARM/EZX SMARTPHONES (A780, A910, A1200, E680, ROKR E2 and ROKR E6)
+M: Robert Jarzmik <robert.jarzmik@free.fr>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+F: arch/arm/mach-pxa/ezx.c
+
+ARM/FARADAY FA526 PORT
+M: Hans Ulli Kroll <ulli.kroll@googlemail.com>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+T: git git://git.berlios.de/gemini-board
+F: arch/arm/mm/*-fa*
+
+ARM/FOOTBRIDGE ARCHITECTURE
+M: Russell King <linux@armlinux.org.uk>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+W: http://www.armlinux.org.uk/
+S: Maintained
+F: arch/arm/include/asm/hardware/dec21285.h
+F: arch/arm/mach-footbridge/
+
+ARM/FREESCALE IMX / MXC ARM ARCHITECTURE
+M: Shawn Guo <shawnguo@kernel.org>
+M: Sascha Hauer <kernel@pengutronix.de>
+R: Fabio Estevam <fabio.estevam@nxp.com>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/shawnguo/linux.git
+F: arch/arm/mach-imx/
+F: arch/arm/mach-mxs/
+F: arch/arm/boot/dts/imx*
+F: arch/arm/configs/imx*_defconfig
+F: drivers/clk/imx/
+F: drivers/soc/imx/
+F: include/soc/imx/
+
+ARM/FREESCALE VYBRID ARM ARCHITECTURE
+M: Shawn Guo <shawnguo@kernel.org>
+M: Sascha Hauer <kernel@pengutronix.de>
+R: Stefan Agner <stefan@agner.ch>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/shawnguo/linux.git
+F: arch/arm/mach-imx/*vf610*
+F: arch/arm/boot/dts/vf*
+
+ARM/GLOMATION GESBC9312SX MACHINE SUPPORT
+M: Lennert Buytenhek <kernel@wantstofly.org>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+
+ARM/GUMSTIX MACHINE SUPPORT
+M: Steve Sakoman <sakoman@gmail.com>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+
+ARM/H4700 (HP IPAQ HX4700) MACHINE SUPPORT
+M: Philipp Zabel <philipp.zabel@gmail.com>
+M: Paul Parsons <lost.distance@yahoo.com>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+F: arch/arm/mach-pxa/hx4700.c
+F: arch/arm/mach-pxa/include/mach/hx4700.h
+F: sound/soc/pxa/hx4700.c
+
+ARM/HISILICON SOC SUPPORT
+M: Wei Xu <xuwei5@hisilicon.com>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+W: http://www.hisilicon.com
+S: Supported
+T: git git://github.com/hisilicon/linux-hisi.git
+F: arch/arm/mach-hisi/
+F: arch/arm/boot/dts/hi3*
+F: arch/arm/boot/dts/hip*
+F: arch/arm/boot/dts/hisi*
+F: arch/arm64/boot/dts/hisilicon/
+
+ARM/HP JORNADA 7XX MACHINE SUPPORT
+M: Kristoffer Ericson <kristoffer.ericson@gmail.com>
+W: www.jlime.com
+S: Maintained
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/kristoffer/linux-hpc.git
+F: arch/arm/mach-sa1100/jornada720.c
+F: arch/arm/mach-sa1100/include/mach/jornada720.h
+
+ARM/IGEP MACHINE SUPPORT
+M: Enric Balletbo i Serra <eballetbo@gmail.com>
+M: Javier Martinez Canillas <javier@dowhile0.org>
+L: linux-omap@vger.kernel.org
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+F: arch/arm/boot/dts/omap3-igep*
+
+ARM/INCOME PXA270 SUPPORT
+M: Marek Vasut <marek.vasut@gmail.com>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+F: arch/arm/mach-pxa/colibri-pxa270-income.c
+
+ARM/INTEL IOP32X ARM ARCHITECTURE
+M: Lennert Buytenhek <kernel@wantstofly.org>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+
+ARM/INTEL IOP33X ARM ARCHITECTURE
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Orphan
+
+ARM/INTEL IOP13XX ARM ARCHITECTURE
+M: Lennert Buytenhek <kernel@wantstofly.org>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+
+ARM/INTEL IQ81342EX MACHINE SUPPORT
+M: Lennert Buytenhek <kernel@wantstofly.org>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+
+ARM/INTEL IXDP2850 MACHINE SUPPORT
+M: Lennert Buytenhek <kernel@wantstofly.org>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+
+ARM/INTEL IXP4XX ARM ARCHITECTURE
+M: Imre Kaloz <kaloz@openwrt.org>
+M: Krzysztof Halasa <khalasa@piap.pl>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+F: arch/arm/mach-ixp4xx/
+
+ARM/INTEL RESEARCH IMOTE/STARGATE 2 MACHINE SUPPORT
+M: Jonathan Cameron <jic23@cam.ac.uk>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+F: arch/arm/mach-pxa/stargate2.c
+F: drivers/pcmcia/pxa2xx_stargate2.c
+
+ARM/INTEL XSC3 (MANZANO) ARM CORE
+M: Lennert Buytenhek <kernel@wantstofly.org>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+
+ARM/IP FABRICS DOUBLE ESPRESSO MACHINE SUPPORT
+M: Lennert Buytenhek <kernel@wantstofly.org>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+
+ARM/TEXAS INSTRUMENT KEYSTONE ARCHITECTURE
+M: Santosh Shilimkar <ssantosh@kernel.org>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+F: arch/arm/mach-keystone/
+F: arch/arm/boot/dts/keystone-*
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/ssantosh/linux-keystone.git
+
+ARM/TEXAS INSTRUMENT KEYSTONE CLOCK FRAMEWORK
+M: Santosh Shilimkar <ssantosh@kernel.org>
+L: linux-kernel@vger.kernel.org
+S: Maintained
+F: drivers/clk/keystone/
+
+ARM/TEXAS INSTRUMENT KEYSTONE ClOCKSOURCE
+M: Santosh Shilimkar <ssantosh@kernel.org>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+L: linux-kernel@vger.kernel.org
+S: Maintained
+F: drivers/clocksource/timer-keystone.c
+
+ARM/TEXAS INSTRUMENT KEYSTONE RESET DRIVER
+M: Santosh Shilimkar <ssantosh@kernel.org>
+L: linux-kernel@vger.kernel.org
+S: Maintained
+F: drivers/power/reset/keystone-reset.c
+
+ARM/TEXAS INSTRUMENT AEMIF/EMIF DRIVERS
+M: Santosh Shilimkar <ssantosh@kernel.org>
+L: linux-kernel@vger.kernel.org
+S: Maintained
+F: drivers/memory/*emif*
+
+ARM/LG1K ARCHITECTURE
+M: Chanho Min <chanho.min@lge.com>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+F: arch/arm64/boot/dts/lg/
+
+ARM/LOGICPD PXA270 MACHINE SUPPORT
+M: Lennert Buytenhek <kernel@wantstofly.org>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+
+ARM/LPC18XX ARCHITECTURE
+M: Joachim Eastwood <manabian@gmail.com>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+F: arch/arm/boot/dts/lpc43*
+F: drivers/clk/nxp/clk-lpc18xx*
+F: drivers/clocksource/time-lpc32xx.c
+F: drivers/i2c/busses/i2c-lpc2k.c
+F: drivers/memory/pl172.c
+F: drivers/mtd/spi-nor/nxp-spifi.c
+F: drivers/rtc/rtc-lpc24xx.c
+N: lpc18xx
+
+ARM/LPC32XX SOC SUPPORT
+M: Vladimir Zapolskiy <vz@mleia.com>
+M: Sylvain Lemieux <slemieux.tyco@gmail.com>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+T: git git://github.com/vzapolskiy/linux-lpc32xx.git
+S: Maintained
+F: arch/arm/boot/dts/lpc32*
+F: arch/arm/mach-lpc32xx/
+F: drivers/i2c/busses/i2c-pnx.c
+F: drivers/net/ethernet/nxp/lpc_eth.c
+F: drivers/usb/host/ohci-nxp.c
+F: drivers/watchdog/pnx4008_wdt.c
+N: lpc32xx
+
+ARM/MAGICIAN MACHINE SUPPORT
+M: Philipp Zabel <philipp.zabel@gmail.com>
+S: Maintained
+
+ARM/Marvell Kirkwood and Armada 370, 375, 38x, 39x, XP, 3700, 7K/8K SOC support
+M: Jason Cooper <jason@lakedaemon.net>
+M: Andrew Lunn <andrew@lunn.ch>
+M: Gregory Clement <gregory.clement@free-electrons.com>
+M: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+F: arch/arm/boot/dts/armada*
+F: arch/arm/boot/dts/kirkwood*
+F: arch/arm/configs/mvebu_*_defconfig
+F: arch/arm/mach-mvebu/
+F: arch/arm64/boot/dts/marvell/armada*
+F: drivers/cpufreq/mvebu-cpufreq.c
+F: drivers/irqchip/irq-armada-370-xp.c
+F: drivers/irqchip/irq-mvebu-*
+F: drivers/rtc/rtc-armada38x.c
+
+ARM/Marvell Berlin SoC support
+M: Jisheng Zhang <jszhang@marvell.com>
+M: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+F: arch/arm/mach-berlin/
+F: arch/arm/boot/dts/berlin*
+F: arch/arm64/boot/dts/marvell/berlin*
+
+
+ARM/Marvell Dove/MV78xx0/Orion SOC support
+M: Jason Cooper <jason@lakedaemon.net>
+M: Andrew Lunn <andrew@lunn.ch>
+M: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
+M: Gregory Clement <gregory.clement@free-electrons.com>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+F: Documentation/devicetree/bindings/soc/dove/
+F: arch/arm/mach-dove/
+F: arch/arm/mach-mv78xx0/
+F: arch/arm/mach-orion5x/
+F: arch/arm/plat-orion/
+F: arch/arm/boot/dts/dove*
+F: arch/arm/boot/dts/orion5x*
+
+
+ARM/Orion SoC/Technologic Systems TS-78xx platform support
+M: Alexander Clouter <alex@digriz.org.uk>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+W: http://www.digriz.org.uk/ts78xx/kernel
+S: Maintained
+F: arch/arm/mach-orion5x/ts78xx-*
+
+ARM/OXNAS platform support
+M: Neil Armstrong <narmstrong@baylibre.com>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+L: linux-oxnas@lists.tuxfamily.org (moderated for non-subscribers)
+S: Maintained
+F: arch/arm/mach-oxnas/
+F: arch/arm/boot/dts/ox8*.dtsi
+F: arch/arm/boot/dts/wd-mbwe.dts
+F: arch/arm/boot/dts/cloudengines-pogoplug-series-3.dts
+N: oxnas
+
+ARM/Mediatek RTC DRIVER
+M: Eddie Huang <eddie.huang@mediatek.com>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+L: linux-mediatek@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+F: drivers/rtc/rtc-mt6397.c
+
+ARM/Mediatek SoC support
+M: Matthias Brugger <matthias.bgg@gmail.com>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+L: linux-mediatek@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+F: arch/arm/boot/dts/mt6*
+F: arch/arm/boot/dts/mt7*
+F: arch/arm/boot/dts/mt8*
+F: arch/arm/mach-mediatek/
+F: arch/arm64/boot/dts/mediatek/
+N: mtk
+K: mediatek
+
+ARM/Mediatek USB3 PHY DRIVER
+M: Chunfeng Yun <chunfeng.yun@mediatek.com>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+L: linux-mediatek@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+F: drivers/phy/phy-mt65xx-usb3.c
+
+ARM/MICREL KS8695 ARCHITECTURE
+M: Greg Ungerer <gerg@uclinux.org>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+F: arch/arm/mach-ks8695/
+S: Odd Fixes
+
+ARM/MIOA701 MACHINE SUPPORT
+M: Robert Jarzmik <robert.jarzmik@free.fr>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+F: arch/arm/mach-pxa/mioa701.c
+S: Maintained
+
+ARM/NEC MOBILEPRO 900/c MACHINE SUPPORT
+M: Michael Petchkovsky <mkpetch@internode.on.net>
+S: Maintained
+
+ARM/NOMADIK ARCHITECTURE
+M: Alessandro Rubini <rubini@unipv.it>
+M: Linus Walleij <linus.walleij@linaro.org>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+F: arch/arm/mach-nomadik/
+F: drivers/pinctrl/nomadik/
+F: drivers/i2c/busses/i2c-nomadik.c
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-nomadik.git
+
+ARM/OPENMOKO NEO FREERUNNER (GTA02) MACHINE SUPPORT
+M: Nelson Castillo <arhuaco@freaks-unidos.net>
+L: openmoko-kernel@lists.openmoko.org (subscribers-only)
+W: http://wiki.openmoko.org/wiki/Neo_FreeRunner
+S: Supported
+
+ARM/TOSA MACHINE SUPPORT
+M: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
+M: Dirk Opfer <dirk@opfer-online.de>
+S: Maintained
+
+ARM/PALMTX,PALMT5,PALMLD,PALMTE2,PALMTC SUPPORT
+M: Marek Vasut <marek.vasut@gmail.com>
+L: linux-arm-kernel@lists.infradead.org
+W: http://hackndev.com
+S: Maintained
+F: arch/arm/mach-pxa/include/mach/palmtx.h
+F: arch/arm/mach-pxa/palmtx.c
+F: arch/arm/mach-pxa/include/mach/palmt5.h
+F: arch/arm/mach-pxa/palmt5.c
+F: arch/arm/mach-pxa/include/mach/palmld.h
+F: arch/arm/mach-pxa/palmld.c
+F: arch/arm/mach-pxa/include/mach/palmte2.h
+F: arch/arm/mach-pxa/palmte2.c
+F: arch/arm/mach-pxa/include/mach/palmtc.h
+F: arch/arm/mach-pxa/palmtc.c
+
+ARM/PALM TREO SUPPORT
+M: Tomas Cech <sleep_walker@suse.com>
+L: linux-arm-kernel@lists.infradead.org
+W: http://hackndev.com
+S: Maintained
+F: arch/arm/mach-pxa/include/mach/palmtreo.h
+F: arch/arm/mach-pxa/palmtreo.c
+
+ARM/PALMZ72 SUPPORT
+M: Sergey Lapin <slapin@ossfans.org>
+L: linux-arm-kernel@lists.infradead.org
+W: http://hackndev.com
+S: Maintained
+F: arch/arm/mach-pxa/include/mach/palmz72.h
+F: arch/arm/mach-pxa/palmz72.c
+
+ARM/PLEB SUPPORT
+M: Peter Chubb <pleb@gelato.unsw.edu.au>
+W: http://www.disy.cse.unsw.edu.au/Hardware/PLEB
+S: Maintained
+
+ARM/PT DIGITAL BOARD PORT
+M: Stefan Eletzhofer <stefan.eletzhofer@eletztrick.de>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+W: http://www.armlinux.org.uk/
+S: Maintained
+
+ARM/QUALCOMM SUPPORT
+M: Andy Gross <andy.gross@linaro.org>
+M: David Brown <david.brown@linaro.org>
+L: linux-arm-msm@vger.kernel.org
+L: linux-soc@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/soc/qcom/
+F: arch/arm/boot/dts/qcom-*.dts
+F: arch/arm/boot/dts/qcom-*.dtsi
+F: arch/arm/mach-qcom/
+F: arch/arm64/boot/dts/qcom/*
+F: drivers/i2c/busses/i2c-qup.c
+F: drivers/clk/qcom/
+F: drivers/pinctrl/qcom/
+F: drivers/dma/qcom/
+F: drivers/soc/qcom/
+F: drivers/spi/spi-qup.c
+F: drivers/tty/serial/msm_serial.h
+F: drivers/tty/serial/msm_serial.c
+F: drivers/*/pm8???-*
+F: drivers/mfd/ssbi.c
+F: drivers/firmware/qcom_scm.c
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/agross/linux.git
+
+ARM/RADISYS ENP2611 MACHINE SUPPORT
+M: Lennert Buytenhek <kernel@wantstofly.org>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+
+ARM/RENESAS ARM64 ARCHITECTURE
+M: Simon Horman <horms@verge.net.au>
+M: Magnus Damm <magnus.damm@gmail.com>
+L: linux-renesas-soc@vger.kernel.org
+Q: http://patchwork.kernel.org/project/linux-renesas-soc/list/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/horms/renesas.git next
+S: Supported
+F: arch/arm64/boot/dts/renesas/
+F: drivers/soc/renesas/
+F: include/linux/soc/renesas/
+
+ARM/RISCPC ARCHITECTURE
+M: Russell King <linux@armlinux.org.uk>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+W: http://www.armlinux.org.uk/
+S: Maintained
+F: arch/arm/include/asm/hardware/entry-macro-iomd.S
+F: arch/arm/include/asm/hardware/ioc.h
+F: arch/arm/include/asm/hardware/iomd.h
+F: arch/arm/include/asm/hardware/memc.h
+F: arch/arm/mach-rpc/
+F: drivers/net/ethernet/8390/etherh.c
+F: drivers/net/ethernet/i825xx/ether1*
+F: drivers/net/ethernet/seeq/ether3*
+F: drivers/scsi/arm/
+
+ARM/Rockchip SoC support
+M: Heiko Stuebner <heiko@sntech.de>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+L: linux-rockchip@lists.infradead.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/mmind/linux-rockchip.git
+S: Maintained
+F: arch/arm/boot/dts/rk3*
+F: arch/arm/mach-rockchip/
+F: drivers/clk/rockchip/
+F: drivers/i2c/busses/i2c-rk3x.c
+F: drivers/*/*rockchip*
+F: drivers/*/*/*rockchip*
+F: sound/soc/rockchip/
+N: rockchip
+
+ARM/SAMSUNG EXYNOS ARM ARCHITECTURES
+M: Kukjin Kim <kgene@kernel.org>
+M: Krzysztof Kozlowski <krzk@kernel.org>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+L: linux-samsung-soc@vger.kernel.org (moderated for non-subscribers)
+Q: https://patchwork.kernel.org/project/linux-samsung-soc/list/
+S: Maintained
+F: arch/arm/boot/dts/s3c*
+F: arch/arm/boot/dts/s5p*
+F: arch/arm/boot/dts/samsung*
+F: arch/arm/boot/dts/exynos*
+F: arch/arm64/boot/dts/exynos/
+F: arch/arm/plat-samsung/
+F: arch/arm/mach-s3c24*/
+F: arch/arm/mach-s3c64xx/
+F: arch/arm/mach-s5p*/
+F: arch/arm/mach-exynos*/
+F: drivers/*/*s3c24*
+F: drivers/*/*/*s3c24*
+F: drivers/*/*s3c64xx*
+F: drivers/*/*s5pv210*
+F: drivers/memory/samsung/*
+F: drivers/soc/samsung/*
+F: Documentation/arm/Samsung/
+F: Documentation/devicetree/bindings/arm/samsung/
+F: Documentation/devicetree/bindings/sram/samsung-sram.txt
+F: Documentation/devicetree/bindings/power/pd-samsung.txt
+N: exynos
+
+ARM/SAMSUNG MOBILE MACHINE SUPPORT
+M: Kyungmin Park <kyungmin.park@samsung.com>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+F: arch/arm/mach-s5pv210/
+
+ARM/SAMSUNG S5P SERIES 2D GRAPHICS ACCELERATION (G2D) SUPPORT
+M: Kyungmin Park <kyungmin.park@samsung.com>
+M: Kamil Debski <kamil@wypas.org>
+M: Andrzej Hajda <a.hajda@samsung.com>
+L: linux-arm-kernel@lists.infradead.org
+L: linux-media@vger.kernel.org
+S: Maintained
+F: drivers/media/platform/s5p-g2d/
+
+ARM/SAMSUNG S5P SERIES Multi Format Codec (MFC) SUPPORT
+M: Kyungmin Park <kyungmin.park@samsung.com>
+M: Kamil Debski <kamil@wypas.org>
+M: Jeongtae Park <jtp.park@samsung.com>
+M: Andrzej Hajda <a.hajda@samsung.com>
+L: linux-arm-kernel@lists.infradead.org
+L: linux-media@vger.kernel.org
+S: Maintained
+F: arch/arm/plat-samsung/s5p-dev-mfc.c
+F: drivers/media/platform/s5p-mfc/
+
+ARM/SAMSUNG S5P SERIES HDMI CEC SUBSYSTEM SUPPORT
+M: Kyungmin Park <kyungmin.park@samsung.com>
+L: linux-arm-kernel@lists.infradead.org
+L: linux-media@vger.kernel.org
+S: Maintained
+F: drivers/staging/media/platform/s5p-cec/
+
+ARM/SAMSUNG S5P SERIES JPEG CODEC SUPPORT
+M: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+M: Jacek Anaszewski <jacek.anaszewski@gmail.com>
+L: linux-arm-kernel@lists.infradead.org
+L: linux-media@vger.kernel.org
+S: Maintained
+F: drivers/media/platform/s5p-jpeg/
+
+ARM/SHMOBILE ARM ARCHITECTURE
+M: Simon Horman <horms@verge.net.au>
+M: Magnus Damm <magnus.damm@gmail.com>
+L: linux-renesas-soc@vger.kernel.org
+Q: http://patchwork.kernel.org/project/linux-renesas-soc/list/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/horms/renesas.git next
+S: Supported
+F: arch/arm/boot/dts/emev2*
+F: arch/arm/boot/dts/r7s*
+F: arch/arm/boot/dts/r8a*
+F: arch/arm/boot/dts/sh*
+F: arch/arm/configs/shmobile_defconfig
+F: arch/arm/include/debug/renesas-scif.S
+F: arch/arm/mach-shmobile/
+F: drivers/soc/renesas/
+F: include/linux/soc/renesas/
+
+ARM/SOCFPGA ARCHITECTURE
+M: Dinh Nguyen <dinguyen@kernel.org>
+S: Maintained
+F: arch/arm/mach-socfpga/
+F: arch/arm/boot/dts/socfpga*
+F: arch/arm/configs/socfpga_defconfig
+F: arch/arm64/boot/dts/altera/
+W: http://www.rocketboards.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/dinguyen/linux.git
+
+ARM/SOCFPGA CLOCK FRAMEWORK SUPPORT
+M: Dinh Nguyen <dinguyen@kernel.org>
+S: Maintained
+F: drivers/clk/socfpga/
+
+ARM/SOCFPGA EDAC SUPPORT
+M: Thor Thayer <thor.thayer@linux.intel.com>
+S: Maintained
+F: drivers/edac/altera_edac.
+
+ARM/STI ARCHITECTURE
+M: Patrice Chotard <patrice.chotard@st.com>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+W: http://www.stlinux.com
+S: Maintained
+F: arch/arm/mach-sti/
+F: arch/arm/boot/dts/sti*
+F: drivers/char/hw_random/st-rng.c
+F: drivers/clocksource/arm_global_timer.c
+F: drivers/clocksource/clksrc_st_lpc.c
+F: drivers/cpufreq/sti-cpufreq.c
+F: drivers/dma/st_fdma*
+F: drivers/i2c/busses/i2c-st.c
+F: drivers/media/rc/st_rc.c
+F: drivers/media/platform/sti/c8sectpfe/
+F: drivers/mmc/host/sdhci-st.c
+F: drivers/phy/phy-miphy28lp.c
+F: drivers/phy/phy-stih407-usb.c
+F: drivers/pinctrl/pinctrl-st.c
+F: drivers/remoteproc/st_remoteproc.c
+F: drivers/remoteproc/st_slim_rproc.c
+F: drivers/reset/sti/
+F: drivers/rtc/rtc-st-lpc.c
+F: drivers/tty/serial/st-asc.c
+F: drivers/usb/dwc3/dwc3-st.c
+F: drivers/usb/host/ehci-st.c
+F: drivers/usb/host/ohci-st.c
+F: drivers/watchdog/st_lpc_wdt.c
+F: drivers/ata/ahci_st.c
+F: include/linux/remoteproc/st_slim_rproc.h
+
+ARM/STM32 ARCHITECTURE
+M: Maxime Coquelin <mcoquelin.stm32@gmail.com>
+M: Alexandre Torgue <alexandre.torgue@st.com>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/mcoquelin/stm32.git
+N: stm32
+F: drivers/clocksource/armv7m_systick.c
+
+ARM/TANGO ARCHITECTURE
+M: Marc Gonzalez <marc_gonzalez@sigmadesigns.com>
+L: linux-arm-kernel@lists.infradead.org
+S: Maintained
+N: tango
+
+ARM/TECHNOLOGIC SYSTEMS TS7250 MACHINE SUPPORT
+M: Lennert Buytenhek <kernel@wantstofly.org>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+
+ARM/TETON BGA MACHINE SUPPORT
+M: "Mark F. Brown" <mark.brown314@gmail.com>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+
+ARM/THECUS N2100 MACHINE SUPPORT
+M: Lennert Buytenhek <kernel@wantstofly.org>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+
+ARM/NUVOTON W90X900 ARM ARCHITECTURE
+M: Wan ZongShun <mcuos.com@gmail.com>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+W: http://www.mcuos.com
+S: Maintained
+F: arch/arm/mach-w90x900/
+F: drivers/input/keyboard/w90p910_keypad.c
+F: drivers/input/touchscreen/w90p910_ts.c
+F: drivers/watchdog/nuc900_wdt.c
+F: drivers/net/ethernet/nuvoton/w90p910_ether.c
+F: drivers/mtd/nand/nuc900_nand.c
+F: drivers/rtc/rtc-nuc900.c
+F: drivers/spi/spi-nuc900.c
+F: drivers/usb/host/ehci-w90x900.c
+F: drivers/video/fbdev/nuc900fb.c
+
+ARM/U300 MACHINE SUPPORT
+M: Linus Walleij <linus.walleij@linaro.org>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Supported
+F: arch/arm/mach-u300/
+F: drivers/clocksource/timer-u300.c
+F: drivers/i2c/busses/i2c-stu300.c
+F: drivers/rtc/rtc-coh901331.c
+F: drivers/watchdog/coh901327_wdt.c
+F: drivers/dma/coh901318*
+F: drivers/mfd/ab3100*
+F: drivers/rtc/rtc-ab3100.c
+F: drivers/rtc/rtc-coh901331.c
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-stericsson.git
+
+ARM/UNIPHIER ARCHITECTURE
+M: Masahiro Yamada <yamada.masahiro@socionext.com>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/masahiroy/linux-uniphier.git
+S: Maintained
+F: arch/arm/boot/dts/uniphier*
+F: arch/arm/include/asm/hardware/cache-uniphier.h
+F: arch/arm/mach-uniphier/
+F: arch/arm/mm/cache-uniphier.c
+F: arch/arm64/boot/dts/socionext/
+F: drivers/bus/uniphier-system-bus.c
+F: drivers/clk/uniphier/
+F: drivers/i2c/busses/i2c-uniphier*
+F: drivers/pinctrl/uniphier/
+F: drivers/reset/reset-uniphier.c
+F: drivers/tty/serial/8250/8250_uniphier.c
+N: uniphier
+
+ARM/Ux500 ARM ARCHITECTURE
+M: Linus Walleij <linus.walleij@linaro.org>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+F: arch/arm/mach-ux500/
+F: drivers/clocksource/clksrc-dbx500-prcmu.c
+F: drivers/dma/ste_dma40*
+F: drivers/hwspinlock/u8500_hsem.c
+F: drivers/mfd/abx500*
+F: drivers/mfd/ab8500*
+F: drivers/mfd/dbx500*
+F: drivers/mfd/db8500*
+F: drivers/pinctrl/nomadik/pinctrl-ab*
+F: drivers/pinctrl/nomadik/pinctrl-nomadik*
+F: drivers/rtc/rtc-ab8500.c
+F: drivers/rtc/rtc-pl031.c
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-stericsson.git
+
+ARM/Ux500 CLOCK FRAMEWORK SUPPORT
+M: Ulf Hansson <ulf.hansson@linaro.org>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+T: git git://git.linaro.org/people/ulfh/clk.git
+S: Maintained
+F: drivers/clk/ux500/
+
+ARM/VERSATILE EXPRESS PLATFORM
+M: Liviu Dudau <liviu.dudau@arm.com>
+M: Sudeep Holla <sudeep.holla@arm.com>
+M: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+F: arch/arm/boot/dts/vexpress*
+F: arch/arm64/boot/dts/arm/
+F: arch/arm/mach-vexpress/
+F: */*/vexpress*
+F: */*/*/vexpress*
+F: drivers/clk/versatile/clk-vexpress-osc.c
+F: drivers/clocksource/versatile.c
+N: mps2
+
+ARM/VFP SUPPORT
+M: Russell King <linux@armlinux.org.uk>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+W: http://www.armlinux.org.uk/
+S: Maintained
+F: arch/arm/vfp/
+
+ARM/VOIPAC PXA270 SUPPORT
+M: Marek Vasut <marek.vasut@gmail.com>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+F: arch/arm/mach-pxa/vpac270.c
+F: arch/arm/mach-pxa/include/mach/vpac270.h
+
+ARM/VT8500 ARM ARCHITECTURE
+M: Tony Prisk <linux@prisktech.co.nz>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+F: arch/arm/mach-vt8500/
+F: drivers/clocksource/vt8500_timer.c
+F: drivers/i2c/busses/i2c-wmt.c
+F: drivers/mmc/host/wmt-sdmmc.c
+F: drivers/pwm/pwm-vt8500.c
+F: drivers/rtc/rtc-vt8500.c
+F: drivers/tty/serial/vt8500_serial.c
+F: drivers/usb/host/ehci-platform.c
+F: drivers/usb/host/uhci-platform.c
+F: drivers/video/fbdev/vt8500lcdfb.*
+F: drivers/video/fbdev/wm8505fb*
+F: drivers/video/fbdev/wmt_ge_rops.*
+
+ARM/ZIPIT Z2 SUPPORT
+M: Marek Vasut <marek.vasut@gmail.com>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+F: arch/arm/mach-pxa/z2.c
+F: arch/arm/mach-pxa/include/mach/z2.h
+
+ARM/ZTE ARCHITECTURE
+M: Jun Nie <jun.nie@linaro.org>
+M: Baoyou Xie <baoyou.xie@linaro.org>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+F: arch/arm/mach-zx/
+F: drivers/clk/zte/
+F: drivers/reset/reset-zx2967.c
+F: drivers/soc/zte/
+F: Documentation/devicetree/bindings/arm/zte.txt
+F: Documentation/devicetree/bindings/clock/zx296702-clk.txt
+F: Documentation/devicetree/bindings/reset/zte,zx2967-reset.txt
+F: Documentation/devicetree/bindings/soc/zte/
+F: include/dt-bindings/soc/zx*.h
+
+ARM/ZYNQ ARCHITECTURE
+M: Michal Simek <michal.simek@xilinx.com>
+R: Sören Brinkmann <soren.brinkmann@xilinx.com>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+W: http://wiki.xilinx.com
+T: git https://github.com/Xilinx/linux-xlnx.git
+S: Supported
+F: arch/arm/mach-zynq/
+F: drivers/cpuidle/cpuidle-zynq.c
+F: drivers/block/xsysace.c
+N: zynq
+N: xilinx
+F: drivers/clocksource/cadence_ttc_timer.c
+F: drivers/i2c/busses/i2c-cadence.c
+F: drivers/mmc/host/sdhci-of-arasan.c
+F: drivers/edac/synopsys_edac.c
+
+ARM SMMU DRIVERS
+M: Will Deacon <will.deacon@arm.com>
+R: Robin Murphy <robin.murphy@arm.com>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+F: drivers/iommu/arm-smmu.c
+F: drivers/iommu/arm-smmu-v3.c
+F: drivers/iommu/io-pgtable-arm.c
+F: drivers/iommu/io-pgtable-arm-v7s.c
+
+ARM64 PORT (AARCH64 ARCHITECTURE)
+M: Catalin Marinas <catalin.marinas@arm.com>
+M: Will Deacon <will.deacon@arm.com>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git
+S: Maintained
+F: arch/arm64/
+F: Documentation/arm64/
+
+AS3645A LED FLASH CONTROLLER DRIVER
+M: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+S: Maintained
+F: drivers/media/i2c/as3645a.c
+F: include/media/i2c/as3645a.h
+
+ASAHI KASEI AK8974 DRIVER
+M: Linus Walleij <linus.walleij@linaro.org>
+L: linux-iio@vger.kernel.org
+W: http://www.akm.com/
+S: Supported
+F: drivers/iio/magnetometer/ak8974.c
+
+ASC7621 HARDWARE MONITOR DRIVER
+M: George Joseph <george.joseph@fairview5.com>
+L: linux-hwmon@vger.kernel.org
+S: Maintained
+F: Documentation/hwmon/asc7621
+F: drivers/hwmon/asc7621.c
+
+ASUS NOTEBOOKS AND EEEPC ACPI/WMI EXTRAS DRIVERS
+M: Corentin Chary <corentin.chary@gmail.com>
+L: acpi4asus-user@lists.sourceforge.net
+L: platform-driver-x86@vger.kernel.org
+W: http://acpi4asus.sf.net
+S: Maintained
+F: drivers/platform/x86/asus*.c
+F: drivers/platform/x86/eeepc*.c
+
+ASUS WIRELESS RADIO CONTROL DRIVER
+M: João Paulo Rechi Vita <jprvita@gmail.com>
+L: platform-driver-x86@vger.kernel.org
+S: Maintained
+F: drivers/platform/x86/asus-wireless.c
+
+ASYMMETRIC KEYS
+M: David Howells <dhowells@redhat.com>
+L: keyrings@vger.kernel.org
+S: Maintained
+F: Documentation/crypto/asymmetric-keys.txt
+F: include/linux/verification.h
+F: include/crypto/public_key.h
+F: include/crypto/pkcs7.h
+F: crypto/asymmetric_keys/
+
+ASYNCHRONOUS TRANSFERS/TRANSFORMS (IOAT) API
+R: Dan Williams <dan.j.williams@intel.com>
+W: http://sourceforge.net/projects/xscaleiop
+S: Odd fixes
+F: Documentation/crypto/async-tx-api.txt
+F: crypto/async_tx/
+F: drivers/dma/
+F: include/linux/dmaengine.h
+F: include/linux/async_tx.h
+
+AT24 EEPROM DRIVER
+M: Wolfram Sang <wsa@the-dreams.de>
+L: linux-i2c@vger.kernel.org
+S: Maintained
+F: drivers/misc/eeprom/at24.c
+F: include/linux/platform_data/at24.h
+
+ATA OVER ETHERNET (AOE) DRIVER
+M: "Ed L. Cashin" <ed.cashin@acm.org>
+W: http://www.openaoe.org/
+S: Supported
+F: Documentation/aoe/
+F: drivers/block/aoe/
+
+ATHEROS 71XX/9XXX GPIO DRIVER
+M: Alban Bedel <albeu@free.fr>
+W: https://github.com/AlbanBedel/linux
+T: git git://github.com/AlbanBedel/linux
+S: Maintained
+F: drivers/gpio/gpio-ath79.c
+F: Documentation/devicetree/bindings/gpio/gpio-ath79.txt
+
+ATHEROS ATH GENERIC UTILITIES
+M: "Luis R. Rodriguez" <mcgrof@do-not-panic.com>
+L: linux-wireless@vger.kernel.org
+S: Supported
+F: drivers/net/wireless/ath/*
+
+ATHEROS ATH5K WIRELESS DRIVER
+M: Jiri Slaby <jirislaby@gmail.com>
+M: Nick Kossifidis <mickflemm@gmail.com>
+M: "Luis R. Rodriguez" <mcgrof@do-not-panic.com>
+L: linux-wireless@vger.kernel.org
+W: http://wireless.kernel.org/en/users/Drivers/ath5k
+S: Maintained
+F: drivers/net/wireless/ath/ath5k/
+
+ATHEROS ATH6KL WIRELESS DRIVER
+M: Kalle Valo <kvalo@qca.qualcomm.com>
+L: linux-wireless@vger.kernel.org
+W: http://wireless.kernel.org/en/users/Drivers/ath6kl
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/kvalo/ath.git
+S: Supported
+F: drivers/net/wireless/ath/ath6kl/
+
+WILOCITY WIL6210 WIRELESS DRIVER
+M: Maya Erez <qca_merez@qca.qualcomm.com>
+L: linux-wireless@vger.kernel.org
+L: wil6210@qca.qualcomm.com
+S: Supported
+W: http://wireless.kernel.org/en/users/Drivers/wil6210
+F: drivers/net/wireless/ath/wil6210/
+F: include/uapi/linux/wil6210_uapi.h
+
+CARL9170 LINUX COMMUNITY WIRELESS DRIVER
+M: Christian Lamparter <chunkeey@googlemail.com>
+L: linux-wireless@vger.kernel.org
+W: http://wireless.kernel.org/en/users/Drivers/carl9170
+S: Maintained
+F: drivers/net/wireless/ath/carl9170/
+
+ATK0110 HWMON DRIVER
+M: Luca Tettamanti <kronos.it@gmail.com>
+L: linux-hwmon@vger.kernel.org
+S: Maintained
+F: drivers/hwmon/asus_atk0110.c
+
+ATI_REMOTE2 DRIVER
+M: Ville Syrjala <syrjala@sci.fi>
+S: Maintained
+F: drivers/input/misc/ati_remote2.c
+
+ATLX ETHERNET DRIVERS
+M: Jay Cliburn <jcliburn@gmail.com>
+M: Chris Snook <chris.snook@gmail.com>
+L: netdev@vger.kernel.org
+W: http://sourceforge.net/projects/atl1
+W: http://atl1.sourceforge.net
+S: Maintained
+F: drivers/net/ethernet/atheros/
+
+ATM
+M: Chas Williams <3chas3@gmail.com>
+L: linux-atm-general@lists.sourceforge.net (moderated for non-subscribers)
+L: netdev@vger.kernel.org
+W: http://linux-atm.sourceforge.net
+S: Maintained
+F: drivers/atm/
+F: include/linux/atm*
+F: include/uapi/linux/atm*
+
+ATMEL AT91 / AT32 MCI DRIVER
+M: Ludovic Desroches <ludovic.desroches@microchip.com>
+S: Maintained
+F: drivers/mmc/host/atmel-mci.c
+
+ATMEL AT91 SAMA5D2-Compatible Shutdown Controller
+M: Nicolas Ferre <nicolas.ferre@microchip.com>
+S: Supported
+F: drivers/power/reset/at91-sama5d2_shdwc.c
+
+ATMEL SAMA5D2 ADC DRIVER
+M: Ludovic Desroches <ludovic.desroches@microchip.com>
+L: linux-iio@vger.kernel.org
+S: Supported
+F: drivers/iio/adc/at91-sama5d2_adc.c
+
+ATMEL Audio ALSA driver
+M: Nicolas Ferre <nicolas.ferre@microchip.com>
+L: alsa-devel@alsa-project.org (moderated for non-subscribers)
+S: Supported
+F: sound/soc/atmel
+
+ATMEL XDMA DRIVER
+M: Ludovic Desroches <ludovic.desroches@microchip.com>
+L: linux-arm-kernel@lists.infradead.org
+L: dmaengine@vger.kernel.org
+S: Supported
+F: drivers/dma/at_xdmac.c
+
+ATMEL I2C DRIVER
+M: Ludovic Desroches <ludovic.desroches@microchip.com>
+L: linux-i2c@vger.kernel.org
+S: Supported
+F: drivers/i2c/busses/i2c-at91.c
+
+ATMEL ISI DRIVER
+M: Ludovic Desroches <ludovic.desroches@microchip.com>
+L: linux-media@vger.kernel.org
+S: Supported
+F: drivers/media/platform/atmel/atmel-isi.c
+F: include/media/atmel-isi.h
+
+ATMEL LCDFB DRIVER
+M: Nicolas Ferre <nicolas.ferre@microchip.com>
+L: linux-fbdev@vger.kernel.org
+S: Maintained
+F: drivers/video/fbdev/atmel_lcdfb.c
+F: include/video/atmel_lcdc.h
+
+ATMEL MACB ETHERNET DRIVER
+M: Nicolas Ferre <nicolas.ferre@microchip.com>
+S: Supported
+F: drivers/net/ethernet/cadence/
+
+ATMEL NAND DRIVER
+M: Wenyou Yang <wenyou.yang@atmel.com>
+M: Josh Wu <rainyfeeling@outlook.com>
+L: linux-mtd@lists.infradead.org
+S: Supported
+F: drivers/mtd/nand/atmel/*
+
+ATMEL SDMMC DRIVER
+M: Ludovic Desroches <ludovic.desroches@microchip.com>
+L: linux-mmc@vger.kernel.org
+S: Supported
+F: drivers/mmc/host/sdhci-of-at91.c
+
+ATMEL SPI DRIVER
+M: Nicolas Ferre <nicolas.ferre@microchip.com>
+S: Supported
+F: drivers/spi/spi-atmel.*
+
+ATMEL SSC DRIVER
+M: Nicolas Ferre <nicolas.ferre@microchip.com>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Supported
+F: drivers/misc/atmel-ssc.c
+F: include/linux/atmel-ssc.h
+
+ATMEL Timer Counter (TC) AND CLOCKSOURCE DRIVERS
+M: Nicolas Ferre <nicolas.ferre@microchip.com>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Supported
+F: drivers/misc/atmel_tclib.c
+F: drivers/clocksource/tcb_clksrc.c
+
+ATMEL USBA UDC DRIVER
+M: Nicolas Ferre <nicolas.ferre@microchip.com>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Supported
+F: drivers/usb/gadget/udc/atmel_usba_udc.*
+
+ATMEL WIRELESS DRIVER
+M: Simon Kelley <simon@thekelleys.org.uk>
+L: linux-wireless@vger.kernel.org
+W: http://www.thekelleys.org.uk/atmel
+W: http://atmelwlandriver.sourceforge.net/
+S: Maintained
+F: drivers/net/wireless/atmel/atmel*
+
+ATMEL MAXTOUCH DRIVER
+M: Nick Dyer <nick@shmanahar.org>
+T: git git://github.com/ndyer/linux.git
+S: Maintained
+F: Documentation/devicetree/bindings/input/atmel,maxtouch.txt
+F: drivers/input/touchscreen/atmel_mxt_ts.c
+F: include/linux/platform_data/atmel_mxt_ts.h
+
+ATTO EXPRESSSAS SAS/SATA RAID SCSI DRIVER
+M: Bradley Grove <linuxdrivers@attotech.com>
+L: linux-scsi@vger.kernel.org
+W: http://www.attotech.com
+S: Supported
+F: drivers/scsi/esas2r
+
+ATUSB IEEE 802.15.4 RADIO DRIVER
+M: Stefan Schmidt <stefan@osg.samsung.com>
+L: linux-wpan@vger.kernel.org
+S: Maintained
+F: drivers/net/ieee802154/atusb.c
+F: drivers/net/ieee802154/atusb.h
+F: drivers/net/ieee802154/at86rf230.h
+
+AUDIT SUBSYSTEM
+M: Paul Moore <paul@paul-moore.com>
+M: Eric Paris <eparis@redhat.com>
+L: linux-audit@redhat.com (moderated for non-subscribers)
+W: http://people.redhat.com/sgrubb/audit/
+T: git git://git.infradead.org/users/pcmoore/audit
+S: Maintained
+F: include/linux/audit.h
+F: include/uapi/linux/audit.h
+F: kernel/audit*
+
+AUXILIARY DISPLAY DRIVERS
+M: Miguel Ojeda Sandonis <miguel.ojeda.sandonis@gmail.com>
+W: http://miguelojeda.es/auxdisplay.htm
+W: http://jair.lab.fi.uva.es/~migojed/auxdisplay.htm
+S: Maintained
+F: drivers/auxdisplay/
+F: include/linux/cfag12864b.h
+
+AX.25 NETWORK LAYER
+M: Ralf Baechle <ralf@linux-mips.org>
+L: linux-hams@vger.kernel.org
+W: http://www.linux-ax25.org/
+S: Maintained
+F: include/uapi/linux/ax25.h
+F: include/net/ax25.h
+F: net/ax25/
+
+AXENTIA ASOC DRIVERS
+M: Peter Rosin <peda@axentia.se>
+L: alsa-devel@alsa-project.org (moderated for non-subscribers)
+S: Maintained
+F: Documentation/devicetree/bindings/sound/axentia,*
+F: sound/soc/atmel/tse850-pcm5142.c
+
+AXENTIA ARM DEVICES
+M: Peter Rosin <peda@axentia.se>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+F: Documentation/devicetree/bindings/arm/axentia.txt
+F: arch/arm/boot/dts/at91-linea.dtsi
+F: arch/arm/boot/dts/at91-tse850-3.dts
+
+AZ6007 DVB DRIVER
+M: Mauro Carvalho Chehab <mchehab@s-opensource.com>
+M: Mauro Carvalho Chehab <mchehab@kernel.org>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+T: git git://linuxtv.org/media_tree.git
+S: Maintained
+F: drivers/media/usb/dvb-usb-v2/az6007.c
+
+AZTECH FM RADIO RECEIVER DRIVER
+M: Hans Verkuil <hverkuil@xs4all.nl>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+W: https://linuxtv.org
+S: Maintained
+F: drivers/media/radio/radio-aztech*
+
+B43 WIRELESS DRIVER
+L: linux-wireless@vger.kernel.org
+L: b43-dev@lists.infradead.org
+W: http://wireless.kernel.org/en/users/Drivers/b43
+S: Odd Fixes
+F: drivers/net/wireless/broadcom/b43/
+
+B43LEGACY WIRELESS DRIVER
+M: Larry Finger <Larry.Finger@lwfinger.net>
+L: linux-wireless@vger.kernel.org
+L: b43-dev@lists.infradead.org
+W: http://wireless.kernel.org/en/users/Drivers/b43
+S: Maintained
+F: drivers/net/wireless/broadcom/b43legacy/
+
+BACKLIGHT CLASS/SUBSYSTEM
+M: Lee Jones <lee.jones@linaro.org>
+M: Daniel Thompson <daniel.thompson@linaro.org>
+M: Jingoo Han <jingoohan1@gmail.com>
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/lee/backlight.git
+S: Maintained
+F: drivers/video/backlight/
+F: include/linux/backlight.h
+F: include/linux/pwm_backlight.h
+F: Documentation/devicetree/bindings/leds/backlight
+
+BATMAN ADVANCED
+M: Marek Lindner <mareklindner@neomailbox.ch>
+M: Simon Wunderlich <sw@simonwunderlich.de>
+M: Antonio Quartulli <a@unstable.cc>
+L: b.a.t.m.a.n@lists.open-mesh.org (moderated for non-subscribers)
+W: https://www.open-mesh.org/
+Q: https://patchwork.open-mesh.org/project/batman/list/
+S: Maintained
+F: Documentation/ABI/testing/sysfs-class-net-batman-adv
+F: Documentation/ABI/testing/sysfs-class-net-mesh
+F: Documentation/networking/batman-adv.txt
+F: include/uapi/linux/batman_adv.h
+F: net/batman-adv/
+
+BAYCOM/HDLCDRV DRIVERS FOR AX.25
+M: Thomas Sailer <t.sailer@alumni.ethz.ch>
+L: linux-hams@vger.kernel.org
+W: http://www.baycom.org/~tom/ham/ham.html
+S: Maintained
+F: drivers/net/hamradio/baycom*
+
+BCACHE (BLOCK LAYER CACHE)
+M: Kent Overstreet <kent.overstreet@gmail.com>
+L: linux-bcache@vger.kernel.org
+W: http://bcache.evilpiepirate.org
+S: Orphan
+F: drivers/md/bcache/
+
+BDISP ST MEDIA DRIVER
+M: Fabien Dessenne <fabien.dessenne@st.com>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+W: https://linuxtv.org
+S: Supported
+F: drivers/media/platform/sti/bdisp
+
+DELTA ST MEDIA DRIVER
+M: Hugues Fruchet <hugues.fruchet@st.com>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+W: https://linuxtv.org
+S: Supported
+F: drivers/media/platform/sti/delta
+
+BEFS FILE SYSTEM
+M: Luis de Bethencourt <luisbg@osg.samsung.com>
+M: Salah Triki <salah.triki@gmail.com>
+S: Maintained
+T: git git://github.com/luisbg/linux-befs.git
+F: Documentation/filesystems/befs.txt
+F: fs/befs/
+
+BECKHOFF CX5020 ETHERCAT MASTER DRIVER
+M: Dariusz Marcinkiewicz <reksio@newterm.pl>
+L: netdev@vger.kernel.org
+S: Maintained
+F: drivers/net/ethernet/ec_bhf.c
+
+BFS FILE SYSTEM
+M: "Tigran A. Aivazian" <aivazian.tigran@gmail.com>
+S: Maintained
+F: Documentation/filesystems/bfs.txt
+F: fs/bfs/
+F: include/uapi/linux/bfs_fs.h
+
+BLACKFIN ARCHITECTURE
+M: Steven Miao <realmz6@gmail.com>
+L: adi-buildroot-devel@lists.sourceforge.net (moderated for non-subscribers)
+T: git git://git.code.sf.net/p/adi-linux/code
+W: http://blackfin.uclinux.org
+S: Supported
+F: arch/blackfin/
+
+BLACKFIN EMAC DRIVER
+L: adi-buildroot-devel@lists.sourceforge.net (moderated for non-subscribers)
+W: http://blackfin.uclinux.org
+S: Supported
+F: drivers/net/ethernet/adi/
+
+BLACKFIN RTC DRIVER
+L: adi-buildroot-devel@lists.sourceforge.net (moderated for non-subscribers)
+W: http://blackfin.uclinux.org
+S: Supported
+F: drivers/rtc/rtc-bfin.c
+
+BLACKFIN SDH DRIVER
+M: Sonic Zhang <sonic.zhang@analog.com>
+L: adi-buildroot-devel@lists.sourceforge.net (moderated for non-subscribers)
+W: http://blackfin.uclinux.org
+S: Supported
+F: drivers/mmc/host/bfin_sdh.c
+
+BLACKFIN SERIAL DRIVER
+M: Sonic Zhang <sonic.zhang@analog.com>
+L: adi-buildroot-devel@lists.sourceforge.net (moderated for non-subscribers)
+W: http://blackfin.uclinux.org
+S: Supported
+F: drivers/tty/serial/bfin_uart.c
+
+BLACKFIN WATCHDOG DRIVER
+L: adi-buildroot-devel@lists.sourceforge.net (moderated for non-subscribers)
+W: http://blackfin.uclinux.org
+S: Supported
+F: drivers/watchdog/bfin_wdt.c
+
+BLACKFIN I2C TWI DRIVER
+M: Sonic Zhang <sonic.zhang@analog.com>
+L: adi-buildroot-devel@lists.sourceforge.net (moderated for non-subscribers)
+W: http://blackfin.uclinux.org/
+S: Supported
+F: drivers/i2c/busses/i2c-bfin-twi.c
+
+BLACKFIN MEDIA DRIVER
+M: Scott Jiang <scott.jiang.linux@gmail.com>
+L: adi-buildroot-devel@lists.sourceforge.net (moderated for non-subscribers)
+W: http://blackfin.uclinux.org/
+S: Supported
+F: drivers/media/platform/blackfin/
+F: drivers/media/i2c/adv7183*
+F: drivers/media/i2c/vs6624*
+
+BLINKM RGB LED DRIVER
+M: Jan-Simon Moeller <jansimon.moeller@gmx.de>
+S: Maintained
+F: drivers/leds/leds-blinkm.c
+
+BLOCK LAYER
+M: Jens Axboe <axboe@kernel.dk>
+L: linux-block@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux-block.git
+S: Maintained
+F: block/
+F: kernel/trace/blktrace.c
+F: lib/sbitmap.c
+
+BFQ I/O SCHEDULER
+M: Paolo Valente <paolo.valente@linaro.org>
+M: Jens Axboe <axboe@kernel.dk>
+L: linux-block@vger.kernel.org
+S: Maintained
+F: block/bfq-*
+F: Documentation/block/bfq-iosched.txt
+
+BLOCK2MTD DRIVER
+M: Joern Engel <joern@lazybastard.org>
+L: linux-mtd@lists.infradead.org
+S: Maintained
+F: drivers/mtd/devices/block2mtd.c
+
+BLUETOOTH DRIVERS
+M: Marcel Holtmann <marcel@holtmann.org>
+M: Gustavo Padovan <gustavo@padovan.org>
+M: Johan Hedberg <johan.hedberg@gmail.com>
+L: linux-bluetooth@vger.kernel.org
+W: http://www.bluez.org/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth.git
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next.git
+S: Maintained
+F: drivers/bluetooth/
+
+BLUETOOTH SUBSYSTEM
+M: Marcel Holtmann <marcel@holtmann.org>
+M: Gustavo Padovan <gustavo@padovan.org>
+M: Johan Hedberg <johan.hedberg@gmail.com>
+L: linux-bluetooth@vger.kernel.org
+W: http://www.bluez.org/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth.git
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next.git
+S: Maintained
+F: net/bluetooth/
+F: include/net/bluetooth/
+
+BONDING DRIVER
+M: Jay Vosburgh <j.vosburgh@gmail.com>
+M: Veaceslav Falico <vfalico@gmail.com>
+M: Andy Gospodarek <andy@greyhouse.net>
+L: netdev@vger.kernel.org
+W: http://sourceforge.net/projects/bonding/
+S: Supported
+F: drivers/net/bonding/
+F: include/uapi/linux/if_bonding.h
+
+BPF (Safe dynamic programs and tools)
+M: Alexei Starovoitov <ast@kernel.org>
+M: Daniel Borkmann <daniel@iogearbox.net>
+L: netdev@vger.kernel.org
+L: linux-kernel@vger.kernel.org
+S: Supported
+F: arch/x86/net/bpf_jit*
+F: Documentation/networking/filter.txt
+F: include/linux/bpf*
+F: include/linux/filter.h
+F: include/uapi/linux/bpf*
+F: include/uapi/linux/filter.h
+F: kernel/bpf/
+F: kernel/trace/bpf_trace.c
+F: lib/test_bpf.c
+F: net/bpf/
+F: net/core/filter.c
+F: net/sched/act_bpf.c
+F: net/sched/cls_bpf.c
+F: samples/bpf/
+F: tools/net/bpf*
+F: tools/testing/selftests/bpf/
+
+BROADCOM B44 10/100 ETHERNET DRIVER
+M: Michael Chan <michael.chan@broadcom.com>
+L: netdev@vger.kernel.org
+S: Supported
+F: drivers/net/ethernet/broadcom/b44.*
+
+BROADCOM B53 ETHERNET SWITCH DRIVER
+M: Florian Fainelli <f.fainelli@gmail.com>
+L: netdev@vger.kernel.org
+L: openwrt-devel@lists.openwrt.org (subscribers-only)
+S: Supported
+F: drivers/net/dsa/b53/*
+F: include/linux/platform_data/b53.h
+
+BROADCOM GENET ETHERNET DRIVER
+M: Florian Fainelli <f.fainelli@gmail.com>
+L: netdev@vger.kernel.org
+S: Supported
+F: drivers/net/ethernet/broadcom/genet/
+
+BROADCOM BNX2 GIGABIT ETHERNET DRIVER
+M: Rasesh Mody <rasesh.mody@cavium.com>
+M: Harish Patil <harish.patil@cavium.com>
+M: Dept-GELinuxNICDev@cavium.com
+L: netdev@vger.kernel.org
+S: Supported
+F: drivers/net/ethernet/broadcom/bnx2.*
+F: drivers/net/ethernet/broadcom/bnx2_*
+
+BROADCOM BNX2X 10 GIGABIT ETHERNET DRIVER
+M: Yuval Mintz <Yuval.Mintz@cavium.com>
+M: Ariel Elior <ariel.elior@cavium.com>
+M: everest-linux-l2@cavium.com
+L: netdev@vger.kernel.org
+S: Supported
+F: drivers/net/ethernet/broadcom/bnx2x/
+
+BROADCOM BNXT_EN 50 GIGABIT ETHERNET DRIVER
+M: Michael Chan <michael.chan@broadcom.com>
+L: netdev@vger.kernel.org
+S: Supported
+F: drivers/net/ethernet/broadcom/bnxt/
+
+BROADCOM BCM281XX/BCM11XXX/BCM216XX ARM ARCHITECTURE
+M: Florian Fainelli <f.fainelli@gmail.com>
+M: Ray Jui <rjui@broadcom.com>
+M: Scott Branden <sbranden@broadcom.com>
+M: bcm-kernel-feedback-list@broadcom.com
+T: git git://github.com/broadcom/mach-bcm
+S: Maintained
+N: bcm281*
+N: bcm113*
+N: bcm216*
+N: kona
+F: arch/arm/mach-bcm/
+
+BROADCOM BCM2835 ARM ARCHITECTURE
+M: Lee Jones <lee@kernel.org>
+M: Eric Anholt <eric@anholt.net>
+M: Stefan Wahren <stefan.wahren@i2se.com>
+L: linux-rpi-kernel@lists.infradead.org (moderated for non-subscribers)
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+T: git git://github.com/anholt/linux
+S: Maintained
+N: bcm2835
+F: drivers/staging/vc04_services
+
+BROADCOM BCM47XX MIPS ARCHITECTURE
+M: Hauke Mehrtens <hauke@hauke-m.de>
+M: Rafał Miłecki <zajec5@gmail.com>
+L: linux-mips@linux-mips.org
+S: Maintained
+F: Documentation/devicetree/bindings/mips/brcm/
+F: arch/mips/bcm47xx/*
+F: arch/mips/include/asm/mach-bcm47xx/*
+
+BROADCOM BCM5301X ARM ARCHITECTURE
+M: Hauke Mehrtens <hauke@hauke-m.de>
+M: Rafał Miłecki <zajec5@gmail.com>
+M: Jon Mason <jonmason@broadcom.com>
+M: bcm-kernel-feedback-list@broadcom.com
+L: linux-arm-kernel@lists.infradead.org
+S: Maintained
+F: arch/arm/mach-bcm/bcm_5301x.c
+F: arch/arm/boot/dts/bcm5301x*.dtsi
+F: arch/arm/boot/dts/bcm470*
+F: arch/arm/boot/dts/bcm953012*
+
+BROADCOM BCM53573 ARM ARCHITECTURE
+M: Rafał Miłecki <rafal@milecki.pl>
+L: linux-arm-kernel@lists.infradead.org
+S: Maintained
+F: arch/arm/boot/dts/bcm53573*
+F: arch/arm/boot/dts/bcm47189*
+
+BROADCOM BCM63XX ARM ARCHITECTURE
+M: Florian Fainelli <f.fainelli@gmail.com>
+M: bcm-kernel-feedback-list@broadcom.com
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+T: git git://github.com/broadcom/stblinux.git
+S: Maintained
+N: bcm63xx
+
+BROADCOM BCM63XX/BCM33XX UDC DRIVER
+M: Kevin Cernekee <cernekee@gmail.com>
+L: linux-usb@vger.kernel.org
+S: Maintained
+F: drivers/usb/gadget/udc/bcm63xx_udc.*
+
+BROADCOM BCM7XXX ARM ARCHITECTURE
+M: Brian Norris <computersforpeace@gmail.com>
+M: Gregory Fong <gregory.0xf0@gmail.com>
+M: Florian Fainelli <f.fainelli@gmail.com>
+M: bcm-kernel-feedback-list@broadcom.com
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+T: git git://github.com/broadcom/stblinux.git
+S: Maintained
+F: arch/arm/mach-bcm/*brcmstb*
+F: arch/arm/boot/dts/bcm7*.dts*
+F: drivers/bus/brcmstb_gisb.c
+N: brcmstb
+
+BROADCOM BMIPS MIPS ARCHITECTURE
+M: Kevin Cernekee <cernekee@gmail.com>
+M: Florian Fainelli <f.fainelli@gmail.com>
+L: linux-mips@linux-mips.org
+T: git git://github.com/broadcom/stblinux.git
+S: Maintained
+F: arch/mips/bmips/*
+F: arch/mips/include/asm/mach-bmips/*
+F: arch/mips/kernel/*bmips*
+F: arch/mips/boot/dts/brcm/bcm*.dts*
+F: drivers/irqchip/irq-bcm63*
+F: drivers/irqchip/irq-bcm7*
+F: drivers/irqchip/irq-brcmstb*
+F: include/linux/bcm963xx_nvram.h
+F: include/linux/bcm963xx_tag.h
+
+BROADCOM BMIPS CPUFREQ DRIVER
+M: Markus Mayer <mmayer@broadcom.com>
+M: bcm-kernel-feedback-list@broadcom.com
+L: linux-pm@vger.kernel.org
+S: Maintained
+F: drivers/cpufreq/bmips-cpufreq.c
+
+BROADCOM TG3 GIGABIT ETHERNET DRIVER
+M: Siva Reddy Kallam <siva.kallam@broadcom.com>
+M: Prashant Sreedharan <prashant@broadcom.com>
+M: Michael Chan <mchan@broadcom.com>
+L: netdev@vger.kernel.org
+S: Supported
+F: drivers/net/ethernet/broadcom/tg3.*
+
+BROADCOM BRCM80211 IEEE802.11n WIRELESS DRIVER
+M: Arend van Spriel <arend.vanspriel@broadcom.com>
+M: Franky Lin <franky.lin@broadcom.com>
+M: Hante Meuleman <hante.meuleman@broadcom.com>
+L: linux-wireless@vger.kernel.org
+L: brcm80211-dev-list.pdl@broadcom.com
+S: Supported
+F: drivers/net/wireless/broadcom/brcm80211/
+
+BROADCOM BNX2FC 10 GIGABIT FCOE DRIVER
+M: QLogic-Storage-Upstream@qlogic.com
+L: linux-scsi@vger.kernel.org
+S: Supported
+F: drivers/scsi/bnx2fc/
+
+BROADCOM BNX2I 1/10 GIGABIT iSCSI DRIVER
+M: QLogic-Storage-Upstream@qlogic.com
+L: linux-scsi@vger.kernel.org
+S: Supported
+F: drivers/scsi/bnx2i/
+
+BROADCOM IPROC ARM ARCHITECTURE
+M: Ray Jui <rjui@broadcom.com>
+M: Scott Branden <sbranden@broadcom.com>
+M: Jon Mason <jonmason@broadcom.com>
+M: bcm-kernel-feedback-list@broadcom.com
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+T: git git://github.com/broadcom/cygnus-linux.git
+S: Maintained
+N: iproc
+N: cygnus
+N: bcm[-_]nsp
+N: bcm9113*
+N: bcm9583*
+N: bcm9585*
+N: bcm9586*
+N: bcm988312
+N: bcm113*
+N: bcm583*
+N: bcm585*
+N: bcm586*
+N: bcm88312
+F: arch/arm64/boot/dts/broadcom/ns2*
+F: drivers/clk/bcm/clk-ns*
+F: drivers/pinctrl/bcm/pinctrl-ns*
+
+BROADCOM BRCMSTB GPIO DRIVER
+M: Gregory Fong <gregory.0xf0@gmail.com>
+L: bcm-kernel-feedback-list@broadcom.com
+S: Supported
+F: drivers/gpio/gpio-brcmstb.c
+F: Documentation/devicetree/bindings/gpio/brcm,brcmstb-gpio.txt
+
+BROADCOM KONA GPIO DRIVER
+M: Ray Jui <rjui@broadcom.com>
+L: bcm-kernel-feedback-list@broadcom.com
+S: Supported
+F: drivers/gpio/gpio-bcm-kona.c
+F: Documentation/devicetree/bindings/gpio/brcm,kona-gpio.txt
+
+BROADCOM NVRAM DRIVER
+M: Rafał Miłecki <zajec5@gmail.com>
+L: linux-mips@linux-mips.org
+S: Maintained
+F: drivers/firmware/broadcom/*
+
+BROADCOM STB NAND FLASH DRIVER
+M: Brian Norris <computersforpeace@gmail.com>
+M: Kamal Dasu <kdasu.kdev@gmail.com>
+L: linux-mtd@lists.infradead.org
+L: bcm-kernel-feedback-list@broadcom.com
+S: Maintained
+F: drivers/mtd/nand/brcmnand/
+
+BROADCOM STB AVS CPUFREQ DRIVER
+M: Markus Mayer <mmayer@broadcom.com>
+M: bcm-kernel-feedback-list@broadcom.com
+L: linux-pm@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/cpufreq/brcm,stb-avs-cpu-freq.txt
+F: drivers/cpufreq/brcmstb*
+
+BROADCOM SPECIFIC AMBA DRIVER (BCMA)
+M: Rafał Miłecki <zajec5@gmail.com>
+L: linux-wireless@vger.kernel.org
+S: Maintained
+F: drivers/bcma/
+F: include/linux/bcma/
+
+BROADCOM SYSTEMPORT ETHERNET DRIVER
+M: Florian Fainelli <f.fainelli@gmail.com>
+L: netdev@vger.kernel.org
+S: Supported
+F: drivers/net/ethernet/broadcom/bcmsysport.*
+
+BROADCOM NETXTREME-E ROCE DRIVER
+M: Selvin Xavier <selvin.xavier@broadcom.com>
+M: Devesh Sharma <devesh.sharma@broadcom.com>
+M: Somnath Kotur <somnath.kotur@broadcom.com>
+M: Sriharsha Basavapatna <sriharsha.basavapatna@broadcom.com>
+L: linux-rdma@vger.kernel.org
+W: http://www.broadcom.com
+S: Supported
+F: drivers/infiniband/hw/bnxt_re/
+F: include/uapi/rdma/bnxt_re-abi.h
+
+BROCADE BFA FC SCSI DRIVER
+M: Anil Gurumurthy <anil.gurumurthy@qlogic.com>
+M: Sudarsana Kalluru <sudarsana.kalluru@qlogic.com>
+L: linux-scsi@vger.kernel.org
+S: Supported
+F: drivers/scsi/bfa/
+
+BROCADE BNA 10 GIGABIT ETHERNET DRIVER
+M: Rasesh Mody <rasesh.mody@cavium.com>
+M: Sudarsana Kalluru <sudarsana.kalluru@cavium.com>
+M: Dept-GELinuxNICDev@cavium.com
+L: netdev@vger.kernel.org
+S: Supported
+F: drivers/net/ethernet/brocade/bna/
+
+BSG (block layer generic sg v4 driver)
+M: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
+L: linux-scsi@vger.kernel.org
+S: Supported
+F: block/bsg.c
+F: include/linux/bsg.h
+F: include/uapi/linux/bsg.h
+
+BT87X AUDIO DRIVER
+M: Clemens Ladisch <clemens@ladisch.de>
+L: alsa-devel@alsa-project.org (moderated for non-subscribers)
+T: git git://git.alsa-project.org/alsa-kernel.git
+S: Maintained
+F: Documentation/sound/alsa/Bt87x.txt
+F: sound/pci/bt87x.c
+
+BT8XXGPIO DRIVER
+M: Michael Buesch <m@bues.ch>
+W: http://bu3sch.de/btgpio.php
+S: Maintained
+F: drivers/gpio/gpio-bt8xx.c
+
+BTRFS FILE SYSTEM
+M: Chris Mason <clm@fb.com>
+M: Josef Bacik <jbacik@fb.com>
+M: David Sterba <dsterba@suse.com>
+L: linux-btrfs@vger.kernel.org
+W: http://btrfs.wiki.kernel.org/
+Q: http://patchwork.kernel.org/project/linux-btrfs/list/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/mason/linux-btrfs.git
+S: Maintained
+F: Documentation/filesystems/btrfs.txt
+F: fs/btrfs/
+F: include/linux/btrfs*
+F: include/uapi/linux/btrfs*
+
+BTTV VIDEO4LINUX DRIVER
+M: Mauro Carvalho Chehab <mchehab@s-opensource.com>
+M: Mauro Carvalho Chehab <mchehab@kernel.org>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+T: git git://linuxtv.org/media_tree.git
+S: Odd fixes
+F: Documentation/media/v4l-drivers/bttv*
+F: drivers/media/pci/bt8xx/bttv*
+
+BUSLOGIC SCSI DRIVER
+M: Khalid Aziz <khalid@gonehiking.org>
+L: linux-scsi@vger.kernel.org
+S: Maintained
+F: drivers/scsi/BusLogic.*
+F: drivers/scsi/FlashPoint.*
+
+C-MEDIA CMI8788 DRIVER
+M: Clemens Ladisch <clemens@ladisch.de>
+L: alsa-devel@alsa-project.org (moderated for non-subscribers)
+T: git git://git.alsa-project.org/alsa-kernel.git
+S: Maintained
+F: sound/pci/oxygen/
+
+C6X ARCHITECTURE
+M: Mark Salter <msalter@redhat.com>
+M: Aurelien Jacquiot <jacquiot.aurelien@gmail.com>
+L: linux-c6x-dev@linux-c6x.org
+W: http://www.linux-c6x.org/wiki/index.php/Main_Page
+S: Maintained
+F: arch/c6x/
+
+CA8210 IEEE-802.15.4 RADIO DRIVER
+M: Harry Morris <h.morris@cascoda.com>
+M: linuxdev@cascoda.com
+L: linux-wpan@vger.kernel.org
+W: https://github.com/Cascoda/ca8210-linux.git
+S: Maintained
+F: drivers/net/ieee802154/ca8210.c
+F: Documentation/devicetree/bindings/net/ieee802154/ca8210.txt
+
+CACHEFILES: FS-CACHE BACKEND FOR CACHING ON MOUNTED FILESYSTEMS
+M: David Howells <dhowells@redhat.com>
+L: linux-cachefs@redhat.com (moderated for non-subscribers)
+S: Supported
+F: Documentation/filesystems/caching/cachefiles.txt
+F: fs/cachefiles/
+
+CADET FM/AM RADIO RECEIVER DRIVER
+M: Hans Verkuil <hverkuil@xs4all.nl>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+W: https://linuxtv.org
+S: Maintained
+F: drivers/media/radio/radio-cadet*
+
+CAFE CMOS INTEGRATED CAMERA CONTROLLER DRIVER
+M: Jonathan Corbet <corbet@lwn.net>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+S: Maintained
+F: Documentation/media/v4l-drivers/cafe_ccic*
+F: drivers/media/platform/marvell-ccic/
+
+CAIF NETWORK LAYER
+M: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+L: netdev@vger.kernel.org
+S: Supported
+F: Documentation/networking/caif/
+F: drivers/net/caif/
+F: include/uapi/linux/caif/
+F: include/net/caif/
+F: net/caif/
+
+CALGARY x86-64 IOMMU
+M: Muli Ben-Yehuda <mulix@mulix.org>
+M: Jon Mason <jdmason@kudzu.us>
+L: iommu@lists.linux-foundation.org
+S: Maintained
+F: arch/x86/kernel/pci-calgary_64.c
+F: arch/x86/kernel/tce_64.c
+F: arch/x86/include/asm/calgary.h
+F: arch/x86/include/asm/tce.h
+
+CAN NETWORK LAYER
+M: Oliver Hartkopp <socketcan@hartkopp.net>
+M: Marc Kleine-Budde <mkl@pengutronix.de>
+L: linux-can@vger.kernel.org
+W: https://github.com/linux-can
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/mkl/linux-can.git
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/mkl/linux-can-next.git
+S: Maintained
+F: Documentation/networking/can.txt
+F: net/can/
+F: include/linux/can/core.h
+F: include/uapi/linux/can.h
+F: include/uapi/linux/can/bcm.h
+F: include/uapi/linux/can/raw.h
+F: include/uapi/linux/can/gw.h
+
+CAN NETWORK DRIVERS
+M: Wolfgang Grandegger <wg@grandegger.com>
+M: Marc Kleine-Budde <mkl@pengutronix.de>
+L: linux-can@vger.kernel.org
+W: https://github.com/linux-can
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/mkl/linux-can.git
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/mkl/linux-can-next.git
+S: Maintained
+F: Documentation/devicetree/bindings/net/can/
+F: drivers/net/can/
+F: include/linux/can/dev.h
+F: include/linux/can/platform/
+F: include/uapi/linux/can/error.h
+F: include/uapi/linux/can/netlink.h
+
+CAPABILITIES
+M: Serge Hallyn <serge@hallyn.com>
+L: linux-security-module@vger.kernel.org
+S: Supported
+F: include/linux/capability.h
+F: include/uapi/linux/capability.h
+F: security/commoncap.c
+F: kernel/capability.c
+
+CAPELLA MICROSYSTEMS LIGHT SENSOR DRIVER
+M: Kevin Tsai <ktsai@capellamicro.com>
+S: Maintained
+F: drivers/iio/light/cm*
+
+CAVIUM THUNDERX2 ARM64 SOC
+M: Jayachandran C <jnair@caviumnetworks.com>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+F: arch/arm64/boot/dts/cavium/thunder2-99xx*
+F: Documentation/devicetree/bindings/arm/cavium-thunder2.txt
+
+CAVIUM I2C DRIVER
+M: Jan Glauber <jglauber@cavium.com>
+M: David Daney <david.daney@cavium.com>
+W: http://www.cavium.com
+S: Supported
+F: drivers/i2c/busses/i2c-octeon*
+F: drivers/i2c/busses/i2c-thunderx*
+
+CAVIUM MMC DRIVER
+M: Jan Glauber <jglauber@cavium.com>
+M: David Daney <david.daney@cavium.com>
+M: Steven J. Hill <Steven.Hill@cavium.com>
+W: http://www.cavium.com
+S: Supported
+F: drivers/mmc/host/cavium*
+
+CAVIUM LIQUIDIO NETWORK DRIVER
+M: Derek Chickles <derek.chickles@caviumnetworks.com>
+M: Satanand Burla <satananda.burla@caviumnetworks.com>
+M: Felix Manlunas <felix.manlunas@caviumnetworks.com>
+M: Raghu Vatsavayi <raghu.vatsavayi@caviumnetworks.com>
+L: netdev@vger.kernel.org
+W: http://www.cavium.com
+S: Supported
+F: drivers/net/ethernet/cavium/liquidio/
+
+CAVIUM OCTEON-TX CRYPTO DRIVER
+M: George Cherian <george.cherian@cavium.com>
+L: linux-crypto@vger.kernel.org
+W: http://www.cavium.com
+S: Supported
+F: drivers/crypto/cavium/cpt/
+
+CC2520 IEEE-802.15.4 RADIO DRIVER
+M: Varka Bhadram <varkabhadram@gmail.com>
+L: linux-wpan@vger.kernel.org
+S: Maintained
+F: drivers/net/ieee802154/cc2520.c
+F: include/linux/spi/cc2520.h
+F: Documentation/devicetree/bindings/net/ieee802154/cc2520.txt
+
+CCREE ARM TRUSTZONE CRYPTOCELL 700 REE DRIVER
+M: Gilad Ben-Yossef <gilad@benyossef.com>
+L: linux-crypto@vger.kernel.org
+L: driverdev-devel@linuxdriverproject.org
+S: Supported
+F: drivers/staging/ccree/
+W: https://developer.arm.com/products/system-ip/trustzone-cryptocell/cryptocell-700-family
+
+CEC FRAMEWORK
+M: Hans Verkuil <hans.verkuil@cisco.com>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+W: http://linuxtv.org
+S: Supported
+F: Documentation/media/kapi/cec-core.rst
+F: Documentation/media/uapi/cec
+F: drivers/media/cec/
+F: drivers/media/rc/keymaps/rc-cec.c
+F: include/media/cec.h
+F: include/media/cec-notifier.h
+F: include/uapi/linux/cec.h
+F: include/uapi/linux/cec-funcs.h
+
+CELL BROADBAND ENGINE ARCHITECTURE
+M: Arnd Bergmann <arnd@arndb.de>
+L: linuxppc-dev@lists.ozlabs.org
+W: http://www.ibm.com/developerworks/power/cell/
+S: Supported
+F: arch/powerpc/include/asm/cell*.h
+F: arch/powerpc/include/asm/spu*.h
+F: arch/powerpc/include/uapi/asm/spu*.h
+F: arch/powerpc/oprofile/*cell*
+F: arch/powerpc/platforms/cell/
+
+CEPH COMMON CODE (LIBCEPH)
+M: Ilya Dryomov <idryomov@gmail.com>
+M: "Yan, Zheng" <zyan@redhat.com>
+M: Sage Weil <sage@redhat.com>
+L: ceph-devel@vger.kernel.org
+W: http://ceph.com/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/sage/ceph-client.git
+T: git git://github.com/ceph/ceph-client.git
+S: Supported
+F: net/ceph/
+F: include/linux/ceph/
+F: include/linux/crush/
+
+CEPH DISTRIBUTED FILE SYSTEM CLIENT (CEPH)
+M: "Yan, Zheng" <zyan@redhat.com>
+M: Sage Weil <sage@redhat.com>
+M: Ilya Dryomov <idryomov@gmail.com>
+L: ceph-devel@vger.kernel.org
+W: http://ceph.com/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/sage/ceph-client.git
+T: git git://github.com/ceph/ceph-client.git
+S: Supported
+F: Documentation/filesystems/ceph.txt
+F: fs/ceph/
+
+CERTIFICATE HANDLING:
+M: David Howells <dhowells@redhat.com>
+M: David Woodhouse <dwmw2@infradead.org>
+L: keyrings@vger.kernel.org
+S: Maintained
+F: Documentation/module-signing.txt
+F: certs/
+F: scripts/sign-file.c
+F: scripts/extract-cert.c
+
+CERTIFIED WIRELESS USB (WUSB) SUBSYSTEM:
+L: linux-usb@vger.kernel.org
+S: Orphan
+F: Documentation/usb/WUSB-Design-overview.txt
+F: Documentation/usb/wusb-cbaf
+F: drivers/usb/host/hwa-hc.c
+F: drivers/usb/host/whci/
+F: drivers/usb/wusbcore/
+F: include/linux/usb/wusb*
+
+HT16K33 LED CONTROLLER DRIVER
+M: Robin van der Gracht <robin@protonic.nl>
+S: Maintained
+F: drivers/auxdisplay/ht16k33.c
+F: Documentation/devicetree/bindings/display/ht16k33.txt
+
+CFAG12864B LCD DRIVER
+M: Miguel Ojeda Sandonis <miguel.ojeda.sandonis@gmail.com>
+W: http://miguelojeda.es/auxdisplay.htm
+W: http://jair.lab.fi.uva.es/~migojed/auxdisplay.htm
+S: Maintained
+F: drivers/auxdisplay/cfag12864b.c
+F: include/linux/cfag12864b.h
+
+CFAG12864BFB LCD FRAMEBUFFER DRIVER
+M: Miguel Ojeda Sandonis <miguel.ojeda.sandonis@gmail.com>
+W: http://miguelojeda.es/auxdisplay.htm
+W: http://jair.lab.fi.uva.es/~migojed/auxdisplay.htm
+S: Maintained
+F: drivers/auxdisplay/cfag12864bfb.c
+F: include/linux/cfag12864b.h
+
+CFG80211 and NL80211
+M: Johannes Berg <johannes@sipsolutions.net>
+L: linux-wireless@vger.kernel.org
+W: http://wireless.kernel.org/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211.git
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next.git
+S: Maintained
+F: include/uapi/linux/nl80211.h
+F: include/net/cfg80211.h
+F: net/wireless/*
+X: net/wireless/wext*
+
+CHAR and MISC DRIVERS
+M: Arnd Bergmann <arnd@arndb.de>
+M: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc.git
+S: Supported
+F: drivers/char/*
+F: drivers/misc/*
+F: include/linux/miscdevice.h
+
+CHECKPATCH
+M: Andy Whitcroft <apw@canonical.com>
+M: Joe Perches <joe@perches.com>
+S: Maintained
+F: scripts/checkpatch.pl
+
+CHINESE DOCUMENTATION
+M: Harry Wei <harryxiyou@gmail.com>
+L: xiyoulinuxkernelgroup@googlegroups.com (subscribers-only)
+L: linux-kernel@zh-kernel.org (moderated for non-subscribers)
+S: Maintained
+F: Documentation/translations/zh_CN/
+
+CHIPIDEA USB HIGH SPEED DUAL ROLE CONTROLLER
+M: Peter Chen <Peter.Chen@nxp.com>
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/peter.chen/usb.git
+L: linux-usb@vger.kernel.org
+S: Maintained
+F: drivers/usb/chipidea/
+
+CHIPONE ICN8318 I2C TOUCHSCREEN DRIVER
+M: Hans de Goede <hdegoede@redhat.com>
+L: linux-input@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/input/touchscreen/chipone_icn8318.txt
+F: drivers/input/touchscreen/chipone_icn8318.c
+
+CHROME HARDWARE PLATFORM SUPPORT
+M: Olof Johansson <olof@lixom.net>
+S: Maintained
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/olof/chrome-platform.git
+F: drivers/platform/chrome/
+
+CISCO VIC ETHERNET NIC DRIVER
+M: Christian Benvenuti <benve@cisco.com>
+M: Govindarajulu Varadarajan <_govind@gmx.com>
+M: Neel Patel <neepatel@cisco.com>
+S: Supported
+F: drivers/net/ethernet/cisco/enic/
+
+CISCO VIC LOW LATENCY NIC DRIVER
+M: Christian Benvenuti <benve@cisco.com>
+M: Dave Goodell <dgoodell@cisco.com>
+S: Supported
+F: drivers/infiniband/hw/usnic/
+
+CIRRUS LOGIC EP93XX ETHERNET DRIVER
+M: Hartley Sweeten <hsweeten@visionengravers.com>
+L: netdev@vger.kernel.org
+S: Maintained
+F: drivers/net/ethernet/cirrus/ep93xx_eth.c
+
+CIRRUS LOGIC AUDIO CODEC DRIVERS
+M: Brian Austin <brian.austin@cirrus.com>
+M: Paul Handrigan <Paul.Handrigan@cirrus.com>
+L: alsa-devel@alsa-project.org (moderated for non-subscribers)
+S: Maintained
+F: sound/soc/codecs/cs*
+
+CLEANCACHE API
+M: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
+L: linux-kernel@vger.kernel.org
+S: Maintained
+F: mm/cleancache.c
+F: include/linux/cleancache.h
+
+CLK API
+M: Russell King <linux@armlinux.org.uk>
+L: linux-clk@vger.kernel.org
+S: Maintained
+F: include/linux/clk.h
+
+CLOCKSOURCE, CLOCKEVENT DRIVERS
+M: Daniel Lezcano <daniel.lezcano@linaro.org>
+M: Thomas Gleixner <tglx@linutronix.de>
+L: linux-kernel@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git timers/core
+S: Supported
+F: drivers/clocksource
+
+CISCO FCOE HBA DRIVER
+M: Satish Kharat <satishkh@cisco.com>
+M: Sesidhar Baddela <sebaddel@cisco.com>
+M: Karan Tilak Kumar <kartilak@cisco.com>
+L: linux-scsi@vger.kernel.org
+S: Supported
+F: drivers/scsi/fnic/
+
+CISCO SCSI HBA DRIVER
+M: Karan Tilak Kumar <kartilak@cisco.com>
+M: Sesidhar Baddela <sebaddel@cisco.com>
+L: linux-scsi@vger.kernel.org
+S: Supported
+F: drivers/scsi/snic/
+
+CMPC ACPI DRIVER
+M: Thadeu Lima de Souza Cascardo <cascardo@holoscopio.com>
+M: Daniel Oliveira Nascimento <don@syst.com.br>
+L: platform-driver-x86@vger.kernel.org
+S: Supported
+F: drivers/platform/x86/classmate-laptop.c
+
+COBALT MEDIA DRIVER
+M: Hans Verkuil <hans.verkuil@cisco.com>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+W: https://linuxtv.org
+S: Supported
+F: drivers/media/pci/cobalt/
+
+COCCINELLE/Semantic Patches (SmPL)
+M: Julia Lawall <Julia.Lawall@lip6.fr>
+M: Gilles Muller <Gilles.Muller@lip6.fr>
+M: Nicolas Palix <nicolas.palix@imag.fr>
+M: Michal Marek <mmarek@suse.com>
+L: cocci@systeme.lip6.fr (moderated for non-subscribers)
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/mmarek/kbuild.git misc
+W: http://coccinelle.lip6.fr/
+S: Supported
+F: Documentation/dev-tools/coccinelle.rst
+F: scripts/coccinelle/
+F: scripts/coccicheck
+
+CODA FILE SYSTEM
+M: Jan Harkes <jaharkes@cs.cmu.edu>
+M: coda@cs.cmu.edu
+L: codalist@coda.cs.cmu.edu
+W: http://www.coda.cs.cmu.edu/
+S: Maintained
+F: Documentation/filesystems/coda.txt
+F: fs/coda/
+F: include/linux/coda*.h
+F: include/uapi/linux/coda*.h
+
+CODA V4L2 MEM2MEM DRIVER
+M: Philipp Zabel <p.zabel@pengutronix.de>
+L: linux-media@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/media/coda.txt
+F: drivers/media/platform/coda/
+
+COMMON CLK FRAMEWORK
+M: Michael Turquette <mturquette@baylibre.com>
+M: Stephen Boyd <sboyd@codeaurora.org>
+L: linux-clk@vger.kernel.org
+Q: http://patchwork.kernel.org/project/linux-clk/list/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/clk/linux.git
+S: Maintained
+F: Documentation/devicetree/bindings/clock/
+F: drivers/clk/
+X: drivers/clk/clkdev.c
+F: include/linux/clk-pr*
+F: include/linux/clk/
+
+COMMON INTERNET FILE SYSTEM (CIFS)
+M: Steve French <sfrench@samba.org>
+L: linux-cifs@vger.kernel.org
+L: samba-technical@lists.samba.org (moderated for non-subscribers)
+W: http://linux-cifs.samba.org/
+T: git git://git.samba.org/sfrench/cifs-2.6.git
+S: Supported
+F: Documentation/filesystems/cifs/
+F: fs/cifs/
+
+COMPACTPCI HOTPLUG CORE
+M: Scott Murray <scott@spiteful.org>
+L: linux-pci@vger.kernel.org
+S: Maintained
+F: drivers/pci/hotplug/cpci_hotplug*
+
+COMPACTPCI HOTPLUG ZIATECH ZT5550 DRIVER
+M: Scott Murray <scott@spiteful.org>
+L: linux-pci@vger.kernel.org
+S: Maintained
+F: drivers/pci/hotplug/cpcihp_zt5550.*
+
+COMPACTPCI HOTPLUG GENERIC DRIVER
+M: Scott Murray <scott@spiteful.org>
+L: linux-pci@vger.kernel.org
+S: Maintained
+F: drivers/pci/hotplug/cpcihp_generic.c
+
+COMPAL LAPTOP SUPPORT
+M: Cezary Jackiewicz <cezary.jackiewicz@gmail.com>
+L: platform-driver-x86@vger.kernel.org
+S: Maintained
+F: drivers/platform/x86/compal-laptop.c
+
+CONEXANT ACCESSRUNNER USB DRIVER
+L: accessrunner-general@lists.sourceforge.net
+W: http://accessrunner.sourceforge.net/
+S: Orphan
+F: drivers/usb/atm/cxacru.c
+
+CONFIGFS
+M: Joel Becker <jlbec@evilplan.org>
+M: Christoph Hellwig <hch@lst.de>
+T: git git://git.infradead.org/users/hch/configfs.git
+S: Supported
+F: fs/configfs/
+F: include/linux/configfs.h
+
+CONNECTOR
+M: Evgeniy Polyakov <zbr@ioremap.net>
+L: netdev@vger.kernel.org
+S: Maintained
+F: drivers/connector/
+
+CONTROL GROUP (CGROUP)
+M: Tejun Heo <tj@kernel.org>
+M: Li Zefan <lizefan@huawei.com>
+M: Johannes Weiner <hannes@cmpxchg.org>
+L: cgroups@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/tj/cgroup.git
+S: Maintained
+F: Documentation/cgroup*
+F: include/linux/cgroup*
+F: kernel/cgroup*
+
+CONTROL GROUP - CPUSET
+M: Li Zefan <lizefan@huawei.com>
+L: cgroups@vger.kernel.org
+W: http://www.bullopensource.org/cpuset/
+W: http://oss.sgi.com/projects/cpusets/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/tj/cgroup.git
+S: Maintained
+F: Documentation/cgroup-v1/cpusets.txt
+F: include/linux/cpuset.h
+F: kernel/cpuset.c
+
+CONTROL GROUP - MEMORY RESOURCE CONTROLLER (MEMCG)
+M: Johannes Weiner <hannes@cmpxchg.org>
+M: Michal Hocko <mhocko@kernel.org>
+M: Vladimir Davydov <vdavydov.dev@gmail.com>
+L: cgroups@vger.kernel.org
+L: linux-mm@kvack.org
+S: Maintained
+F: mm/memcontrol.c
+F: mm/swap_cgroup.c
+
+CORETEMP HARDWARE MONITORING DRIVER
+M: Fenghua Yu <fenghua.yu@intel.com>
+L: linux-hwmon@vger.kernel.org
+S: Maintained
+F: Documentation/hwmon/coretemp
+F: drivers/hwmon/coretemp.c
+
+COSA/SRP SYNC SERIAL DRIVER
+M: Jan "Yenya" Kasprzak <kas@fi.muni.cz>
+W: http://www.fi.muni.cz/~kas/cosa/
+S: Maintained
+F: drivers/net/wan/cosa*
+
+CPMAC ETHERNET DRIVER
+M: Florian Fainelli <f.fainelli@gmail.com>
+L: netdev@vger.kernel.org
+S: Maintained
+F: drivers/net/ethernet/ti/cpmac.c
+
+CPU FREQUENCY DRIVERS
+M: "Rafael J. Wysocki" <rjw@rjwysocki.net>
+M: Viresh Kumar <viresh.kumar@linaro.org>
+L: linux-pm@vger.kernel.org
+S: Maintained
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git
+T: git git://git.linaro.org/people/vireshk/linux.git (For ARM Updates)
+B: https://bugzilla.kernel.org
+F: Documentation/cpu-freq/
+F: Documentation/devicetree/bindings/cpufreq/
+F: drivers/cpufreq/
+F: include/linux/cpufreq.h
+F: tools/testing/selftests/cpufreq/
+
+CPU FREQUENCY DRIVERS - ARM BIG LITTLE
+M: Viresh Kumar <viresh.kumar@linaro.org>
+M: Sudeep Holla <sudeep.holla@arm.com>
+L: linux-pm@vger.kernel.org
+W: http://www.arm.com/products/processors/technologies/biglittleprocessing.php
+S: Maintained
+F: drivers/cpufreq/arm_big_little.h
+F: drivers/cpufreq/arm_big_little.c
+F: drivers/cpufreq/arm_big_little_dt.c
+
+CPUIDLE DRIVER - ARM BIG LITTLE
+M: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
+M: Daniel Lezcano <daniel.lezcano@linaro.org>
+L: linux-pm@vger.kernel.org
+L: linux-arm-kernel@lists.infradead.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git
+S: Maintained
+F: drivers/cpuidle/cpuidle-big_little.c
+
+CPUIDLE DRIVER - ARM EXYNOS
+M: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
+M: Daniel Lezcano <daniel.lezcano@linaro.org>
+M: Kukjin Kim <kgene@kernel.org>
+L: linux-pm@vger.kernel.org
+L: linux-samsung-soc@vger.kernel.org
+S: Supported
+F: drivers/cpuidle/cpuidle-exynos.c
+F: arch/arm/mach-exynos/pm.c
+
+CPUIDLE DRIVERS
+M: "Rafael J. Wysocki" <rjw@rjwysocki.net>
+M: Daniel Lezcano <daniel.lezcano@linaro.org>
+L: linux-pm@vger.kernel.org
+S: Maintained
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git
+B: https://bugzilla.kernel.org
+F: drivers/cpuidle/*
+F: include/linux/cpuidle.h
+
+CPUID/MSR DRIVER
+M: "H. Peter Anvin" <hpa@zytor.com>
+S: Maintained
+F: arch/x86/kernel/cpuid.c
+F: arch/x86/kernel/msr.c
+
+CPU POWER MONITORING SUBSYSTEM
+M: Thomas Renninger <trenn@suse.com>
+L: linux-pm@vger.kernel.org
+S: Maintained
+F: tools/power/cpupower/
+
+CRAMFS FILESYSTEM
+W: http://sourceforge.net/projects/cramfs/
+S: Orphan / Obsolete
+F: Documentation/filesystems/cramfs.txt
+F: fs/cramfs/
+
+CRIS PORT
+M: Mikael Starvik <starvik@axis.com>
+M: Jesper Nilsson <jesper.nilsson@axis.com>
+L: linux-cris-kernel@axis.com
+W: http://developer.axis.com
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/jesper/cris.git
+S: Maintained
+F: arch/cris/
+F: drivers/tty/serial/crisv10.*
+
+CRYPTO API
+M: Herbert Xu <herbert@gondor.apana.org.au>
+M: "David S. Miller" <davem@davemloft.net>
+L: linux-crypto@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/herbert/cryptodev-2.6.git
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/herbert/crypto-2.6.git
+S: Maintained
+F: Documentation/crypto/
+F: Documentation/devicetree/bindings/crypto/
+F: Documentation/DocBook/crypto-API.tmpl
+F: arch/*/crypto/
+F: crypto/
+F: drivers/crypto/
+F: include/crypto/
+F: include/linux/crypto*
+
+CRYPTOGRAPHIC RANDOM NUMBER GENERATOR
+M: Neil Horman <nhorman@tuxdriver.com>
+L: linux-crypto@vger.kernel.org
+S: Maintained
+F: crypto/ansi_cprng.c
+F: crypto/rng.c
+
+CS3308 MEDIA DRIVER
+M: Hans Verkuil <hverkuil@xs4all.nl>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+W: http://linuxtv.org
+S: Odd Fixes
+F: drivers/media/i2c/cs3308.c
+F: drivers/media/i2c/cs3308.h
+
+CS5535 Audio ALSA driver
+M: Jaya Kumar <jayakumar.alsa@gmail.com>
+S: Maintained
+F: sound/pci/cs5535audio/
+
+CW1200 WLAN driver
+M: Solomon Peachy <pizza@shaftnet.org>
+S: Maintained
+F: drivers/net/wireless/st/cw1200/
+
+CX18 VIDEO4LINUX DRIVER
+M: Andy Walls <awalls@md.metrocast.net>
+L: ivtv-devel@ivtvdriver.org (subscribers-only)
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+W: https://linuxtv.org
+W: http://www.ivtvdriver.org/index.php/Cx18
+S: Maintained
+F: Documentation/media/v4l-drivers/cx18*
+F: drivers/media/pci/cx18/
+F: include/uapi/linux/ivtv*
+
+CX2341X MPEG ENCODER HELPER MODULE
+M: Hans Verkuil <hverkuil@xs4all.nl>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+W: https://linuxtv.org
+S: Maintained
+F: drivers/media/common/cx2341x*
+F: include/media/cx2341x*
+
+CX24120 MEDIA DRIVER
+M: Jemma Denson <jdenson@gmail.com>
+M: Patrick Boettcher <patrick.boettcher@posteo.de>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+S: Maintained
+F: drivers/media/dvb-frontends/cx24120*
+
+CX88 VIDEO4LINUX DRIVER
+M: Mauro Carvalho Chehab <mchehab@s-opensource.com>
+M: Mauro Carvalho Chehab <mchehab@kernel.org>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+T: git git://linuxtv.org/media_tree.git
+S: Odd fixes
+F: Documentation/media/v4l-drivers/cx88*
+F: drivers/media/pci/cx88/
+
+CXD2820R MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+W: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/media/dvb-frontends/cxd2820r*
+
+CXGB3 ETHERNET DRIVER (CXGB3)
+M: Santosh Raspatur <santosh@chelsio.com>
+L: netdev@vger.kernel.org
+W: http://www.chelsio.com
+S: Supported
+F: drivers/net/ethernet/chelsio/cxgb3/
+
+CXGB3 ISCSI DRIVER (CXGB3I)
+M: Karen Xie <kxie@chelsio.com>
+L: linux-scsi@vger.kernel.org
+W: http://www.chelsio.com
+S: Supported
+F: drivers/scsi/cxgbi/cxgb3i
+
+CXGB3 IWARP RNIC DRIVER (IW_CXGB3)
+M: Steve Wise <swise@chelsio.com>
+L: linux-rdma@vger.kernel.org
+W: http://www.openfabrics.org
+S: Supported
+F: drivers/infiniband/hw/cxgb3/
+F: include/uapi/rdma/cxgb3-abi.h
+
+CXGB4 ETHERNET DRIVER (CXGB4)
+M: Ganesh Goudar <ganeshgr@chelsio.com>
+L: netdev@vger.kernel.org
+W: http://www.chelsio.com
+S: Supported
+F: drivers/net/ethernet/chelsio/cxgb4/
+
+CXGB4 ISCSI DRIVER (CXGB4I)
+M: Karen Xie <kxie@chelsio.com>
+L: linux-scsi@vger.kernel.org
+W: http://www.chelsio.com
+S: Supported
+F: drivers/scsi/cxgbi/cxgb4i
+
+CXGB4 IWARP RNIC DRIVER (IW_CXGB4)
+M: Steve Wise <swise@chelsio.com>
+L: linux-rdma@vger.kernel.org
+W: http://www.openfabrics.org
+S: Supported
+F: drivers/infiniband/hw/cxgb4/
+F: include/uapi/rdma/cxgb4-abi.h
+
+CXGB4VF ETHERNET DRIVER (CXGB4VF)
+M: Casey Leedom <leedom@chelsio.com>
+L: netdev@vger.kernel.org
+W: http://www.chelsio.com
+S: Supported
+F: drivers/net/ethernet/chelsio/cxgb4vf/
+
+CXL (IBM Coherent Accelerator Processor Interface CAPI) DRIVER
+M: Ian Munsie <imunsie@au1.ibm.com>
+M: Frederic Barrat <fbarrat@linux.vnet.ibm.com>
+L: linuxppc-dev@lists.ozlabs.org
+S: Supported
+F: arch/powerpc/platforms/powernv/pci-cxl.c
+F: drivers/misc/cxl/
+F: include/misc/cxl*
+F: include/uapi/misc/cxl.h
+F: Documentation/powerpc/cxl.txt
+F: Documentation/ABI/testing/sysfs-class-cxl
+
+CXLFLASH (IBM Coherent Accelerator Processor Interface CAPI Flash) SCSI DRIVER
+M: Manoj N. Kumar <manoj@linux.vnet.ibm.com>
+M: Matthew R. Ochs <mrochs@linux.vnet.ibm.com>
+M: Uma Krishnan <ukrishn@linux.vnet.ibm.com>
+L: linux-scsi@vger.kernel.org
+S: Supported
+F: drivers/scsi/cxlflash/
+F: include/uapi/scsi/cxlflash_ioctls.h
+F: Documentation/powerpc/cxlflash.txt
+
+STMMAC ETHERNET DRIVER
+M: Giuseppe Cavallaro <peppe.cavallaro@st.com>
+M: Alexandre Torgue <alexandre.torgue@st.com>
+L: netdev@vger.kernel.org
+W: http://www.stlinux.com
+S: Supported
+F: drivers/net/ethernet/stmicro/stmmac/
+
+CYBERPRO FB DRIVER
+M: Russell King <linux@armlinux.org.uk>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+W: http://www.armlinux.org.uk/
+S: Maintained
+F: drivers/video/fbdev/cyber2000fb.*
+
+CYCLADES ASYNC MUX DRIVER
+W: http://www.cyclades.com/
+S: Orphan
+F: drivers/tty/cyclades.c
+F: include/linux/cyclades.h
+F: include/uapi/linux/cyclades.h
+
+CYCLADES PC300 DRIVER
+W: http://www.cyclades.com/
+S: Orphan
+F: drivers/net/wan/pc300*
+
+CYPRESS_FIRMWARE MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+W: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/media/common/cypress_firmware*
+
+CYTTSP TOUCHSCREEN DRIVER
+M: Ferruh Yigit <fery@cypress.com>
+L: linux-input@vger.kernel.org
+S: Supported
+F: drivers/input/touchscreen/cyttsp*
+F: include/linux/input/cyttsp.h
+
+DALLAS/MAXIM DS1685-FAMILY REAL TIME CLOCK
+M: Joshua Kinard <kumba@gentoo.org>
+S: Maintained
+F: drivers/rtc/rtc-ds1685.c
+F: include/linux/rtc/ds1685.h
+
+DAMA SLAVE for AX.25
+M: Joerg Reuter <jreuter@yaina.de>
+W: http://yaina.de/jreuter/
+W: http://www.qsl.net/dl1bke/
+L: linux-hams@vger.kernel.org
+S: Maintained
+F: net/ax25/af_ax25.c
+F: net/ax25/ax25_dev.c
+F: net/ax25/ax25_ds_*
+F: net/ax25/ax25_in.c
+F: net/ax25/ax25_out.c
+F: net/ax25/ax25_timer.c
+F: net/ax25/sysctl_net_ax25.c
+
+DAVICOM FAST ETHERNET (DMFE) NETWORK DRIVER
+L: netdev@vger.kernel.org
+S: Orphan
+F: Documentation/networking/dmfe.txt
+F: drivers/net/ethernet/dec/tulip/dmfe.c
+
+DC390/AM53C974 SCSI driver
+M: Hannes Reinecke <hare@suse.com>
+L: linux-scsi@vger.kernel.org
+S: Maintained
+F: drivers/scsi/am53c974.c
+
+DC395x SCSI driver
+M: Oliver Neukum <oliver@neukum.org>
+M: Ali Akcaagac <aliakc@web.de>
+M: Jamie Lenehan <lenehan@twibble.org>
+L: dc395x@twibble.org
+W: http://twibble.org/dist/dc395x/
+W: http://lists.twibble.org/mailman/listinfo/dc395x/
+S: Maintained
+F: Documentation/scsi/dc395x.txt
+F: drivers/scsi/dc395x.*
+
+DCCP PROTOCOL
+M: Gerrit Renker <gerrit@erg.abdn.ac.uk>
+L: dccp@vger.kernel.org
+W: http://www.linuxfoundation.org/collaborate/workgroups/networking/dccp
+S: Maintained
+F: include/linux/dccp.h
+F: include/uapi/linux/dccp.h
+F: include/linux/tfrc.h
+F: net/dccp/
+
+DECnet NETWORK LAYER
+W: http://linux-decnet.sourceforge.net
+L: linux-decnet-user@lists.sourceforge.net
+S: Orphan
+F: Documentation/networking/decnet.txt
+F: net/decnet/
+
+DECSTATION PLATFORM SUPPORT
+M: "Maciej W. Rozycki" <macro@linux-mips.org>
+L: linux-mips@linux-mips.org
+W: http://www.linux-mips.org/wiki/DECstation
+S: Maintained
+F: arch/mips/dec/
+F: arch/mips/include/asm/dec/
+F: arch/mips/include/asm/mach-dec/
+
+DEFXX FDDI NETWORK DRIVER
+M: "Maciej W. Rozycki" <macro@linux-mips.org>
+S: Maintained
+F: drivers/net/fddi/defxx.*
+
+DELL LAPTOP DRIVER
+M: Matthew Garrett <mjg59@srcf.ucam.org>
+M: Pali Rohár <pali.rohar@gmail.com>
+L: platform-driver-x86@vger.kernel.org
+S: Maintained
+F: drivers/platform/x86/dell-laptop.c
+
+DELL LAPTOP RBTN DRIVER
+M: Pali Rohár <pali.rohar@gmail.com>
+S: Maintained
+F: drivers/platform/x86/dell-rbtn.*
+
+DELL LAPTOP FREEFALL DRIVER
+M: Pali Rohár <pali.rohar@gmail.com>
+S: Maintained
+F: drivers/platform/x86/dell-smo8800.c
+
+DELL LAPTOP SMM DRIVER
+M: Pali Rohár <pali.rohar@gmail.com>
+S: Maintained
+F: drivers/hwmon/dell-smm-hwmon.c
+F: include/uapi/linux/i8k.h
+
+DELL SYSTEMS MANAGEMENT BASE DRIVER (dcdbas)
+M: Doug Warzecha <Douglas_Warzecha@dell.com>
+S: Maintained
+F: Documentation/dcdbas.txt
+F: drivers/firmware/dcdbas.*
+
+DELL WMI EXTRAS DRIVER
+M: Matthew Garrett <mjg59@srcf.ucam.org>
+M: Pali Rohár <pali.rohar@gmail.com>
+S: Maintained
+F: drivers/platform/x86/dell-wmi.c
+
+DESIGNWARE USB2 DRD IP DRIVER
+M: John Youn <johnyoun@synopsys.com>
+L: linux-usb@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb.git
+S: Maintained
+F: drivers/usb/dwc2/
+
+DESIGNWARE USB3 DRD IP DRIVER
+M: Felipe Balbi <balbi@kernel.org>
+L: linux-usb@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb.git
+S: Maintained
+F: drivers/usb/dwc3/
+
+DEVANTECH SRF ULTRASONIC RANGER IIO DRIVER
+M: Andreas Klinger <ak@it-klinger.de>
+L: linux-iio@vger.kernel.org
+S: Maintained
+F: drivers/iio/proximity/srf*.c
+
+DEVICE COREDUMP (DEV_COREDUMP)
+M: Johannes Berg <johannes@sipsolutions.net>
+L: linux-kernel@vger.kernel.org
+S: Maintained
+F: drivers/base/devcoredump.c
+F: include/linux/devcoredump.h
+
+DEVICE FREQUENCY (DEVFREQ)
+M: MyungJoo Ham <myungjoo.ham@samsung.com>
+M: Kyungmin Park <kyungmin.park@samsung.com>
+R: Chanwoo Choi <cw00.choi@samsung.com>
+L: linux-pm@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/mzx/devfreq.git
+S: Maintained
+F: drivers/devfreq/
+F: include/linux/devfreq.h
+F: Documentation/devicetree/bindings/devfreq/
+
+DEVICE FREQUENCY EVENT (DEVFREQ-EVENT)
+M: Chanwoo Choi <cw00.choi@samsung.com>
+L: linux-pm@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/mzx/devfreq.git
+S: Supported
+F: drivers/devfreq/event/
+F: drivers/devfreq/devfreq-event.c
+F: include/linux/devfreq-event.h
+F: Documentation/devicetree/bindings/devfreq/event/
+
+BUS FREQUENCY DRIVER FOR SAMSUNG EXYNOS
+M: Chanwoo Choi <cw00.choi@samsung.com>
+L: linux-pm@vger.kernel.org
+L: linux-samsung-soc@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/mzx/devfreq.git
+S: Maintained
+F: drivers/devfreq/exynos-bus.c
+F: Documentation/devicetree/bindings/devfreq/exynos-bus.txt
+
+DEVICE NUMBER REGISTRY
+M: Torben Mathiasen <device@lanana.org>
+W: http://lanana.org/docs/device-list/index.html
+S: Maintained
+
+DEVICE-MAPPER (LVM)
+M: Alasdair Kergon <agk@redhat.com>
+M: Mike Snitzer <snitzer@redhat.com>
+M: dm-devel@redhat.com
+L: dm-devel@redhat.com
+W: http://sources.redhat.com/dm
+Q: http://patchwork.kernel.org/project/dm-devel/list/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/device-mapper/linux-dm.git
+T: quilt http://people.redhat.com/agk/patches/linux/editing/
+S: Maintained
+F: Documentation/device-mapper/
+F: drivers/md/dm*
+F: drivers/md/persistent-data/
+F: include/linux/device-mapper.h
+F: include/linux/dm-*.h
+F: include/uapi/linux/dm-*.h
+
+DEVLINK
+M: Jiri Pirko <jiri@mellanox.com>
+L: netdev@vger.kernel.org
+S: Supported
+F: net/core/devlink.c
+F: include/net/devlink.h
+F: include/uapi/linux/devlink.h
+
+DIALOG SEMICONDUCTOR DRIVERS
+M: Support Opensource <support.opensource@diasemi.com>
+W: http://www.dialog-semiconductor.com/products
+S: Supported
+F: Documentation/hwmon/da90??
+F: Documentation/devicetree/bindings/mfd/da90*.txt
+F: Documentation/devicetree/bindings/regulator/da92*.txt
+F: Documentation/devicetree/bindings/sound/da[79]*.txt
+F: drivers/gpio/gpio-da90??.c
+F: drivers/hwmon/da90??-hwmon.c
+F: drivers/iio/adc/da91??-*.c
+F: drivers/input/misc/da90??_onkey.c
+F: drivers/input/touchscreen/da9052_tsi.c
+F: drivers/leds/leds-da90??.c
+F: drivers/mfd/da903x.c
+F: drivers/mfd/da90??-*.c
+F: drivers/mfd/da91??-*.c
+F: drivers/power/supply/da9052-battery.c
+F: drivers/power/supply/da91??-*.c
+F: drivers/regulator/da903x.c
+F: drivers/regulator/da9???-regulator.[ch]
+F: drivers/rtc/rtc-da90??.c
+F: drivers/video/backlight/da90??_bl.c
+F: drivers/watchdog/da90??_wdt.c
+F: include/linux/mfd/da903x.h
+F: include/linux/mfd/da9052/
+F: include/linux/mfd/da9055/
+F: include/linux/mfd/da9062/
+F: include/linux/mfd/da9063/
+F: include/linux/mfd/da9150/
+F: include/linux/regulator/da9211.h
+F: include/sound/da[79]*.h
+F: sound/soc/codecs/da[79]*.[ch]
+
+DIAMOND SYSTEMS GPIO-MM GPIO DRIVER
+M: William Breathitt Gray <vilhelm.gray@gmail.com>
+L: linux-gpio@vger.kernel.org
+S: Maintained
+F: drivers/gpio/gpio-gpio-mm.c
+
+DIGI NEO AND CLASSIC PCI PRODUCTS
+M: Lidza Louina <lidza.louina@gmail.com>
+M: Mark Hounschell <markh@compro.net>
+L: driverdev-devel@linuxdriverproject.org
+S: Maintained
+F: drivers/staging/dgnc/
+
+DIOLAN U2C-12 I2C DRIVER
+M: Guenter Roeck <linux@roeck-us.net>
+L: linux-i2c@vger.kernel.org
+S: Maintained
+F: drivers/i2c/busses/i2c-diolan-u2c.c
+
+DIRECT ACCESS (DAX)
+M: Matthew Wilcox <mawilcox@microsoft.com>
+M: Ross Zwisler <ross.zwisler@linux.intel.com>
+L: linux-fsdevel@vger.kernel.org
+S: Supported
+F: fs/dax.c
+F: include/linux/dax.h
+F: include/trace/events/fs_dax.h
+
+DIRECTORY NOTIFICATION (DNOTIFY)
+M: Eric Paris <eparis@parisplace.org>
+S: Maintained
+F: Documentation/filesystems/dnotify.txt
+F: fs/notify/dnotify/
+F: include/linux/dnotify.h
+
+DISK GEOMETRY AND PARTITION HANDLING
+M: Andries Brouwer <aeb@cwi.nl>
+W: http://www.win.tue.nl/~aeb/linux/Large-Disk.html
+W: http://www.win.tue.nl/~aeb/linux/zip/zip-1.html
+W: http://www.win.tue.nl/~aeb/partitions/partition_types-1.html
+S: Maintained
+
+DISKQUOTA
+M: Jan Kara <jack@suse.com>
+S: Maintained
+F: Documentation/filesystems/quota.txt
+F: fs/quota/
+F: include/linux/quota*.h
+F: include/uapi/linux/quota*.h
+
+DISPLAYLINK USB 2.0 FRAMEBUFFER DRIVER (UDLFB)
+M: Bernie Thompson <bernie@plugable.com>
+L: linux-fbdev@vger.kernel.org
+S: Maintained
+W: http://plugable.com/category/projects/udlfb/
+F: drivers/video/fbdev/udlfb.c
+F: include/video/udlfb.h
+F: Documentation/fb/udlfb.txt
+
+DISTRIBUTED LOCK MANAGER (DLM)
+M: Christine Caulfield <ccaulfie@redhat.com>
+M: David Teigland <teigland@redhat.com>
+L: cluster-devel@redhat.com
+W: http://sources.redhat.com/cluster/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/teigland/linux-dlm.git
+S: Supported
+F: fs/dlm/
+
+DMA BUFFER SHARING FRAMEWORK
+M: Sumit Semwal <sumit.semwal@linaro.org>
+S: Maintained
+L: linux-media@vger.kernel.org
+L: dri-devel@lists.freedesktop.org
+L: linaro-mm-sig@lists.linaro.org (moderated for non-subscribers)
+F: drivers/dma-buf/
+F: include/linux/dma-buf*
+F: include/linux/reservation.h
+F: include/linux/*fence.h
+F: Documentation/driver-api/dma-buf.rst
+T: git git://anongit.freedesktop.org/drm/drm-misc
+
+SYNC FILE FRAMEWORK
+M: Sumit Semwal <sumit.semwal@linaro.org>
+R: Gustavo Padovan <gustavo@padovan.org>
+S: Maintained
+L: linux-media@vger.kernel.org
+L: dri-devel@lists.freedesktop.org
+F: drivers/dma-buf/sync_*
+F: drivers/dma-buf/dma-fence*
+F: drivers/dma-buf/sw_sync.c
+F: include/linux/sync_file.h
+F: include/uapi/linux/sync_file.h
+F: Documentation/sync_file.txt
+T: git git://anongit.freedesktop.org/drm/drm-misc
+
+DMA GENERIC OFFLOAD ENGINE SUBSYSTEM
+M: Vinod Koul <vinod.koul@intel.com>
+L: dmaengine@vger.kernel.org
+Q: https://patchwork.kernel.org/project/linux-dmaengine/list/
+S: Maintained
+F: drivers/dma/
+F: include/linux/dmaengine.h
+F: Documentation/devicetree/bindings/dma/
+F: Documentation/dmaengine/
+T: git git://git.infradead.org/users/vkoul/slave-dma.git
+
+DME1737 HARDWARE MONITOR DRIVER
+M: Juerg Haefliger <juergh@gmail.com>
+L: linux-hwmon@vger.kernel.org
+S: Maintained
+F: Documentation/hwmon/dme1737
+F: drivers/hwmon/dme1737.c
+
+DMI/SMBIOS SUPPORT
+M: Jean Delvare <jdelvare@suse.com>
+S: Maintained
+T: quilt http://jdelvare.nerim.net/devel/linux/jdelvare-dmi/
+F: Documentation/ABI/testing/sysfs-firmware-dmi-tables
+F: drivers/firmware/dmi-id.c
+F: drivers/firmware/dmi_scan.c
+F: include/linux/dmi.h
+
+DOCUMENTATION
+M: Jonathan Corbet <corbet@lwn.net>
+L: linux-doc@vger.kernel.org
+S: Maintained
+F: Documentation/
+F: scripts/docproc.c
+F: scripts/kernel-doc*
+X: Documentation/ABI/
+X: Documentation/devicetree/
+X: Documentation/acpi
+X: Documentation/power
+X: Documentation/spi
+X: Documentation/media
+T: git git://git.lwn.net/linux.git docs-next
+
+DOUBLETALK DRIVER
+M: "James R. Van Zandt" <jrv@vanzandt.mv.com>
+L: blinux-list@redhat.com
+S: Maintained
+F: drivers/char/dtlk.c
+F: include/linux/dtlk.h
+
+DPAA2 DATAPATH I/O (DPIO) DRIVER
+M: Roy Pledge <Roy.Pledge@nxp.com>
+L: linux-kernel@vger.kernel.org
+S: Maintained
+F: drivers/staging/fsl-mc/bus/dpio
+
+DPAA2 ETHERNET DRIVER
+M: Ioana Radulescu <ruxandra.radulescu@nxp.com>
+L: linux-kernel@vger.kernel.org
+S: Maintained
+F: drivers/staging/fsl-dpaa2/ethernet
+
+DPT_I2O SCSI RAID DRIVER
+M: Adaptec OEM Raid Solutions <aacraid@adaptec.com>
+L: linux-scsi@vger.kernel.org
+W: http://www.adaptec.com/
+S: Maintained
+F: drivers/scsi/dpt*
+F: drivers/scsi/dpt/
+
+DRBD DRIVER
+M: Philipp Reisner <philipp.reisner@linbit.com>
+M: Lars Ellenberg <lars.ellenberg@linbit.com>
+L: drbd-dev@lists.linbit.com
+W: http://www.drbd.org
+T: git git://git.linbit.com/linux-drbd.git
+T: git git://git.linbit.com/drbd-8.4.git
+S: Supported
+F: drivers/block/drbd/
+F: lib/lru_cache.c
+F: Documentation/blockdev/drbd/
+
+DRIVER CORE, KOBJECTS, DEBUGFS AND SYSFS
+M: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core.git
+S: Supported
+F: Documentation/kobject.txt
+F: drivers/base/
+F: fs/debugfs/
+F: fs/sysfs/
+F: include/linux/debugfs.h
+F: include/linux/kobj*
+F: lib/kobj*
+
+DRM DRIVERS
+M: David Airlie <airlied@linux.ie>
+L: dri-devel@lists.freedesktop.org
+T: git git://people.freedesktop.org/~airlied/linux
+B: https://bugs.freedesktop.org/
+C: irc://chat.freenode.net/dri-devel
+S: Maintained
+F: drivers/gpu/drm/
+F: drivers/gpu/vga/
+F: Documentation/devicetree/bindings/display/
+F: Documentation/devicetree/bindings/gpu/
+F: Documentation/devicetree/bindings/video/
+F: Documentation/gpu/
+F: include/drm/
+F: include/uapi/drm/
+F: include/linux/vga*
+
+DRM DRIVERS AND MISC GPU PATCHES
+M: Daniel Vetter <daniel.vetter@intel.com>
+M: Jani Nikula <jani.nikula@linux.intel.com>
+M: Sean Paul <seanpaul@chromium.org>
+W: https://01.org/linuxgraphics/gfx-docs/maintainer-tools/drm-misc.html
+S: Maintained
+T: git git://anongit.freedesktop.org/drm/drm-misc
+F: Documentation/gpu/
+F: drivers/gpu/vga/
+F: drivers/gpu/drm/*
+F: include/drm/drm*
+F: include/uapi/drm/drm*
+F: include/linux/vga*
+
+DRM DRIVER FOR AST SERVER GRAPHICS CHIPS
+M: Dave Airlie <airlied@redhat.com>
+S: Odd Fixes
+F: drivers/gpu/drm/ast/
+
+DRM DRIVERS FOR BRIDGE CHIPS
+M: Archit Taneja <architt@codeaurora.org>
+S: Maintained
+T: git git://anongit.freedesktop.org/drm/drm-misc
+F: drivers/gpu/drm/bridge/
+
+DRM DRIVER FOR BOCHS VIRTUAL GPU
+M: Gerd Hoffmann <kraxel@redhat.com>
+L: virtualization@lists.linux-foundation.org
+T: git git://anongit.freedesktop.org/drm/drm-misc
+S: Maintained
+F: drivers/gpu/drm/bochs/
+
+DRM DRIVER FOR QEMU'S CIRRUS DEVICE
+M: Dave Airlie <airlied@redhat.com>
+M: Gerd Hoffmann <kraxel@redhat.com>
+L: virtualization@lists.linux-foundation.org
+T: git git://anongit.freedesktop.org/drm/drm-misc
+S: Obsolete
+W: https://www.kraxel.org/blog/2014/10/qemu-using-cirrus-considered-harmful/
+F: drivers/gpu/drm/cirrus/
+
+RADEON and AMDGPU DRM DRIVERS
+M: Alex Deucher <alexander.deucher@amd.com>
+M: Christian König <christian.koenig@amd.com>
+L: amd-gfx@lists.freedesktop.org
+T: git git://people.freedesktop.org/~agd5f/linux
+S: Supported
+F: drivers/gpu/drm/radeon/
+F: include/uapi/drm/radeon_drm.h
+F: drivers/gpu/drm/amd/
+F: include/uapi/drm/amdgpu_drm.h
+
+DRM PANEL DRIVERS
+M: Thierry Reding <thierry.reding@gmail.com>
+L: dri-devel@lists.freedesktop.org
+T: git git://anongit.freedesktop.org/tegra/linux.git
+S: Maintained
+F: drivers/gpu/drm/drm_panel.c
+F: drivers/gpu/drm/panel/
+F: include/drm/drm_panel.h
+F: Documentation/devicetree/bindings/display/panel/
+
+INTEL DRM DRIVERS (excluding Poulsbo, Moorestown and derivative chipsets)
+M: Daniel Vetter <daniel.vetter@intel.com>
+M: Jani Nikula <jani.nikula@linux.intel.com>
+L: intel-gfx@lists.freedesktop.org
+W: https://01.org/linuxgraphics/
+B: https://01.org/linuxgraphics/documentation/how-report-bugs
+C: irc://chat.freenode.net/intel-gfx
+Q: http://patchwork.freedesktop.org/project/intel-gfx/
+T: git git://anongit.freedesktop.org/drm-intel
+S: Supported
+F: drivers/gpu/drm/i915/
+F: include/drm/i915*
+F: include/uapi/drm/i915_drm.h
+F: Documentation/gpu/i915.rst
+
+INTEL GVT-g DRIVERS (Intel GPU Virtualization)
+M: Zhenyu Wang <zhenyuw@linux.intel.com>
+M: Zhi Wang <zhi.a.wang@intel.com>
+L: intel-gvt-dev@lists.freedesktop.org
+L: intel-gfx@lists.freedesktop.org
+W: https://01.org/igvt-g
+T: git https://github.com/01org/gvt-linux.git
+S: Supported
+F: drivers/gpu/drm/i915/gvt/
+
+DRM DRIVERS FOR ATMEL HLCDC
+M: Boris Brezillon <boris.brezillon@free-electrons.com>
+L: dri-devel@lists.freedesktop.org
+S: Supported
+F: drivers/gpu/drm/atmel-hlcdc/
+F: Documentation/devicetree/bindings/drm/atmel/
+T: git git://anongit.freedesktop.org/drm/drm-misc
+
+DRM DRIVERS FOR ALLWINNER A10
+M: Maxime Ripard <maxime.ripard@free-electrons.com>
+L: dri-devel@lists.freedesktop.org
+S: Supported
+F: drivers/gpu/drm/sun4i/
+F: Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/mripard/linux.git
+
+DRM DRIVERS FOR AMLOGIC SOCS
+M: Neil Armstrong <narmstrong@baylibre.com>
+L: dri-devel@lists.freedesktop.org
+L: linux-amlogic@lists.infradead.org
+W: http://linux-meson.com/
+S: Supported
+F: drivers/gpu/drm/meson/
+F: Documentation/devicetree/bindings/display/amlogic,meson-vpu.txt
+F: Documentation/devicetree/bindings/display/amlogic,meson-dw-hdmi.txt
+F: Documentation/gpu/meson.rst
+T: git git://anongit.freedesktop.org/drm/drm-misc
+
+DRM DRIVERS FOR EXYNOS
+M: Inki Dae <inki.dae@samsung.com>
+M: Joonyoung Shim <jy0922.shim@samsung.com>
+M: Seung-Woo Kim <sw0312.kim@samsung.com>
+M: Kyungmin Park <kyungmin.park@samsung.com>
+L: dri-devel@lists.freedesktop.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos.git
+S: Supported
+F: drivers/gpu/drm/exynos/
+F: include/uapi/drm/exynos_drm.h
+F: Documentation/devicetree/bindings/display/exynos/
+
+DRM DRIVERS FOR FREESCALE DCU
+M: Stefan Agner <stefan@agner.ch>
+M: Alison Wang <alison.wang@freescale.com>
+L: dri-devel@lists.freedesktop.org
+S: Supported
+F: drivers/gpu/drm/fsl-dcu/
+F: Documentation/devicetree/bindings/display/fsl,dcu.txt
+F: Documentation/devicetree/bindings/display/fsl,tcon.txt
+F: Documentation/devicetree/bindings/display/panel/nec,nl4827hc19_05b.txt
+
+DRM DRIVERS FOR FREESCALE IMX
+M: Philipp Zabel <p.zabel@pengutronix.de>
+L: dri-devel@lists.freedesktop.org
+S: Maintained
+F: drivers/gpu/drm/imx/
+F: drivers/gpu/ipu-v3/
+F: Documentation/devicetree/bindings/display/imx/
+
+DRM DRIVERS FOR GMA500 (Poulsbo, Moorestown and derivative chipsets)
+M: Patrik Jakobsson <patrik.r.jakobsson@gmail.com>
+L: dri-devel@lists.freedesktop.org
+T: git git://github.com/patjak/drm-gma500
+S: Maintained
+F: drivers/gpu/drm/gma500/
+
+DRM DRIVERS FOR HISILICON
+M: Xinliang Liu <z.liuxinliang@hisilicon.com>
+M: Rongrong Zou <zourongrong@gmail.com>
+R: Xinwei Kong <kong.kongxinwei@hisilicon.com>
+R: Chen Feng <puck.chen@hisilicon.com>
+L: dri-devel@lists.freedesktop.org
+T: git git://github.com/xin3liang/linux.git
+S: Maintained
+F: drivers/gpu/drm/hisilicon/
+F: Documentation/devicetree/bindings/display/hisilicon/
+
+DRM DRIVER FOR INTEL I810 VIDEO CARDS
+S: Orphan / Obsolete
+F: drivers/gpu/drm/i810/
+F: include/uapi/drm/i810_drm.h
+
+DRM DRIVERS FOR MEDIATEK
+M: CK Hu <ck.hu@mediatek.com>
+M: Philipp Zabel <p.zabel@pengutronix.de>
+L: dri-devel@lists.freedesktop.org
+S: Supported
+F: drivers/gpu/drm/mediatek/
+F: Documentation/devicetree/bindings/display/mediatek/
+
+DRM DRIVER FOR MI0283QT
+M: Noralf Trønnes <noralf@tronnes.org>
+S: Maintained
+F: drivers/gpu/drm/tinydrm/mi0283qt.c
+F: Documentation/devicetree/bindings/display/multi-inno,mi0283qt.txt
+
+DRM DRIVER FOR MSM ADRENO GPU
+M: Rob Clark <robdclark@gmail.com>
+L: linux-arm-msm@vger.kernel.org
+L: dri-devel@lists.freedesktop.org
+L: freedreno@lists.freedesktop.org
+T: git git://people.freedesktop.org/~robclark/linux
+S: Maintained
+F: drivers/gpu/drm/msm/
+F: include/uapi/drm/msm_drm.h
+F: Documentation/devicetree/bindings/display/msm/
+
+DRM DRIVER FOR NVIDIA GEFORCE/QUADRO GPUS
+M: Ben Skeggs <bskeggs@redhat.com>
+L: dri-devel@lists.freedesktop.org
+L: nouveau@lists.freedesktop.org
+T: git git://github.com/skeggsb/linux
+S: Supported
+F: drivers/gpu/drm/nouveau/
+F: include/uapi/drm/nouveau_drm.h
+
+DRM DRIVERS FOR NVIDIA TEGRA
+M: Thierry Reding <thierry.reding@gmail.com>
+L: dri-devel@lists.freedesktop.org
+L: linux-tegra@vger.kernel.org
+T: git git://anongit.freedesktop.org/tegra/linux.git
+S: Supported
+F: drivers/gpu/drm/tegra/
+F: drivers/gpu/host1x/
+F: include/linux/host1x.h
+F: include/uapi/drm/tegra_drm.h
+F: Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.txt
+
+DRM DRIVER FOR MATROX G200/G400 GRAPHICS CARDS
+S: Orphan / Obsolete
+F: drivers/gpu/drm/mga/
+F: include/uapi/drm/mga_drm.h
+
+DRM DRIVER FOR MGA G200 SERVER GRAPHICS CHIPS
+M: Dave Airlie <airlied@redhat.com>
+S: Odd Fixes
+F: drivers/gpu/drm/mgag200/
+
+DRM DRIVER FOR RAGE 128 VIDEO CARDS
+S: Orphan / Obsolete
+F: drivers/gpu/drm/r128/
+F: include/uapi/drm/r128_drm.h
+
+DRM DRIVERS FOR RENESAS
+M: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+L: dri-devel@lists.freedesktop.org
+L: linux-renesas-soc@vger.kernel.org
+T: git git://linuxtv.org/pinchartl/fbdev
+S: Supported
+F: drivers/gpu/drm/rcar-du/
+F: drivers/gpu/drm/shmobile/
+F: include/linux/platform_data/shmob_drm.h
+F: Documentation/devicetree/bindings/display/bridge/renesas,dw-hdmi.txt
+F: Documentation/devicetree/bindings/display/renesas,du.txt
+
+DRM DRIVER FOR QXL VIRTUAL GPU
+M: Dave Airlie <airlied@redhat.com>
+M: Gerd Hoffmann <kraxel@redhat.com>
+L: virtualization@lists.linux-foundation.org
+T: git git://anongit.freedesktop.org/drm/drm-misc
+S: Maintained
+F: drivers/gpu/drm/qxl/
+F: include/uapi/drm/qxl_drm.h
+
+DRM DRIVERS FOR ROCKCHIP
+M: Mark Yao <mark.yao@rock-chips.com>
+L: dri-devel@lists.freedesktop.org
+S: Maintained
+F: drivers/gpu/drm/rockchip/
+F: Documentation/devicetree/bindings/display/rockchip/
+T: git git://anongit.freedesktop.org/drm/drm-misc
+
+DRM DRIVER FOR SAVAGE VIDEO CARDS
+S: Orphan / Obsolete
+F: drivers/gpu/drm/savage/
+F: include/uapi/drm/savage_drm.h
+
+DRM DRIVER FOR SIS VIDEO CARDS
+S: Orphan / Obsolete
+F: drivers/gpu/drm/sis/
+F: include/uapi/drm/sis_drm.h
+
+DRM DRIVERS FOR STI
+M: Benjamin Gaignard <benjamin.gaignard@linaro.org>
+M: Vincent Abriou <vincent.abriou@st.com>
+L: dri-devel@lists.freedesktop.org
+T: git git://anongit.freedesktop.org/drm/drm-misc
+S: Maintained
+F: drivers/gpu/drm/sti
+F: Documentation/devicetree/bindings/display/st,stih4xx.txt
+
+DRM DRIVER FOR TDFX VIDEO CARDS
+S: Orphan / Obsolete
+F: drivers/gpu/drm/tdfx/
+
+DRM DRIVER FOR USB DISPLAYLINK VIDEO ADAPTERS
+M: Dave Airlie <airlied@redhat.com>
+S: Odd Fixes
+F: drivers/gpu/drm/udl/
+
+DRM DRIVERS FOR VIVANTE GPU IP
+M: Lucas Stach <l.stach@pengutronix.de>
+R: Russell King <linux+etnaviv@armlinux.org.uk>
+R: Christian Gmeiner <christian.gmeiner@gmail.com>
+L: etnaviv@lists.freedesktop.org
+L: dri-devel@lists.freedesktop.org
+S: Maintained
+F: drivers/gpu/drm/etnaviv/
+F: include/uapi/drm/etnaviv_drm.h
+F: Documentation/devicetree/bindings/display/etnaviv/
+
+DRM DRIVER FOR VMWARE VIRTUAL GPU
+M: "VMware Graphics" <linux-graphics-maintainer@vmware.com>
+M: Sinclair Yeh <syeh@vmware.com>
+M: Thomas Hellstrom <thellstrom@vmware.com>
+L: dri-devel@lists.freedesktop.org
+T: git git://people.freedesktop.org/~syeh/repos_linux
+T: git git://people.freedesktop.org/~thomash/linux
+S: Supported
+F: drivers/gpu/drm/vmwgfx/
+F: include/uapi/drm/vmwgfx_drm.h
+
+DRM DRIVERS FOR VC4
+M: Eric Anholt <eric@anholt.net>
+T: git git://github.com/anholt/linux
+S: Supported
+F: drivers/gpu/drm/vc4/
+F: include/uapi/drm/vc4_drm.h
+F: Documentation/devicetree/bindings/display/brcm,bcm-vc4.txt
+T: git git://anongit.freedesktop.org/drm/drm-misc
+
+DRM DRIVERS FOR TI OMAP
+M: Tomi Valkeinen <tomi.valkeinen@ti.com>
+L: dri-devel@lists.freedesktop.org
+S: Maintained
+F: drivers/gpu/drm/omapdrm/
+F: Documentation/devicetree/bindings/display/ti/
+
+DRM DRIVERS FOR TI LCDC
+M: Jyri Sarha <jsarha@ti.com>
+R: Tomi Valkeinen <tomi.valkeinen@ti.com>
+L: dri-devel@lists.freedesktop.org
+S: Maintained
+F: drivers/gpu/drm/tilcdc/
+F: Documentation/devicetree/bindings/display/tilcdc/
+
+DRM DRIVERS FOR ZTE ZX
+M: Shawn Guo <shawnguo@kernel.org>
+L: dri-devel@lists.freedesktop.org
+S: Maintained
+F: drivers/gpu/drm/zte/
+F: Documentation/devicetree/bindings/display/zte,vou.txt
+T: git git://anongit.freedesktop.org/drm/drm-misc
+
+DSBR100 USB FM RADIO DRIVER
+M: Alexey Klimov <klimov.linux@gmail.com>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+S: Maintained
+F: drivers/media/radio/dsbr100.c
+
+DSCC4 DRIVER
+M: Francois Romieu <romieu@fr.zoreil.com>
+L: netdev@vger.kernel.org
+S: Maintained
+F: drivers/net/wan/dscc4.c
+
+DT3155 MEDIA DRIVER
+M: Hans Verkuil <hverkuil@xs4all.nl>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+W: https://linuxtv.org
+S: Odd Fixes
+F: drivers/media/pci/dt3155/
+
+DVB_USB_AF9015 MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+W: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/media/usb/dvb-usb-v2/af9015*
+
+DVB_USB_AF9035 MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+W: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/media/usb/dvb-usb-v2/af9035*
+
+DVB_USB_ANYSEE MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+W: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/media/usb/dvb-usb-v2/anysee*
+
+DVB_USB_AU6610 MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+W: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/media/usb/dvb-usb-v2/au6610*
+
+DVB_USB_CE6230 MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+W: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/media/usb/dvb-usb-v2/ce6230*
+
+DVB_USB_CXUSB MEDIA DRIVER
+M: Michael Krufky <mkrufky@linuxtv.org>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+W: http://github.com/mkrufky
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/media_tree.git
+S: Maintained
+F: drivers/media/usb/dvb-usb/cxusb*
+
+DVB_USB_EC168 MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+W: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/media/usb/dvb-usb-v2/ec168*
+
+DVB_USB_GL861 MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/media/usb/dvb-usb-v2/gl861*
+
+DVB_USB_MXL111SF MEDIA DRIVER
+M: Michael Krufky <mkrufky@linuxtv.org>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+W: http://github.com/mkrufky
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/mkrufky/mxl111sf.git
+S: Maintained
+F: drivers/media/usb/dvb-usb-v2/mxl111sf*
+
+DVB_USB_RTL28XXU MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+W: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/media/usb/dvb-usb-v2/rtl28xxu*
+
+DVB_USB_V2 MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+W: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/media/usb/dvb-usb-v2/dvb_usb*
+F: drivers/media/usb/dvb-usb-v2/usb_urb.c
+
+DYNAMIC DEBUG
+M: Jason Baron <jbaron@akamai.com>
+S: Maintained
+F: lib/dynamic_debug.c
+F: include/linux/dynamic_debug.h
+
+DZ DECSTATION DZ11 SERIAL DRIVER
+M: "Maciej W. Rozycki" <macro@linux-mips.org>
+S: Maintained
+F: drivers/tty/serial/dz.*
+
+E3X0 POWER BUTTON DRIVER
+M: Moritz Fischer <moritz.fischer@ettus.com>
+L: usrp-users@lists.ettus.com
+W: http://www.ettus.com
+S: Supported
+F: drivers/input/misc/e3x0-button.c
+F: Documentation/devicetree/bindings/input/e3x0-button.txt
+
+E4000 MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+W: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/media/tuners/e4000*
+
+EATA ISA/EISA/PCI SCSI DRIVER
+M: Dario Ballabio <ballabio_dario@emc.com>
+L: linux-scsi@vger.kernel.org
+S: Maintained
+F: drivers/scsi/eata.c
+
+EC100 MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+W: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/media/dvb-frontends/ec100*
+
+ECRYPT FILE SYSTEM
+M: Tyler Hicks <tyhicks@canonical.com>
+L: ecryptfs@vger.kernel.org
+W: http://ecryptfs.org
+W: https://launchpad.net/ecryptfs
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/tyhicks/ecryptfs.git
+S: Supported
+F: Documentation/filesystems/ecryptfs.txt
+F: fs/ecryptfs/
+
+EDAC-CORE
+M: Borislav Petkov <bp@alien8.de>
+M: Mauro Carvalho Chehab <mchehab@s-opensource.com>
+M: Mauro Carvalho Chehab <mchehab@kernel.org>
+L: linux-edac@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/bp/bp.git for-next
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-edac.git linux_next
+S: Supported
+F: Documentation/admin-guide/ras.rst
+F: Documentation/driver-api/edac.rst
+F: drivers/edac/
+F: include/linux/edac.h
+
+EDAC-AMD64
+M: Borislav Petkov <bp@alien8.de>
+L: linux-edac@vger.kernel.org
+S: Maintained
+F: drivers/edac/amd64_edac*
+
+EDAC-CALXEDA
+M: Robert Richter <rric@kernel.org>
+L: linux-edac@vger.kernel.org
+S: Maintained
+F: drivers/edac/highbank*
+
+EDAC-CAVIUM
+M: Ralf Baechle <ralf@linux-mips.org>
+M: David Daney <david.daney@cavium.com>
+L: linux-edac@vger.kernel.org
+L: linux-mips@linux-mips.org
+S: Supported
+F: drivers/edac/octeon_edac*
+F: drivers/edac/thunderx_edac*
+
+EDAC-E752X
+M: Mark Gross <mark.gross@intel.com>
+L: linux-edac@vger.kernel.org
+S: Maintained
+F: drivers/edac/e752x_edac.c
+
+EDAC-E7XXX
+L: linux-edac@vger.kernel.org
+S: Maintained
+F: drivers/edac/e7xxx_edac.c
+
+EDAC-FSL_DDR
+M: York Sun <york.sun@nxp.com>
+L: linux-edac@vger.kernel.org
+S: Maintained
+F: drivers/edac/fsl_ddr_edac.*
+
+EDAC-GHES
+M: Mauro Carvalho Chehab <mchehab@s-opensource.com>
+M: Mauro Carvalho Chehab <mchehab@kernel.org>
+L: linux-edac@vger.kernel.org
+S: Maintained
+F: drivers/edac/ghes_edac.c
+
+EDAC-I82443BXGX
+M: Tim Small <tim@buttersideup.com>
+L: linux-edac@vger.kernel.org
+S: Maintained
+F: drivers/edac/i82443bxgx_edac.c
+
+EDAC-I3000
+L: linux-edac@vger.kernel.org
+S: Orphan
+F: drivers/edac/i3000_edac.c
+
+EDAC-I5000
+L: linux-edac@vger.kernel.org
+S: Maintained
+F: drivers/edac/i5000_edac.c
+
+EDAC-I5400
+M: Mauro Carvalho Chehab <mchehab@s-opensource.com>
+M: Mauro Carvalho Chehab <mchehab@kernel.org>
+L: linux-edac@vger.kernel.org
+S: Maintained
+F: drivers/edac/i5400_edac.c
+
+EDAC-I7300
+M: Mauro Carvalho Chehab <mchehab@s-opensource.com>
+M: Mauro Carvalho Chehab <mchehab@kernel.org>
+L: linux-edac@vger.kernel.org
+S: Maintained
+F: drivers/edac/i7300_edac.c
+
+EDAC-I7CORE
+M: Mauro Carvalho Chehab <mchehab@s-opensource.com>
+M: Mauro Carvalho Chehab <mchehab@kernel.org>
+L: linux-edac@vger.kernel.org
+S: Maintained
+F: drivers/edac/i7core_edac.c
+
+EDAC-I82975X
+M: Ranganathan Desikan <ravi@jetztechnologies.com>
+M: "Arvind R." <arvino55@gmail.com>
+L: linux-edac@vger.kernel.org
+S: Maintained
+F: drivers/edac/i82975x_edac.c
+
+EDAC-IE31200
+M: Jason Baron <jbaron@akamai.com>
+L: linux-edac@vger.kernel.org
+S: Maintained
+F: drivers/edac/ie31200_edac.c
+
+EDAC-MPC85XX
+M: Johannes Thumshirn <morbidrsa@gmail.com>
+L: linux-edac@vger.kernel.org
+S: Maintained
+F: drivers/edac/mpc85xx_edac.[ch]
+
+EDAC-PND2
+M: Tony Luck <tony.luck@intel.com>
+L: linux-edac@vger.kernel.org
+S: Maintained
+F: drivers/edac/pnd2_edac.[ch]
+
+EDAC-PASEMI
+M: Egor Martovetsky <egor@pasemi.com>
+L: linux-edac@vger.kernel.org
+S: Maintained
+F: drivers/edac/pasemi_edac.c
+
+EDAC-R82600
+M: Tim Small <tim@buttersideup.com>
+L: linux-edac@vger.kernel.org
+S: Maintained
+F: drivers/edac/r82600_edac.c
+
+EDAC-SBRIDGE
+M: Mauro Carvalho Chehab <mchehab@s-opensource.com>
+M: Mauro Carvalho Chehab <mchehab@kernel.org>
+L: linux-edac@vger.kernel.org
+S: Maintained
+F: drivers/edac/sb_edac.c
+
+EDAC-SKYLAKE
+M: Tony Luck <tony.luck@intel.com>
+L: linux-edac@vger.kernel.org
+S: Maintained
+F: drivers/edac/skx_edac.c
+
+EDAC-XGENE
+APPLIED MICRO (APM) X-GENE SOC EDAC
+M: Loc Ho <lho@apm.com>
+S: Supported
+F: drivers/edac/xgene_edac.c
+F: Documentation/devicetree/bindings/edac/apm-xgene-edac.txt
+
+EDIROL UA-101/UA-1000 DRIVER
+M: Clemens Ladisch <clemens@ladisch.de>
+L: alsa-devel@alsa-project.org (moderated for non-subscribers)
+T: git git://git.alsa-project.org/alsa-kernel.git
+S: Maintained
+F: sound/usb/misc/ua101.c
+
+EXTENSIBLE FIRMWARE INTERFACE (EFI)
+M: Matt Fleming <matt@codeblueprint.co.uk>
+M: Ard Biesheuvel <ard.biesheuvel@linaro.org>
+L: linux-efi@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/efi/efi.git
+S: Maintained
+F: Documentation/efi-stub.txt
+F: arch/*/kernel/efi.c
+F: arch/x86/boot/compressed/eboot.[ch]
+F: arch/*/include/asm/efi.h
+F: arch/x86/platform/efi/
+F: drivers/firmware/efi/
+F: include/linux/efi*.h
+F: arch/arm/boot/compressed/efi-header.S
+F: arch/arm64/kernel/efi-entry.S
+
+EFI VARIABLE FILESYSTEM
+M: Matthew Garrett <matthew.garrett@nebula.com>
+M: Jeremy Kerr <jk@ozlabs.org>
+M: Matt Fleming <matt@codeblueprint.co.uk>
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/mfleming/efi.git
+L: linux-efi@vger.kernel.org
+S: Maintained
+F: fs/efivarfs/
+
+EFIFB FRAMEBUFFER DRIVER
+L: linux-fbdev@vger.kernel.org
+M: Peter Jones <pjones@redhat.com>
+S: Maintained
+F: drivers/video/fbdev/efifb.c
+
+EFI TEST DRIVER
+L: linux-efi@vger.kernel.org
+M: Ivan Hu <ivan.hu@canonical.com>
+M: Matt Fleming <matt@codeblueprint.co.uk>
+S: Maintained
+F: drivers/firmware/efi/test/
+
+EFS FILESYSTEM
+W: http://aeschi.ch.eu.org/efs/
+S: Orphan
+F: fs/efs/
+
+EHEA (IBM pSeries eHEA 10Gb ethernet adapter) DRIVER
+M: Douglas Miller <dougmill@linux.vnet.ibm.com>
+L: netdev@vger.kernel.org
+S: Maintained
+F: drivers/net/ethernet/ibm/ehea/
+
+EM28XX VIDEO4LINUX DRIVER
+M: Mauro Carvalho Chehab <mchehab@s-opensource.com>
+M: Mauro Carvalho Chehab <mchehab@kernel.org>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+T: git git://linuxtv.org/media_tree.git
+S: Maintained
+F: drivers/media/usb/em28xx/
+F: Documentation/media/v4l-drivers/em28xx*
+
+EMBEDDED LINUX
+M: Paul Gortmaker <paul.gortmaker@windriver.com>
+M: Matt Mackall <mpm@selenic.com>
+M: David Woodhouse <dwmw2@infradead.org>
+L: linux-embedded@vger.kernel.org
+S: Maintained
+
+EMULEX/BROADCOM LPFC FC/FCOE SCSI DRIVER
+M: James Smart <james.smart@broadcom.com>
+M: Dick Kennedy <dick.kennedy@broadcom.com>
+L: linux-scsi@vger.kernel.org
+W: http://www.broadcom.com
+S: Supported
+F: drivers/scsi/lpfc/
+
+ENE CB710 FLASH CARD READER DRIVER
+M: Michał Mirosław <mirq-linux@rere.qmqm.pl>
+S: Maintained
+F: drivers/misc/cb710/
+F: drivers/mmc/host/cb710-mmc.*
+F: include/linux/cb710.h
+
+ENE KB2426 (ENE0100/ENE020XX) INFRARED RECEIVER
+M: Maxim Levitsky <maximlevitsky@gmail.com>
+S: Maintained
+F: drivers/media/rc/ene_ir.*
+
+EPSON S1D13XXX FRAMEBUFFER DRIVER
+M: Kristoffer Ericson <kristoffer.ericson@gmail.com>
+S: Maintained
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/kristoffer/linux-hpc.git
+F: drivers/video/fbdev/s1d13xxxfb.c
+F: include/video/s1d13xxxfb.h
+
+ET131X NETWORK DRIVER
+M: Mark Einon <mark.einon@gmail.com>
+S: Odd Fixes
+F: drivers/net/ethernet/agere/
+
+ETHERNET BRIDGE
+M: Stephen Hemminger <stephen@networkplumber.org>
+L: bridge@lists.linux-foundation.org (moderated for non-subscribers)
+L: netdev@vger.kernel.org
+W: http://www.linuxfoundation.org/en/Net:Bridge
+S: Maintained
+F: include/linux/netfilter_bridge/
+F: net/bridge/
+
+ETHERNET PHY LIBRARY
+M: Andrew Lunn <andrew@lunn.ch>
+M: Florian Fainelli <f.fainelli@gmail.com>
+L: netdev@vger.kernel.org
+S: Maintained
+F: include/linux/phy.h
+F: include/linux/phy_fixed.h
+F: drivers/net/phy/
+F: Documentation/networking/phy.txt
+F: drivers/of/of_mdio.c
+F: drivers/of/of_net.c
+
+EXT2 FILE SYSTEM
+M: Jan Kara <jack@suse.com>
+L: linux-ext4@vger.kernel.org
+S: Maintained
+F: Documentation/filesystems/ext2.txt
+F: fs/ext2/
+F: include/linux/ext2*
+
+EXT4 FILE SYSTEM
+M: "Theodore Ts'o" <tytso@mit.edu>
+M: Andreas Dilger <adilger.kernel@dilger.ca>
+L: linux-ext4@vger.kernel.org
+W: http://ext4.wiki.kernel.org
+Q: http://patchwork.ozlabs.org/project/linux-ext4/list/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4.git
+S: Maintained
+F: Documentation/filesystems/ext4.txt
+F: fs/ext4/
+
+Extended Verification Module (EVM)
+M: Mimi Zohar <zohar@linux.vnet.ibm.com>
+L: linux-ima-devel@lists.sourceforge.net
+L: linux-security-module@vger.kernel.org
+S: Supported
+F: security/integrity/evm/
+
+EXTERNAL CONNECTOR SUBSYSTEM (EXTCON)
+M: MyungJoo Ham <myungjoo.ham@samsung.com>
+M: Chanwoo Choi <cw00.choi@samsung.com>
+L: linux-kernel@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/chanwoo/extcon.git
+S: Maintained
+F: drivers/extcon/
+F: include/linux/extcon/
+F: include/linux/extcon.h
+F: Documentation/extcon/
+F: Documentation/devicetree/bindings/extcon/
+
+EXYNOS DP DRIVER
+M: Jingoo Han <jingoohan1@gmail.com>
+L: dri-devel@lists.freedesktop.org
+S: Maintained
+F: drivers/gpu/drm/exynos/exynos_dp*
+
+EXYNOS SYSMMU (IOMMU) driver
+M: Marek Szyprowski <m.szyprowski@samsung.com>
+L: iommu@lists.linux-foundation.org
+S: Maintained
+F: drivers/iommu/exynos-iommu.c
+
+EZchip NPS platform support
+M: Noam Camus <noamc@ezchip.com>
+S: Supported
+F: arch/arc/plat-eznps
+F: arch/arc/boot/dts/eznps.dts
+
+F71805F HARDWARE MONITORING DRIVER
+M: Jean Delvare <jdelvare@suse.com>
+L: linux-hwmon@vger.kernel.org
+S: Maintained
+F: Documentation/hwmon/f71805f
+F: drivers/hwmon/f71805f.c
+
+FC0011 TUNER DRIVER
+M: Michael Buesch <m@bues.ch>
+L: linux-media@vger.kernel.org
+S: Maintained
+F: drivers/media/tuners/fc0011.h
+F: drivers/media/tuners/fc0011.c
+
+FC2580 MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+W: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/media/tuners/fc2580*
+
+FANOTIFY
+M: Eric Paris <eparis@redhat.com>
+S: Maintained
+F: fs/notify/fanotify/
+F: include/linux/fanotify.h
+F: include/uapi/linux/fanotify.h
+
+FARSYNC SYNCHRONOUS DRIVER
+M: Kevin Curtis <kevin.curtis@farsite.co.uk>
+W: http://www.farsite.co.uk/
+S: Supported
+F: drivers/net/wan/farsync.*
+
+FAULT INJECTION SUPPORT
+M: Akinobu Mita <akinobu.mita@gmail.com>
+S: Supported
+F: Documentation/fault-injection/
+F: lib/fault-inject.c
+
+FBTFT Framebuffer drivers
+M: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
+S: Maintained
+F: drivers/staging/fbtft/
+
+FCOE SUBSYSTEM (libfc, libfcoe, fcoe)
+M: Johannes Thumshirn <jth@kernel.org>
+L: fcoe-devel@open-fcoe.org
+W: www.Open-FCoE.org
+S: Supported
+F: drivers/scsi/libfc/
+F: drivers/scsi/fcoe/
+F: include/scsi/fc/
+F: include/scsi/libfc.h
+F: include/scsi/libfcoe.h
+F: include/uapi/scsi/fc/
+
+FILE LOCKING (flock() and fcntl()/lockf())
+M: Jeff Layton <jlayton@poochiereds.net>
+M: "J. Bruce Fields" <bfields@fieldses.org>
+L: linux-fsdevel@vger.kernel.org
+S: Maintained
+F: include/linux/fcntl.h
+F: include/linux/fs.h
+F: include/uapi/linux/fcntl.h
+F: include/uapi/linux/fs.h
+F: fs/fcntl.c
+F: fs/locks.c
+
+FILESYSTEMS (VFS and infrastructure)
+M: Alexander Viro <viro@zeniv.linux.org.uk>
+L: linux-fsdevel@vger.kernel.org
+S: Maintained
+F: fs/*
+
+FINTEK F75375S HARDWARE MONITOR AND FAN CONTROLLER DRIVER
+M: Riku Voipio <riku.voipio@iki.fi>
+L: linux-hwmon@vger.kernel.org
+S: Maintained
+F: drivers/hwmon/f75375s.c
+F: include/linux/f75375s.h
+
+FIREWIRE AUDIO DRIVERS
+M: Clemens Ladisch <clemens@ladisch.de>
+L: alsa-devel@alsa-project.org (moderated for non-subscribers)
+T: git git://git.alsa-project.org/alsa-kernel.git
+S: Maintained
+F: sound/firewire/
+
+FIREWIRE MEDIA DRIVERS (firedtv)
+M: Stefan Richter <stefanr@s5r6.in-berlin.de>
+L: linux-media@vger.kernel.org
+L: linux1394-devel@lists.sourceforge.net
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media.git
+S: Maintained
+F: drivers/media/firewire/
+
+FIREWIRE SBP-2 TARGET
+M: Chris Boot <bootc@bootc.net>
+L: linux-scsi@vger.kernel.org
+L: target-devel@vger.kernel.org
+L: linux1394-devel@lists.sourceforge.net
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/nab/lio-core-2.6.git master
+S: Maintained
+F: drivers/target/sbp/
+
+FIREWIRE SUBSYSTEM
+M: Stefan Richter <stefanr@s5r6.in-berlin.de>
+L: linux1394-devel@lists.sourceforge.net
+W: http://ieee1394.wiki.kernel.org/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/ieee1394/linux1394.git
+S: Maintained
+F: drivers/firewire/
+F: include/linux/firewire.h
+F: include/uapi/linux/firewire*.h
+F: tools/firewire/
+
+FIRMWARE LOADER (request_firmware)
+M: Luis R. Rodriguez <mcgrof@kernel.org>
+L: linux-kernel@vger.kernel.org
+S: Maintained
+F: Documentation/firmware_class/
+F: drivers/base/firmware*.c
+F: include/linux/firmware.h
+
+FLASH ADAPTER DRIVER (IBM Flash Adapter 900GB Full Height PCI Flash Card)
+M: Joshua Morris <josh.h.morris@us.ibm.com>
+M: Philip Kelleher <pjk1939@linux.vnet.ibm.com>
+S: Maintained
+F: drivers/block/rsxx/
+
+FLOPPY DRIVER
+M: Jiri Kosina <jikos@kernel.org>
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/jikos/floppy.git
+S: Odd fixes
+F: drivers/block/floppy.c
+
+FMC SUBSYSTEM
+M: Alessandro Rubini <rubini@gnudd.com>
+W: http://www.ohwr.org/projects/fmc-bus
+S: Supported
+F: drivers/fmc/
+F: include/linux/fmc*.h
+F: include/linux/ipmi-fru.h
+K: fmc_d.*register
+
+FPGA MANAGER FRAMEWORK
+M: Alan Tull <atull@kernel.org>
+R: Moritz Fischer <moritz.fischer@ettus.com>
+L: linux-fpga@vger.kernel.org
+S: Maintained
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/atull/linux-fpga.git
+F: Documentation/fpga/
+F: Documentation/devicetree/bindings/fpga/
+F: drivers/fpga/
+F: include/linux/fpga/
+W: http://www.rocketboards.org
+
+FPU EMULATOR
+M: Bill Metzenthen <billm@melbpc.org.au>
+W: http://floatingpoint.sourceforge.net/emulator/index.html
+S: Maintained
+F: arch/x86/math-emu/
+
+FRAME RELAY DLCI/FRAD (Sangoma drivers too)
+L: netdev@vger.kernel.org
+S: Orphan
+F: drivers/net/wan/dlci.c
+F: drivers/net/wan/sdla.c
+
+FRAMEBUFFER LAYER
+M: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
+L: linux-fbdev@vger.kernel.org
+T: git git://github.com/bzolnier/linux.git
+Q: http://patchwork.kernel.org/project/linux-fbdev/list/
+S: Maintained
+F: Documentation/fb/
+F: drivers/video/
+F: include/video/
+F: include/linux/fb.h
+F: include/uapi/video/
+F: include/uapi/linux/fb.h
+
+FREESCALE CAAM (Cryptographic Acceleration and Assurance Module) DRIVER
+M: Horia Geantă <horia.geanta@nxp.com>
+M: Dan Douglass <dan.douglass@nxp.com>
+L: linux-crypto@vger.kernel.org
+S: Maintained
+F: drivers/crypto/caam/
+F: Documentation/devicetree/bindings/crypto/fsl-sec4.txt
+
+FREESCALE DIU FRAMEBUFFER DRIVER
+M: Timur Tabi <timur@tabi.org>
+L: linux-fbdev@vger.kernel.org
+S: Maintained
+F: drivers/video/fbdev/fsl-diu-fb.*
+
+FREESCALE DMA DRIVER
+M: Li Yang <leoli@freescale.com>
+M: Zhang Wei <zw@zh-kernel.org>
+L: linuxppc-dev@lists.ozlabs.org
+S: Maintained
+F: drivers/dma/fsldma.*
+
+FREESCALE GPMI NAND DRIVER
+M: Han Xu <han.xu@nxp.com>
+L: linux-mtd@lists.infradead.org
+S: Maintained
+F: drivers/mtd/nand/gpmi-nand/*
+
+FREESCALE I2C CPM DRIVER
+M: Jochen Friedrich <jochen@scram.de>
+L: linuxppc-dev@lists.ozlabs.org
+L: linux-i2c@vger.kernel.org
+S: Maintained
+F: drivers/i2c/busses/i2c-cpm.c
+
+FREESCALE IMX / MXC FRAMEBUFFER DRIVER
+M: Sascha Hauer <kernel@pengutronix.de>
+L: linux-fbdev@vger.kernel.org
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+F: include/linux/platform_data/video-imxfb.h
+F: drivers/video/fbdev/imxfb.c
+
+FREESCALE QUAD SPI DRIVER
+M: Han Xu <han.xu@nxp.com>
+L: linux-mtd@lists.infradead.org
+S: Maintained
+F: drivers/mtd/spi-nor/fsl-quadspi.c
+
+FREESCALE SOC FS_ENET DRIVER
+M: Pantelis Antoniou <pantelis.antoniou@gmail.com>
+M: Vitaly Bordug <vbordug@ru.mvista.com>
+L: linuxppc-dev@lists.ozlabs.org
+L: netdev@vger.kernel.org
+S: Maintained
+F: drivers/net/ethernet/freescale/fs_enet/
+F: include/linux/fs_enet_pd.h
+
+FREESCALE IMX / MXC FEC DRIVER
+M: Fugang Duan <fugang.duan@nxp.com>
+L: netdev@vger.kernel.org
+S: Maintained
+F: drivers/net/ethernet/freescale/fec_main.c
+F: drivers/net/ethernet/freescale/fec_ptp.c
+F: drivers/net/ethernet/freescale/fec.h
+F: Documentation/devicetree/bindings/net/fsl-fec.txt
+
+FREESCALE QORIQ DPAA FMAN DRIVER
+M: Madalin Bucur <madalin.bucur@nxp.com>
+L: netdev@vger.kernel.org
+S: Maintained
+F: drivers/net/ethernet/freescale/fman
+F: Documentation/devicetree/bindings/powerpc/fsl/fman.txt
+
+FREESCALE QORIQ DPAA ETHERNET DRIVER
+M: Madalin Bucur <madalin.bucur@nxp.com>
+L: netdev@vger.kernel.org
+S: Maintained
+F: drivers/net/ethernet/freescale/dpaa
+
+FREESCALE SOC DRIVERS
+M: Scott Wood <oss@buserror.net>
+L: linuxppc-dev@lists.ozlabs.org
+L: linux-arm-kernel@lists.infradead.org
+S: Maintained
+F: Documentation/devicetree/bindings/powerpc/fsl/
+F: drivers/soc/fsl/
+F: include/linux/fsl/
+
+FREESCALE QUICC ENGINE LIBRARY
+M: Qiang Zhao <qiang.zhao@nxp.com>
+L: linuxppc-dev@lists.ozlabs.org
+S: Maintained
+F: drivers/soc/fsl/qe/
+F: include/soc/fsl/*qe*.h
+F: include/soc/fsl/*ucc*.h
+
+FREESCALE USB PERIPHERAL DRIVERS
+M: Li Yang <leoli@freescale.com>
+L: linux-usb@vger.kernel.org
+L: linuxppc-dev@lists.ozlabs.org
+S: Maintained
+F: drivers/usb/gadget/udc/fsl*
+
+FREESCALE QUICC ENGINE UCC ETHERNET DRIVER
+M: Li Yang <leoli@freescale.com>
+L: netdev@vger.kernel.org
+L: linuxppc-dev@lists.ozlabs.org
+S: Maintained
+F: drivers/net/ethernet/freescale/ucc_geth*
+
+FREESCALE eTSEC ETHERNET DRIVER (GIANFAR)
+M: Claudiu Manoil <claudiu.manoil@freescale.com>
+L: netdev@vger.kernel.org
+S: Maintained
+F: drivers/net/ethernet/freescale/gianfar*
+X: drivers/net/ethernet/freescale/gianfar_ptp.c
+F: Documentation/devicetree/bindings/net/fsl-tsec-phy.txt
+
+FREESCALE QUICC ENGINE UCC HDLC DRIVER
+M: Zhao Qiang <qiang.zhao@nxp.com>
+L: netdev@vger.kernel.org
+L: linuxppc-dev@lists.ozlabs.org
+S: Maintained
+F: drivers/net/wan/fsl_ucc_hdlc*
+
+FREESCALE QUICC ENGINE UCC UART DRIVER
+M: Timur Tabi <timur@tabi.org>
+L: linuxppc-dev@lists.ozlabs.org
+S: Maintained
+F: drivers/tty/serial/ucc_uart.c
+
+FREESCALE SOC SOUND DRIVERS
+M: Timur Tabi <timur@tabi.org>
+M: Nicolin Chen <nicoleotsuka@gmail.com>
+M: Xiubo Li <Xiubo.Lee@gmail.com>
+R: Fabio Estevam <fabio.estevam@nxp.com>
+L: alsa-devel@alsa-project.org (moderated for non-subscribers)
+L: linuxppc-dev@lists.ozlabs.org
+S: Maintained
+F: sound/soc/fsl/fsl*
+F: sound/soc/fsl/imx*
+F: sound/soc/fsl/mpc8610_hpcd.c
+
+FREEVXFS FILESYSTEM
+M: Christoph Hellwig <hch@infradead.org>
+W: ftp://ftp.openlinux.org/pub/people/hch/vxfs
+S: Maintained
+F: fs/freevxfs/
+
+FREEZER
+M: "Rafael J. Wysocki" <rjw@rjwysocki.net>
+M: Pavel Machek <pavel@ucw.cz>
+L: linux-pm@vger.kernel.org
+S: Supported
+F: Documentation/power/freezing-of-tasks.txt
+F: include/linux/freezer.h
+F: kernel/freezer.c
+
+FRONTSWAP API
+M: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
+L: linux-kernel@vger.kernel.org
+S: Maintained
+F: mm/frontswap.c
+F: include/linux/frontswap.h
+
+FS-CACHE: LOCAL CACHING FOR NETWORK FILESYSTEMS
+M: David Howells <dhowells@redhat.com>
+L: linux-cachefs@redhat.com (moderated for non-subscribers)
+S: Supported
+F: Documentation/filesystems/caching/
+F: fs/fscache/
+F: include/linux/fscache*.h
+
+FSCRYPT: FILE SYSTEM LEVEL ENCRYPTION SUPPORT
+M: Theodore Y. Ts'o <tytso@mit.edu>
+M: Jaegeuk Kim <jaegeuk@kernel.org>
+L: linux-fscrypt@vger.kernel.org
+Q: https://patchwork.kernel.org/project/linux-fscrypt/list/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/tytso/fscrypt.git
+S: Supported
+F: fs/crypto/
+F: include/linux/fscrypt*.h
+
+F2FS FILE SYSTEM
+M: Jaegeuk Kim <jaegeuk@kernel.org>
+M: Chao Yu <yuchao0@huawei.com>
+L: linux-f2fs-devel@lists.sourceforge.net
+W: https://f2fs.wiki.kernel.org/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs.git
+S: Maintained
+F: Documentation/filesystems/f2fs.txt
+F: Documentation/ABI/testing/sysfs-fs-f2fs
+F: fs/f2fs/
+F: include/linux/f2fs_fs.h
+F: include/trace/events/f2fs.h
+
+FUJITSU FR-V (FRV) PORT
+S: Orphan
+F: arch/frv/
+
+FUJITSU LAPTOP EXTRAS
+M: Jonathan Woithe <jwoithe@just42.net>
+L: platform-driver-x86@vger.kernel.org
+S: Maintained
+F: drivers/platform/x86/fujitsu-laptop.c
+
+FUJITSU M-5MO LS CAMERA ISP DRIVER
+M: Kyungmin Park <kyungmin.park@samsung.com>
+M: Heungjun Kim <riverful.kim@samsung.com>
+L: linux-media@vger.kernel.org
+S: Maintained
+F: drivers/media/i2c/m5mols/
+F: include/media/i2c/m5mols.h
+
+FUJITSU TABLET EXTRAS
+M: Robert Gerlach <khnz@gmx.de>
+L: platform-driver-x86@vger.kernel.org
+S: Maintained
+F: drivers/platform/x86/fujitsu-tablet.c
+
+FUSE: FILESYSTEM IN USERSPACE
+M: Miklos Szeredi <miklos@szeredi.hu>
+L: linux-fsdevel@vger.kernel.org
+W: http://fuse.sourceforge.net/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse.git
+S: Maintained
+F: fs/fuse/
+F: include/uapi/linux/fuse.h
+F: Documentation/filesystems/fuse.txt
+
+FUTEX SUBSYSTEM
+M: Thomas Gleixner <tglx@linutronix.de>
+M: Ingo Molnar <mingo@redhat.com>
+R: Peter Zijlstra <peterz@infradead.org>
+R: Darren Hart <dvhart@infradead.org>
+L: linux-kernel@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git locking/core
+S: Maintained
+F: kernel/futex.c
+F: kernel/futex_compat.c
+F: include/asm-generic/futex.h
+F: include/linux/futex.h
+F: include/uapi/linux/futex.h
+F: tools/testing/selftests/futex/
+F: tools/perf/bench/futex*
+F: Documentation/*futex*
+
+FUTURE DOMAIN TMC-16x0 SCSI DRIVER (16-bit)
+M: Rik Faith <faith@cs.unc.edu>
+L: linux-scsi@vger.kernel.org
+S: Odd Fixes (e.g., new signatures)
+F: drivers/scsi/fdomain.*
+
+GCC PLUGINS
+M: Kees Cook <keescook@chromium.org>
+R: Emese Revfy <re.emese@gmail.com>
+L: kernel-hardening@lists.openwall.com
+S: Maintained
+F: scripts/gcc-plugins/
+F: scripts/gcc-plugin.sh
+F: scripts/Makefile.gcc-plugins
+F: Documentation/gcc-plugins.txt
+
+GCOV BASED KERNEL PROFILING
+M: Peter Oberparleiter <oberpar@linux.vnet.ibm.com>
+S: Maintained
+F: kernel/gcov/
+F: Documentation/dev-tools/gcov.rst
+
+GDT SCSI DISK ARRAY CONTROLLER DRIVER
+M: Achim Leubner <achim_leubner@adaptec.com>
+L: linux-scsi@vger.kernel.org
+W: http://www.icp-vortex.com/
+S: Supported
+F: drivers/scsi/gdt*
+
+GDB KERNEL DEBUGGING HELPER SCRIPTS
+M: Jan Kiszka <jan.kiszka@siemens.com>
+M: Kieran Bingham <kieran@bingham.xyz>
+S: Supported
+F: scripts/gdb/
+
+GEMTEK FM RADIO RECEIVER DRIVER
+M: Hans Verkuil <hverkuil@xs4all.nl>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+W: https://linuxtv.org
+S: Maintained
+F: drivers/media/radio/radio-gemtek*
+
+GENERIC GPIO I2C DRIVER
+M: Haavard Skinnemoen <hskinnemoen@gmail.com>
+S: Supported
+F: drivers/i2c/busses/i2c-gpio.c
+F: include/linux/i2c-gpio.h
+
+GENERIC GPIO I2C MULTIPLEXER DRIVER
+M: Peter Korsgaard <peter.korsgaard@barco.com>
+L: linux-i2c@vger.kernel.org
+S: Supported
+F: drivers/i2c/muxes/i2c-mux-gpio.c
+F: include/linux/i2c-mux-gpio.h
+F: Documentation/i2c/muxes/i2c-mux-gpio
+
+GENERIC HDLC (WAN) DRIVERS
+M: Krzysztof Halasa <khc@pm.waw.pl>
+W: http://www.kernel.org/pub/linux/utils/net/hdlc/
+S: Maintained
+F: drivers/net/wan/c101.c
+F: drivers/net/wan/hd6457*
+F: drivers/net/wan/hdlc*
+F: drivers/net/wan/n2.c
+F: drivers/net/wan/pc300too.c
+F: drivers/net/wan/pci200syn.c
+F: drivers/net/wan/wanxl*
+
+GENERIC INCLUDE/ASM HEADER FILES
+M: Arnd Bergmann <arnd@arndb.de>
+L: linux-arch@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/arnd/asm-generic.git
+S: Maintained
+F: include/asm-generic/
+F: include/uapi/asm-generic/
+
+GENERIC PHY FRAMEWORK
+M: Kishon Vijay Abraham I <kishon@ti.com>
+L: linux-kernel@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/kishon/linux-phy.git
+S: Supported
+F: drivers/phy/
+F: include/linux/phy/
+
+GENERIC PM DOMAINS
+M: "Rafael J. Wysocki" <rjw@rjwysocki.net>
+M: Kevin Hilman <khilman@kernel.org>
+M: Ulf Hansson <ulf.hansson@linaro.org>
+L: linux-pm@vger.kernel.org
+S: Supported
+F: drivers/base/power/domain*.c
+F: include/linux/pm_domain.h
+F: Documentation/devicetree/bindings/power/power_domain.txt
+
+GENERIC UIO DRIVER FOR PCI DEVICES
+M: "Michael S. Tsirkin" <mst@redhat.com>
+L: kvm@vger.kernel.org
+S: Supported
+F: drivers/uio/uio_pci_generic.c
+
+GET_MAINTAINER SCRIPT
+M: Joe Perches <joe@perches.com>
+S: Maintained
+F: scripts/get_maintainer.pl
+
+GENWQE (IBM Generic Workqueue Card)
+M: Frank Haverkamp <haver@linux.vnet.ibm.com>
+M: Guilherme G. Piccoli <gpiccoli@linux.vnet.ibm.com>
+S: Supported
+F: drivers/misc/genwqe/
+
+GFS2 FILE SYSTEM
+M: Steven Whitehouse <swhiteho@redhat.com>
+M: Bob Peterson <rpeterso@redhat.com>
+L: cluster-devel@redhat.com
+W: http://sources.redhat.com/cluster/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/gfs2/linux-gfs2.git
+S: Supported
+F: Documentation/filesystems/gfs2*.txt
+F: fs/gfs2/
+F: include/uapi/linux/gfs2_ondisk.h
+
+GIGASET ISDN DRIVERS
+M: Paul Bolle <pebolle@tiscali.nl>
+L: gigaset307x-common@lists.sourceforge.net
+W: http://gigaset307x.sourceforge.net/
+S: Odd Fixes
+F: Documentation/isdn/README.gigaset
+F: drivers/isdn/gigaset/
+F: include/uapi/linux/gigaset_dev.h
+
+GO7007 MPEG CODEC
+M: Hans Verkuil <hans.verkuil@cisco.com>
+L: linux-media@vger.kernel.org
+S: Maintained
+F: drivers/media/usb/go7007/
+
+GOODIX TOUCHSCREEN
+M: Bastien Nocera <hadess@hadess.net>
+L: linux-input@vger.kernel.org
+S: Maintained
+F: drivers/input/touchscreen/goodix.c
+
+GPIO MOCKUP DRIVER
+M: Bamvor Jian Zhang <bamvor.zhangjian@linaro.org>
+L: linux-gpio@vger.kernel.org
+S: Maintained
+F: drivers/gpio/gpio-mockup.c
+F: tools/testing/selftests/gpio/
+
+GPIO SUBSYSTEM
+M: Linus Walleij <linus.walleij@linaro.org>
+L: linux-gpio@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio.git
+S: Maintained
+F: Documentation/devicetree/bindings/gpio/
+F: Documentation/gpio/
+F: Documentation/ABI/testing/gpio-cdev
+F: Documentation/ABI/obsolete/sysfs-gpio
+F: drivers/gpio/
+F: include/linux/gpio/
+F: include/linux/gpio.h
+F: include/asm-generic/gpio.h
+F: include/uapi/linux/gpio.h
+F: tools/gpio/
+
+GRE DEMULTIPLEXER DRIVER
+M: Dmitry Kozlov <xeb@mail.ru>
+L: netdev@vger.kernel.org
+S: Maintained
+F: net/ipv4/gre_demux.c
+F: net/ipv4/gre_offload.c
+F: include/net/gre.h
+
+GRETH 10/100/1G Ethernet MAC device driver
+M: Andreas Larsson <andreas@gaisler.com>
+L: netdev@vger.kernel.org
+S: Maintained
+F: drivers/net/ethernet/aeroflex/
+
+GREYBUS SUBSYSTEM
+M: Johan Hovold <johan@kernel.org>
+M: Alex Elder <elder@kernel.org>
+M: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+S: Maintained
+F: drivers/staging/greybus/
+L: greybus-dev@lists.linaro.org (moderated for non-subscribers)
+
+GREYBUS AUDIO PROTOCOLS DRIVERS
+M: Vaibhav Agarwal <vaibhav.sr@gmail.com>
+M: Mark Greer <mgreer@animalcreek.com>
+S: Maintained
+F: drivers/staging/greybus/audio_apbridgea.c
+F: drivers/staging/greybus/audio_apbridgea.h
+F: drivers/staging/greybus/audio_codec.c
+F: drivers/staging/greybus/audio_codec.h
+F: drivers/staging/greybus/audio_gb.c
+F: drivers/staging/greybus/audio_manager.c
+F: drivers/staging/greybus/audio_manager.h
+F: drivers/staging/greybus/audio_manager_module.c
+F: drivers/staging/greybus/audio_manager_private.h
+F: drivers/staging/greybus/audio_manager_sysfs.c
+F: drivers/staging/greybus/audio_module.c
+F: drivers/staging/greybus/audio_topology.c
+
+GREYBUS PROTOCOLS DRIVERS
+M: Rui Miguel Silva <rmfrfs@gmail.com>
+S: Maintained
+F: drivers/staging/greybus/sdio.c
+F: drivers/staging/greybus/light.c
+F: drivers/staging/greybus/gpio.c
+F: drivers/staging/greybus/power_supply.c
+F: drivers/staging/greybus/spi.c
+F: drivers/staging/greybus/spilib.c
+
+GREYBUS PROTOCOLS DRIVERS
+M: Bryan O'Donoghue <pure.logic@nexus-software.ie>
+S: Maintained
+F: drivers/staging/greybus/loopback.c
+F: drivers/staging/greybus/timesync.c
+F: drivers/staging/greybus/timesync_platform.c
+
+GREYBUS PROTOCOLS DRIVERS
+M: Viresh Kumar <vireshk@kernel.org>
+S: Maintained
+F: drivers/staging/greybus/authentication.c
+F: drivers/staging/greybus/bootrom.c
+F: drivers/staging/greybus/firmware.h
+F: drivers/staging/greybus/fw-core.c
+F: drivers/staging/greybus/fw-download.c
+F: drivers/staging/greybus/fw-managament.c
+F: drivers/staging/greybus/greybus_authentication.h
+F: drivers/staging/greybus/greybus_firmware.h
+F: drivers/staging/greybus/hid.c
+F: drivers/staging/greybus/i2c.c
+F: drivers/staging/greybus/spi.c
+F: drivers/staging/greybus/spilib.c
+F: drivers/staging/greybus/spilib.h
+
+GREYBUS PROTOCOLS DRIVERS
+M: David Lin <dtwlin@gmail.com>
+S: Maintained
+F: drivers/staging/greybus/uart.c
+F: drivers/staging/greybus/log.c
+
+GREYBUS PLATFORM DRIVERS
+M: Vaibhav Hiremath <hvaibhav.linux@gmail.com>
+S: Maintained
+F: drivers/staging/greybus/arche-platform.c
+F: drivers/staging/greybus/arche-apb-ctrl.c
+F: drivers/staging/greybus/arche_platform.h
+
+GS1662 VIDEO SERIALIZER
+M: Charles-Antoine Couret <charles-antoine.couret@nexvision.fr>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+S: Maintained
+F: drivers/media/spi/gs1662.c
+
+GSPCA FINEPIX SUBDRIVER
+M: Frank Zago <frank@zago.net>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+S: Maintained
+F: drivers/media/usb/gspca/finepix.c
+
+GSPCA GL860 SUBDRIVER
+M: Olivier Lorin <o.lorin@laposte.net>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+S: Maintained
+F: drivers/media/usb/gspca/gl860/
+
+GSPCA M5602 SUBDRIVER
+M: Erik Andren <erik.andren@gmail.com>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+S: Maintained
+F: drivers/media/usb/gspca/m5602/
+
+GSPCA PAC207 SONIXB SUBDRIVER
+M: Hans Verkuil <hverkuil@xs4all.nl>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+S: Odd Fixes
+F: drivers/media/usb/gspca/pac207.c
+
+GSPCA SN9C20X SUBDRIVER
+M: Brian Johnson <brijohn@gmail.com>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+S: Maintained
+F: drivers/media/usb/gspca/sn9c20x.c
+
+GSPCA T613 SUBDRIVER
+M: Leandro Costantino <lcostantino@gmail.com>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+S: Maintained
+F: drivers/media/usb/gspca/t613.c
+
+GSPCA USB WEBCAM DRIVER
+M: Hans Verkuil <hverkuil@xs4all.nl>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+S: Odd Fixes
+F: drivers/media/usb/gspca/
+
+GTP (GPRS Tunneling Protocol)
+M: Pablo Neira Ayuso <pablo@netfilter.org>
+M: Harald Welte <laforge@gnumonks.org>
+L: osmocom-net-gprs@lists.osmocom.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/pablo/gtp.git
+S: Maintained
+F: drivers/net/gtp.c
+
+GUID PARTITION TABLE (GPT)
+M: Davidlohr Bueso <dave@stgolabs.net>
+L: linux-efi@vger.kernel.org
+S: Maintained
+F: block/partitions/efi.*
+
+STK1160 USB VIDEO CAPTURE DRIVER
+M: Ezequiel Garcia <ezequiel@vanguardiasur.com.ar>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+S: Maintained
+F: drivers/media/usb/stk1160/
+
+H8/300 ARCHITECTURE
+M: Yoshinori Sato <ysato@users.sourceforge.jp>
+L: uclinux-h8-devel@lists.sourceforge.jp (moderated for non-subscribers)
+W: http://uclinux-h8.sourceforge.jp
+T: git git://git.sourceforge.jp/gitroot/uclinux-h8/linux.git
+S: Maintained
+F: arch/h8300/
+F: drivers/clocksource/h8300_*.c
+F: drivers/clk/h8300/
+F: drivers/irqchip/irq-renesas-h8*.c
+
+HARD DRIVE ACTIVE PROTECTION SYSTEM (HDAPS) DRIVER
+M: Frank Seidel <frank@f-seidel.de>
+L: platform-driver-x86@vger.kernel.org
+W: http://www.kernel.org/pub/linux/kernel/people/fseidel/hdaps/
+S: Maintained
+F: drivers/platform/x86/hdaps.c
+
+HDPVR USB VIDEO ENCODER DRIVER
+M: Hans Verkuil <hverkuil@xs4all.nl>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+W: https://linuxtv.org
+S: Odd Fixes
+F: drivers/media/usb/hdpvr/
+
+HWPOISON MEMORY FAILURE HANDLING
+M: Naoya Horiguchi <n-horiguchi@ah.jp.nec.com>
+L: linux-mm@kvack.org
+S: Maintained
+F: mm/memory-failure.c
+F: mm/hwpoison-inject.c
+
+HYPERVISOR VIRTUAL CONSOLE DRIVER
+L: linuxppc-dev@lists.ozlabs.org
+S: Odd Fixes
+F: drivers/tty/hvc/
+
+HACKRF MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+W: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/media/usb/hackrf/
+
+HARDWARE MONITORING
+M: Jean Delvare <jdelvare@suse.com>
+M: Guenter Roeck <linux@roeck-us.net>
+L: linux-hwmon@vger.kernel.org
+W: http://hwmon.wiki.kernel.org/
+T: quilt http://jdelvare.nerim.net/devel/linux/jdelvare-hwmon/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging.git
+S: Maintained
+F: Documentation/hwmon/
+F: drivers/hwmon/
+F: include/linux/hwmon*.h
+
+HARDWARE RANDOM NUMBER GENERATOR CORE
+M: Matt Mackall <mpm@selenic.com>
+M: Herbert Xu <herbert@gondor.apana.org.au>
+L: linux-crypto@vger.kernel.org
+S: Odd fixes
+F: Documentation/devicetree/bindings/rng/
+F: Documentation/hw_random.txt
+F: drivers/char/hw_random/
+F: include/linux/hw_random.h
+
+HARDWARE SPINLOCK CORE
+M: Ohad Ben-Cohen <ohad@wizery.com>
+M: Bjorn Andersson <bjorn.andersson@linaro.org>
+L: linux-remoteproc@vger.kernel.org
+S: Maintained
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/ohad/hwspinlock.git
+F: Documentation/devicetree/bindings/hwlock/
+F: Documentation/hwspinlock.txt
+F: drivers/hwspinlock/
+F: include/linux/hwspinlock.h
+
+HARMONY SOUND DRIVER
+L: linux-parisc@vger.kernel.org
+S: Maintained
+F: sound/parisc/harmony.*
+
+HEWLETT PACKARD ENTERPRISE ILO NMI WATCHDOG DRIVER
+M: Jimmy Vance <jimmy.vance@hpe.com>
+S: Supported
+F: Documentation/watchdog/hpwdt.txt
+F: drivers/watchdog/hpwdt.c
+
+HEWLETT-PACKARD SMART ARRAY RAID DRIVER (hpsa)
+M: Don Brace <don.brace@microsemi.com>
+L: esc.storagedev@microsemi.com
+L: linux-scsi@vger.kernel.org
+S: Supported
+F: Documentation/scsi/hpsa.txt
+F: drivers/scsi/hpsa*.[ch]
+F: include/linux/cciss*.h
+F: include/uapi/linux/cciss*.h
+
+HEWLETT-PACKARD SMART CISS RAID DRIVER (cciss)
+M: Don Brace <don.brace@microsemi.com>
+L: esc.storagedev@microsemi.com
+L: linux-scsi@vger.kernel.org
+S: Supported
+F: Documentation/blockdev/cciss.txt
+F: drivers/block/cciss*
+F: include/linux/cciss_ioctl.h
+F: include/uapi/linux/cciss_ioctl.h
+
+OPA-VNIC DRIVER
+M: Dennis Dalessandro <dennis.dalessandro@intel.com>
+M: Niranjana Vishwanathapura <niranjana.vishwanathapura@intel.com>
+L: linux-rdma@vger.kernel.org
+S: Supported
+F: drivers/infiniband/ulp/opa_vnic
+
+HFI1 DRIVER
+M: Mike Marciniszyn <mike.marciniszyn@intel.com>
+M: Dennis Dalessandro <dennis.dalessandro@intel.com>
+L: linux-rdma@vger.kernel.org
+S: Supported
+F: drivers/infiniband/hw/hfi1
+
+HFS FILESYSTEM
+L: linux-fsdevel@vger.kernel.org
+S: Orphan
+F: Documentation/filesystems/hfs.txt
+F: fs/hfs/
+
+HFSPLUS FILESYSTEM
+L: linux-fsdevel@vger.kernel.org
+S: Orphan
+F: Documentation/filesystems/hfsplus.txt
+F: fs/hfsplus/
+
+HGA FRAMEBUFFER DRIVER
+M: Ferenc Bakonyi <fero@drama.obuda.kando.hu>
+L: linux-nvidia@lists.surfsouth.com
+W: http://drama.obuda.kando.hu/~fero/cgi-bin/hgafb.shtml
+S: Maintained
+F: drivers/video/fbdev/hgafb.c
+
+HIBERNATION (aka Software Suspend, aka swsusp)
+M: "Rafael J. Wysocki" <rjw@rjwysocki.net>
+M: Pavel Machek <pavel@ucw.cz>
+L: linux-pm@vger.kernel.org
+B: https://bugzilla.kernel.org
+S: Supported
+F: arch/x86/power/
+F: drivers/base/power/
+F: kernel/power/
+F: include/linux/suspend.h
+F: include/linux/freezer.h
+F: include/linux/pm.h
+F: arch/*/include/asm/suspend*.h
+
+HID CORE LAYER
+M: Jiri Kosina <jikos@kernel.org>
+R: Benjamin Tissoires <benjamin.tissoires@redhat.com>
+L: linux-input@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid.git
+S: Maintained
+F: drivers/hid/
+F: include/linux/hid*
+F: include/uapi/linux/hid*
+
+HID SENSOR HUB DRIVERS
+M: Jiri Kosina <jikos@kernel.org>
+M: Jonathan Cameron <jic23@kernel.org>
+M: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
+L: linux-input@vger.kernel.org
+L: linux-iio@vger.kernel.org
+S: Maintained
+F: Documentation/hid/hid-sensor*
+F: drivers/hid/hid-sensor-*
+F: drivers/iio/*/hid-*
+F: include/linux/hid-sensor-*
+
+HIGH-RESOLUTION TIMERS, CLOCKEVENTS, DYNTICKS
+M: Thomas Gleixner <tglx@linutronix.de>
+L: linux-kernel@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git timers/core
+S: Maintained
+F: Documentation/timers/
+F: kernel/time/hrtimer.c
+F: kernel/time/clockevents.c
+F: kernel/time/tick*.*
+F: kernel/time/timer_*.c
+F: include/linux/clockchips.h
+F: include/linux/hrtimer.h
+
+HIGH-SPEED SCC DRIVER FOR AX.25
+L: linux-hams@vger.kernel.org
+S: Orphan
+F: drivers/net/hamradio/dmascc.c
+F: drivers/net/hamradio/scc.c
+
+HIGHPOINT ROCKETRAID 3xxx RAID DRIVER
+M: HighPoint Linux Team <linux@highpoint-tech.com>
+W: http://www.highpoint-tech.com
+S: Supported
+F: Documentation/scsi/hptiop.txt
+F: drivers/scsi/hptiop.c
+
+HIPPI
+M: Jes Sorensen <jes@trained-monkey.org>
+L: linux-hippi@sunsite.dk
+S: Maintained
+F: include/linux/hippidevice.h
+F: include/uapi/linux/if_hippi.h
+F: net/802/hippi.c
+F: drivers/net/hippi/
+
+HISILICON NETWORK SUBSYSTEM DRIVER
+M: Yisen Zhuang <yisen.zhuang@huawei.com>
+M: Salil Mehta <salil.mehta@huawei.com>
+L: netdev@vger.kernel.org
+W: http://www.hisilicon.com
+S: Maintained
+F: drivers/net/ethernet/hisilicon/
+F: Documentation/devicetree/bindings/net/hisilicon*.txt
+
+HISILICON ROCE DRIVER
+M: Lijun Ou <oulijun@huawei.com>
+M: Wei Hu(Xavier) <xavier.huwei@huawei.com>
+L: linux-rdma@vger.kernel.org
+S: Maintained
+F: drivers/infiniband/hw/hns/
+F: Documentation/devicetree/bindings/infiniband/hisilicon-hns-roce.txt
+
+HISILICON SAS Controller
+M: John Garry <john.garry@huawei.com>
+W: http://www.hisilicon.com
+S: Supported
+F: drivers/scsi/hisi_sas/
+F: Documentation/devicetree/bindings/scsi/hisilicon-sas.txt
+
+HOST AP DRIVER
+M: Jouni Malinen <j@w1.fi>
+L: linux-wireless@vger.kernel.org
+W: http://w1.fi/hostap-driver.html
+S: Obsolete
+F: drivers/net/wireless/intersil/hostap/
+
+HP COMPAQ TC1100 TABLET WMI EXTRAS DRIVER
+L: platform-driver-x86@vger.kernel.org
+S: Orphan
+F: drivers/platform/x86/tc1100-wmi.c
+
+HP100: Driver for HP 10/100 Mbit/s Voice Grade Network Adapter Series
+M: Jaroslav Kysela <perex@perex.cz>
+S: Maintained
+F: drivers/net/ethernet/hp/hp100.*
+
+HPET: High Precision Event Timers driver
+M: Clemens Ladisch <clemens@ladisch.de>
+S: Maintained
+F: Documentation/timers/hpet.txt
+F: drivers/char/hpet.c
+F: include/linux/hpet.h
+F: include/uapi/linux/hpet.h
+
+HPET: x86
+S: Orphan
+F: arch/x86/kernel/hpet.c
+F: arch/x86/include/asm/hpet.h
+
+HPFS FILESYSTEM
+M: Mikulas Patocka <mikulas@artax.karlin.mff.cuni.cz>
+W: http://artax.karlin.mff.cuni.cz/~mikulas/vyplody/hpfs/index-e.cgi
+S: Maintained
+F: fs/hpfs/
+
+HSI SUBSYSTEM
+M: Sebastian Reichel <sre@kernel.org>
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-hsi.git
+S: Maintained
+F: Documentation/ABI/testing/sysfs-bus-hsi
+F: Documentation/driver-api/hsi.rst
+F: drivers/hsi/
+F: include/linux/hsi/
+F: include/uapi/linux/hsi/
+
+HSO 3G MODEM DRIVER
+L: linux-usb@vger.kernel.org
+S: Orphan
+F: drivers/net/usb/hso.c
+
+HSR NETWORK PROTOCOL
+M: Arvid Brodin <arvid.brodin@alten.se>
+L: netdev@vger.kernel.org
+S: Maintained
+F: net/hsr/
+
+HTCPEN TOUCHSCREEN DRIVER
+M: Pau Oliva Fora <pof@eslack.org>
+L: linux-input@vger.kernel.org
+S: Maintained
+F: drivers/input/touchscreen/htcpen.c
+
+HUGETLB FILESYSTEM
+M: Nadia Yvette Chambers <nyc@holomorphy.com>
+S: Maintained
+F: fs/hugetlbfs/
+
+HVA ST MEDIA DRIVER
+M: Jean-Christophe Trotin <jean-christophe.trotin@st.com>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+W: https://linuxtv.org
+S: Supported
+F: drivers/media/platform/sti/hva
+
+Hyper-V CORE AND DRIVERS
+M: "K. Y. Srinivasan" <kys@microsoft.com>
+M: Haiyang Zhang <haiyangz@microsoft.com>
+M: Stephen Hemminger <sthemmin@microsoft.com>
+L: devel@linuxdriverproject.org
+S: Maintained
+F: arch/x86/include/asm/mshyperv.h
+F: arch/x86/include/uapi/asm/hyperv.h
+F: arch/x86/kernel/cpu/mshyperv.c
+F: arch/x86/hyperv
+F: drivers/hid/hid-hyperv.c
+F: drivers/hv/
+F: drivers/input/serio/hyperv-keyboard.c
+F: drivers/pci/host/pci-hyperv.c
+F: drivers/net/hyperv/
+F: drivers/scsi/storvsc_drv.c
+F: drivers/uio/uio_hv_generic.c
+F: drivers/video/fbdev/hyperv_fb.c
+F: include/linux/hyperv.h
+F: tools/hv/
+F: Documentation/ABI/stable/sysfs-bus-vmbus
+
+I2C MUXES
+M: Peter Rosin <peda@axentia.se>
+L: linux-i2c@vger.kernel.org
+S: Maintained
+F: Documentation/i2c/i2c-topology
+F: Documentation/i2c/muxes/
+F: Documentation/devicetree/bindings/i2c/i2c-mux*
+F: Documentation/devicetree/bindings/i2c/i2c-arb*
+F: Documentation/devicetree/bindings/i2c/i2c-gate*
+F: drivers/i2c/i2c-mux.c
+F: drivers/i2c/muxes/
+F: include/linux/i2c-mux.h
+
+I2C OVER PARALLEL PORT
+M: Jean Delvare <jdelvare@suse.com>
+L: linux-i2c@vger.kernel.org
+S: Maintained
+F: Documentation/i2c/busses/i2c-parport
+F: Documentation/i2c/busses/i2c-parport-light
+F: drivers/i2c/busses/i2c-parport.c
+F: drivers/i2c/busses/i2c-parport-light.c
+
+I2C/SMBUS CONTROLLER DRIVERS FOR PC
+M: Jean Delvare <jdelvare@suse.com>
+L: linux-i2c@vger.kernel.org
+S: Maintained
+F: Documentation/i2c/busses/i2c-ali1535
+F: Documentation/i2c/busses/i2c-ali1563
+F: Documentation/i2c/busses/i2c-ali15x3
+F: Documentation/i2c/busses/i2c-amd756
+F: Documentation/i2c/busses/i2c-amd8111
+F: Documentation/i2c/busses/i2c-i801
+F: Documentation/i2c/busses/i2c-nforce2
+F: Documentation/i2c/busses/i2c-piix4
+F: Documentation/i2c/busses/i2c-sis5595
+F: Documentation/i2c/busses/i2c-sis630
+F: Documentation/i2c/busses/i2c-sis96x
+F: Documentation/i2c/busses/i2c-via
+F: Documentation/i2c/busses/i2c-viapro
+F: drivers/i2c/busses/i2c-ali1535.c
+F: drivers/i2c/busses/i2c-ali1563.c
+F: drivers/i2c/busses/i2c-ali15x3.c
+F: drivers/i2c/busses/i2c-amd756.c
+F: drivers/i2c/busses/i2c-amd756-s4882.c
+F: drivers/i2c/busses/i2c-amd8111.c
+F: drivers/i2c/busses/i2c-i801.c
+F: drivers/i2c/busses/i2c-isch.c
+F: drivers/i2c/busses/i2c-nforce2.c
+F: drivers/i2c/busses/i2c-nforce2-s4985.c
+F: drivers/i2c/busses/i2c-piix4.c
+F: drivers/i2c/busses/i2c-sis5595.c
+F: drivers/i2c/busses/i2c-sis630.c
+F: drivers/i2c/busses/i2c-sis96x.c
+F: drivers/i2c/busses/i2c-via.c
+F: drivers/i2c/busses/i2c-viapro.c
+
+I2C/SMBUS ISMT DRIVER
+M: Seth Heasley <seth.heasley@intel.com>
+M: Neil Horman <nhorman@tuxdriver.com>
+L: linux-i2c@vger.kernel.org
+F: drivers/i2c/busses/i2c-ismt.c
+F: Documentation/i2c/busses/i2c-ismt
+
+I2C/SMBUS STUB DRIVER
+M: Jean Delvare <jdelvare@suse.com>
+L: linux-i2c@vger.kernel.org
+S: Maintained
+F: drivers/i2c/i2c-stub.c
+
+I2C SUBSYSTEM
+M: Wolfram Sang <wsa@the-dreams.de>
+L: linux-i2c@vger.kernel.org
+W: https://i2c.wiki.kernel.org/
+Q: https://patchwork.ozlabs.org/project/linux-i2c/list/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux.git
+S: Maintained
+F: Documentation/devicetree/bindings/i2c/
+F: Documentation/i2c/
+F: drivers/i2c/
+F: drivers/i2c/*/
+F: include/linux/i2c.h
+F: include/linux/i2c-*.h
+F: include/uapi/linux/i2c.h
+F: include/uapi/linux/i2c-*.h
+
+I2C ACPI SUPPORT
+M: Mika Westerberg <mika.westerberg@linux.intel.com>
+L: linux-i2c@vger.kernel.org
+L: linux-acpi@vger.kernel.org
+S: Maintained
+
+I2C-TAOS-EVM DRIVER
+M: Jean Delvare <jdelvare@suse.com>
+L: linux-i2c@vger.kernel.org
+S: Maintained
+F: Documentation/i2c/busses/i2c-taos-evm
+F: drivers/i2c/busses/i2c-taos-evm.c
+
+I2C-TINY-USB DRIVER
+M: Till Harbaum <till@harbaum.org>
+L: linux-i2c@vger.kernel.org
+W: http://www.harbaum.org/till/i2c_tiny_usb
+S: Maintained
+F: drivers/i2c/busses/i2c-tiny-usb.c
+
+i386 BOOT CODE
+M: "H. Peter Anvin" <hpa@zytor.com>
+S: Maintained
+F: arch/x86/boot/
+
+i386 SETUP CODE / CPU ERRATA WORKAROUNDS
+M: "H. Peter Anvin" <hpa@zytor.com>
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/hpa/linux-2.6-x86setup.git
+S: Maintained
+
+IA64 (Itanium) PLATFORM
+M: Tony Luck <tony.luck@intel.com>
+M: Fenghua Yu <fenghua.yu@intel.com>
+L: linux-ia64@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/aegl/linux.git
+S: Maintained
+F: arch/ia64/
+
+IBM Power VMX Cryptographic instructions
+M: Leonidas S. Barbosa <leosilva@linux.vnet.ibm.com>
+M: Paulo Flabiano Smorigo <pfsmorigo@linux.vnet.ibm.com>
+L: linux-crypto@vger.kernel.org
+S: Supported
+F: drivers/crypto/vmx/Makefile
+F: drivers/crypto/vmx/Kconfig
+F: drivers/crypto/vmx/vmx.c
+F: drivers/crypto/vmx/aes*
+F: drivers/crypto/vmx/ghash*
+F: drivers/crypto/vmx/ppc-xlate.pl
+
+IBM Power in-Nest Crypto Acceleration
+M: Leonidas S. Barbosa <leosilva@linux.vnet.ibm.com>
+M: Paulo Flabiano Smorigo <pfsmorigo@linux.vnet.ibm.com>
+L: linux-crypto@vger.kernel.org
+S: Supported
+F: drivers/crypto/nx/Makefile
+F: drivers/crypto/nx/Kconfig
+F: drivers/crypto/nx/nx-aes*
+F: drivers/crypto/nx/nx-sha*
+F: drivers/crypto/nx/nx.*
+F: drivers/crypto/nx/nx_csbcpb.h
+F: drivers/crypto/nx/nx_debugfs.h
+
+IBM Power 842 compression accelerator
+M: Haren Myneni <haren@us.ibm.com>
+S: Supported
+F: drivers/crypto/nx/Makefile
+F: drivers/crypto/nx/Kconfig
+F: drivers/crypto/nx/nx-842*
+F: include/linux/sw842.h
+F: crypto/842.c
+F: lib/842/
+
+IBM Power Linux RAID adapter
+M: Brian King <brking@us.ibm.com>
+S: Supported
+F: drivers/scsi/ipr.*
+
+IBM Power Virtual Ethernet Device Driver
+M: Thomas Falcon <tlfalcon@linux.vnet.ibm.com>
+L: netdev@vger.kernel.org
+S: Supported
+F: drivers/net/ethernet/ibm/ibmveth.*
+
+IBM Power SRIOV Virtual NIC Device Driver
+M: Thomas Falcon <tlfalcon@linux.vnet.ibm.com>
+M: John Allen <jallen@linux.vnet.ibm.com>
+L: netdev@vger.kernel.org
+S: Supported
+F: drivers/net/ethernet/ibm/ibmvnic.*
+
+IBM Power Virtual SCSI Device Drivers
+M: Tyrel Datwyler <tyreld@linux.vnet.ibm.com>
+L: linux-scsi@vger.kernel.org
+S: Supported
+F: drivers/scsi/ibmvscsi/ibmvscsi*
+F: include/scsi/viosrp.h
+
+IBM Power Virtual SCSI Device Target Driver
+M: Bryant G. Ly <bryantly@linux.vnet.ibm.com>
+M: Michael Cyr <mikecyr@linux.vnet.ibm.com>
+L: linux-scsi@vger.kernel.org
+L: target-devel@vger.kernel.org
+S: Supported
+F: drivers/scsi/ibmvscsi_tgt/
+
+IBM Power Virtual FC Device Drivers
+M: Tyrel Datwyler <tyreld@linux.vnet.ibm.com>
+L: linux-scsi@vger.kernel.org
+S: Supported
+F: drivers/scsi/ibmvscsi/ibmvfc*
+
+IBM ServeRAID RAID DRIVER
+S: Orphan
+F: drivers/scsi/ips.*
+
+ICH LPC AND GPIO DRIVER
+M: Peter Tyser <ptyser@xes-inc.com>
+S: Maintained
+F: drivers/mfd/lpc_ich.c
+F: drivers/gpio/gpio-ich.c
+
+IDT VersaClock 5 CLOCK DRIVER
+M: Marek Vasut <marek.vasut@gmail.com>
+S: Maintained
+F: drivers/clk/clk-versaclock5.c
+
+IDE SUBSYSTEM
+M: "David S. Miller" <davem@davemloft.net>
+L: linux-ide@vger.kernel.org
+Q: http://patchwork.ozlabs.org/project/linux-ide/list/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/davem/ide.git
+S: Maintained
+F: Documentation/ide/
+F: drivers/ide/
+F: include/linux/ide.h
+
+IDEAPAD LAPTOP EXTRAS DRIVER
+M: Ike Panhc <ike.pan@canonical.com>
+L: platform-driver-x86@vger.kernel.org
+W: http://launchpad.net/ideapad-laptop
+S: Maintained
+F: drivers/platform/x86/ideapad-laptop.c
+
+IDEAPAD LAPTOP SLIDEBAR DRIVER
+M: Andrey Moiseev <o2g.org.ru@gmail.com>
+L: linux-input@vger.kernel.org
+W: https://github.com/o2genum/ideapad-slidebar
+S: Maintained
+F: drivers/input/misc/ideapad_slidebar.c
+
+IDE/ATAPI DRIVERS
+M: Borislav Petkov <bp@alien8.de>
+L: linux-ide@vger.kernel.org
+S: Maintained
+F: Documentation/cdrom/ide-cd
+F: drivers/ide/ide-cd*
+
+IEEE 802.15.4 SUBSYSTEM
+M: Alexander Aring <aar@pengutronix.de>
+M: Stefan Schmidt <stefan@osg.samsung.com>
+L: linux-wpan@vger.kernel.org
+W: http://wpan.cakelab.org/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth.git
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next.git
+S: Maintained
+F: net/ieee802154/
+F: net/mac802154/
+F: drivers/net/ieee802154/
+F: include/linux/nl802154.h
+F: include/linux/ieee802154.h
+F: include/net/nl802154.h
+F: include/net/mac802154.h
+F: include/net/af_ieee802154.h
+F: include/net/cfg802154.h
+F: include/net/ieee802154_netdev.h
+F: Documentation/networking/ieee802154.txt
+
+IFE PROTOCOL
+M: Yotam Gigi <yotamg@mellanox.com>
+M: Jamal Hadi Salim <jhs@mojatatu.com>
+F: net/ife
+F: include/net/ife.h
+F: include/uapi/linux/ife.h
+
+IGORPLUG-USB IR RECEIVER
+M: Sean Young <sean@mess.org>
+L: linux-media@vger.kernel.org
+S: Maintained
+F: drivers/media/rc/igorplugusb.c
+
+IGUANAWORKS USB IR TRANSCEIVER
+M: Sean Young <sean@mess.org>
+L: linux-media@vger.kernel.org
+S: Maintained
+F: drivers/media/rc/iguanair.c
+
+IIO DIGITAL POTENTIOMETER DAC
+M: Peter Rosin <peda@axentia.se>
+L: linux-iio@vger.kernel.org
+S: Maintained
+F: Documentation/ABI/testing/sysfs-bus-iio-dac-dpot-dac
+F: Documentation/devicetree/bindings/iio/dac/dpot-dac.txt
+F: drivers/iio/dac/dpot-dac.c
+
+IIO ENVELOPE DETECTOR
+M: Peter Rosin <peda@axentia.se>
+L: linux-iio@vger.kernel.org
+S: Maintained
+F: Documentation/ABI/testing/sysfs-bus-iio-adc-envelope-detector
+F: Documentation/devicetree/bindings/iio/adc/envelope-detector.txt
+F: drivers/iio/adc/envelope-detector.c
+
+IIO SUBSYSTEM AND DRIVERS
+M: Jonathan Cameron <jic23@kernel.org>
+R: Hartmut Knaack <knaack.h@gmx.de>
+R: Lars-Peter Clausen <lars@metafoo.de>
+R: Peter Meerwald-Stadler <pmeerw@pmeerw.net>
+L: linux-iio@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git
+S: Maintained
+F: Documentation/devicetree/bindings/iio/
+F: drivers/iio/
+F: drivers/staging/iio/
+F: include/linux/iio/
+F: tools/iio/
+
+IKANOS/ADI EAGLE ADSL USB DRIVER
+M: Matthieu Castet <castet.matthieu@free.fr>
+M: Stanislaw Gruszka <stf_xl@wp.pl>
+S: Maintained
+F: drivers/usb/atm/ueagle-atm.c
+
+IMGTEC ASCII LCD DRIVER
+M: Paul Burton <paul.burton@imgtec.com>
+S: Maintained
+F: Documentation/devicetree/bindings/auxdisplay/img-ascii-lcd.txt
+F: drivers/auxdisplay/img-ascii-lcd.c
+
+INA209 HARDWARE MONITOR DRIVER
+M: Guenter Roeck <linux@roeck-us.net>
+L: linux-hwmon@vger.kernel.org
+S: Maintained
+F: Documentation/hwmon/ina209
+F: Documentation/devicetree/bindings/i2c/ina209.txt
+F: drivers/hwmon/ina209.c
+
+INA2XX HARDWARE MONITOR DRIVER
+M: Guenter Roeck <linux@roeck-us.net>
+L: linux-hwmon@vger.kernel.org
+S: Maintained
+F: Documentation/hwmon/ina2xx
+F: drivers/hwmon/ina2xx.c
+F: include/linux/platform_data/ina2xx.h
+
+INDUSTRY PACK SUBSYSTEM (IPACK)
+M: Samuel Iglesias Gonsalvez <siglesias@igalia.com>
+M: Jens Taprogge <jens.taprogge@taprogge.org>
+M: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+L: industrypack-devel@lists.sourceforge.net
+W: http://industrypack.sourceforge.net
+S: Maintained
+F: drivers/ipack/
+
+INGENIC JZ4780 DMA Driver
+M: Zubair Lutfullah Kakakhel <Zubair.Kakakhel@imgtec.com>
+S: Maintained
+F: drivers/dma/dma-jz4780.c
+
+INGENIC JZ4780 NAND DRIVER
+M: Harvey Hunt <harveyhuntnexus@gmail.com>
+L: linux-mtd@lists.infradead.org
+S: Maintained
+F: drivers/mtd/nand/jz4780_*
+
+INTEGRITY MEASUREMENT ARCHITECTURE (IMA)
+M: Mimi Zohar <zohar@linux.vnet.ibm.com>
+M: Dmitry Kasatkin <dmitry.kasatkin@gmail.com>
+L: linux-ima-devel@lists.sourceforge.net
+L: linux-ima-user@lists.sourceforge.net
+L: linux-security-module@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/zohar/linux-integrity.git
+S: Supported
+F: security/integrity/ima/
+
+IMGTEC IR DECODER DRIVER
+M: James Hogan <james.hogan@imgtec.com>
+S: Maintained
+F: drivers/media/rc/img-ir/
+
+IMS TWINTURBO FRAMEBUFFER DRIVER
+L: linux-fbdev@vger.kernel.org
+S: Orphan
+F: drivers/video/fbdev/imsttfb.c
+
+INFINIBAND SUBSYSTEM
+M: Doug Ledford <dledford@redhat.com>
+M: Sean Hefty <sean.hefty@intel.com>
+M: Hal Rosenstock <hal.rosenstock@gmail.com>
+L: linux-rdma@vger.kernel.org
+W: http://www.openfabrics.org/
+Q: http://patchwork.kernel.org/project/linux-rdma/list/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/dledford/rdma.git
+S: Supported
+F: Documentation/devicetree/bindings/infiniband/
+F: Documentation/infiniband/
+F: drivers/infiniband/
+F: include/uapi/linux/if_infiniband.h
+F: include/uapi/rdma/
+F: include/rdma/
+
+INOTIFY
+M: John McCutchan <john@johnmccutchan.com>
+M: Robert Love <rlove@rlove.org>
+M: Eric Paris <eparis@parisplace.org>
+S: Maintained
+F: Documentation/filesystems/inotify.txt
+F: fs/notify/inotify/
+F: include/linux/inotify.h
+F: include/uapi/linux/inotify.h
+
+INPUT (KEYBOARD, MOUSE, JOYSTICK, TOUCHSCREEN) DRIVERS
+M: Dmitry Torokhov <dmitry.torokhov@gmail.com>
+L: linux-input@vger.kernel.org
+Q: http://patchwork.kernel.org/project/linux-input/list/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input.git
+S: Maintained
+F: drivers/input/
+F: include/linux/input.h
+F: include/uapi/linux/input.h
+F: include/linux/input/
+F: Documentation/devicetree/bindings/input/
+
+INPUT MULTITOUCH (MT) PROTOCOL
+M: Henrik Rydberg <rydberg@bitmath.org>
+L: linux-input@vger.kernel.org
+S: Odd fixes
+F: Documentation/input/multi-touch-protocol.rst
+F: drivers/input/input-mt.c
+K: \b(ABS|SYN)_MT_
+
+INTEL ASoC BDW/HSW DRIVERS
+M: Jie Yang <yang.jie@linux.intel.com>
+L: alsa-devel@alsa-project.org (moderated for non-subscribers)
+S: Supported
+F: sound/soc/intel/common/sst-dsp*
+F: sound/soc/intel/common/sst-firmware.c
+F: sound/soc/intel/boards/broadwell.c
+F: sound/soc/intel/haswell/
+
+INTEL C600 SERIES SAS CONTROLLER DRIVER
+M: Intel SCU Linux support <intel-linux-scu@intel.com>
+M: Artur Paszkiewicz <artur.paszkiewicz@intel.com>
+L: linux-scsi@vger.kernel.org
+T: git git://git.code.sf.net/p/intel-sas/isci
+S: Supported
+F: drivers/scsi/isci/
+
+INTEL HID EVENT DRIVER
+M: Alex Hung <alex.hung@canonical.com>
+L: platform-driver-x86@vger.kernel.org
+S: Maintained
+F: drivers/platform/x86/intel-hid.c
+
+INTEL VIRTUAL BUTTON DRIVER
+M: AceLan Kao <acelan.kao@canonical.com>
+L: platform-driver-x86@vger.kernel.org
+S: Maintained
+F: drivers/platform/x86/intel-vbtn.c
+
+INTEL IDLE DRIVER
+M: Jacob Pan <jacob.jun.pan@linux.intel.com>
+M: Len Brown <lenb@kernel.org>
+L: linux-pm@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/lenb/linux.git
+B: https://bugzilla.kernel.org
+S: Supported
+F: drivers/idle/intel_idle.c
+
+INTEL INTEGRATED SENSOR HUB DRIVER
+M: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
+M: Jiri Kosina <jikos@kernel.org>
+L: linux-input@vger.kernel.org
+S: Maintained
+F: drivers/hid/intel-ish-hid/
+
+INTEL PSTATE DRIVER
+M: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
+M: Len Brown <lenb@kernel.org>
+L: linux-pm@vger.kernel.org
+S: Supported
+F: drivers/cpufreq/intel_pstate.c
+
+INTEL FRAMEBUFFER DRIVER (excluding 810 and 815)
+M: Maik Broemme <mbroemme@libmpq.org>
+L: linux-fbdev@vger.kernel.org
+S: Maintained
+F: Documentation/fb/intelfb.txt
+F: drivers/video/fbdev/intelfb/
+
+INTEL 810/815 FRAMEBUFFER DRIVER
+M: Antonino Daplas <adaplas@gmail.com>
+L: linux-fbdev@vger.kernel.org
+S: Maintained
+F: drivers/video/fbdev/i810/
+
+INTEL MENLOW THERMAL DRIVER
+M: Sujith Thomas <sujith.thomas@intel.com>
+L: platform-driver-x86@vger.kernel.org
+W: https://01.org/linux-acpi
+S: Supported
+F: drivers/platform/x86/intel_menlow.c
+
+INTEL I/OAT DMA DRIVER
+M: Dave Jiang <dave.jiang@intel.com>
+R: Dan Williams <dan.j.williams@intel.com>
+L: dmaengine@vger.kernel.org
+Q: https://patchwork.kernel.org/project/linux-dmaengine/list/
+S: Supported
+F: drivers/dma/ioat*
+
+INTEL IOMMU (VT-d)
+M: David Woodhouse <dwmw2@infradead.org>
+L: iommu@lists.linux-foundation.org
+T: git git://git.infradead.org/iommu-2.6.git
+S: Supported
+F: drivers/iommu/intel-iommu.c
+F: include/linux/intel-iommu.h
+
+INTEL IOP-ADMA DMA DRIVER
+R: Dan Williams <dan.j.williams@intel.com>
+S: Odd fixes
+F: drivers/dma/iop-adma.c
+
+INTEL IXP4XX QMGR, NPE, ETHERNET and HSS SUPPORT
+M: Krzysztof Halasa <khalasa@piap.pl>
+S: Maintained
+F: arch/arm/mach-ixp4xx/include/mach/qmgr.h
+F: arch/arm/mach-ixp4xx/include/mach/npe.h
+F: arch/arm/mach-ixp4xx/ixp4xx_qmgr.c
+F: arch/arm/mach-ixp4xx/ixp4xx_npe.c
+F: drivers/net/ethernet/xscale/ixp4xx_eth.c
+F: drivers/net/wan/ixp4xx_hss.c
+
+INTEL IXP4XX RANDOM NUMBER GENERATOR SUPPORT
+M: Deepak Saxena <dsaxena@plexity.net>
+S: Maintained
+F: drivers/char/hw_random/ixp4xx-rng.c
+
+INTEL ETHERNET DRIVERS
+M: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
+L: intel-wired-lan@lists.osuosl.org (moderated for non-subscribers)
+W: http://www.intel.com/support/feedback.htm
+W: http://e1000.sourceforge.net/
+Q: http://patchwork.ozlabs.org/project/intel-wired-lan/list/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/jkirsher/net-queue.git
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/jkirsher/next-queue.git
+S: Supported
+F: Documentation/networking/e100.txt
+F: Documentation/networking/e1000.txt
+F: Documentation/networking/e1000e.txt
+F: Documentation/networking/igb.txt
+F: Documentation/networking/igbvf.txt
+F: Documentation/networking/ixgb.txt
+F: Documentation/networking/ixgbe.txt
+F: Documentation/networking/ixgbevf.txt
+F: Documentation/networking/i40e.txt
+F: Documentation/networking/i40evf.txt
+F: drivers/net/ethernet/intel/
+F: drivers/net/ethernet/intel/*/
+
+INTEL RDMA RNIC DRIVER
+M: Faisal Latif <faisal.latif@intel.com>
+M: Shiraz Saleem <shiraz.saleem@intel.com>
+L: linux-rdma@vger.kernel.org
+S: Supported
+F: drivers/infiniband/hw/i40iw/
+
+INTEL MERRIFIELD GPIO DRIVER
+M: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+L: linux-gpio@vger.kernel.org
+S: Maintained
+F: drivers/gpio/gpio-merrifield.c
+
+INTEL-MID GPIO DRIVER
+M: David Cohen <david.a.cohen@linux.intel.com>
+L: linux-gpio@vger.kernel.org
+S: Maintained
+F: drivers/gpio/gpio-intel-mid.c
+
+INTEL PRO/WIRELESS 2100, 2200BG, 2915ABG NETWORK CONNECTION SUPPORT
+M: Stanislav Yakovlev <stas.yakovlev@gmail.com>
+L: linux-wireless@vger.kernel.org
+S: Maintained
+F: Documentation/networking/README.ipw2100
+F: Documentation/networking/README.ipw2200
+F: drivers/net/wireless/intel/ipw2x00/
+
+INTEL(R) TRACE HUB
+M: Alexander Shishkin <alexander.shishkin@linux.intel.com>
+S: Supported
+F: Documentation/trace/intel_th.txt
+F: drivers/hwtracing/intel_th/
+
+INTEL(R) TRUSTED EXECUTION TECHNOLOGY (TXT)
+M: Ning Sun <ning.sun@intel.com>
+L: tboot-devel@lists.sourceforge.net
+W: http://tboot.sourceforge.net
+T: hg http://tboot.hg.sourceforge.net:8000/hgroot/tboot/tboot
+S: Supported
+F: Documentation/intel_txt.txt
+F: include/linux/tboot.h
+F: arch/x86/kernel/tboot.c
+
+INTEL WIRELESS WIMAX CONNECTION 2400
+M: Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+M: linux-wimax@intel.com
+L: wimax@linuxwimax.org (subscribers-only)
+S: Supported
+W: http://linuxwimax.org
+F: Documentation/wimax/README.i2400m
+F: drivers/net/wimax/i2400m/
+F: include/uapi/linux/wimax/i2400m.h
+
+INTEL WIRELESS 3945ABG/BG, 4965AGN (iwlegacy)
+M: Stanislaw Gruszka <sgruszka@redhat.com>
+L: linux-wireless@vger.kernel.org
+S: Supported
+F: drivers/net/wireless/intel/iwlegacy/
+
+INTEL WIRELESS WIFI LINK (iwlwifi)
+M: Johannes Berg <johannes.berg@intel.com>
+M: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
+M: Luca Coelho <luciano.coelho@intel.com>
+M: Intel Linux Wireless <linuxwifi@intel.com>
+L: linux-wireless@vger.kernel.org
+W: http://intellinuxwireless.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/iwlwifi/iwlwifi.git
+S: Supported
+F: drivers/net/wireless/intel/iwlwifi/
+
+INTEL MANAGEMENT ENGINE (mei)
+M: Tomas Winkler <tomas.winkler@intel.com>
+L: linux-kernel@vger.kernel.org
+S: Supported
+F: include/uapi/linux/mei.h
+F: include/linux/mei_cl_bus.h
+F: drivers/misc/mei/*
+F: drivers/watchdog/mei_wdt.c
+F: Documentation/misc-devices/mei/*
+F: samples/mei/*
+
+INTEL MIC DRIVERS (mic)
+M: Sudeep Dutt <sudeep.dutt@intel.com>
+M: Ashutosh Dixit <ashutosh.dixit@intel.com>
+S: Supported
+W: https://github.com/sudeepdutt/mic
+W: http://software.intel.com/en-us/mic-developer
+F: include/linux/mic_bus.h
+F: include/linux/scif.h
+F: include/uapi/linux/mic_common.h
+F: include/uapi/linux/mic_ioctl.h
+F: include/uapi/linux/scif_ioctl.h
+F: drivers/misc/mic/
+F: drivers/dma/mic_x100_dma.c
+F: drivers/dma/mic_x100_dma.h
+F: Documentation/mic/
+
+INTEL PMC/P-Unit IPC DRIVER
+M: Zha Qipeng<qipeng.zha@intel.com>
+L: platform-driver-x86@vger.kernel.org
+S: Maintained
+F: drivers/platform/x86/intel_pmc_ipc.c
+F: drivers/platform/x86/intel_punit_ipc.c
+F: arch/x86/include/asm/intel_pmc_ipc.h
+F: arch/x86/include/asm/intel_punit_ipc.h
+
+INTEL TELEMETRY DRIVER
+M: Souvik Kumar Chakravarty <souvik.k.chakravarty@intel.com>
+L: platform-driver-x86@vger.kernel.org
+S: Maintained
+F: arch/x86/include/asm/intel_telemetry.h
+F: drivers/platform/x86/intel_telemetry*
+
+INTEL PMC CORE DRIVER
+M: Rajneesh Bhardwaj <rajneesh.bhardwaj@intel.com>
+M: Vishwanath Somayaji <vishwanath.somayaji@intel.com>
+L: platform-driver-x86@vger.kernel.org
+S: Maintained
+F: arch/x86/include/asm/pmc_core.h
+F: drivers/platform/x86/intel_pmc_core*
+
+INVENSENSE MPU-3050 GYROSCOPE DRIVER
+M: Linus Walleij <linus.walleij@linaro.org>
+L: linux-iio@vger.kernel.org
+S: Maintained
+F: drivers/iio/gyro/mpu3050*
+F: Documentation/devicetree/bindings/iio/gyroscope/inv,mpu3050.txt
+
+IOC3 ETHERNET DRIVER
+M: Ralf Baechle <ralf@linux-mips.org>
+L: linux-mips@linux-mips.org
+S: Maintained
+F: drivers/net/ethernet/sgi/ioc3-eth.c
+
+IOC3 SERIAL DRIVER
+M: Pat Gefre <pfg@sgi.com>
+L: linux-serial@vger.kernel.org
+S: Maintained
+F: drivers/tty/serial/ioc3_serial.c
+
+IOMMU DRIVERS
+M: Joerg Roedel <joro@8bytes.org>
+L: iommu@lists.linux-foundation.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu.git
+S: Maintained
+F: Documentation/devicetree/bindings/iommu/
+F: drivers/iommu/
+F: include/linux/iommu.h
+F: include/linux/iova.h
+
+IP MASQUERADING
+M: Juanjo Ciarlante <jjciarla@raiz.uncu.edu.ar>
+S: Maintained
+F: net/ipv4/netfilter/ipt_MASQUERADE.c
+
+IPMI SUBSYSTEM
+M: Corey Minyard <minyard@acm.org>
+L: openipmi-developer@lists.sourceforge.net (moderated for non-subscribers)
+W: http://openipmi.sourceforge.net/
+S: Supported
+F: Documentation/IPMI.txt
+F: drivers/char/ipmi/
+F: include/linux/ipmi*
+F: include/uapi/linux/ipmi*
+
+QCOM AUDIO (ASoC) DRIVERS
+M: Patrick Lai <plai@codeaurora.org>
+M: Banajit Goswami <bgoswami@codeaurora.org>
+L: alsa-devel@alsa-project.org (moderated for non-subscribers)
+S: Supported
+F: sound/soc/qcom/
+
+IPS SCSI RAID DRIVER
+M: Adaptec OEM Raid Solutions <aacraid@adaptec.com>
+L: linux-scsi@vger.kernel.org
+W: http://www.adaptec.com/
+S: Maintained
+F: drivers/scsi/ips*
+
+IPVS
+M: Wensong Zhang <wensong@linux-vs.org>
+M: Simon Horman <horms@verge.net.au>
+M: Julian Anastasov <ja@ssi.bg>
+L: netdev@vger.kernel.org
+L: lvs-devel@vger.kernel.org
+S: Maintained
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/horms/ipvs-next.git
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/horms/ipvs.git
+F: Documentation/networking/ipvs-sysctl.txt
+F: include/net/ip_vs.h
+F: include/uapi/linux/ip_vs.h
+F: net/netfilter/ipvs/
+
+IPWIRELESS DRIVER
+M: Jiri Kosina <jikos@kernel.org>
+M: David Sterba <dsterba@suse.com>
+S: Odd Fixes
+F: drivers/tty/ipwireless/
+
+IPX NETWORK LAYER
+L: netdev@vger.kernel.org
+S: Odd fixes
+F: include/net/ipx.h
+F: include/uapi/linux/ipx.h
+F: net/ipx/
+
+IRDA SUBSYSTEM
+M: Samuel Ortiz <samuel@sortiz.org>
+L: irda-users@lists.sourceforge.net (subscribers-only)
+L: netdev@vger.kernel.org
+W: http://irda.sourceforge.net/
+S: Maintained
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/sameo/irda-2.6.git
+F: Documentation/networking/irda.txt
+F: drivers/net/irda/
+F: include/net/irda/
+F: net/irda/
+
+IRQ SUBSYSTEM
+M: Thomas Gleixner <tglx@linutronix.de>
+L: linux-kernel@vger.kernel.org
+S: Maintained
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git irq/core
+F: kernel/irq/
+
+IRQCHIP DRIVERS
+M: Thomas Gleixner <tglx@linutronix.de>
+M: Jason Cooper <jason@lakedaemon.net>
+M: Marc Zyngier <marc.zyngier@arm.com>
+L: linux-kernel@vger.kernel.org
+S: Maintained
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git irq/core
+T: git git://git.infradead.org/users/jcooper/linux.git irqchip/core
+F: Documentation/devicetree/bindings/interrupt-controller/
+F: drivers/irqchip/
+
+IRQ DOMAINS (IRQ NUMBER MAPPING LIBRARY)
+M: Marc Zyngier <marc.zyngier@arm.com>
+S: Maintained
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git irq/core
+F: Documentation/IRQ-domain.txt
+F: include/linux/irqdomain.h
+F: kernel/irq/irqdomain.c
+F: kernel/irq/msi.c
+
+ISA
+M: William Breathitt Gray <vilhelm.gray@gmail.com>
+S: Maintained
+F: Documentation/isa.txt
+F: drivers/base/isa.c
+F: include/linux/isa.h
+
+ISAPNP
+M: Jaroslav Kysela <perex@perex.cz>
+S: Maintained
+F: Documentation/isapnp.txt
+F: drivers/pnp/isapnp/
+F: include/linux/isapnp.h
+
+ISA RADIO MODULE
+M: Hans Verkuil <hverkuil@xs4all.nl>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+W: https://linuxtv.org
+S: Maintained
+F: drivers/media/radio/radio-isa*
+
+iSCSI BOOT FIRMWARE TABLE (iBFT) DRIVER
+M: Peter Jones <pjones@redhat.com>
+M: Konrad Rzeszutek Wilk <konrad@kernel.org>
+S: Maintained
+F: drivers/firmware/iscsi_ibft*
+
+ISCSI
+M: Lee Duncan <lduncan@suse.com>
+M: Chris Leech <cleech@redhat.com>
+L: open-iscsi@googlegroups.com
+W: www.open-iscsi.com
+S: Maintained
+F: drivers/scsi/*iscsi*
+F: include/scsi/*iscsi*
+
+ISCSI EXTENSIONS FOR RDMA (ISER) INITIATOR
+M: Or Gerlitz <ogerlitz@mellanox.com>
+M: Sagi Grimberg <sagi@grimberg.me>
+M: Roi Dayan <roid@mellanox.com>
+L: linux-rdma@vger.kernel.org
+S: Supported
+W: http://www.openfabrics.org
+W: www.open-iscsi.org
+Q: http://patchwork.kernel.org/project/linux-rdma/list/
+F: drivers/infiniband/ulp/iser/
+
+ISCSI EXTENSIONS FOR RDMA (ISER) TARGET
+M: Sagi Grimberg <sagi@grimberg.me>
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/nab/target-pending.git master
+L: linux-rdma@vger.kernel.org
+L: target-devel@vger.kernel.org
+S: Supported
+W: http://www.linux-iscsi.org
+F: drivers/infiniband/ulp/isert
+
+ISDN SUBSYSTEM
+M: Karsten Keil <isdn@linux-pingi.de>
+L: isdn4linux@listserv.isdn4linux.de (subscribers-only)
+L: netdev@vger.kernel.org
+W: http://www.isdn4linux.de
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/kkeil/isdn-2.6.git
+S: Maintained
+F: Documentation/isdn/
+F: drivers/isdn/
+F: include/linux/isdn.h
+F: include/linux/isdn/
+F: include/uapi/linux/isdn.h
+F: include/uapi/linux/isdn/
+
+ISDN SUBSYSTEM (Eicon active card driver)
+M: Armin Schindler <mac@melware.de>
+L: isdn4linux@listserv.isdn4linux.de (subscribers-only)
+W: http://www.melware.de
+S: Maintained
+F: drivers/isdn/hardware/eicon/
+
+IT87 HARDWARE MONITORING DRIVER
+M: Jean Delvare <jdelvare@suse.com>
+L: linux-hwmon@vger.kernel.org
+S: Maintained
+F: Documentation/hwmon/it87
+F: drivers/hwmon/it87.c
+
+IT913X MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+W: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/media/tuners/it913x*
+
+IVTV VIDEO4LINUX DRIVER
+M: Andy Walls <awalls@md.metrocast.net>
+L: ivtv-devel@ivtvdriver.org (subscribers-only)
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+W: http://www.ivtvdriver.org
+S: Maintained
+F: Documentation/media/v4l-drivers/ivtv*
+F: drivers/media/pci/ivtv/
+F: include/uapi/linux/ivtv*
+
+IX2505V MEDIA DRIVER
+M: Malcolm Priestley <tvboxspy@gmail.com>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+S: Maintained
+F: drivers/media/dvb-frontends/ix2505v*
+
+JC42.4 TEMPERATURE SENSOR DRIVER
+M: Guenter Roeck <linux@roeck-us.net>
+L: linux-hwmon@vger.kernel.org
+S: Maintained
+F: drivers/hwmon/jc42.c
+F: Documentation/hwmon/jc42
+
+JFS FILESYSTEM
+M: Dave Kleikamp <shaggy@kernel.org>
+L: jfs-discussion@lists.sourceforge.net
+W: http://jfs.sourceforge.net/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/shaggy/jfs-2.6.git
+S: Maintained
+F: Documentation/filesystems/jfs.txt
+F: fs/jfs/
+
+JME NETWORK DRIVER
+M: Guo-Fu Tseng <cooldavid@cooldavid.org>
+L: netdev@vger.kernel.org
+S: Maintained
+F: drivers/net/ethernet/jme.*
+
+JOURNALLING FLASH FILE SYSTEM V2 (JFFS2)
+M: David Woodhouse <dwmw2@infradead.org>
+L: linux-mtd@lists.infradead.org
+W: http://www.linux-mtd.infradead.org/doc/jffs2.html
+S: Maintained
+F: fs/jffs2/
+F: include/uapi/linux/jffs2.h
+
+JOURNALLING LAYER FOR BLOCK DEVICES (JBD2)
+M: "Theodore Ts'o" <tytso@mit.edu>
+M: Jan Kara <jack@suse.com>
+L: linux-ext4@vger.kernel.org
+S: Maintained
+F: fs/jbd2/
+F: include/linux/jbd2.h
+
+JPU V4L2 MEM2MEM DRIVER FOR RENESAS
+M: Mikhail Ulyanov <mikhail.ulyanov@cogentembedded.com>
+L: linux-media@vger.kernel.org
+S: Maintained
+F: drivers/media/platform/rcar_jpu.c
+
+JSM Neo PCI based serial card
+M: Guilherme G. Piccoli <gpiccoli@linux.vnet.ibm.com>
+L: linux-serial@vger.kernel.org
+S: Maintained
+F: drivers/tty/serial/jsm/
+
+K10TEMP HARDWARE MONITORING DRIVER
+M: Clemens Ladisch <clemens@ladisch.de>
+L: linux-hwmon@vger.kernel.org
+S: Maintained
+F: Documentation/hwmon/k10temp
+F: drivers/hwmon/k10temp.c
+
+K8TEMP HARDWARE MONITORING DRIVER
+M: Rudolf Marek <r.marek@assembler.cz>
+L: linux-hwmon@vger.kernel.org
+S: Maintained
+F: Documentation/hwmon/k8temp
+F: drivers/hwmon/k8temp.c
+
+KASAN
+M: Andrey Ryabinin <aryabinin@virtuozzo.com>
+R: Alexander Potapenko <glider@google.com>
+R: Dmitry Vyukov <dvyukov@google.com>
+L: kasan-dev@googlegroups.com
+S: Maintained
+F: arch/*/include/asm/kasan.h
+F: arch/*/mm/kasan_init*
+F: Documentation/dev-tools/kasan.rst
+F: include/linux/kasan*.h
+F: lib/test_kasan.c
+F: mm/kasan/
+F: scripts/Makefile.kasan
+
+KCONFIG
+M: "Yann E. MORIN" <yann.morin.1998@free.fr>
+L: linux-kbuild@vger.kernel.org
+T: git git://gitorious.org/linux-kconfig/linux-kconfig
+S: Maintained
+F: Documentation/kbuild/kconfig-language.txt
+F: scripts/kconfig/
+
+KDUMP
+M: Dave Young <dyoung@redhat.com>
+M: Baoquan He <bhe@redhat.com>
+R: Vivek Goyal <vgoyal@redhat.com>
+L: kexec@lists.infradead.org
+W: http://lse.sourceforge.net/kdump/
+S: Maintained
+F: Documentation/kdump/
+
+KEENE FM RADIO TRANSMITTER DRIVER
+M: Hans Verkuil <hverkuil@xs4all.nl>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+W: https://linuxtv.org
+S: Maintained
+F: drivers/media/radio/radio-keene*
+
+KERNEL AUTOMOUNTER v4 (AUTOFS4)
+M: Ian Kent <raven@themaw.net>
+L: autofs@vger.kernel.org
+S: Maintained
+F: fs/autofs4/
+
+KERNEL BUILD + files below scripts/ (unless maintained elsewhere)
+M: Masahiro Yamada <yamada.masahiro@socionext.com>
+M: Michal Marek <mmarek@suse.com>
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/masahiroy/linux-kbuild.git
+L: linux-kbuild@vger.kernel.org
+S: Maintained
+F: Documentation/kbuild/
+F: Makefile
+F: scripts/Makefile.*
+F: scripts/basic/
+F: scripts/mk*
+F: scripts/package/
+
+KERNEL JANITORS
+L: kernel-janitors@vger.kernel.org
+W: http://kernelnewbies.org/KernelJanitors
+S: Odd Fixes
+
+KERNEL NFSD, SUNRPC, AND LOCKD SERVERS
+M: "J. Bruce Fields" <bfields@fieldses.org>
+M: Jeff Layton <jlayton@poochiereds.net>
+L: linux-nfs@vger.kernel.org
+W: http://nfs.sourceforge.net/
+T: git git://linux-nfs.org/~bfields/linux.git
+S: Supported
+F: fs/nfsd/
+F: include/uapi/linux/nfsd/
+F: fs/lockd/
+F: fs/nfs_common/
+F: net/sunrpc/
+F: include/linux/lockd/
+F: include/linux/sunrpc/
+F: include/uapi/linux/sunrpc/
+
+KERNEL SELFTEST FRAMEWORK
+M: Shuah Khan <shuahkh@osg.samsung.com>
+M: Shuah Khan <shuah@kernel.org>
+L: linux-kselftest@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/shuah/linux-kselftest
+S: Maintained
+F: tools/testing/selftests
+
+KERNEL VIRTUAL MACHINE (KVM)
+M: Paolo Bonzini <pbonzini@redhat.com>
+M: Radim Krčmář <rkrcmar@redhat.com>
+L: kvm@vger.kernel.org
+W: http://www.linux-kvm.org
+T: git git://git.kernel.org/pub/scm/virt/kvm/kvm.git
+S: Supported
+F: Documentation/*/kvm*.txt
+F: Documentation/virtual/kvm/
+F: arch/*/kvm/
+F: arch/x86/kernel/kvm.c
+F: arch/x86/kernel/kvmclock.c
+F: arch/*/include/asm/kvm*
+F: include/linux/kvm*
+F: include/uapi/linux/kvm*
+F: virt/kvm/
+F: tools/kvm/
+
+KERNEL VIRTUAL MACHINE (KVM) FOR AMD-V
+M: Joerg Roedel <joro@8bytes.org>
+L: kvm@vger.kernel.org
+W: http://www.linux-kvm.org/
+S: Maintained
+F: arch/x86/include/asm/svm.h
+F: arch/x86/kvm/svm.c
+
+KERNEL VIRTUAL MACHINE (KVM) FOR POWERPC
+M: Alexander Graf <agraf@suse.com>
+L: kvm-ppc@vger.kernel.org
+W: http://www.linux-kvm.org/
+T: git git://github.com/agraf/linux-2.6.git
+S: Supported
+F: arch/powerpc/include/asm/kvm*
+F: arch/powerpc/kvm/
+
+KERNEL VIRTUAL MACHINE for s390 (KVM/s390)
+M: Christian Borntraeger <borntraeger@de.ibm.com>
+M: Cornelia Huck <cornelia.huck@de.ibm.com>
+L: linux-s390@vger.kernel.org
+W: http://www.ibm.com/developerworks/linux/linux390/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/kvms390/linux.git
+S: Supported
+F: Documentation/s390/kvm.txt
+F: arch/s390/include/asm/kvm*
+F: arch/s390/kvm/
+F: arch/s390/mm/gmap.c
+
+KERNEL VIRTUAL MACHINE (KVM) FOR ARM
+M: Christoffer Dall <christoffer.dall@linaro.org>
+M: Marc Zyngier <marc.zyngier@arm.com>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+L: kvmarm@lists.cs.columbia.edu
+W: http://systems.cs.columbia.edu/projects/kvm-arm
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/kvmarm/kvmarm.git
+S: Supported
+F: arch/arm/include/uapi/asm/kvm*
+F: arch/arm/include/asm/kvm*
+F: arch/arm/kvm/
+F: virt/kvm/arm/
+F: include/kvm/arm_*
+
+KERNEL VIRTUAL MACHINE FOR ARM64 (KVM/arm64)
+M: Christoffer Dall <christoffer.dall@linaro.org>
+M: Marc Zyngier <marc.zyngier@arm.com>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+L: kvmarm@lists.cs.columbia.edu
+S: Maintained
+F: arch/arm64/include/uapi/asm/kvm*
+F: arch/arm64/include/asm/kvm*
+F: arch/arm64/kvm/
+
+KERNEL VIRTUAL MACHINE FOR MIPS (KVM/mips)
+M: James Hogan <james.hogan@imgtec.com>
+L: linux-mips@linux-mips.org
+S: Supported
+F: arch/mips/include/uapi/asm/kvm*
+F: arch/mips/include/asm/kvm*
+F: arch/mips/kvm/
+
+KERNFS
+M: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+M: Tejun Heo <tj@kernel.org>
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core.git
+S: Supported
+F: include/linux/kernfs.h
+F: fs/kernfs/
+
+KEXEC
+M: Eric Biederman <ebiederm@xmission.com>
+W: http://kernel.org/pub/linux/utils/kernel/kexec/
+L: kexec@lists.infradead.org
+S: Maintained
+F: include/linux/kexec.h
+F: include/uapi/linux/kexec.h
+F: kernel/kexec*
+
+KEYS/KEYRINGS:
+M: David Howells <dhowells@redhat.com>
+L: keyrings@vger.kernel.org
+S: Maintained
+F: Documentation/security/keys.txt
+F: include/linux/key.h
+F: include/linux/key-type.h
+F: include/linux/keyctl.h
+F: include/uapi/linux/keyctl.h
+F: include/keys/
+F: security/keys/
+
+KEYS-TRUSTED
+M: David Safford <safford@us.ibm.com>
+M: Mimi Zohar <zohar@linux.vnet.ibm.com>
+L: linux-security-module@vger.kernel.org
+L: keyrings@vger.kernel.org
+S: Supported
+F: Documentation/security/keys-trusted-encrypted.txt
+F: include/keys/trusted-type.h
+F: security/keys/trusted.c
+F: security/keys/trusted.h
+
+KEYS-ENCRYPTED
+M: Mimi Zohar <zohar@linux.vnet.ibm.com>
+M: David Safford <safford@us.ibm.com>
+L: linux-security-module@vger.kernel.org
+L: keyrings@vger.kernel.org
+S: Supported
+F: Documentation/security/keys-trusted-encrypted.txt
+F: include/keys/encrypted-type.h
+F: security/keys/encrypted-keys/
+
+KGDB / KDB /debug_core
+M: Jason Wessel <jason.wessel@windriver.com>
+W: http://kgdb.wiki.kernel.org/
+L: kgdb-bugreport@lists.sourceforge.net
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/jwessel/kgdb.git
+S: Maintained
+F: Documentation/DocBook/kgdb.tmpl
+F: drivers/misc/kgdbts.c
+F: drivers/tty/serial/kgdboc.c
+F: include/linux/kdb.h
+F: include/linux/kgdb.h
+F: kernel/debug/
+
+KMEMCHECK
+M: Vegard Nossum <vegardno@ifi.uio.no>
+M: Pekka Enberg <penberg@kernel.org>
+S: Maintained
+F: Documentation/dev-tools/kmemcheck.rst
+F: arch/x86/include/asm/kmemcheck.h
+F: arch/x86/mm/kmemcheck/
+F: include/linux/kmemcheck.h
+F: mm/kmemcheck.c
+
+KMEMLEAK
+M: Catalin Marinas <catalin.marinas@arm.com>
+S: Maintained
+F: Documentation/dev-tools/kmemleak.rst
+F: include/linux/kmemleak.h
+F: mm/kmemleak.c
+F: mm/kmemleak-test.c
+
+KPROBES
+M: Ananth N Mavinakayanahalli <ananth@linux.vnet.ibm.com>
+M: Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com>
+M: "David S. Miller" <davem@davemloft.net>
+M: Masami Hiramatsu <mhiramat@kernel.org>
+S: Maintained
+F: Documentation/kprobes.txt
+F: include/linux/kprobes.h
+F: include/asm-generic/kprobes.h
+F: kernel/kprobes.c
+
+KS0108 LCD CONTROLLER DRIVER
+M: Miguel Ojeda Sandonis <miguel.ojeda.sandonis@gmail.com>
+W: http://miguelojeda.es/auxdisplay.htm
+W: http://jair.lab.fi.uva.es/~migojed/auxdisplay.htm
+S: Maintained
+F: Documentation/auxdisplay/ks0108
+F: drivers/auxdisplay/ks0108.c
+F: include/linux/ks0108.h
+
+L3MDEV
+M: David Ahern <dsa@cumulusnetworks.com>
+L: netdev@vger.kernel.org
+S: Maintained
+F: net/l3mdev
+F: include/net/l3mdev.h
+
+LANTIQ MIPS ARCHITECTURE
+M: John Crispin <john@phrozen.org>
+L: linux-mips@linux-mips.org
+S: Maintained
+F: arch/mips/lantiq
+
+LAPB module
+L: linux-x25@vger.kernel.org
+S: Orphan
+F: Documentation/networking/lapb-module.txt
+F: include/*/lapb.h
+F: net/lapb/
+
+LASI 53c700 driver for PARISC
+M: "James E.J. Bottomley" <James.Bottomley@HansenPartnership.com>
+L: linux-scsi@vger.kernel.org
+S: Maintained
+F: Documentation/scsi/53c700.txt
+F: drivers/scsi/53c700*
+
+LED SUBSYSTEM
+M: Richard Purdie <rpurdie@rpsys.net>
+M: Jacek Anaszewski <jacek.anaszewski@gmail.com>
+M: Pavel Machek <pavel@ucw.cz>
+L: linux-leds@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/j.anaszewski/linux-leds.git
+S: Maintained
+F: Documentation/devicetree/bindings/leds/
+F: drivers/leds/
+F: include/linux/leds.h
+
+LEGACY EEPROM DRIVER
+M: Jean Delvare <jdelvare@suse.com>
+S: Maintained
+F: Documentation/misc-devices/eeprom
+F: drivers/misc/eeprom/eeprom.c
+
+LEGO USB Tower driver
+M: Juergen Stuber <starblue@users.sourceforge.net>
+L: legousb-devel@lists.sourceforge.net
+W: http://legousb.sourceforge.net/
+S: Maintained
+F: drivers/usb/misc/legousbtower.c
+
+LG2160 MEDIA DRIVER
+M: Michael Krufky <mkrufky@linuxtv.org>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+W: http://github.com/mkrufky
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/mkrufky/tuners.git
+S: Maintained
+F: drivers/media/dvb-frontends/lg2160.*
+
+LGDT3305 MEDIA DRIVER
+M: Michael Krufky <mkrufky@linuxtv.org>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+W: http://github.com/mkrufky
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/mkrufky/tuners.git
+S: Maintained
+F: drivers/media/dvb-frontends/lgdt3305.*
+
+LGUEST
+M: Rusty Russell <rusty@rustcorp.com.au>
+L: lguest@lists.ozlabs.org
+W: http://lguest.ozlabs.org/
+S: Odd Fixes
+F: arch/x86/include/asm/lguest*.h
+F: arch/x86/lguest/
+F: drivers/lguest/
+F: include/linux/lguest*.h
+F: tools/lguest/
+
+LIBATA SUBSYSTEM (Serial and Parallel ATA drivers)
+M: Tejun Heo <tj@kernel.org>
+L: linux-ide@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/tj/libata.git
+S: Maintained
+F: drivers/ata/
+F: include/linux/ata.h
+F: include/linux/libata.h
+F: Documentation/devicetree/bindings/ata/
+
+LIBATA PATA ARASAN COMPACT FLASH CONTROLLER
+M: Viresh Kumar <vireshk@kernel.org>
+L: linux-ide@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/tj/libata.git
+S: Maintained
+F: include/linux/pata_arasan_cf_data.h
+F: drivers/ata/pata_arasan_cf.c
+
+LIBATA PATA DRIVERS
+M: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
+M: Tejun Heo <tj@kernel.org>
+L: linux-ide@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/tj/libata.git
+S: Maintained
+F: drivers/ata/pata_*.c
+F: drivers/ata/ata_generic.c
+
+LIBATA SATA AHCI PLATFORM devices support
+M: Hans de Goede <hdegoede@redhat.com>
+M: Tejun Heo <tj@kernel.org>
+L: linux-ide@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/tj/libata.git
+S: Maintained
+F: drivers/ata/ahci_platform.c
+F: drivers/ata/libahci_platform.c
+F: include/linux/ahci_platform.h
+
+LIBATA SATA PROMISE TX2/TX4 CONTROLLER DRIVER
+M: Mikael Pettersson <mikpelinux@gmail.com>
+L: linux-ide@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/tj/libata.git
+S: Maintained
+F: drivers/ata/sata_promise.*
+
+LIBLOCKDEP
+M: Sasha Levin <sasha.levin@oracle.com>
+S: Maintained
+F: tools/lib/lockdep/
+
+LIBNVDIMM: NON-VOLATILE MEMORY DEVICE SUBSYSTEM
+M: Dan Williams <dan.j.williams@intel.com>
+L: linux-nvdimm@lists.01.org
+Q: https://patchwork.kernel.org/project/linux-nvdimm/list/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/nvdimm/nvdimm.git
+S: Supported
+F: drivers/nvdimm/*
+F: include/linux/nd.h
+F: include/linux/libnvdimm.h
+F: include/uapi/linux/ndctl.h
+
+LIBNVDIMM BLK: MMIO-APERTURE DRIVER
+M: Ross Zwisler <ross.zwisler@linux.intel.com>
+L: linux-nvdimm@lists.01.org
+Q: https://patchwork.kernel.org/project/linux-nvdimm/list/
+S: Supported
+F: drivers/nvdimm/blk.c
+F: drivers/nvdimm/region_devs.c
+F: drivers/acpi/nfit*
+
+LIBNVDIMM BTT: BLOCK TRANSLATION TABLE
+M: Vishal Verma <vishal.l.verma@intel.com>
+L: linux-nvdimm@lists.01.org
+Q: https://patchwork.kernel.org/project/linux-nvdimm/list/
+S: Supported
+F: drivers/nvdimm/btt*
+
+LIBNVDIMM PMEM: PERSISTENT MEMORY DRIVER
+M: Ross Zwisler <ross.zwisler@linux.intel.com>
+L: linux-nvdimm@lists.01.org
+Q: https://patchwork.kernel.org/project/linux-nvdimm/list/
+S: Supported
+F: drivers/nvdimm/pmem.c
+F: include/linux/pmem.h
+F: arch/*/include/asm/pmem.h
+
+LIGHTNVM PLATFORM SUPPORT
+M: Matias Bjorling <mb@lightnvm.io>
+W: http://github/OpenChannelSSD
+L: linux-block@vger.kernel.org
+S: Maintained
+F: drivers/lightnvm/
+F: include/linux/lightnvm.h
+F: include/uapi/linux/lightnvm.h
+
+LINUX FOR POWERPC (32-BIT AND 64-BIT)
+M: Benjamin Herrenschmidt <benh@kernel.crashing.org>
+M: Paul Mackerras <paulus@samba.org>
+M: Michael Ellerman <mpe@ellerman.id.au>
+W: https://github.com/linuxppc/linux/wiki
+L: linuxppc-dev@lists.ozlabs.org
+Q: http://patchwork.ozlabs.org/project/linuxppc-dev/list/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/powerpc/linux.git
+S: Supported
+F: Documentation/ABI/stable/sysfs-firmware-opal-*
+F: Documentation/devicetree/bindings/powerpc/
+F: Documentation/devicetree/bindings/rtc/rtc-opal.txt
+F: Documentation/devicetree/bindings/i2c/i2c-opal.txt
+F: Documentation/powerpc/
+F: arch/powerpc/
+F: drivers/char/tpm/tpm_ibmvtpm*
+F: drivers/crypto/nx/
+F: drivers/crypto/vmx/
+F: drivers/i2c/busses/i2c-opal.c
+F: drivers/net/ethernet/ibm/ibmveth.*
+F: drivers/net/ethernet/ibm/ibmvnic.*
+F: drivers/pci/hotplug/pnv_php.c
+F: drivers/pci/hotplug/rpa*
+F: drivers/rtc/rtc-opal.c
+F: drivers/scsi/ibmvscsi/
+F: drivers/tty/hvc/hvc_opal.c
+F: tools/testing/selftests/powerpc
+N: /pmac
+N: powermac
+N: powernv
+N: [^a-z0-9]ps3
+N: pseries
+
+LINUX FOR POWER MACINTOSH
+M: Benjamin Herrenschmidt <benh@kernel.crashing.org>
+W: http://www.penguinppc.org/
+L: linuxppc-dev@lists.ozlabs.org
+S: Maintained
+F: arch/powerpc/platforms/powermac/
+F: drivers/macintosh/
+
+LINUX FOR POWERPC EMBEDDED MPC5XXX
+M: Anatolij Gustschin <agust@denx.de>
+L: linuxppc-dev@lists.ozlabs.org
+T: git git://git.denx.de/linux-denx-agust.git
+S: Maintained
+F: arch/powerpc/platforms/512x/
+F: arch/powerpc/platforms/52xx/
+
+LINUX FOR POWERPC EMBEDDED PPC4XX
+M: Alistair Popple <alistair@popple.id.au>
+M: Matt Porter <mporter@kernel.crashing.org>
+W: http://www.penguinppc.org/
+L: linuxppc-dev@lists.ozlabs.org
+S: Maintained
+F: arch/powerpc/platforms/40x/
+F: arch/powerpc/platforms/44x/
+
+LINUX FOR POWERPC EMBEDDED XILINX VIRTEX
+L: linuxppc-dev@lists.ozlabs.org
+S: Orphan
+F: arch/powerpc/*/*virtex*
+F: arch/powerpc/*/*/*virtex*
+
+LINUX FOR POWERPC EMBEDDED PPC8XX
+M: Vitaly Bordug <vitb@kernel.crashing.org>
+W: http://www.penguinppc.org/
+L: linuxppc-dev@lists.ozlabs.org
+S: Maintained
+F: arch/powerpc/platforms/8xx/
+
+LINUX FOR POWERPC EMBEDDED PPC83XX AND PPC85XX
+M: Scott Wood <oss@buserror.net>
+M: Kumar Gala <galak@kernel.crashing.org>
+W: http://www.penguinppc.org/
+L: linuxppc-dev@lists.ozlabs.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/scottwood/linux.git
+S: Maintained
+F: arch/powerpc/platforms/83xx/
+F: arch/powerpc/platforms/85xx/
+
+LINUX FOR POWERPC PA SEMI PWRFICIENT
+L: linuxppc-dev@lists.ozlabs.org
+S: Orphan
+F: arch/powerpc/platforms/pasemi/
+F: drivers/*/*pasemi*
+F: drivers/*/*/*pasemi*
+
+LINUX SECURITY MODULE (LSM) FRAMEWORK
+M: Chris Wright <chrisw@sous-sol.org>
+L: linux-security-module@vger.kernel.org
+S: Supported
+
+LIS3LV02D ACCELEROMETER DRIVER
+M: Eric Piel <eric.piel@tremplin-utc.net>
+S: Maintained
+F: Documentation/misc-devices/lis3lv02d
+F: drivers/misc/lis3lv02d/
+F: drivers/platform/x86/hp_accel.c
+
+LIVE PATCHING
+M: Josh Poimboeuf <jpoimboe@redhat.com>
+M: Jessica Yu <jeyu@kernel.org>
+M: Jiri Kosina <jikos@kernel.org>
+M: Miroslav Benes <mbenes@suse.cz>
+R: Petr Mladek <pmladek@suse.com>
+S: Maintained
+F: kernel/livepatch/
+F: include/linux/livepatch.h
+F: arch/x86/include/asm/livepatch.h
+F: arch/x86/kernel/livepatch.c
+F: Documentation/livepatch/
+F: Documentation/ABI/testing/sysfs-kernel-livepatch
+F: samples/livepatch/
+L: live-patching@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/jikos/livepatching.git
+
+LINUX KERNEL DUMP TEST MODULE (LKDTM)
+M: Kees Cook <keescook@chromium.org>
+S: Maintained
+F: drivers/misc/lkdtm*
+
+LLC (802.2)
+L: netdev@vger.kernel.org
+S: Odd fixes
+F: include/linux/llc.h
+F: include/uapi/linux/llc.h
+F: include/net/llc*
+F: net/llc/
+
+LM73 HARDWARE MONITOR DRIVER
+M: Guillaume Ligneul <guillaume.ligneul@gmail.com>
+L: linux-hwmon@vger.kernel.org
+S: Maintained
+F: drivers/hwmon/lm73.c
+
+LM78 HARDWARE MONITOR DRIVER
+M: Jean Delvare <jdelvare@suse.com>
+L: linux-hwmon@vger.kernel.org
+S: Maintained
+F: Documentation/hwmon/lm78
+F: drivers/hwmon/lm78.c
+
+LM83 HARDWARE MONITOR DRIVER
+M: Jean Delvare <jdelvare@suse.com>
+L: linux-hwmon@vger.kernel.org
+S: Maintained
+F: Documentation/hwmon/lm83
+F: drivers/hwmon/lm83.c
+
+LM90 HARDWARE MONITOR DRIVER
+M: Jean Delvare <jdelvare@suse.com>
+L: linux-hwmon@vger.kernel.org
+S: Maintained
+F: Documentation/hwmon/lm90
+F: Documentation/devicetree/bindings/hwmon/lm90.txt
+F: drivers/hwmon/lm90.c
+F: include/dt-bindings/thermal/lm90.h
+
+LM95234 HARDWARE MONITOR DRIVER
+M: Guenter Roeck <linux@roeck-us.net>
+L: linux-hwmon@vger.kernel.org
+S: Maintained
+F: Documentation/hwmon/lm95234
+F: drivers/hwmon/lm95234.c
+
+LME2510 MEDIA DRIVER
+M: Malcolm Priestley <tvboxspy@gmail.com>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+S: Maintained
+F: drivers/media/usb/dvb-usb-v2/lmedm04*
+
+LOCKING PRIMITIVES
+M: Peter Zijlstra <peterz@infradead.org>
+M: Ingo Molnar <mingo@redhat.com>
+L: linux-kernel@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git locking/core
+S: Maintained
+F: Documentation/locking/
+F: include/linux/lockdep.h
+F: include/linux/spinlock*.h
+F: arch/*/include/asm/spinlock*.h
+F: include/linux/rwlock*.h
+F: include/linux/mutex*.h
+F: arch/*/include/asm/mutex*.h
+F: include/linux/rwsem*.h
+F: arch/*/include/asm/rwsem.h
+F: include/linux/seqlock.h
+F: lib/locking*.[ch]
+F: kernel/locking/
+
+LOGICAL DISK MANAGER SUPPORT (LDM, Windows 2000/XP/Vista Dynamic Disks)
+M: "Richard Russon (FlatCap)" <ldm@flatcap.org>
+L: linux-ntfs-dev@lists.sourceforge.net
+W: http://www.linux-ntfs.org/content/view/19/37/
+S: Maintained
+F: Documentation/ldm.txt
+F: block/partitions/ldm.*
+
+LSILOGIC MPT FUSION DRIVERS (FC/SAS/SPI)
+M: Sathya Prakash <sathya.prakash@broadcom.com>
+M: Chaitra P B <chaitra.basappa@broadcom.com>
+M: Suganath Prabu Subramani <suganath-prabu.subramani@broadcom.com>
+L: MPT-FusionLinux.pdl@broadcom.com
+L: linux-scsi@vger.kernel.org
+W: http://www.avagotech.com/support/
+S: Supported
+F: drivers/message/fusion/
+F: drivers/scsi/mpt2sas/
+F: drivers/scsi/mpt3sas/
+
+LSILOGIC/SYMBIOS/NCR 53C8XX and 53C1010 PCI-SCSI drivers
+M: Matthew Wilcox <matthew@wil.cx>
+L: linux-scsi@vger.kernel.org
+S: Maintained
+F: drivers/scsi/sym53c8xx_2/
+
+LTC4261 HARDWARE MONITOR DRIVER
+M: Guenter Roeck <linux@roeck-us.net>
+L: linux-hwmon@vger.kernel.org
+S: Maintained
+F: Documentation/hwmon/ltc4261
+F: drivers/hwmon/ltc4261.c
+
+LTC4306 I2C MULTIPLEXER DRIVER
+M: Michael Hennerich <michael.hennerich@analog.com>
+W: http://ez.analog.com/community/linux-device-drivers
+L: linux-i2c@vger.kernel.org
+S: Supported
+F: drivers/i2c/muxes/i2c-mux-ltc4306.c
+F: Documentation/devicetree/bindings/i2c/i2c-mux-ltc4306.txt
+
+LTP (Linux Test Project)
+M: Mike Frysinger <vapier@gentoo.org>
+M: Cyril Hrubis <chrubis@suse.cz>
+M: Wanlong Gao <wanlong.gao@gmail.com>
+M: Jan Stancek <jstancek@redhat.com>
+M: Stanislav Kholmanskikh <stanislav.kholmanskikh@oracle.com>
+M: Alexey Kodanev <alexey.kodanev@oracle.com>
+L: ltp@lists.linux.it (subscribers-only)
+W: http://linux-test-project.github.io/
+T: git git://github.com/linux-test-project/ltp.git
+S: Maintained
+
+M32R ARCHITECTURE
+W: http://www.linux-m32r.org/
+S: Orphan
+F: arch/m32r/
+
+M68K ARCHITECTURE
+M: Geert Uytterhoeven <geert@linux-m68k.org>
+L: linux-m68k@lists.linux-m68k.org
+W: http://www.linux-m68k.org/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/geert/linux-m68k.git
+S: Maintained
+F: arch/m68k/
+F: drivers/zorro/
+
+M68K ON APPLE MACINTOSH
+M: Joshua Thompson <funaho@jurai.org>
+W: http://www.mac.linux-m68k.org/
+L: linux-m68k@lists.linux-m68k.org
+S: Maintained
+F: arch/m68k/mac/
+
+M68K ON HP9000/300
+M: Philip Blundell <philb@gnu.org>
+W: http://www.tazenda.demon.co.uk/phil/linux-hp
+S: Maintained
+F: arch/m68k/hp300/
+
+M88DS3103 MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+W: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/media/dvb-frontends/m88ds3103*
+
+M88RS2000 MEDIA DRIVER
+M: Malcolm Priestley <tvboxspy@gmail.com>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+S: Maintained
+F: drivers/media/dvb-frontends/m88rs2000*
+
+MA901 MASTERKIT USB FM RADIO DRIVER
+M: Alexey Klimov <klimov.linux@gmail.com>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+S: Maintained
+F: drivers/media/radio/radio-ma901.c
+
+MAC80211
+M: Johannes Berg <johannes@sipsolutions.net>
+L: linux-wireless@vger.kernel.org
+W: http://wireless.kernel.org/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211.git
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next.git
+S: Maintained
+F: Documentation/networking/mac80211-injection.txt
+F: include/net/mac80211.h
+F: net/mac80211/
+F: drivers/net/wireless/mac80211_hwsim.[ch]
+
+MAILBOX API
+M: Jassi Brar <jassisinghbrar@gmail.com>
+L: linux-kernel@vger.kernel.org
+S: Maintained
+F: drivers/mailbox/
+F: include/linux/mailbox_client.h
+F: include/linux/mailbox_controller.h
+
+MAN-PAGES: MANUAL PAGES FOR LINUX -- Sections 2, 3, 4, 5, and 7
+M: Michael Kerrisk <mtk.manpages@gmail.com>
+W: http://www.kernel.org/doc/man-pages
+L: linux-man@vger.kernel.org
+S: Maintained
+
+MARDUK (CREATOR CI40) DEVICE TREE SUPPORT
+M: Rahul Bedarkar <rahulbedarkar89@gmail.com>
+L: linux-mips@linux-mips.org
+S: Maintained
+F: arch/mips/boot/dts/img/pistachio_marduk.dts
+
+MARVELL 88E6XXX ETHERNET SWITCH FABRIC DRIVER
+M: Andrew Lunn <andrew@lunn.ch>
+M: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
+L: netdev@vger.kernel.org
+S: Maintained
+F: drivers/net/dsa/mv88e6xxx/
+F: Documentation/devicetree/bindings/net/dsa/marvell.txt
+
+MARVELL ARMADA DRM SUPPORT
+M: Russell King <linux@armlinux.org.uk>
+S: Maintained
+T: git git://git.armlinux.org.uk/~rmk/linux-arm.git drm-armada-devel
+T: git git://git.armlinux.org.uk/~rmk/linux-arm.git drm-armada-fixes
+F: drivers/gpu/drm/armada/
+F: include/uapi/drm/armada_drm.h
+F: Documentation/devicetree/bindings/display/armada/
+
+MARVELL CRYPTO DRIVER
+M: Boris Brezillon <boris.brezillon@free-electrons.com>
+M: Arnaud Ebalard <arno@natisbad.org>
+F: drivers/crypto/marvell/
+S: Maintained
+L: linux-crypto@vger.kernel.org
+
+MARVELL GIGABIT ETHERNET DRIVERS (skge/sky2)
+M: Mirko Lindner <mlindner@marvell.com>
+M: Stephen Hemminger <stephen@networkplumber.org>
+L: netdev@vger.kernel.org
+S: Maintained
+F: drivers/net/ethernet/marvell/sk*
+
+MARVELL LIBERTAS WIRELESS DRIVER
+L: libertas-dev@lists.infradead.org
+S: Orphan
+F: drivers/net/wireless/marvell/libertas/
+
+MARVELL MV643XX ETHERNET DRIVER
+M: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
+L: netdev@vger.kernel.org
+S: Maintained
+F: drivers/net/ethernet/marvell/mv643xx_eth.*
+F: include/linux/mv643xx.h
+
+MARVELL MVNETA ETHERNET DRIVER
+M: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
+L: netdev@vger.kernel.org
+S: Maintained
+F: drivers/net/ethernet/marvell/mvneta.*
+
+MARVELL MWIFIEX WIRELESS DRIVER
+M: Amitkumar Karwar <amitkarwar@gmail.com>
+M: Nishant Sarmukadam <nishants@marvell.com>
+M: Ganapathi Bhat <gbhat@marvell.com>
+M: Xinming Hu <huxm@marvell.com>
+L: linux-wireless@vger.kernel.org
+S: Maintained
+F: drivers/net/wireless/marvell/mwifiex/
+
+MARVELL MWL8K WIRELESS DRIVER
+M: Lennert Buytenhek <buytenh@wantstofly.org>
+L: linux-wireless@vger.kernel.org
+S: Odd Fixes
+F: drivers/net/wireless/marvell/mwl8k.c
+
+MARVELL SOC MMC/SD/SDIO CONTROLLER DRIVER
+M: Nicolas Pitre <nico@fluxnic.net>
+S: Odd Fixes
+F: drivers/mmc/host/mvsdio.*
+
+MARVELL XENON MMC/SD/SDIO HOST CONTROLLER DRIVER
+M: Hu Ziji <huziji@marvell.com>
+L: linux-mmc@vger.kernel.org
+S: Supported
+F: drivers/mmc/host/sdhci-xenon*
+F: Documentation/devicetree/bindings/mmc/marvell,xenon-sdhci.txt
+
+MATROX FRAMEBUFFER DRIVER
+L: linux-fbdev@vger.kernel.org
+S: Orphan
+F: drivers/video/fbdev/matrox/matroxfb_*
+F: include/uapi/linux/matroxfb.h
+
+MAX16065 HARDWARE MONITOR DRIVER
+M: Guenter Roeck <linux@roeck-us.net>
+L: linux-hwmon@vger.kernel.org
+S: Maintained
+F: Documentation/hwmon/max16065
+F: drivers/hwmon/max16065.c
+
+MAX20751 HARDWARE MONITOR DRIVER
+M: Guenter Roeck <linux@roeck-us.net>
+L: linux-hwmon@vger.kernel.org
+S: Maintained
+F: Documentation/hwmon/max20751
+F: drivers/hwmon/max20751.c
+
+MAX6650 HARDWARE MONITOR AND FAN CONTROLLER DRIVER
+L: linux-hwmon@vger.kernel.org
+S: Orphan
+F: Documentation/hwmon/max6650
+F: drivers/hwmon/max6650.c
+
+MAX6697 HARDWARE MONITOR DRIVER
+M: Guenter Roeck <linux@roeck-us.net>
+L: linux-hwmon@vger.kernel.org
+S: Maintained
+F: Documentation/hwmon/max6697
+F: Documentation/devicetree/bindings/i2c/max6697.txt
+F: drivers/hwmon/max6697.c
+F: include/linux/platform_data/max6697.h
+
+MAX9860 MONO AUDIO VOICE CODEC DRIVER
+M: Peter Rosin <peda@axentia.se>
+L: alsa-devel@alsa-project.org (moderated for non-subscribers)
+S: Maintained
+F: Documentation/devicetree/bindings/sound/max9860.txt
+F: sound/soc/codecs/max9860.*
+
+MAXIM MUIC CHARGER DRIVERS FOR EXYNOS BASED BOARDS
+M: Krzysztof Kozlowski <krzk@kernel.org>
+M: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
+L: linux-pm@vger.kernel.org
+S: Supported
+F: drivers/power/supply/max14577_charger.c
+F: drivers/power/supply/max77693_charger.c
+
+MAXIM MAX77802 MULTIFUNCTION PMIC DEVICE DRIVERS
+M: Javier Martinez Canillas <javier@osg.samsung.com>
+L: linux-kernel@vger.kernel.org
+S: Supported
+F: drivers/*/*max77802*.c
+F: Documentation/devicetree/bindings/*/*max77802.txt
+F: include/dt-bindings/*/*max77802.h
+
+MAXIM PMIC AND MUIC DRIVERS FOR EXYNOS BASED BOARDS
+M: Chanwoo Choi <cw00.choi@samsung.com>
+M: Krzysztof Kozlowski <krzk@kernel.org>
+M: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
+L: linux-kernel@vger.kernel.org
+S: Supported
+F: drivers/*/max14577*.c
+F: drivers/*/max77686*.c
+F: drivers/*/max77693*.c
+F: drivers/extcon/extcon-max14577.c
+F: drivers/extcon/extcon-max77693.c
+F: drivers/rtc/rtc-max77686.c
+F: drivers/clk/clk-max77686.c
+F: Documentation/devicetree/bindings/mfd/max14577.txt
+F: Documentation/devicetree/bindings/*/max77686.txt
+F: Documentation/devicetree/bindings/mfd/max77693.txt
+F: Documentation/devicetree/bindings/clock/maxim,max77686.txt
+F: include/linux/mfd/max14577*.h
+F: include/linux/mfd/max77686*.h
+F: include/linux/mfd/max77693*.h
+
+MAXIRADIO FM RADIO RECEIVER DRIVER
+M: Hans Verkuil <hverkuil@xs4all.nl>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+W: https://linuxtv.org
+S: Maintained
+F: drivers/media/radio/radio-maxiradio*
+
+MCP4531 MICROCHIP DIGITAL POTENTIOMETER DRIVER
+M: Peter Rosin <peda@axentia.se>
+L: linux-iio@vger.kernel.org
+S: Maintained
+F: Documentation/ABI/testing/sysfs-bus-iio-potentiometer-mcp4531
+F: drivers/iio/potentiometer/mcp4531.c
+
+MEASUREMENT COMPUTING CIO-DAC IIO DRIVER
+M: William Breathitt Gray <vilhelm.gray@gmail.com>
+L: linux-iio@vger.kernel.org
+S: Maintained
+F: drivers/iio/dac/cio-dac.c
+
+MEDIA DRIVERS FOR RENESAS - FCP
+M: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+L: linux-media@vger.kernel.org
+L: linux-renesas-soc@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+S: Supported
+F: Documentation/devicetree/bindings/media/renesas,fcp.txt
+F: drivers/media/platform/rcar-fcp.c
+F: include/media/rcar-fcp.h
+
+MEDIA DRIVERS FOR RENESAS - FDP1
+M: Kieran Bingham <kieran@bingham.xyz>
+L: linux-media@vger.kernel.org
+L: linux-renesas-soc@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+S: Supported
+F: Documentation/devicetree/bindings/media/renesas,fdp1.txt
+F: drivers/media/platform/rcar_fdp1.c
+
+MEDIA DRIVERS FOR RENESAS - VIN
+M: Niklas Söderlund <niklas.soderlund@ragnatech.se>
+L: linux-media@vger.kernel.org
+L: linux-renesas-soc@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+S: Supported
+F: Documentation/devicetree/bindings/media/rcar_vin.txt
+F: drivers/media/platform/rcar-vin/
+
+MEDIA DRIVERS FOR RENESAS - VSP1
+M: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+L: linux-media@vger.kernel.org
+L: linux-renesas-soc@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+S: Supported
+F: Documentation/devicetree/bindings/media/renesas,vsp1.txt
+F: drivers/media/platform/vsp1/
+
+MEDIA DRIVERS FOR HELENE
+M: Abylay Ospan <aospan@netup.ru>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+W: http://netup.tv/
+T: git git://linuxtv.org/media_tree.git
+S: Supported
+F: drivers/media/dvb-frontends/helene*
+
+MEDIA DRIVERS FOR ASCOT2E
+M: Sergey Kozlov <serjk@netup.ru>
+M: Abylay Ospan <aospan@netup.ru>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+W: http://netup.tv/
+T: git git://linuxtv.org/media_tree.git
+S: Supported
+F: drivers/media/dvb-frontends/ascot2e*
+
+MEDIA DRIVERS FOR CXD2841ER
+M: Sergey Kozlov <serjk@netup.ru>
+M: Abylay Ospan <aospan@netup.ru>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+W: http://netup.tv/
+T: git git://linuxtv.org/media_tree.git
+S: Supported
+F: drivers/media/dvb-frontends/cxd2841er*
+
+MEDIA DRIVERS FOR HORUS3A
+M: Sergey Kozlov <serjk@netup.ru>
+M: Abylay Ospan <aospan@netup.ru>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+W: http://netup.tv/
+T: git git://linuxtv.org/media_tree.git
+S: Supported
+F: drivers/media/dvb-frontends/horus3a*
+
+MEDIA DRIVERS FOR LNBH25
+M: Sergey Kozlov <serjk@netup.ru>
+M: Abylay Ospan <aospan@netup.ru>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+W: http://netup.tv/
+T: git git://linuxtv.org/media_tree.git
+S: Supported
+F: drivers/media/dvb-frontends/lnbh25*
+
+MEDIA DRIVERS FOR NETUP PCI UNIVERSAL DVB devices
+M: Sergey Kozlov <serjk@netup.ru>
+M: Abylay Ospan <aospan@netup.ru>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+W: http://netup.tv/
+T: git git://linuxtv.org/media_tree.git
+S: Supported
+F: drivers/media/pci/netup_unidvb/*
+
+MEDIA INPUT INFRASTRUCTURE (V4L/DVB)
+M: Mauro Carvalho Chehab <mchehab@s-opensource.com>
+M: Mauro Carvalho Chehab <mchehab@kernel.org>
+P: LinuxTV.org Project
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+Q: http://patchwork.kernel.org/project/linux-media/list/
+T: git git://linuxtv.org/media_tree.git
+S: Maintained
+F: Documentation/devicetree/bindings/media/
+F: Documentation/media/
+F: drivers/media/
+F: drivers/staging/media/
+F: include/linux/platform_data/media/
+F: include/media/
+F: include/uapi/linux/dvb/
+F: include/uapi/linux/videodev2.h
+F: include/uapi/linux/media.h
+F: include/uapi/linux/v4l2-*
+F: include/uapi/linux/meye.h
+F: include/uapi/linux/ivtv*
+F: include/uapi/linux/uvcvideo.h
+
+MEDIATEK ETHERNET DRIVER
+M: Felix Fietkau <nbd@openwrt.org>
+M: John Crispin <blogic@openwrt.org>
+L: netdev@vger.kernel.org
+S: Maintained
+F: drivers/net/ethernet/mediatek/
+
+MEDIATEK JPEG DRIVER
+M: Rick Chang <rick.chang@mediatek.com>
+M: Bin Liu <bin.liu@mediatek.com>
+S: Supported
+F: drivers/media/platform/mtk-jpeg/
+F: Documentation/devicetree/bindings/media/mediatek-jpeg-decoder.txt
+
+MEDIATEK MEDIA DRIVER
+M: Tiffany Lin <tiffany.lin@mediatek.com>
+M: Andrew-CT Chen <andrew-ct.chen@mediatek.com>
+S: Supported
+F: drivers/media/platform/mtk-vcodec/
+F: drivers/media/platform/mtk-vpu/
+F: Documentation/devicetree/bindings/media/mediatek-vcodec.txt
+F: Documentation/devicetree/bindings/media/mediatek-vpu.txt
+
+MEDIATEK MDP DRIVER
+M: Minghsiu Tsai <minghsiu.tsai@mediatek.com>
+M: Houlong Wei <houlong.wei@mediatek.com>
+M: Andrew-CT Chen <andrew-ct.chen@mediatek.com>
+S: Supported
+F: drivers/media/platform/mtk-mdp/
+F: drivers/media/platform/mtk-vpu/
+F: Documentation/devicetree/bindings/media/mediatek-mdp.txt
+
+MEDIATEK MT7601U WIRELESS LAN DRIVER
+M: Jakub Kicinski <kubakici@wp.pl>
+L: linux-wireless@vger.kernel.org
+S: Maintained
+F: drivers/net/wireless/mediatek/mt7601u/
+
+MEGACHIPS STDPXXXX-GE-B850V3-FW LVDS/DP++ BRIDGES
+M: Peter Senna Tschudin <peter.senna@collabora.com>
+M: Martin Donnelly <martin.donnelly@ge.com>
+M: Martyn Welch <martyn.welch@collabora.co.uk>
+S: Maintained
+F: drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c
+F: Documentation/devicetree/bindings/video/bridge/megachips-stdpxxxx-ge-b850v3-fw.txt
+
+MEGARAID SCSI/SAS DRIVERS
+M: Kashyap Desai <kashyap.desai@broadcom.com>
+M: Sumit Saxena <sumit.saxena@broadcom.com>
+M: Shivasharan S <shivasharan.srikanteshwara@broadcom.com>
+L: megaraidlinux.pdl@broadcom.com
+L: linux-scsi@vger.kernel.org
+W: http://www.avagotech.com/support/
+S: Maintained
+F: Documentation/scsi/megaraid.txt
+F: drivers/scsi/megaraid.*
+F: drivers/scsi/megaraid/
+
+MELFAS MIP4 TOUCHSCREEN DRIVER
+M: Sangwon Jee <jeesw@melfas.com>
+W: http://www.melfas.com
+S: Supported
+F: drivers/input/touchscreen/melfas_mip4.c
+F: Documentation/devicetree/bindings/input/touchscreen/melfas_mip4.txt
+
+MELLANOX ETHERNET DRIVER (mlx4_en)
+M: Tariq Toukan <tariqt@mellanox.com>
+L: netdev@vger.kernel.org
+S: Supported
+W: http://www.mellanox.com
+Q: http://patchwork.ozlabs.org/project/netdev/list/
+F: drivers/net/ethernet/mellanox/mlx4/en_*
+
+MELLANOX ETHERNET DRIVER (mlx5e)
+M: Saeed Mahameed <saeedm@mellanox.com>
+L: netdev@vger.kernel.org
+S: Supported
+W: http://www.mellanox.com
+Q: http://patchwork.ozlabs.org/project/netdev/list/
+F: drivers/net/ethernet/mellanox/mlx5/core/en_*
+
+MELLANOX ETHERNET SWITCH DRIVERS
+M: Jiri Pirko <jiri@mellanox.com>
+M: Ido Schimmel <idosch@mellanox.com>
+L: netdev@vger.kernel.org
+S: Supported
+W: http://www.mellanox.com
+Q: http://patchwork.ozlabs.org/project/netdev/list/
+F: drivers/net/ethernet/mellanox/mlxsw/
+
+MELLANOX MLXCPLD I2C AND MUX DRIVER
+M: Vadim Pasternak <vadimp@mellanox.com>
+M: Michael Shych <michaelsh@mellanox.com>
+L: linux-i2c@vger.kernel.org
+S: Supported
+F: drivers/i2c/busses/i2c-mlxcpld.c
+F: drivers/i2c/muxes/i2c-mux-mlxcpld.c
+F: Documentation/i2c/busses/i2c-mlxcpld
+
+MELLANOX MLXCPLD LED DRIVER
+M: Vadim Pasternak <vadimp@mellanox.com>
+L: linux-leds@vger.kernel.org
+S: Supported
+F: drivers/leds/leds-mlxcpld.c
+F: Documentation/leds/leds-mlxcpld.txt
+
+MELLANOX PLATFORM DRIVER
+M: Vadim Pasternak <vadimp@mellanox.com>
+L: platform-driver-x86@vger.kernel.org
+S: Supported
+F: drivers/platform/x86/mlx-platform.c
+
+MELLANOX MLX CPLD HOTPLUG DRIVER
+M: Vadim Pasternak <vadimp@mellanox.com>
+L: platform-driver-x86@vger.kernel.org
+S: Supported
+F: drivers/platform/x86/mlxcpld-hotplug.c
+F: include/linux/platform_data/mlxcpld-hotplug.h
+
+SOFT-ROCE DRIVER (rxe)
+M: Moni Shoua <monis@mellanox.com>
+L: linux-rdma@vger.kernel.org
+S: Supported
+W: https://github.com/SoftRoCE/rxe-dev/wiki/rxe-dev:-Home
+Q: http://patchwork.kernel.org/project/linux-rdma/list/
+F: drivers/infiniband/sw/rxe/
+F: include/uapi/rdma/rdma_user_rxe.h
+
+MEMBARRIER SUPPORT
+M: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+M: "Paul E. McKenney" <paulmck@linux.vnet.ibm.com>
+L: linux-kernel@vger.kernel.org
+S: Supported
+F: kernel/membarrier.c
+F: include/uapi/linux/membarrier.h
+
+MEMORY MANAGEMENT
+L: linux-mm@kvack.org
+W: http://www.linux-mm.org
+S: Maintained
+F: include/linux/mm.h
+F: include/linux/gfp.h
+F: include/linux/mmzone.h
+F: include/linux/memory_hotplug.h
+F: include/linux/vmalloc.h
+F: mm/
+
+MEMORY TECHNOLOGY DEVICES (MTD)
+M: David Woodhouse <dwmw2@infradead.org>
+M: Brian Norris <computersforpeace@gmail.com>
+M: Boris Brezillon <boris.brezillon@free-electrons.com>
+M: Marek Vasut <marek.vasut@gmail.com>
+M: Richard Weinberger <richard@nod.at>
+M: Cyrille Pitchen <cyrille.pitchen@wedev4u.fr>
+L: linux-mtd@lists.infradead.org
+W: http://www.linux-mtd.infradead.org/
+Q: http://patchwork.ozlabs.org/project/linux-mtd/list/
+T: git git://git.infradead.org/linux-mtd.git master
+T: git git://git.infradead.org/l2-mtd.git master
+S: Maintained
+F: Documentation/devicetree/bindings/mtd/
+F: drivers/mtd/
+F: include/linux/mtd/
+F: include/uapi/mtd/
+
+MEN A21 WATCHDOG DRIVER
+M: Johannes Thumshirn <morbidrsa@gmail.com>
+L: linux-watchdog@vger.kernel.org
+S: Maintained
+F: drivers/watchdog/mena21_wdt.c
+
+MEN CHAMELEON BUS (mcb)
+M: Johannes Thumshirn <morbidrsa@gmail.com>
+S: Maintained
+F: drivers/mcb/
+F: include/linux/mcb.h
+F: Documentation/men-chameleon-bus.txt
+
+MEN F21BMC (Board Management Controller)
+M: Andreas Werner <andreas.werner@men.de>
+S: Supported
+F: drivers/mfd/menf21bmc.c
+F: drivers/watchdog/menf21bmc_wdt.c
+F: drivers/leds/leds-menf21bmc.c
+F: drivers/hwmon/menf21bmc_hwmon.c
+F: Documentation/hwmon/menf21bmc
+
+METAG ARCHITECTURE
+M: James Hogan <james.hogan@imgtec.com>
+L: linux-metag@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/jhogan/metag.git
+S: Odd Fixes
+F: arch/metag/
+F: Documentation/metag/
+F: Documentation/devicetree/bindings/metag/
+F: Documentation/devicetree/bindings/interrupt-controller/img,*
+F: drivers/clocksource/metag_generic.c
+F: drivers/irqchip/irq-metag.c
+F: drivers/irqchip/irq-metag-ext.c
+F: drivers/tty/metag_da.c
+
+MICROBLAZE ARCHITECTURE
+M: Michal Simek <monstr@monstr.eu>
+W: http://www.monstr.eu/fdt/
+T: git git://git.monstr.eu/linux-2.6-microblaze.git
+S: Supported
+F: arch/microblaze/
+
+MICROCHIP / ATMEL AT91 / AT32 SERIAL DRIVER
+M: Richard Genoud <richard.genoud@gmail.com>
+S: Maintained
+F: drivers/tty/serial/atmel_serial.c
+F: drivers/tty/serial/atmel_serial.h
+
+MICROCHIP / ATMEL DMA DRIVER
+M: Ludovic Desroches <ludovic.desroches@microchip.com>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+L: dmaengine@vger.kernel.org
+S: Supported
+F: drivers/dma/at_hdmac.c
+F: drivers/dma/at_hdmac_regs.h
+F: include/linux/platform_data/dma-atmel.h
+
+MICROCHIP / ATMEL ISC DRIVER
+M: Songjun Wu <songjun.wu@microchip.com>
+L: linux-media@vger.kernel.org
+S: Supported
+F: drivers/media/platform/atmel/atmel-isc.c
+F: drivers/media/platform/atmel/atmel-isc-regs.h
+F: devicetree/bindings/media/atmel-isc.txt
+
+MICROCHIP USB251XB DRIVER
+M: Richard Leitner <richard.leitner@skidata.com>
+L: linux-usb@vger.kernel.org
+S: Maintained
+F: drivers/usb/misc/usb251xb.c
+F: Documentation/devicetree/bindings/usb/usb251xb.txt
+
+MICROSOFT SURFACE PRO 3 BUTTON DRIVER
+M: Chen Yu <yu.c.chen@intel.com>
+L: platform-driver-x86@vger.kernel.org
+S: Supported
+F: drivers/platform/x86/surfacepro3_button.c
+
+MICROTEK X6 SCANNER
+M: Oliver Neukum <oliver@neukum.org>
+S: Maintained
+F: drivers/usb/image/microtek.*
+
+MIPS
+M: Ralf Baechle <ralf@linux-mips.org>
+L: linux-mips@linux-mips.org
+W: http://www.linux-mips.org/
+T: git git://git.linux-mips.org/pub/scm/ralf/linux.git
+Q: http://patchwork.linux-mips.org/project/linux-mips/list/
+S: Supported
+F: Documentation/devicetree/bindings/mips/
+F: Documentation/mips/
+F: arch/mips/
+
+MIPS/LOONGSON1 ARCHITECTURE
+M: Keguang Zhang <keguang.zhang@gmail.com>
+L: linux-mips@linux-mips.org
+S: Maintained
+F: arch/mips/loongson32/
+F: arch/mips/include/asm/mach-loongson32/
+F: drivers/*/*loongson1*
+F: drivers/*/*/*loongson1*
+
+MIROSOUND PCM20 FM RADIO RECEIVER DRIVER
+M: Hans Verkuil <hverkuil@xs4all.nl>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+W: https://linuxtv.org
+S: Odd Fixes
+F: drivers/media/radio/radio-miropcm20*
+
+MELLANOX MLX4 core VPI driver
+M: Tariq Toukan <tariqt@mellanox.com>
+L: netdev@vger.kernel.org
+L: linux-rdma@vger.kernel.org
+W: http://www.mellanox.com
+Q: http://patchwork.ozlabs.org/project/netdev/list/
+S: Supported
+F: drivers/net/ethernet/mellanox/mlx4/
+F: include/linux/mlx4/
+
+MELLANOX MLX4 IB driver
+M: Yishai Hadas <yishaih@mellanox.com>
+L: linux-rdma@vger.kernel.org
+W: http://www.mellanox.com
+Q: http://patchwork.kernel.org/project/linux-rdma/list/
+S: Supported
+F: drivers/infiniband/hw/mlx4/
+F: include/linux/mlx4/
+F: include/uapi/rdma/mlx4-abi.h
+
+MELLANOX MLX5 core VPI driver
+M: Saeed Mahameed <saeedm@mellanox.com>
+M: Matan Barak <matanb@mellanox.com>
+M: Leon Romanovsky <leonro@mellanox.com>
+L: netdev@vger.kernel.org
+L: linux-rdma@vger.kernel.org
+W: http://www.mellanox.com
+Q: http://patchwork.ozlabs.org/project/netdev/list/
+S: Supported
+F: drivers/net/ethernet/mellanox/mlx5/core/
+F: include/linux/mlx5/
+
+MELLANOX MLX5 IB driver
+M: Matan Barak <matanb@mellanox.com>
+M: Leon Romanovsky <leonro@mellanox.com>
+L: linux-rdma@vger.kernel.org
+W: http://www.mellanox.com
+Q: http://patchwork.kernel.org/project/linux-rdma/list/
+S: Supported
+F: drivers/infiniband/hw/mlx5/
+F: include/linux/mlx5/
+F: include/uapi/rdma/mlx5-abi.h
+
+MELEXIS MLX90614 DRIVER
+M: Crt Mori <cmo@melexis.com>
+L: linux-iio@vger.kernel.org
+W: http://www.melexis.com
+S: Supported
+F: drivers/iio/temperature/mlx90614.c
+
+MICROSEMI SMART ARRAY SMARTPQI DRIVER (smartpqi)
+M: Don Brace <don.brace@microsemi.com>
+L: esc.storagedev@microsemi.com
+L: linux-scsi@vger.kernel.org
+S: Supported
+F: drivers/scsi/smartpqi/smartpqi*.[ch]
+F: drivers/scsi/smartpqi/Kconfig
+F: drivers/scsi/smartpqi/Makefile
+F: include/linux/cciss*.h
+F: include/uapi/linux/cciss*.h
+F: Documentation/scsi/smartpqi.txt
+
+MN88472 MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+W: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+S: Maintained
+F: drivers/media/dvb-frontends/mn88472*
+
+MN88473 MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+W: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+S: Maintained
+F: drivers/media/dvb-frontends/mn88473*
+
+MODULE SUPPORT
+M: Jessica Yu <jeyu@kernel.org>
+M: Rusty Russell <rusty@rustcorp.com.au>
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/jeyu/linux.git modules-next
+S: Maintained
+F: include/linux/module.h
+F: kernel/module.c
+
+MOTION EYE VAIO PICTUREBOOK CAMERA DRIVER
+W: http://popies.net/meye/
+S: Orphan
+F: Documentation/media/v4l-drivers/meye*
+F: drivers/media/pci/meye/
+F: include/uapi/linux/meye.h
+
+MOXA SMARTIO/INDUSTIO/INTELLIO SERIAL CARD
+M: Jiri Slaby <jirislaby@gmail.com>
+S: Maintained
+F: Documentation/serial/moxa-smartio
+F: drivers/tty/mxser.*
+
+MR800 AVERMEDIA USB FM RADIO DRIVER
+M: Alexey Klimov <klimov.linux@gmail.com>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+S: Maintained
+F: drivers/media/radio/radio-mr800.c
+
+MRF24J40 IEEE 802.15.4 RADIO DRIVER
+M: Alan Ott <alan@signal11.us>
+L: linux-wpan@vger.kernel.org
+S: Maintained
+F: drivers/net/ieee802154/mrf24j40.c
+F: Documentation/devicetree/bindings/net/ieee802154/mrf24j40.txt
+
+MSI LAPTOP SUPPORT
+M: "Lee, Chun-Yi" <jlee@suse.com>
+L: platform-driver-x86@vger.kernel.org
+S: Maintained
+F: drivers/platform/x86/msi-laptop.c
+
+MSI WMI SUPPORT
+L: platform-driver-x86@vger.kernel.org
+S: Orphan
+F: drivers/platform/x86/msi-wmi.c
+
+MSI001 MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+W: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/media/tuners/msi001*
+
+MSI2500 MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+W: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/media/usb/msi2500/
+
+MSYSTEMS DISKONCHIP G3 MTD DRIVER
+M: Robert Jarzmik <robert.jarzmik@free.fr>
+L: linux-mtd@lists.infradead.org
+S: Maintained
+F: drivers/mtd/devices/docg3*
+
+MT9M032 APTINA SENSOR DRIVER
+M: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+S: Maintained
+F: drivers/media/i2c/mt9m032.c
+F: include/media/i2c/mt9m032.h
+
+MT9P031 APTINA CAMERA SENSOR
+M: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+S: Maintained
+F: drivers/media/i2c/mt9p031.c
+F: include/media/i2c/mt9p031.h
+
+MT9T001 APTINA CAMERA SENSOR
+M: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+S: Maintained
+F: drivers/media/i2c/mt9t001.c
+F: include/media/i2c/mt9t001.h
+
+MT9V032 APTINA CAMERA SENSOR
+M: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+S: Maintained
+F: Documentation/devicetree/bindings/media/i2c/mt9v032.txt
+F: drivers/media/i2c/mt9v032.c
+F: include/media/i2c/mt9v032.h
+
+MULTIFUNCTION DEVICES (MFD)
+M: Lee Jones <lee.jones@linaro.org>
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd.git
+S: Supported
+F: Documentation/devicetree/bindings/mfd/
+F: drivers/mfd/
+F: include/linux/mfd/
+F: include/dt-bindings/mfd/
+
+MULTIMEDIA CARD (MMC), SECURE DIGITAL (SD) AND SDIO SUBSYSTEM
+M: Ulf Hansson <ulf.hansson@linaro.org>
+L: linux-mmc@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc.git
+S: Maintained
+F: Documentation/devicetree/bindings/mmc/
+F: drivers/mmc/
+F: include/linux/mmc/
+F: include/uapi/linux/mmc/
+
+MULTIMEDIA CARD (MMC) ETC. OVER SPI
+S: Orphan
+F: drivers/mmc/host/mmc_spi.c
+F: include/linux/spi/mmc_spi.h
+
+MULTISOUND SOUND DRIVER
+M: Andrew Veliath <andrewtv@usa.net>
+S: Maintained
+F: Documentation/sound/oss/MultiSound
+F: sound/oss/msnd*
+
+MULTITECH MULTIPORT CARD (ISICOM)
+S: Orphan
+F: drivers/tty/isicom.c
+F: include/linux/isicom.h
+
+MUSB MULTIPOINT HIGH SPEED DUAL-ROLE CONTROLLER
+M: Bin Liu <b-liu@ti.com>
+L: linux-usb@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb.git
+S: Maintained
+F: drivers/usb/musb/
+
+MXL5007T MEDIA DRIVER
+M: Michael Krufky <mkrufky@linuxtv.org>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+W: http://github.com/mkrufky
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/mkrufky/tuners.git
+S: Maintained
+F: drivers/media/tuners/mxl5007t.*
+
+MXSFB DRM DRIVER
+M: Marek Vasut <marex@denx.de>
+S: Supported
+F: drivers/gpu/drm/mxsfb/
+F: Documentation/devicetree/bindings/display/mxsfb-drm.txt
+
+MYRICOM MYRI-10G 10GbE DRIVER (MYRI10GE)
+M: Hyong-Youb Kim <hykim@myri.com>
+L: netdev@vger.kernel.org
+W: https://www.myricom.com/support/downloads/myri10ge.html
+S: Supported
+F: drivers/net/ethernet/myricom/myri10ge/
+
+NAND FLASH SUBSYSTEM
+M: Boris Brezillon <boris.brezillon@free-electrons.com>
+R: Richard Weinberger <richard@nod.at>
+L: linux-mtd@lists.infradead.org
+W: http://www.linux-mtd.infradead.org/
+Q: http://patchwork.ozlabs.org/project/linux-mtd/list/
+T: git git://git.infradead.org/linux-mtd.git nand/fixes
+T: git git://git.infradead.org/l2-mtd.git nand/next
+S: Maintained
+F: drivers/mtd/nand/
+F: include/linux/mtd/nand*.h
+
+NATSEMI ETHERNET DRIVER (DP8381x)
+S: Orphan
+F: drivers/net/ethernet/natsemi/natsemi.c
+
+NATIVE INSTRUMENTS USB SOUND INTERFACE DRIVER
+M: Daniel Mack <zonque@gmail.com>
+S: Maintained
+L: alsa-devel@alsa-project.org (moderated for non-subscribers)
+W: http://www.native-instruments.com
+F: sound/usb/caiaq/
+
+NCP FILESYSTEM
+M: Petr Vandrovec <petr@vandrovec.name>
+S: Odd Fixes
+F: fs/ncpfs/
+
+NCR 5380 SCSI DRIVERS
+M: Finn Thain <fthain@telegraphics.com.au>
+M: Michael Schmitz <schmitzmic@gmail.com>
+L: linux-scsi@vger.kernel.org
+S: Maintained
+F: Documentation/scsi/g_NCR5380.txt
+F: drivers/scsi/NCR5380.*
+F: drivers/scsi/arm/cumana_1.c
+F: drivers/scsi/arm/oak.c
+F: drivers/scsi/atari_scsi.*
+F: drivers/scsi/dmx3191d.c
+F: drivers/scsi/g_NCR5380.*
+F: drivers/scsi/mac_scsi.*
+F: drivers/scsi/sun3_scsi.*
+F: drivers/scsi/sun3_scsi_vme.c
+
+NCR DUAL 700 SCSI DRIVER (MICROCHANNEL)
+M: "James E.J. Bottomley" <James.Bottomley@HansenPartnership.com>
+L: linux-scsi@vger.kernel.org
+S: Maintained
+F: drivers/scsi/NCR_D700.*
+
+NCT6775 HARDWARE MONITOR DRIVER
+M: Guenter Roeck <linux@roeck-us.net>
+L: linux-hwmon@vger.kernel.org
+S: Maintained
+F: Documentation/hwmon/nct6775
+F: drivers/hwmon/nct6775.c
+
+NETEFFECT IWARP RNIC DRIVER (IW_NES)
+M: Faisal Latif <faisal.latif@intel.com>
+L: linux-rdma@vger.kernel.org
+W: http://www.intel.com/Products/Server/Adapters/Server-Cluster/Server-Cluster-overview.htm
+S: Supported
+F: drivers/infiniband/hw/nes/
+F: include/uapi/rdma/nes-abi.h
+
+NETEM NETWORK EMULATOR
+M: Stephen Hemminger <stephen@networkplumber.org>
+L: netem@lists.linux-foundation.org (moderated for non-subscribers)
+S: Maintained
+F: net/sched/sch_netem.c
+
+NETERION 10GbE DRIVERS (s2io/vxge)
+M: Jon Mason <jdmason@kudzu.us>
+L: netdev@vger.kernel.org
+S: Supported
+F: Documentation/networking/s2io.txt
+F: Documentation/networking/vxge.txt
+F: drivers/net/ethernet/neterion/
+
+NETFILTER
+M: Pablo Neira Ayuso <pablo@netfilter.org>
+M: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+M: Florian Westphal <fw@strlen.de>
+L: netfilter-devel@vger.kernel.org
+L: coreteam@netfilter.org
+W: http://www.netfilter.org/
+W: http://www.iptables.org/
+W: http://www.nftables.org/
+Q: http://patchwork.ozlabs.org/project/netfilter-devel/list/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf.git
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf-next.git
+S: Maintained
+F: include/linux/netfilter*
+F: include/linux/netfilter/
+F: include/net/netfilter/
+F: include/uapi/linux/netfilter*
+F: include/uapi/linux/netfilter/
+F: net/*/netfilter.c
+F: net/*/netfilter/
+F: net/netfilter/
+F: net/bridge/br_netfilter*.c
+
+NETLABEL
+M: Paul Moore <paul@paul-moore.com>
+W: http://netlabel.sf.net
+L: netdev@vger.kernel.org
+S: Maintained
+F: Documentation/netlabel/
+F: include/net/netlabel.h
+F: net/netlabel/
+
+NETROM NETWORK LAYER
+M: Ralf Baechle <ralf@linux-mips.org>
+L: linux-hams@vger.kernel.org
+W: http://www.linux-ax25.org/
+S: Maintained
+F: include/net/netrom.h
+F: include/uapi/linux/netrom.h
+F: net/netrom/
+
+NETRONOME ETHERNET DRIVERS
+M: Jakub Kicinski <jakub.kicinski@netronome.com>
+L: oss-drivers@netronome.com
+S: Maintained
+F: drivers/net/ethernet/netronome/
+
+NETWORK BLOCK DEVICE (NBD)
+M: Josef Bacik <jbacik@fb.com>
+S: Maintained
+L: linux-block@vger.kernel.org
+L: nbd-general@lists.sourceforge.net
+F: Documentation/blockdev/nbd.txt
+F: drivers/block/nbd.c
+F: include/uapi/linux/nbd.h
+
+NETWORK DROP MONITOR
+M: Neil Horman <nhorman@tuxdriver.com>
+L: netdev@vger.kernel.org
+S: Maintained
+W: https://fedorahosted.org/dropwatch/
+F: net/core/drop_monitor.c
+
+NETWORKING [DSA]
+M: Andrew Lunn <andrew@lunn.ch>
+M: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
+M: Florian Fainelli <f.fainelli@gmail.com>
+S: Maintained
+F: net/dsa/
+F: include/net/dsa.h
+F: drivers/net/dsa/
+
+NETWORKING [GENERAL]
+M: "David S. Miller" <davem@davemloft.net>
+L: netdev@vger.kernel.org
+W: http://www.linuxfoundation.org/en/Net
+Q: http://patchwork.ozlabs.org/project/netdev/list/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/davem/net.git
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next.git
+B: mailto:netdev@vger.kernel.org
+S: Maintained
+F: net/
+F: include/net/
+F: include/linux/in.h
+F: include/linux/net.h
+F: include/linux/netdevice.h
+F: include/uapi/linux/in.h
+F: include/uapi/linux/net.h
+F: include/uapi/linux/netdevice.h
+F: include/uapi/linux/net_namespace.h
+F: tools/net/
+F: tools/testing/selftests/net/
+F: lib/random32.c
+
+NETWORKING [IPv4/IPv6]
+M: "David S. Miller" <davem@davemloft.net>
+M: Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
+M: James Morris <jmorris@namei.org>
+M: Hideaki YOSHIFUJI <yoshfuji@linux-ipv6.org>
+M: Patrick McHardy <kaber@trash.net>
+L: netdev@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/davem/net.git
+S: Maintained
+F: net/ipv4/
+F: net/ipv6/
+F: include/net/ip*
+F: arch/x86/net/*
+
+NETWORKING [IPSEC]
+M: Steffen Klassert <steffen.klassert@secunet.com>
+M: Herbert Xu <herbert@gondor.apana.org.au>
+M: "David S. Miller" <davem@davemloft.net>
+L: netdev@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/klassert/ipsec.git
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/klassert/ipsec-next.git
+S: Maintained
+F: net/core/flow.c
+F: net/xfrm/
+F: net/key/
+F: net/ipv4/xfrm*
+F: net/ipv4/esp4*
+F: net/ipv4/ah4.c
+F: net/ipv4/ipcomp.c
+F: net/ipv4/ip_vti.c
+F: net/ipv6/xfrm*
+F: net/ipv6/esp6*
+F: net/ipv6/ah6.c
+F: net/ipv6/ipcomp6.c
+F: net/ipv6/ip6_vti.c
+F: include/uapi/linux/xfrm.h
+F: include/net/xfrm.h
+
+NETWORKING [LABELED] (NetLabel, CIPSO, Labeled IPsec, SECMARK)
+M: Paul Moore <paul@paul-moore.com>
+L: netdev@vger.kernel.org
+S: Maintained
+
+NETWORKING [WIRELESS]
+L: linux-wireless@vger.kernel.org
+Q: http://patchwork.kernel.org/project/linux-wireless/list/
+
+NETWORKING DRIVERS
+L: netdev@vger.kernel.org
+W: http://www.linuxfoundation.org/en/Net
+Q: http://patchwork.ozlabs.org/project/netdev/list/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/davem/net.git
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next.git
+S: Odd Fixes
+F: Documentation/devicetree/bindings/net/
+F: drivers/net/
+F: include/linux/if_*
+F: include/linux/netdevice.h
+F: include/linux/etherdevice.h
+F: include/linux/fcdevice.h
+F: include/linux/fddidevice.h
+F: include/linux/hippidevice.h
+F: include/linux/inetdevice.h
+F: include/uapi/linux/if_*
+F: include/uapi/linux/netdevice.h
+
+NETWORKING DRIVERS (WIRELESS)
+M: Kalle Valo <kvalo@codeaurora.org>
+L: linux-wireless@vger.kernel.org
+Q: http://patchwork.kernel.org/project/linux-wireless/list/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/kvalo/wireless-drivers.git
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/kvalo/wireless-drivers-next.git
+S: Maintained
+F: Documentation/devicetree/bindings/net/wireless/
+F: drivers/net/wireless/
+
+NETXEN (1/10) GbE SUPPORT
+M: Manish Chopra <manish.chopra@cavium.com>
+M: Rahul Verma <rahul.verma@cavium.com>
+M: Dept-GELinuxNICDev@cavium.com
+L: netdev@vger.kernel.org
+S: Supported
+F: drivers/net/ethernet/qlogic/netxen/
+
+NFC SUBSYSTEM
+M: Samuel Ortiz <sameo@linux.intel.com>
+L: linux-wireless@vger.kernel.org
+L: linux-nfc@lists.01.org (subscribers-only)
+S: Supported
+F: net/nfc/
+F: include/net/nfc/
+F: include/uapi/linux/nfc.h
+F: drivers/nfc/
+F: include/linux/platform_data/nfcmrvl.h
+F: include/linux/platform_data/nxp-nci.h
+F: include/linux/platform_data/pn544.h
+F: include/linux/platform_data/st21nfca.h
+F: include/linux/platform_data/st-nci.h
+F: Documentation/devicetree/bindings/net/nfc/
+
+NFS, SUNRPC, AND LOCKD CLIENTS
+M: Trond Myklebust <trond.myklebust@primarydata.com>
+M: Anna Schumaker <anna.schumaker@netapp.com>
+L: linux-nfs@vger.kernel.org
+W: http://client.linux-nfs.org
+T: git git://git.linux-nfs.org/projects/trondmy/linux-nfs.git
+S: Maintained
+F: fs/lockd/
+F: fs/nfs/
+F: fs/nfs_common/
+F: net/sunrpc/
+F: include/linux/lockd/
+F: include/linux/nfs*
+F: include/linux/sunrpc/
+F: include/uapi/linux/nfs*
+F: include/uapi/linux/sunrpc/
+
+NILFS2 FILESYSTEM
+M: Ryusuke Konishi <konishi.ryusuke@lab.ntt.co.jp>
+L: linux-nilfs@vger.kernel.org
+W: http://nilfs.sourceforge.net/
+W: http://nilfs.osdn.jp/
+T: git git://github.com/konis/nilfs2.git
+S: Supported
+F: Documentation/filesystems/nilfs2.txt
+F: fs/nilfs2/
+F: include/trace/events/nilfs2.h
+F: include/uapi/linux/nilfs2_api.h
+F: include/uapi/linux/nilfs2_ondisk.h
+
+NINJA SCSI-3 / NINJA SCSI-32Bi (16bit/CardBus) PCMCIA SCSI HOST ADAPTER DRIVER
+M: YOKOTA Hiroshi <yokota@netlab.is.tsukuba.ac.jp>
+W: http://www.netlab.is.tsukuba.ac.jp/~yokota/izumi/ninja/
+S: Maintained
+F: Documentation/scsi/NinjaSCSI.txt
+F: drivers/scsi/pcmcia/nsp_*
+
+NINJA SCSI-32Bi/UDE PCI/CARDBUS SCSI HOST ADAPTER DRIVER
+M: GOTO Masanori <gotom@debian.or.jp>
+M: YOKOTA Hiroshi <yokota@netlab.is.tsukuba.ac.jp>
+W: http://www.netlab.is.tsukuba.ac.jp/~yokota/izumi/ninja/
+S: Maintained
+F: Documentation/scsi/NinjaSCSI.txt
+F: drivers/scsi/nsp32*
+
+NIOS2 ARCHITECTURE
+M: Ley Foon Tan <lftan@altera.com>
+L: nios2-dev@lists.rocketboards.org (moderated for non-subscribers)
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/lftan/nios2.git
+S: Maintained
+F: arch/nios2/
+
+NOKIA N900 CAMERA SUPPORT (ET8EK8 SENSOR, AD5820 FOCUS)
+M: Pavel Machek <pavel@ucw.cz>
+M: Sakari Ailus <sakari.ailus@iki.fi>
+L: linux-media@vger.kernel.org
+S: Maintained
+F: drivers/media/i2c/et8ek8
+F: drivers/media/i2c/ad5820.c
+
+NOKIA N900 CAMERA SUPPORT (ET8EK8 SENSOR, AD5820 FOCUS)
+M: Pavel Machek <pavel@ucw.cz>
+M: Sakari Ailus <sakari.ailus@iki.fi>
+L: linux-media@vger.kernel.org
+S: Maintained
+F: drivers/media/i2c/et8ek8
+F: drivers/media/i2c/ad5820.c
+
+NOKIA N900 POWER SUPPLY DRIVERS
+R: Pali Rohár <pali.rohar@gmail.com>
+F: include/linux/power/bq2415x_charger.h
+F: include/linux/power/bq27xxx_battery.h
+F: include/linux/power/isp1704_charger.h
+F: drivers/power/supply/bq2415x_charger.c
+F: drivers/power/supply/bq27xxx_battery.c
+F: drivers/power/supply/bq27xxx_battery_i2c.c
+F: drivers/power/supply/isp1704_charger.c
+F: drivers/power/supply/rx51_battery.c
+
+NTB DRIVER CORE
+M: Jon Mason <jdmason@kudzu.us>
+M: Dave Jiang <dave.jiang@intel.com>
+M: Allen Hubbe <Allen.Hubbe@emc.com>
+L: linux-ntb@googlegroups.com
+S: Supported
+W: https://github.com/jonmason/ntb/wiki
+T: git git://github.com/jonmason/ntb.git
+F: drivers/ntb/
+F: drivers/net/ntb_netdev.c
+F: include/linux/ntb.h
+F: include/linux/ntb_transport.h
+F: tools/testing/selftests/ntb/
+
+NTB INTEL DRIVER
+M: Jon Mason <jdmason@kudzu.us>
+M: Dave Jiang <dave.jiang@intel.com>
+L: linux-ntb@googlegroups.com
+S: Supported
+W: https://github.com/jonmason/ntb/wiki
+T: git git://github.com/jonmason/ntb.git
+F: drivers/ntb/hw/intel/
+
+NTB AMD DRIVER
+M: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
+L: linux-ntb@googlegroups.com
+S: Supported
+F: drivers/ntb/hw/amd/
+
+NTFS FILESYSTEM
+M: Anton Altaparmakov <anton@tuxera.com>
+L: linux-ntfs-dev@lists.sourceforge.net
+W: http://www.tuxera.com/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/aia21/ntfs.git
+S: Supported
+F: Documentation/filesystems/ntfs.txt
+F: fs/ntfs/
+
+NVIDIA (rivafb and nvidiafb) FRAMEBUFFER DRIVER
+M: Antonino Daplas <adaplas@gmail.com>
+L: linux-fbdev@vger.kernel.org
+S: Maintained
+F: drivers/video/fbdev/riva/
+F: drivers/video/fbdev/nvidia/
+
+NVM EXPRESS DRIVER
+M: Keith Busch <keith.busch@intel.com>
+M: Jens Axboe <axboe@fb.com>
+M: Christoph Hellwig <hch@lst.de>
+M: Sagi Grimberg <sagi@grimberg.me>
+L: linux-nvme@lists.infradead.org
+T: git://git.infradead.org/nvme.git
+W: http://git.infradead.org/nvme.git
+S: Supported
+F: drivers/nvme/host/
+F: include/linux/nvme.h
+F: include/uapi/linux/nvme_ioctl.h
+
+NVM EXPRESS TARGET DRIVER
+M: Christoph Hellwig <hch@lst.de>
+M: Sagi Grimberg <sagi@grimberg.me>
+L: linux-nvme@lists.infradead.org
+T: git://git.infradead.org/nvme.git
+W: http://git.infradead.org/nvme.git
+S: Supported
+F: drivers/nvme/target/
+
+NVM EXPRESS FC TRANSPORT DRIVERS
+M: James Smart <james.smart@broadcom.com>
+L: linux-nvme@lists.infradead.org
+S: Supported
+F: include/linux/nvme-fc.h
+F: include/linux/nvme-fc-driver.h
+F: drivers/nvme/host/fc.c
+F: drivers/nvme/target/fc.c
+F: drivers/nvme/target/fcloop.c
+
+NVMEM FRAMEWORK
+M: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+S: Maintained
+F: drivers/nvmem/
+F: Documentation/devicetree/bindings/nvmem/
+F: include/linux/nvmem-consumer.h
+F: include/linux/nvmem-provider.h
+
+NXP-NCI NFC DRIVER
+M: Clément Perrochaud <clement.perrochaud@effinnov.com>
+R: Charles Gorand <charles.gorand@effinnov.com>
+L: linux-nfc@lists.01.org (moderated for non-subscribers)
+S: Supported
+F: drivers/nfc/nxp-nci
+
+NXP TDA998X DRM DRIVER
+M: Russell King <linux@armlinux.org.uk>
+S: Supported
+T: git git://git.armlinux.org.uk/~rmk/linux-arm.git drm-tda998x-devel
+T: git git://git.armlinux.org.uk/~rmk/linux-arm.git drm-tda998x-fixes
+F: drivers/gpu/drm/i2c/tda998x_drv.c
+F: include/drm/i2c/tda998x.h
+
+NXP TFA9879 DRIVER
+M: Peter Rosin <peda@axentia.se>
+L: alsa-devel@alsa-project.org (moderated for non-subscribers)
+S: Maintained
+F: sound/soc/codecs/tfa9879*
+
+OBJTOOL
+M: Josh Poimboeuf <jpoimboe@redhat.com>
+S: Supported
+F: tools/objtool/
+
+OMAP1 SUPPORT
+M: Aaro Koskinen <aaro.koskinen@iki.fi>
+M: Tony Lindgren <tony@atomide.com>
+L: linux-omap@vger.kernel.org
+Q: http://patchwork.kernel.org/project/linux-omap/list/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/tmlind/linux-omap.git
+S: Maintained
+F: arch/arm/mach-omap1/
+F: arch/arm/plat-omap/
+F: arch/arm/configs/omap1_defconfig
+F: drivers/i2c/busses/i2c-omap.c
+F: include/linux/i2c-omap.h
+
+OMAP2+ SUPPORT
+M: Tony Lindgren <tony@atomide.com>
+L: linux-omap@vger.kernel.org
+W: http://www.muru.com/linux/omap/
+W: http://linux.omap.com/
+Q: http://patchwork.kernel.org/project/linux-omap/list/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/tmlind/linux-omap.git
+S: Maintained
+F: arch/arm/mach-omap2/
+F: arch/arm/plat-omap/
+F: arch/arm/configs/omap2plus_defconfig
+F: drivers/i2c/busses/i2c-omap.c
+F: drivers/irqchip/irq-omap-intc.c
+F: drivers/mfd/*omap*.c
+F: drivers/mfd/menelaus.c
+F: drivers/mfd/palmas.c
+F: drivers/mfd/tps65217.c
+F: drivers/mfd/tps65218.c
+F: drivers/mfd/tps65910.c
+F: drivers/mfd/twl-core.[ch]
+F: drivers/mfd/twl4030*.c
+F: drivers/mfd/twl6030*.c
+F: drivers/mfd/twl6040*.c
+F: drivers/regulator/palmas-regulator*.c
+F: drivers/regulator/pbias-regulator.c
+F: drivers/regulator/tps65217-regulator.c
+F: drivers/regulator/tps65218-regulator.c
+F: drivers/regulator/tps65910-regulator.c
+F: drivers/regulator/twl-regulator.c
+F: drivers/regulator/twl6030-regulator.c
+F: include/linux/i2c-omap.h
+
+OMAP DEVICE TREE SUPPORT
+M: Benoît Cousson <bcousson@baylibre.com>
+M: Tony Lindgren <tony@atomide.com>
+L: linux-omap@vger.kernel.org
+L: devicetree@vger.kernel.org
+S: Maintained
+F: arch/arm/boot/dts/*omap*
+F: arch/arm/boot/dts/*am3*
+F: arch/arm/boot/dts/*am4*
+F: arch/arm/boot/dts/*am5*
+F: arch/arm/boot/dts/*dra7*
+
+OMAP CLOCK FRAMEWORK SUPPORT
+M: Paul Walmsley <paul@pwsan.com>
+L: linux-omap@vger.kernel.org
+S: Maintained
+F: arch/arm/*omap*/*clock*
+
+OMAP POWER MANAGEMENT SUPPORT
+M: Kevin Hilman <khilman@kernel.org>
+L: linux-omap@vger.kernel.org
+S: Maintained
+F: arch/arm/*omap*/*pm*
+F: drivers/cpufreq/omap-cpufreq.c
+
+OMAP POWERDOMAIN SOC ADAPTATION LAYER SUPPORT
+M: Rajendra Nayak <rnayak@codeaurora.org>
+M: Paul Walmsley <paul@pwsan.com>
+L: linux-omap@vger.kernel.org
+S: Maintained
+F: arch/arm/mach-omap2/prm*
+
+OMAP AUDIO SUPPORT
+M: Peter Ujfalusi <peter.ujfalusi@ti.com>
+M: Jarkko Nikula <jarkko.nikula@bitmer.com>
+L: alsa-devel@alsa-project.org (moderated for non-subscribers)
+L: linux-omap@vger.kernel.org
+S: Maintained
+F: sound/soc/omap/
+
+OMAP GENERAL PURPOSE MEMORY CONTROLLER SUPPORT
+M: Roger Quadros <rogerq@ti.com>
+M: Tony Lindgren <tony@atomide.com>
+L: linux-omap@vger.kernel.org
+S: Maintained
+F: drivers/memory/omap-gpmc.c
+F: arch/arm/mach-omap2/*gpmc*
+
+OMAP FRAMEBUFFER SUPPORT
+M: Tomi Valkeinen <tomi.valkeinen@ti.com>
+L: linux-fbdev@vger.kernel.org
+L: linux-omap@vger.kernel.org
+S: Maintained
+F: drivers/video/fbdev/omap/
+
+OMAP DISPLAY SUBSYSTEM and FRAMEBUFFER SUPPORT (DSS2)
+M: Tomi Valkeinen <tomi.valkeinen@ti.com>
+L: linux-omap@vger.kernel.org
+L: linux-fbdev@vger.kernel.org
+S: Maintained
+F: drivers/video/fbdev/omap2/
+F: Documentation/arm/OMAP/DSS
+
+OMAP HARDWARE SPINLOCK SUPPORT
+M: Ohad Ben-Cohen <ohad@wizery.com>
+L: linux-omap@vger.kernel.org
+S: Maintained
+F: drivers/hwspinlock/omap_hwspinlock.c
+
+OMAP MMC SUPPORT
+M: Jarkko Lavinen <jarkko.lavinen@nokia.com>
+L: linux-omap@vger.kernel.org
+S: Maintained
+F: drivers/mmc/host/omap.c
+
+OMAP HS MMC SUPPORT
+L: linux-mmc@vger.kernel.org
+L: linux-omap@vger.kernel.org
+S: Orphan
+F: drivers/mmc/host/omap_hsmmc.c
+
+OMAP RANDOM NUMBER GENERATOR SUPPORT
+M: Deepak Saxena <dsaxena@plexity.net>
+S: Maintained
+F: drivers/char/hw_random/omap-rng.c
+
+OMAP HWMOD SUPPORT
+M: Benoît Cousson <bcousson@baylibre.com>
+M: Paul Walmsley <paul@pwsan.com>
+L: linux-omap@vger.kernel.org
+S: Maintained
+F: arch/arm/mach-omap2/omap_hwmod.*
+
+OMAP HWMOD DATA
+M: Paul Walmsley <paul@pwsan.com>
+L: linux-omap@vger.kernel.org
+S: Maintained
+F: arch/arm/mach-omap2/omap_hwmod*data*
+
+OMAP HWMOD DATA FOR OMAP4-BASED DEVICES
+M: Benoît Cousson <bcousson@baylibre.com>
+L: linux-omap@vger.kernel.org
+S: Maintained
+F: arch/arm/mach-omap2/omap_hwmod_44xx_data.c
+
+OMAP IMAGING SUBSYSTEM (OMAP3 ISP and OMAP4 ISS)
+M: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+L: linux-media@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/media/ti,omap3isp.txt
+F: drivers/media/platform/omap3isp/
+F: drivers/staging/media/omap4iss/
+
+OMAP USB SUPPORT
+L: linux-usb@vger.kernel.org
+L: linux-omap@vger.kernel.org
+S: Orphan
+F: drivers/usb/*/*omap*
+F: arch/arm/*omap*/usb*
+
+OMAP GPIO DRIVER
+M: Grygorii Strashko <grygorii.strashko@ti.com>
+M: Santosh Shilimkar <ssantosh@kernel.org>
+M: Kevin Hilman <khilman@kernel.org>
+L: linux-omap@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/gpio/gpio-omap.txt
+F: drivers/gpio/gpio-omap.c
+
+OMAP/NEWFLOW NANOBONE MACHINE SUPPORT
+M: Mark Jackson <mpfj@newflow.co.uk>
+L: linux-omap@vger.kernel.org
+S: Maintained
+F: arch/arm/boot/dts/am335x-nano.dts
+
+OMFS FILESYSTEM
+M: Bob Copeland <me@bobcopeland.com>
+L: linux-karma-devel@lists.sourceforge.net
+S: Maintained
+F: Documentation/filesystems/omfs.txt
+F: fs/omfs/
+
+OMNIKEY CARDMAN 4000 DRIVER
+M: Harald Welte <laforge@gnumonks.org>
+S: Maintained
+F: drivers/char/pcmcia/cm4000_cs.c
+F: include/linux/cm4000_cs.h
+F: include/uapi/linux/cm4000_cs.h
+
+OMNIKEY CARDMAN 4040 DRIVER
+M: Harald Welte <laforge@gnumonks.org>
+S: Maintained
+F: drivers/char/pcmcia/cm4040_cs.*
+
+OMNIVISION OV5647 SENSOR DRIVER
+M: Ramiro Oliveira <roliveir@synopsys.com>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+S: Maintained
+F: drivers/media/i2c/ov5647.c
+
+OMNIVISION OV7670 SENSOR DRIVER
+M: Jonathan Corbet <corbet@lwn.net>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+S: Maintained
+F: drivers/media/i2c/ov7670.c
+F: Documentation/devicetree/bindings/media/i2c/ov7670.txt
+
+ONENAND FLASH DRIVER
+M: Kyungmin Park <kyungmin.park@samsung.com>
+L: linux-mtd@lists.infradead.org
+S: Maintained
+F: drivers/mtd/onenand/
+F: include/linux/mtd/onenand*.h
+
+ONSTREAM SCSI TAPE DRIVER
+M: Willem Riede <osst@riede.org>
+L: osst-users@lists.sourceforge.net
+L: linux-scsi@vger.kernel.org
+S: Maintained
+F: Documentation/scsi/osst.txt
+F: drivers/scsi/osst.*
+F: drivers/scsi/osst_*.h
+F: drivers/scsi/st.h
+
+OPENCORES I2C BUS DRIVER
+M: Peter Korsgaard <jacmet@sunsite.dk>
+L: linux-i2c@vger.kernel.org
+S: Maintained
+F: Documentation/i2c/busses/i2c-ocores
+F: drivers/i2c/busses/i2c-ocores.c
+
+OPEN FIRMWARE AND FLATTENED DEVICE TREE
+M: Rob Herring <robh+dt@kernel.org>
+M: Frank Rowand <frowand.list@gmail.com>
+L: devicetree@vger.kernel.org
+W: http://www.devicetree.org/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git
+S: Maintained
+F: drivers/of/
+F: include/linux/of*.h
+F: scripts/dtc/
+
+OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS
+M: Rob Herring <robh+dt@kernel.org>
+M: Mark Rutland <mark.rutland@arm.com>
+L: devicetree@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git
+Q: http://patchwork.ozlabs.org/project/devicetree-bindings/list/
+S: Maintained
+F: Documentation/devicetree/
+F: arch/*/boot/dts/
+F: include/dt-bindings/
+
+OPEN FIRMWARE AND DEVICE TREE OVERLAYS
+M: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
+L: devicetree@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/dynamic-resolution-notes.txt
+F: Documentation/devicetree/overlay-notes.txt
+F: drivers/of/overlay.c
+F: drivers/of/resolver.c
+
+OPENRISC ARCHITECTURE
+M: Jonas Bonn <jonas@southpole.se>
+M: Stefan Kristiansson <stefan.kristiansson@saunalahti.fi>
+M: Stafford Horne <shorne@gmail.com>
+T: git git://github.com/openrisc/linux.git
+L: openrisc@lists.librecores.org
+W: http://openrisc.io
+S: Maintained
+F: arch/openrisc/
+
+OPENVSWITCH
+M: Pravin Shelar <pshelar@nicira.com>
+L: netdev@vger.kernel.org
+L: dev@openvswitch.org
+W: http://openvswitch.org
+S: Maintained
+F: net/openvswitch/
+F: include/uapi/linux/openvswitch.h
+
+OPERATING PERFORMANCE POINTS (OPP)
+M: Viresh Kumar <vireshk@kernel.org>
+M: Nishanth Menon <nm@ti.com>
+M: Stephen Boyd <sboyd@codeaurora.org>
+L: linux-pm@vger.kernel.org
+S: Maintained
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/vireshk/pm.git
+F: drivers/base/power/opp/
+F: include/linux/pm_opp.h
+F: Documentation/power/opp.txt
+F: Documentation/devicetree/bindings/opp/
+
+OPL4 DRIVER
+M: Clemens Ladisch <clemens@ladisch.de>
+L: alsa-devel@alsa-project.org (moderated for non-subscribers)
+T: git git://git.alsa-project.org/alsa-kernel.git
+S: Maintained
+F: sound/drivers/opl4/
+
+OPROFILE
+M: Robert Richter <rric@kernel.org>
+L: oprofile-list@lists.sf.net
+S: Maintained
+F: arch/*/include/asm/oprofile*.h
+F: arch/*/oprofile/
+F: drivers/oprofile/
+F: include/linux/oprofile.h
+
+OP-TEE DRIVER
+M: Jens Wiklander <jens.wiklander@linaro.org>
+S: Maintained
+F: drivers/tee/optee/
+
+ORACLE CLUSTER FILESYSTEM 2 (OCFS2)
+M: Mark Fasheh <mfasheh@versity.com>
+M: Joel Becker <jlbec@evilplan.org>
+L: ocfs2-devel@oss.oracle.com (moderated for non-subscribers)
+W: http://ocfs2.wiki.kernel.org
+S: Supported
+F: Documentation/filesystems/ocfs2.txt
+F: Documentation/filesystems/dlmfs.txt
+F: fs/ocfs2/
+
+ORINOCO DRIVER
+L: linux-wireless@vger.kernel.org
+W: http://wireless.kernel.org/en/users/Drivers/orinoco
+W: http://www.nongnu.org/orinoco/
+S: Orphan
+F: drivers/net/wireless/intersil/orinoco/
+
+OSD LIBRARY and FILESYSTEM
+M: Boaz Harrosh <ooo@electrozaur.com>
+S: Maintained
+F: drivers/scsi/osd/
+F: include/scsi/osd_*
+F: fs/exofs/
+
+OVERLAY FILESYSTEM
+M: Miklos Szeredi <miklos@szeredi.hu>
+L: linux-unionfs@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs.git
+S: Supported
+F: fs/overlayfs/
+F: Documentation/filesystems/overlayfs.txt
+
+ORANGEFS FILESYSTEM
+M: Mike Marshall <hubcap@omnibond.com>
+L: pvfs2-developers@beowulf-underground.org (subscribers-only)
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/hubcap/linux.git
+S: Supported
+F: fs/orangefs/
+F: Documentation/filesystems/orangefs.txt
+
+P54 WIRELESS DRIVER
+M: Christian Lamparter <chunkeey@googlemail.com>
+L: linux-wireless@vger.kernel.org
+W: http://wireless.kernel.org/en/users/Drivers/p54
+S: Maintained
+F: drivers/net/wireless/intersil/p54/
+
+PA SEMI ETHERNET DRIVER
+L: netdev@vger.kernel.org
+S: Orphan
+F: drivers/net/ethernet/pasemi/*
+
+PA SEMI SMBUS DRIVER
+L: linux-i2c@vger.kernel.org
+S: Orphan
+F: drivers/i2c/busses/i2c-pasemi.c
+
+PADATA PARALLEL EXECUTION MECHANISM
+M: Steffen Klassert <steffen.klassert@secunet.com>
+L: linux-crypto@vger.kernel.org
+S: Maintained
+F: kernel/padata.c
+F: include/linux/padata.h
+F: Documentation/padata.txt
+
+PANASONIC LAPTOP ACPI EXTRAS DRIVER
+M: Harald Welte <laforge@gnumonks.org>
+L: platform-driver-x86@vger.kernel.org
+S: Maintained
+F: drivers/platform/x86/panasonic-laptop.c
+
+PANASONIC MN10300/AM33/AM34 PORT
+M: David Howells <dhowells@redhat.com>
+L: linux-am33-list@redhat.com (moderated for non-subscribers)
+W: ftp://ftp.redhat.com/pub/redhat/gnupro/AM33/
+S: Maintained
+F: Documentation/mn10300/
+F: arch/mn10300/
+
+PARALLEL LCD/KEYPAD PANEL DRIVER
+M: Willy Tarreau <willy@haproxy.com>
+M: Ksenija Stanojevic <ksenija.stanojevic@gmail.com>
+S: Odd Fixes
+F: Documentation/misc-devices/lcd-panel-cgram.txt
+F: drivers/misc/panel.c
+
+PARALLEL PORT SUBSYSTEM
+M: Sudip Mukherjee <sudipm.mukherjee@gmail.com>
+M: Sudip Mukherjee <sudip.mukherjee@codethink.co.uk>
+L: linux-parport@lists.infradead.org (subscribers-only)
+S: Maintained
+F: drivers/parport/
+F: include/linux/parport*.h
+F: drivers/char/ppdev.c
+F: include/uapi/linux/ppdev.h
+F: Documentation/parport*.txt
+
+PARAVIRT_OPS INTERFACE
+M: Jeremy Fitzhardinge <jeremy@goop.org>
+M: Chris Wright <chrisw@sous-sol.org>
+M: Alok Kataria <akataria@vmware.com>
+M: Rusty Russell <rusty@rustcorp.com.au>
+L: virtualization@lists.linux-foundation.org
+S: Supported
+F: Documentation/virtual/paravirt_ops.txt
+F: arch/*/kernel/paravirt*
+F: arch/*/include/asm/paravirt.h
+F: include/linux/hypervisor.h
+
+PARIDE DRIVERS FOR PARALLEL PORT IDE DEVICES
+M: Tim Waugh <tim@cyberelk.net>
+L: linux-parport@lists.infradead.org (subscribers-only)
+S: Maintained
+F: Documentation/blockdev/paride.txt
+F: drivers/block/paride/
+
+PARISC ARCHITECTURE
+M: "James E.J. Bottomley" <jejb@parisc-linux.org>
+M: Helge Deller <deller@gmx.de>
+L: linux-parisc@vger.kernel.org
+W: http://www.parisc-linux.org/
+Q: http://patchwork.kernel.org/project/linux-parisc/list/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/jejb/parisc-2.6.git
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/deller/parisc-linux.git
+S: Maintained
+F: arch/parisc/
+F: Documentation/parisc/
+F: drivers/parisc/
+F: drivers/char/agp/parisc-agp.c
+F: drivers/input/serio/gscps2.c
+F: drivers/parport/parport_gsc.*
+F: drivers/tty/serial/8250/8250_gsc.c
+F: drivers/video/fbdev/sti*
+F: drivers/video/console/sti*
+F: drivers/video/logo/logo_parisc*
+
+PARMAN
+M: Jiri Pirko <jiri@mellanox.com>
+L: netdev@vger.kernel.org
+S: Supported
+F: lib/parman.c
+F: lib/test_parman.c
+F: include/linux/parman.h
+
+PC87360 HARDWARE MONITORING DRIVER
+M: Jim Cromie <jim.cromie@gmail.com>
+L: linux-hwmon@vger.kernel.org
+S: Maintained
+F: Documentation/hwmon/pc87360
+F: drivers/hwmon/pc87360.c
+
+PC8736x GPIO DRIVER
+M: Jim Cromie <jim.cromie@gmail.com>
+S: Maintained
+F: drivers/char/pc8736x_gpio.c
+
+PC87427 HARDWARE MONITORING DRIVER
+M: Jean Delvare <jdelvare@suse.com>
+L: linux-hwmon@vger.kernel.org
+S: Maintained
+F: Documentation/hwmon/pc87427
+F: drivers/hwmon/pc87427.c
+
+PCA9532 LED DRIVER
+M: Riku Voipio <riku.voipio@iki.fi>
+S: Maintained
+F: drivers/leds/leds-pca9532.c
+F: include/linux/leds-pca9532.h
+
+PCA9541 I2C BUS MASTER SELECTOR DRIVER
+M: Guenter Roeck <linux@roeck-us.net>
+L: linux-i2c@vger.kernel.org
+S: Maintained
+F: drivers/i2c/muxes/i2c-mux-pca9541.c
+
+PCDP - PRIMARY CONSOLE AND DEBUG PORT
+M: Khalid Aziz <khalid@gonehiking.org>
+S: Maintained
+F: drivers/firmware/pcdp.*
+
+PCI ERROR RECOVERY
+M: Linas Vepstas <linasvepstas@gmail.com>
+L: linux-pci@vger.kernel.org
+S: Supported
+F: Documentation/PCI/pci-error-recovery.txt
+
+PCI ENHANCED ERROR HANDLING (EEH) FOR POWERPC
+M: Russell Currey <ruscur@russell.cc>
+L: linuxppc-dev@lists.ozlabs.org
+S: Supported
+F: Documentation/powerpc/eeh-pci-error-recovery.txt
+F: arch/powerpc/kernel/eeh*.c
+F: arch/powerpc/platforms/*/eeh*.c
+F: arch/powerpc/include/*/eeh*.h
+
+PCI SUBSYSTEM
+M: Bjorn Helgaas <bhelgaas@google.com>
+L: linux-pci@vger.kernel.org
+Q: http://patchwork.ozlabs.org/project/linux-pci/list/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci.git
+S: Supported
+F: Documentation/devicetree/bindings/pci/
+F: Documentation/PCI/
+F: drivers/pci/
+F: include/linux/pci*
+F: arch/x86/pci/
+F: arch/x86/kernel/quirks.c
+
+PCI ENDPOINT SUBSYSTEM
+M: Kishon Vijay Abraham I <kishon@ti.com>
+L: linux-pci@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/kishon/pci-endpoint.git
+S: Supported
+F: drivers/pci/endpoint/
+F: drivers/misc/pci_endpoint_test.c
+F: tools/pci/
+
+PCI DRIVER FOR ALTERA PCIE IP
+M: Ley Foon Tan <lftan@altera.com>
+L: rfi@lists.rocketboards.org (moderated for non-subscribers)
+L: linux-pci@vger.kernel.org
+S: Supported
+F: Documentation/devicetree/bindings/pci/altera-pcie.txt
+F: drivers/pci/host/pcie-altera.c
+
+PCI DRIVER FOR ARM VERSATILE PLATFORM
+M: Rob Herring <robh@kernel.org>
+L: linux-pci@vger.kernel.org
+L: linux-arm-kernel@lists.infradead.org
+S: Maintained
+F: Documentation/devicetree/bindings/pci/versatile.txt
+F: drivers/pci/host/pci-versatile.c
+
+PCI DRIVER FOR ARMADA 8K
+M: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
+L: linux-pci@vger.kernel.org
+L: linux-arm-kernel@lists.infradead.org
+S: Maintained
+F: Documentation/devicetree/bindings/pci/pci-armada8k.txt
+F: drivers/pci/dwc/pcie-armada8k.c
+
+PCI DRIVER FOR APPLIEDMICRO XGENE
+M: Tanmay Inamdar <tinamdar@apm.com>
+L: linux-pci@vger.kernel.org
+L: linux-arm-kernel@lists.infradead.org
+S: Maintained
+F: Documentation/devicetree/bindings/pci/xgene-pci.txt
+F: drivers/pci/host/pci-xgene.c
+
+PCI DRIVER FOR FREESCALE LAYERSCAPE
+M: Minghuan Lian <minghuan.Lian@freescale.com>
+M: Mingkai Hu <mingkai.hu@freescale.com>
+M: Roy Zang <tie-fei.zang@freescale.com>
+L: linuxppc-dev@lists.ozlabs.org
+L: linux-pci@vger.kernel.org
+L: linux-arm-kernel@lists.infradead.org
+S: Maintained
+F: drivers/pci/dwc/*layerscape*
+
+PCI DRIVER FOR IMX6
+M: Richard Zhu <hongxing.zhu@nxp.com>
+M: Lucas Stach <l.stach@pengutronix.de>
+L: linux-pci@vger.kernel.org
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+F: Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.txt
+F: drivers/pci/dwc/*imx6*
+
+PCI DRIVER FOR TI KEYSTONE
+M: Murali Karicheri <m-karicheri2@ti.com>
+L: linux-pci@vger.kernel.org
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+F: drivers/pci/dwc/*keystone*
+
+PCI DRIVER FOR MVEBU (Marvell Armada 370 and Armada XP SOC support)
+M: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
+M: Jason Cooper <jason@lakedaemon.net>
+L: linux-pci@vger.kernel.org
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+F: drivers/pci/host/*mvebu*
+
+PCI DRIVER FOR AARDVARK (Marvell Armada 3700)
+M: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
+L: linux-pci@vger.kernel.org
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+F: Documentation/devicetree/bindings/pci/aardvark-pci.txt
+F: drivers/pci/host/pci-aardvark.c
+
+PCI DRIVER FOR MICROSEMI SWITCHTEC
+M: Kurt Schwemmer <kurt.schwemmer@microsemi.com>
+M: Stephen Bates <stephen.bates@microsemi.com>
+M: Logan Gunthorpe <logang@deltatee.com>
+L: linux-pci@vger.kernel.org
+S: Maintained
+F: Documentation/switchtec.txt
+F: Documentation/ABI/testing/sysfs-class-switchtec
+F: drivers/pci/switch/switchtec*
+F: include/uapi/linux/switchtec_ioctl.h
+
+PCI DRIVER FOR NVIDIA TEGRA
+M: Thierry Reding <thierry.reding@gmail.com>
+L: linux-tegra@vger.kernel.org
+L: linux-pci@vger.kernel.org
+S: Supported
+F: Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.txt
+F: drivers/pci/host/pci-tegra.c
+
+PCI DRIVER FOR TI DRA7XX
+M: Kishon Vijay Abraham I <kishon@ti.com>
+L: linux-omap@vger.kernel.org
+L: linux-pci@vger.kernel.org
+S: Supported
+F: Documentation/devicetree/bindings/pci/ti-pci.txt
+F: drivers/pci/dwc/pci-dra7xx.c
+
+PCI DRIVER FOR RENESAS R-CAR
+M: Simon Horman <horms@verge.net.au>
+L: linux-pci@vger.kernel.org
+L: linux-renesas-soc@vger.kernel.org
+S: Maintained
+F: drivers/pci/host/*rcar*
+
+PCI DRIVER FOR SAMSUNG EXYNOS
+M: Jingoo Han <jingoohan1@gmail.com>
+L: linux-pci@vger.kernel.org
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+L: linux-samsung-soc@vger.kernel.org (moderated for non-subscribers)
+S: Maintained
+F: drivers/pci/dwc/pci-exynos.c
+
+PCI DRIVER FOR SYNOPSIS DESIGNWARE
+M: Jingoo Han <jingoohan1@gmail.com>
+M: Joao Pinto <Joao.Pinto@synopsys.com>
+L: linux-pci@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/pci/designware-pcie.txt
+F: drivers/pci/dwc/*designware*
+
+PCI DRIVER FOR GENERIC OF HOSTS
+M: Will Deacon <will.deacon@arm.com>
+L: linux-pci@vger.kernel.org
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+F: Documentation/devicetree/bindings/pci/host-generic-pci.txt
+F: drivers/pci/host/pci-host-common.c
+F: drivers/pci/host/pci-host-generic.c
+
+PCI DRIVER FOR INTEL VOLUME MANAGEMENT DEVICE (VMD)
+M: Keith Busch <keith.busch@intel.com>
+L: linux-pci@vger.kernel.org
+S: Supported
+F: drivers/pci/host/vmd.c
+
+PCIE DRIVER FOR ST SPEAR13XX
+M: Pratyush Anand <pratyush.anand@gmail.com>
+L: linux-pci@vger.kernel.org
+S: Maintained
+F: drivers/pci/dwc/*spear*
+
+PCI MSI DRIVER FOR ALTERA MSI IP
+M: Ley Foon Tan <lftan@altera.com>
+L: rfi@lists.rocketboards.org (moderated for non-subscribers)
+L: linux-pci@vger.kernel.org
+S: Supported
+F: Documentation/devicetree/bindings/pci/altera-pcie-msi.txt
+F: drivers/pci/host/pcie-altera-msi.c
+
+PCI MSI DRIVER FOR APPLIEDMICRO XGENE
+M: Duc Dang <dhdang@apm.com>
+L: linux-pci@vger.kernel.org
+L: linux-arm-kernel@lists.infradead.org
+S: Maintained
+F: Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
+F: drivers/pci/host/pci-xgene-msi.c
+
+PCIE DRIVER FOR AXIS ARTPEC
+M: Niklas Cassel <niklas.cassel@axis.com>
+M: Jesper Nilsson <jesper.nilsson@axis.com>
+L: linux-arm-kernel@axis.com
+L: linux-pci@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/pci/axis,artpec*
+F: drivers/pci/dwc/*artpec*
+
+PCIE DRIVER FOR HISILICON
+M: Zhou Wang <wangzhou1@hisilicon.com>
+M: Gabriele Paoloni <gabriele.paoloni@huawei.com>
+L: linux-pci@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/pci/hisilicon-pcie.txt
+F: drivers/pci/dwc/pcie-hisi.c
+
+PCIE DRIVER FOR ROCKCHIP
+M: Shawn Lin <shawn.lin@rock-chips.com>
+M: Wenrui Li <wenrui.li@rock-chips.com>
+L: linux-pci@vger.kernel.org
+L: linux-rockchip@lists.infradead.org
+S: Maintained
+F: Documentation/devicetree/bindings/pci/rockchip-pcie.txt
+F: drivers/pci/host/pcie-rockchip.c
+
+PCIE DRIVER FOR QUALCOMM MSM
+M: Stanimir Varbanov <svarbanov@mm-sol.com>
+L: linux-pci@vger.kernel.org
+L: linux-arm-msm@vger.kernel.org
+S: Maintained
+F: drivers/pci/dwc/*qcom*
+
+PCIE DRIVER FOR CAVIUM THUNDERX
+M: David Daney <david.daney@cavium.com>
+L: linux-pci@vger.kernel.org
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Supported
+F: Documentation/devicetree/bindings/pci/pci-thunder-*
+F: drivers/pci/host/pci-thunder-*
+
+PCMCIA SUBSYSTEM
+P: Linux PCMCIA Team
+L: linux-pcmcia@lists.infradead.org
+W: http://lists.infradead.org/mailman/listinfo/linux-pcmcia
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/brodo/pcmcia.git
+S: Maintained
+F: Documentation/pcmcia/
+F: tools/pcmcia/
+F: drivers/pcmcia/
+F: include/pcmcia/
+
+PCNET32 NETWORK DRIVER
+M: Don Fry <pcnet32@frontier.com>
+L: netdev@vger.kernel.org
+S: Maintained
+F: drivers/net/ethernet/amd/pcnet32.c
+
+PCRYPT PARALLEL CRYPTO ENGINE
+M: Steffen Klassert <steffen.klassert@secunet.com>
+L: linux-crypto@vger.kernel.org
+S: Maintained
+F: crypto/pcrypt.c
+F: include/crypto/pcrypt.h
+
+PER-CPU MEMORY ALLOCATOR
+M: Tejun Heo <tj@kernel.org>
+M: Christoph Lameter <cl@linux.com>
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/tj/percpu.git
+S: Maintained
+F: include/linux/percpu*.h
+F: mm/percpu*.c
+F: arch/*/include/asm/percpu.h
+
+PER-TASK DELAY ACCOUNTING
+M: Balbir Singh <bsingharora@gmail.com>
+S: Maintained
+F: include/linux/delayacct.h
+F: kernel/delayacct.c
+
+PERFORMANCE EVENTS SUBSYSTEM
+M: Peter Zijlstra <peterz@infradead.org>
+M: Ingo Molnar <mingo@redhat.com>
+M: Arnaldo Carvalho de Melo <acme@kernel.org>
+R: Alexander Shishkin <alexander.shishkin@linux.intel.com>
+L: linux-kernel@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git perf/core
+S: Supported
+F: kernel/events/*
+F: include/linux/perf_event.h
+F: include/uapi/linux/perf_event.h
+F: arch/*/kernel/perf_event*.c
+F: arch/*/kernel/*/perf_event*.c
+F: arch/*/kernel/*/*/perf_event*.c
+F: arch/*/include/asm/perf_event.h
+F: arch/*/kernel/perf_callchain.c
+F: arch/*/events/*
+F: tools/perf/
+
+PERSONALITY HANDLING
+M: Christoph Hellwig <hch@infradead.org>
+L: linux-abi-devel@lists.sourceforge.net
+S: Maintained
+F: include/linux/personality.h
+F: include/uapi/linux/personality.h
+
+PHONET PROTOCOL
+M: Remi Denis-Courmont <courmisch@gmail.com>
+S: Supported
+F: Documentation/networking/phonet.txt
+F: include/linux/phonet.h
+F: include/net/phonet/
+F: include/uapi/linux/phonet.h
+F: net/phonet/
+
+PHRAM MTD DRIVER
+M: Joern Engel <joern@lazybastard.org>
+L: linux-mtd@lists.infradead.org
+S: Maintained
+F: drivers/mtd/devices/phram.c
+
+PICOLCD HID DRIVER
+M: Bruno Prémont <bonbons@linux-vserver.org>
+L: linux-input@vger.kernel.org
+S: Maintained
+F: drivers/hid/hid-picolcd*
+
+PICOXCELL SUPPORT
+M: Jamie Iles <jamie@jamieiles.com>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+T: git git://github.com/jamieiles/linux-2.6-ji.git
+S: Supported
+F: arch/arm/boot/dts/picoxcell*
+F: arch/arm/mach-picoxcell/
+F: drivers/crypto/picoxcell*
+
+PIN CONTROL SUBSYSTEM
+M: Linus Walleij <linus.walleij@linaro.org>
+L: linux-gpio@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-pinctrl.git
+S: Maintained
+F: Documentation/devicetree/bindings/pinctrl/
+F: Documentation/pinctrl.txt
+F: drivers/pinctrl/
+F: include/linux/pinctrl/
+
+PIN CONTROLLER - ATMEL AT91
+M: Jean-Christophe Plagniol-Villard <plagnioj@jcrosoft.com>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+F: drivers/pinctrl/pinctrl-at91.*
+
+PIN CONTROLLER - ATMEL AT91 PIO4
+M: Ludovic Desroches <ludovic.desroches@microchip.com>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+L: linux-gpio@vger.kernel.org
+S: Supported
+F: drivers/pinctrl/pinctrl-at91-pio4.*
+
+PIN CONTROLLER - INTEL
+M: Mika Westerberg <mika.westerberg@linux.intel.com>
+M: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+S: Maintained
+F: drivers/pinctrl/intel/
+
+PIN CONTROLLER - RENESAS
+M: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+M: Geert Uytterhoeven <geert+renesas@glider.be>
+L: linux-renesas-soc@vger.kernel.org
+S: Maintained
+F: drivers/pinctrl/sh-pfc/
+
+PIN CONTROLLER - SAMSUNG
+M: Tomasz Figa <tomasz.figa@gmail.com>
+M: Krzysztof Kozlowski <krzk@kernel.org>
+M: Sylwester Nawrocki <s.nawrocki@samsung.com>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+L: linux-samsung-soc@vger.kernel.org (moderated for non-subscribers)
+Q: https://patchwork.kernel.org/project/linux-samsung-soc/list/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/pinctrl/samsung.git
+S: Maintained
+F: drivers/pinctrl/samsung/
+F: include/dt-bindings/pinctrl/samsung.h
+F: Documentation/devicetree/bindings/pinctrl/samsung-pinctrl.txt
+
+PIN CONTROLLER - SINGLE
+M: Tony Lindgren <tony@atomide.com>
+M: Haojian Zhuang <haojian.zhuang@linaro.org>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+L: linux-omap@vger.kernel.org
+S: Maintained
+F: drivers/pinctrl/pinctrl-single.c
+
+PIN CONTROLLER - ST SPEAR
+M: Viresh Kumar <vireshk@kernel.org>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+W: http://www.st.com/spear
+S: Maintained
+F: drivers/pinctrl/spear/
+
+PISTACHIO SOC SUPPORT
+M: James Hartley <james.hartley@imgtec.com>
+M: Ionela Voinescu <ionela.voinescu@imgtec.com>
+L: linux-mips@linux-mips.org
+S: Maintained
+F: arch/mips/pistachio/
+F: arch/mips/include/asm/mach-pistachio/
+F: arch/mips/boot/dts/img/pistachio*
+F: arch/mips/configs/pistachio*_defconfig
+
+PKTCDVD DRIVER
+S: Orphan
+M: linux-block@vger.kernel.org
+F: drivers/block/pktcdvd.c
+F: include/linux/pktcdvd.h
+F: include/uapi/linux/pktcdvd.h
+
+PKUNITY SOC DRIVERS
+M: Guan Xuetao <gxt@mprc.pku.edu.cn>
+W: http://mprc.pku.edu.cn/~guanxuetao/linux
+S: Maintained
+T: git git://github.com/gxt/linux.git
+F: drivers/input/serio/i8042-unicore32io.h
+F: drivers/i2c/busses/i2c-puv3.c
+F: drivers/video/fbdev/fb-puv3.c
+F: drivers/rtc/rtc-puv3.c
+
+PMBUS HARDWARE MONITORING DRIVERS
+M: Guenter Roeck <linux@roeck-us.net>
+L: linux-hwmon@vger.kernel.org
+W: http://hwmon.wiki.kernel.org/
+W: http://www.roeck-us.net/linux/drivers/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging.git
+S: Maintained
+F: Documentation/hwmon/pmbus
+F: drivers/hwmon/pmbus/
+F: include/linux/i2c/pmbus.h
+
+PMC SIERRA MaxRAID DRIVER
+L: linux-scsi@vger.kernel.org
+W: http://www.pmc-sierra.com/
+S: Orphan
+F: drivers/scsi/pmcraid.*
+
+PMC SIERRA PM8001 DRIVER
+M: Jack Wang <jinpu.wang@profitbricks.com>
+M: lindar_liu@usish.com
+L: linux-scsi@vger.kernel.org
+S: Supported
+F: drivers/scsi/pm8001/
+
+POSIX CLOCKS and TIMERS
+M: Thomas Gleixner <tglx@linutronix.de>
+L: linux-kernel@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git timers/core
+S: Maintained
+F: fs/timerfd.c
+F: include/linux/timer*
+F: kernel/time/*timer*
+
+POWER MANAGEMENT CORE
+M: "Rafael J. Wysocki" <rjw@rjwysocki.net>
+L: linux-pm@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm
+B: https://bugzilla.kernel.org
+S: Supported
+F: drivers/base/power/
+F: include/linux/pm.h
+F: include/linux/pm_*
+F: include/linux/powercap.h
+F: drivers/powercap/
+
+POWER SUPPLY CLASS/SUBSYSTEM and DRIVERS
+M: Sebastian Reichel <sre@kernel.org>
+L: linux-pm@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply.git
+S: Maintained
+F: Documentation/devicetree/bindings/power/supply/
+F: include/linux/power_supply.h
+F: drivers/power/supply/
+
+POWER STATE COORDINATION INTERFACE (PSCI)
+M: Mark Rutland <mark.rutland@arm.com>
+M: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
+L: linux-arm-kernel@lists.infradead.org
+S: Maintained
+F: drivers/firmware/psci*.c
+F: include/linux/psci.h
+F: include/uapi/linux/psci.h
+
+POWERNV OPERATOR PANEL LCD DISPLAY DRIVER
+M: Suraj Jitindar Singh <sjitindarsingh@gmail.com>
+L: linuxppc-dev@lists.ozlabs.org
+S: Maintained
+F: drivers/char/powernv-op-panel.c
+
+PNP SUPPORT
+M: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>
+S: Maintained
+F: drivers/pnp/
+
+PPP PROTOCOL DRIVERS AND COMPRESSORS
+M: Paul Mackerras <paulus@samba.org>
+L: linux-ppp@vger.kernel.org
+S: Maintained
+F: drivers/net/ppp/ppp_*
+
+PPP OVER ATM (RFC 2364)
+M: Mitchell Blank Jr <mitch@sfgoth.com>
+S: Maintained
+F: net/atm/pppoatm.c
+F: include/uapi/linux/atmppp.h
+
+PPP OVER ETHERNET
+M: Michal Ostrowski <mostrows@earthlink.net>
+S: Maintained
+F: drivers/net/ppp/pppoe.c
+F: drivers/net/ppp/pppox.c
+
+PPP OVER L2TP
+M: James Chapman <jchapman@katalix.com>
+S: Maintained
+F: net/l2tp/l2tp_ppp.c
+F: include/linux/if_pppol2tp.h
+F: include/uapi/linux/if_pppol2tp.h
+
+PPS SUPPORT
+M: Rodolfo Giometti <giometti@enneenne.com>
+W: http://wiki.enneenne.com/index.php/LinuxPPS_support
+L: linuxpps@ml.enneenne.com (subscribers-only)
+S: Maintained
+F: Documentation/pps/
+F: drivers/pps/
+F: include/linux/pps*.h
+
+PPTP DRIVER
+M: Dmitry Kozlov <xeb@mail.ru>
+L: netdev@vger.kernel.org
+S: Maintained
+F: drivers/net/ppp/pptp.c
+W: http://sourceforge.net/projects/accel-pptp
+
+PREEMPTIBLE KERNEL
+M: Robert Love <rml@tech9.net>
+L: kpreempt-tech@lists.sourceforge.net
+W: https://www.kernel.org/pub/linux/kernel/people/rml/preempt-kernel
+S: Supported
+F: Documentation/preempt-locking.txt
+F: include/linux/preempt.h
+
+PRINTK
+M: Petr Mladek <pmladek@suse.com>
+M: Sergey Senozhatsky <sergey.senozhatsky@gmail.com>
+R: Steven Rostedt <rostedt@goodmis.org>
+S: Maintained
+F: kernel/printk/
+F: include/linux/printk.h
+
+PRISM54 WIRELESS DRIVER
+M: "Luis R. Rodriguez" <mcgrof@gmail.com>
+L: linux-wireless@vger.kernel.org
+W: http://wireless.kernel.org/en/users/Drivers/p54
+S: Obsolete
+F: drivers/net/wireless/intersil/prism54/
+
+PS3 NETWORK SUPPORT
+M: Geoff Levand <geoff@infradead.org>
+L: netdev@vger.kernel.org
+L: linuxppc-dev@lists.ozlabs.org
+S: Maintained
+F: drivers/net/ethernet/toshiba/ps3_gelic_net.*
+
+PS3 PLATFORM SUPPORT
+M: Geoff Levand <geoff@infradead.org>
+L: linuxppc-dev@lists.ozlabs.org
+S: Maintained
+F: arch/powerpc/boot/ps3*
+F: arch/powerpc/include/asm/lv1call.h
+F: arch/powerpc/include/asm/ps3*.h
+F: arch/powerpc/platforms/ps3/
+F: drivers/*/ps3*
+F: drivers/ps3/
+F: drivers/rtc/rtc-ps3.c
+F: drivers/usb/host/*ps3.c
+F: sound/ppc/snd_ps3*
+
+PS3VRAM DRIVER
+M: Jim Paris <jim@jtan.com>
+M: Geoff Levand <geoff@infradead.org>
+L: linuxppc-dev@lists.ozlabs.org
+S: Maintained
+F: drivers/block/ps3vram.c
+
+PSAMPLE PACKET SAMPLING SUPPORT:
+M: Yotam Gigi <yotamg@mellanox.com>
+S: Maintained
+F: net/psample
+F: include/net/psample.h
+F: include/uapi/linux/psample.h
+
+PSTORE FILESYSTEM
+M: Kees Cook <keescook@chromium.org>
+M: Anton Vorontsov <anton@enomsg.org>
+M: Colin Cross <ccross@android.com>
+M: Tony Luck <tony.luck@intel.com>
+S: Maintained
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git for-next/pstore
+F: fs/pstore/
+F: include/linux/pstore*
+F: drivers/firmware/efi/efi-pstore.c
+F: drivers/acpi/apei/erst.c
+F: Documentation/admin-guide/ramoops.rst
+F: Documentation/devicetree/bindings/reserved-memory/ramoops.txt
+K: \b(pstore|ramoops)
+
+PTP HARDWARE CLOCK SUPPORT
+M: Richard Cochran <richardcochran@gmail.com>
+L: netdev@vger.kernel.org
+S: Maintained
+W: http://linuxptp.sourceforge.net/
+F: Documentation/ABI/testing/sysfs-ptp
+F: Documentation/ptp/*
+F: drivers/net/ethernet/freescale/gianfar_ptp.c
+F: drivers/net/phy/dp83640*
+F: drivers/ptp/*
+F: include/linux/ptp_cl*
+
+PTRACE SUPPORT
+M: Roland McGrath <roland@hack.frob.com>
+M: Oleg Nesterov <oleg@redhat.com>
+S: Maintained
+F: include/asm-generic/syscall.h
+F: include/linux/ptrace.h
+F: include/linux/regset.h
+F: include/linux/tracehook.h
+F: include/uapi/linux/ptrace.h
+F: kernel/ptrace.c
+
+PULSE8-CEC DRIVER
+M: Hans Verkuil <hverkuil@xs4all.nl>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+S: Maintained
+F: drivers/media/usb/pulse8-cec/*
+
+PVRUSB2 VIDEO4LINUX DRIVER
+M: Mike Isely <isely@pobox.com>
+L: pvrusb2@isely.net (subscribers-only)
+L: linux-media@vger.kernel.org
+W: http://www.isely.net/pvrusb2/
+T: git git://linuxtv.org/media_tree.git
+S: Maintained
+F: Documentation/media/v4l-drivers/pvrusb2*
+F: drivers/media/usb/pvrusb2/
+
+PWC WEBCAM DRIVER
+M: Hans Verkuil <hverkuil@xs4all.nl>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+S: Odd Fixes
+F: drivers/media/usb/pwc/*
+
+PWM FAN DRIVER
+M: Kamil Debski <kamil@wypas.org>
+M: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
+L: linux-hwmon@vger.kernel.org
+S: Supported
+F: Documentation/devicetree/bindings/hwmon/pwm-fan.txt
+F: Documentation/hwmon/pwm-fan
+F: drivers/hwmon/pwm-fan.c
+
+PWM SUBSYSTEM
+M: Thierry Reding <thierry.reding@gmail.com>
+L: linux-pwm@vger.kernel.org
+S: Maintained
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/thierry.reding/linux-pwm.git
+F: Documentation/pwm.txt
+F: Documentation/devicetree/bindings/pwm/
+F: include/linux/pwm.h
+F: drivers/pwm/
+F: drivers/video/backlight/pwm_bl.c
+F: include/linux/pwm_backlight.h
+F: drivers/gpio/gpio-mvebu.c
+F: Documentation/devicetree/bindings/gpio/gpio-mvebu.txt
+
+PXA2xx/PXA3xx SUPPORT
+M: Daniel Mack <daniel@zonque.org>
+M: Haojian Zhuang <haojian.zhuang@gmail.com>
+M: Robert Jarzmik <robert.jarzmik@free.fr>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+T: git git://github.com/hzhuang1/linux.git
+T: git git://github.com/rjarzmik/linux.git
+S: Maintained
+F: arch/arm/boot/dts/pxa*
+F: arch/arm/mach-pxa/
+F: drivers/dma/pxa*
+F: drivers/pcmcia/pxa2xx*
+F: drivers/pinctrl/pxa/
+F: drivers/spi/spi-pxa2xx*
+F: drivers/usb/gadget/udc/pxa2*
+F: include/sound/pxa2xx-lib.h
+F: sound/arm/pxa*
+F: sound/soc/pxa/
+
+PXA GPIO DRIVER
+M: Robert Jarzmik <robert.jarzmik@free.fr>
+L: linux-gpio@vger.kernel.org
+S: Maintained
+F: drivers/gpio/gpio-pxa.c
+
+PXA3xx NAND FLASH DRIVER
+M: Ezequiel Garcia <ezequiel.garcia@free-electrons.com>
+L: linux-mtd@lists.infradead.org
+S: Maintained
+F: drivers/mtd/nand/pxa3xx_nand.c
+
+MMP SUPPORT
+M: Eric Miao <eric.y.miao@gmail.com>
+M: Haojian Zhuang <haojian.zhuang@gmail.com>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+T: git git://github.com/hzhuang1/linux.git
+T: git git://git.linaro.org/people/ycmiao/pxa-linux.git
+S: Maintained
+F: arch/arm/boot/dts/mmp*
+F: arch/arm/mach-mmp/
+
+PXA MMCI DRIVER
+S: Orphan
+
+PXA RTC DRIVER
+M: Robert Jarzmik <robert.jarzmik@free.fr>
+L: linux-rtc@vger.kernel.org
+S: Maintained
+
+QAT DRIVER
+M: Giovanni Cabiddu <giovanni.cabiddu@intel.com>
+M: Salvatore Benedetto <salvatore.benedetto@intel.com>
+L: qat-linux@intel.com
+S: Supported
+F: drivers/crypto/qat/
+
+QIB DRIVER
+M: Mike Marciniszyn <infinipath@intel.com>
+L: linux-rdma@vger.kernel.org
+S: Supported
+F: drivers/infiniband/hw/qib/
+
+QLOGIC QLA1280 SCSI DRIVER
+M: Michael Reed <mdr@sgi.com>
+L: linux-scsi@vger.kernel.org
+S: Maintained
+F: drivers/scsi/qla1280.[ch]
+
+QLOGIC QLA2XXX FC-SCSI DRIVER
+M: qla2xxx-upstream@qlogic.com
+L: linux-scsi@vger.kernel.org
+S: Supported
+F: Documentation/scsi/LICENSE.qla2xxx
+F: drivers/scsi/qla2xxx/
+
+QLOGIC QLA4XXX iSCSI DRIVER
+M: QLogic-Storage-Upstream@qlogic.com
+L: linux-scsi@vger.kernel.org
+S: Supported
+F: Documentation/scsi/LICENSE.qla4xxx
+F: drivers/scsi/qla4xxx/
+
+QLOGIC QLA3XXX NETWORK DRIVER
+M: Dept-GELinuxNICDev@cavium.com
+L: netdev@vger.kernel.org
+S: Supported
+F: Documentation/networking/LICENSE.qla3xxx
+F: drivers/net/ethernet/qlogic/qla3xxx.*
+
+QLOGIC QLCNIC (1/10)Gb ETHERNET DRIVER
+M: Harish Patil <harish.patil@cavium.com>
+M: Manish Chopra <manish.chopra@cavium.com>
+M: Dept-GELinuxNICDev@cavium.com
+L: netdev@vger.kernel.org
+S: Supported
+F: drivers/net/ethernet/qlogic/qlcnic/
+
+QLOGIC QLGE 10Gb ETHERNET DRIVER
+M: Harish Patil <harish.patil@cavium.com>
+M: Manish Chopra <manish.chopra@cavium.com>
+M: Dept-GELinuxNICDev@cavium.com
+L: netdev@vger.kernel.org
+S: Supported
+F: drivers/net/ethernet/qlogic/qlge/
+
+QLOGIC QL4xxx ETHERNET DRIVER
+M: Yuval Mintz <Yuval.Mintz@cavium.com>
+M: Ariel Elior <Ariel.Elior@cavium.com>
+M: everest-linux-l2@cavium.com
+L: netdev@vger.kernel.org
+S: Supported
+F: drivers/net/ethernet/qlogic/qed/
+F: include/linux/qed/
+F: drivers/net/ethernet/qlogic/qede/
+
+QLOGIC QL41xxx ISCSI DRIVER
+M: QLogic-Storage-Upstream@cavium.com
+L: linux-scsi@vger.kernel.org
+S: Supported
+F: drivers/scsi/qedi/
+
+QLOGIC QL41xxx FCOE DRIVER
+M: QLogic-Storage-Upstream@cavium.com
+L: linux-scsi@vger.kernel.org
+S: Supported
+F: drivers/scsi/qedf/
+
+QNX4 FILESYSTEM
+M: Anders Larsen <al@alarsen.net>
+W: http://www.alarsen.net/linux/qnx4fs/
+S: Maintained
+F: fs/qnx4/
+F: include/uapi/linux/qnx4_fs.h
+F: include/uapi/linux/qnxtypes.h
+
+QORIQ DPAA2 FSL-MC BUS DRIVER
+M: Stuart Yoder <stuyoder@gmail.com>
+M: Laurentiu Tudor <laurentiu.tudor@nxp.com>
+L: linux-kernel@vger.kernel.org
+S: Maintained
+F: drivers/staging/fsl-mc/
+
+QT1010 MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+W: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/media/tuners/qt1010*
+
+QUALCOMM ATHEROS ATH9K WIRELESS DRIVER
+M: QCA ath9k Development <ath9k-devel@qca.qualcomm.com>
+L: linux-wireless@vger.kernel.org
+W: http://wireless.kernel.org/en/users/Drivers/ath9k
+S: Supported
+F: drivers/net/wireless/ath/ath9k/
+
+QUALCOMM ATHEROS ATH10K WIRELESS DRIVER
+M: Kalle Valo <kvalo@qca.qualcomm.com>
+L: ath10k@lists.infradead.org
+W: http://wireless.kernel.org/en/users/Drivers/ath10k
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/kvalo/ath.git
+S: Supported
+F: drivers/net/wireless/ath/ath10k/
+
+QUALCOMM EMAC GIGABIT ETHERNET DRIVER
+M: Timur Tabi <timur@codeaurora.org>
+L: netdev@vger.kernel.org
+S: Supported
+F: drivers/net/ethernet/qualcomm/emac/
+
+QUALCOMM HEXAGON ARCHITECTURE
+M: Richard Kuo <rkuo@codeaurora.org>
+L: linux-hexagon@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/rkuo/linux-hexagon-kernel.git
+S: Supported
+F: arch/hexagon/
+
+QUALCOMM WCN36XX WIRELESS DRIVER
+M: Eugene Krasnikov <k.eugene.e@gmail.com>
+L: wcn36xx@lists.infradead.org
+W: http://wireless.kernel.org/en/users/Drivers/wcn36xx
+T: git git://github.com/KrasnikovEugene/wcn36xx.git
+S: Supported
+F: drivers/net/wireless/ath/wcn36xx/
+
+QEMU MACHINE EMULATOR AND VIRTUALIZER SUPPORT
+M: Gabriel Somlo <somlo@cmu.edu>
+M: "Michael S. Tsirkin" <mst@redhat.com>
+L: qemu-devel@nongnu.org
+S: Maintained
+F: drivers/firmware/qemu_fw_cfg.c
+
+RADOS BLOCK DEVICE (RBD)
+M: Ilya Dryomov <idryomov@gmail.com>
+M: Sage Weil <sage@redhat.com>
+M: Alex Elder <elder@kernel.org>
+L: ceph-devel@vger.kernel.org
+W: http://ceph.com/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/sage/ceph-client.git
+T: git git://github.com/ceph/ceph-client.git
+S: Supported
+F: Documentation/ABI/testing/sysfs-bus-rbd
+F: drivers/block/rbd.c
+F: drivers/block/rbd_types.h
+
+RADEON FRAMEBUFFER DISPLAY DRIVER
+M: Benjamin Herrenschmidt <benh@kernel.crashing.org>
+L: linux-fbdev@vger.kernel.org
+S: Maintained
+F: drivers/video/fbdev/aty/radeon*
+F: include/uapi/linux/radeonfb.h
+
+RADIOSHARK RADIO DRIVER
+M: Hans Verkuil <hverkuil@xs4all.nl>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+S: Maintained
+F: drivers/media/radio/radio-shark.c
+
+RADIOSHARK2 RADIO DRIVER
+M: Hans Verkuil <hverkuil@xs4all.nl>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+S: Maintained
+F: drivers/media/radio/radio-shark2.c
+F: drivers/media/radio/radio-tea5777.c
+
+RAGE128 FRAMEBUFFER DISPLAY DRIVER
+M: Paul Mackerras <paulus@samba.org>
+L: linux-fbdev@vger.kernel.org
+S: Maintained
+F: drivers/video/fbdev/aty/aty128fb.c
+
+RAINSHADOW-CEC DRIVER
+M: Hans Verkuil <hverkuil@xs4all.nl>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+S: Maintained
+F: drivers/media/usb/rainshadow-cec/*
+
+RALINK MIPS ARCHITECTURE
+M: John Crispin <john@phrozen.org>
+L: linux-mips@linux-mips.org
+S: Maintained
+F: arch/mips/ralink
+
+RALINK RT2X00 WIRELESS LAN DRIVER
+P: rt2x00 project
+M: Stanislaw Gruszka <sgruszka@redhat.com>
+M: Helmut Schaa <helmut.schaa@googlemail.com>
+L: linux-wireless@vger.kernel.org
+S: Maintained
+F: drivers/net/wireless/ralink/rt2x00/
+
+RAMDISK RAM BLOCK DEVICE DRIVER
+M: Jens Axboe <axboe@kernel.dk>
+S: Maintained
+F: Documentation/blockdev/ramdisk.txt
+F: drivers/block/brd.c
+
+RANDOM NUMBER DRIVER
+M: "Theodore Ts'o" <tytso@mit.edu>
+S: Maintained
+F: drivers/char/random.c
+
+RAPIDIO SUBSYSTEM
+M: Matt Porter <mporter@kernel.crashing.org>
+M: Alexandre Bounine <alexandre.bounine@idt.com>
+S: Maintained
+F: drivers/rapidio/
+
+RAYLINK/WEBGEAR 802.11 WIRELESS LAN DRIVER
+L: linux-wireless@vger.kernel.org
+S: Orphan
+F: drivers/net/wireless/ray*
+
+RCUTORTURE MODULE
+M: Josh Triplett <josh@joshtriplett.org>
+M: "Paul E. McKenney" <paulmck@linux.vnet.ibm.com>
+L: linux-kernel@vger.kernel.org
+S: Supported
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/paulmck/linux-rcu.git
+F: Documentation/RCU/torture.txt
+F: kernel/rcu/rcutorture.c
+
+RCUTORTURE TEST FRAMEWORK
+M: "Paul E. McKenney" <paulmck@linux.vnet.ibm.com>
+M: Josh Triplett <josh@joshtriplett.org>
+R: Steven Rostedt <rostedt@goodmis.org>
+R: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+R: Lai Jiangshan <jiangshanlai@gmail.com>
+L: linux-kernel@vger.kernel.org
+S: Supported
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/paulmck/linux-rcu.git
+F: tools/testing/selftests/rcutorture
+
+RDC R-321X SoC
+M: Florian Fainelli <florian@openwrt.org>
+S: Maintained
+
+RDC R6040 FAST ETHERNET DRIVER
+M: Florian Fainelli <f.fainelli@gmail.com>
+L: netdev@vger.kernel.org
+S: Maintained
+F: drivers/net/ethernet/rdc/r6040.c
+
+RDS - RELIABLE DATAGRAM SOCKETS
+M: Santosh Shilimkar <santosh.shilimkar@oracle.com>
+L: netdev@vger.kernel.org
+L: linux-rdma@vger.kernel.org
+L: rds-devel@oss.oracle.com (moderated for non-subscribers)
+W: https://oss.oracle.com/projects/rds/
+S: Supported
+F: net/rds/
+F: Documentation/networking/rds.txt
+
+RDMAVT - RDMA verbs software
+M: Dennis Dalessandro <dennis.dalessandro@intel.com>
+L: linux-rdma@vger.kernel.org
+S: Supported
+F: drivers/infiniband/sw/rdmavt
+
+RDT - RESOURCE ALLOCATION
+M: Fenghua Yu <fenghua.yu@intel.com>
+L: linux-kernel@vger.kernel.org
+S: Supported
+F: arch/x86/kernel/cpu/intel_rdt*
+F: arch/x86/include/asm/intel_rdt*
+F: Documentation/x86/intel_rdt*
+
+READ-COPY UPDATE (RCU)
+M: "Paul E. McKenney" <paulmck@linux.vnet.ibm.com>
+M: Josh Triplett <josh@joshtriplett.org>
+R: Steven Rostedt <rostedt@goodmis.org>
+R: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+R: Lai Jiangshan <jiangshanlai@gmail.com>
+L: linux-kernel@vger.kernel.org
+W: http://www.rdrop.com/users/paulmck/RCU/
+S: Supported
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/paulmck/linux-rcu.git
+F: Documentation/RCU/
+X: Documentation/RCU/torture.txt
+F: include/linux/rcu*
+X: include/linux/srcu.h
+F: kernel/rcu/
+X: kernel/torture.c
+
+REAL TIME CLOCK (RTC) SUBSYSTEM
+M: Alessandro Zummo <a.zummo@towertech.it>
+M: Alexandre Belloni <alexandre.belloni@free-electrons.com>
+L: linux-rtc@vger.kernel.org
+Q: http://patchwork.ozlabs.org/project/rtc-linux/list/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux.git
+S: Maintained
+F: Documentation/devicetree/bindings/rtc/
+F: Documentation/rtc.txt
+F: drivers/rtc/
+F: include/linux/rtc.h
+F: include/uapi/linux/rtc.h
+F: include/linux/rtc/
+F: include/linux/platform_data/rtc-*
+F: tools/testing/selftests/timers/rtctest.c
+
+REALTEK AUDIO CODECS
+M: Bard Liao <bardliao@realtek.com>
+M: Oder Chiou <oder_chiou@realtek.com>
+S: Maintained
+F: sound/soc/codecs/rt*
+F: include/sound/rt*.h
+
+REISERFS FILE SYSTEM
+L: reiserfs-devel@vger.kernel.org
+S: Supported
+F: fs/reiserfs/
+
+REGISTER MAP ABSTRACTION
+M: Mark Brown <broonie@kernel.org>
+L: linux-kernel@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap.git
+S: Supported
+F: Documentation/devicetree/bindings/regmap/
+F: drivers/base/regmap/
+F: include/linux/regmap.h
+
+REMOTE PROCESSOR (REMOTEPROC) SUBSYSTEM
+M: Ohad Ben-Cohen <ohad@wizery.com>
+M: Bjorn Andersson <bjorn.andersson@linaro.org>
+L: linux-remoteproc@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/ohad/remoteproc.git
+S: Maintained
+F: Documentation/devicetree/bindings/remoteproc/
+F: Documentation/remoteproc.txt
+F: drivers/remoteproc/
+F: include/linux/remoteproc.h
+
+REMOTE PROCESSOR MESSAGING (RPMSG) SUBSYSTEM
+M: Ohad Ben-Cohen <ohad@wizery.com>
+M: Bjorn Andersson <bjorn.andersson@linaro.org>
+L: linux-remoteproc@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/ohad/rpmsg.git
+S: Maintained
+F: drivers/rpmsg/
+F: Documentation/rpmsg.txt
+F: include/linux/rpmsg.h
+F: include/linux/rpmsg/
+
+RENESAS CLOCK DRIVERS
+M: Geert Uytterhoeven <geert+renesas@glider.be>
+L: linux-renesas-soc@vger.kernel.org
+S: Supported
+F: drivers/clk/renesas/
+
+RENESAS ETHERNET DRIVERS
+R: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>
+L: netdev@vger.kernel.org
+L: linux-renesas-soc@vger.kernel.org
+F: drivers/net/ethernet/renesas/
+F: include/linux/sh_eth.h
+
+RENESAS R-CAR GYROADC DRIVER
+M: Marek Vasut <marek.vasut@gmail.com>
+L: linux-iio@vger.kernel.org
+S: Supported
+F: drivers/iio/adc/rcar_gyro_adc.c
+
+RENESAS USB2 PHY DRIVER
+M: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
+L: linux-renesas-soc@vger.kernel.org
+S: Maintained
+F: drivers/phy/phy-rcar-gen3-usb2.c
+
+RESET CONTROLLER FRAMEWORK
+M: Philipp Zabel <p.zabel@pengutronix.de>
+T: git git://git.pengutronix.de/git/pza/linux
+S: Maintained
+F: drivers/reset/
+F: Documentation/devicetree/bindings/reset/
+F: include/dt-bindings/reset/
+F: include/linux/reset.h
+F: include/linux/reset-controller.h
+
+RFKILL
+M: Johannes Berg <johannes@sipsolutions.net>
+L: linux-wireless@vger.kernel.org
+W: http://wireless.kernel.org/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211.git
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next.git
+S: Maintained
+F: Documentation/rfkill.txt
+F: net/rfkill/
+
+RHASHTABLE
+M: Thomas Graf <tgraf@suug.ch>
+M: Herbert Xu <herbert@gondor.apana.org.au>
+L: netdev@vger.kernel.org
+S: Maintained
+F: lib/rhashtable.c
+F: include/linux/rhashtable.h
+
+RICOH SMARTMEDIA/XD DRIVER
+M: Maxim Levitsky <maximlevitsky@gmail.com>
+S: Maintained
+F: drivers/mtd/nand/r852.c
+F: drivers/mtd/nand/r852.h
+
+RICOH R5C592 MEMORYSTICK DRIVER
+M: Maxim Levitsky <maximlevitsky@gmail.com>
+S: Maintained
+F: drivers/memstick/host/r592.*
+
+ROCCAT DRIVERS
+M: Stefan Achatz <erazor_de@users.sourceforge.net>
+W: http://sourceforge.net/projects/roccat/
+S: Maintained
+F: drivers/hid/hid-roccat*
+F: include/linux/hid-roccat*
+F: Documentation/ABI/*/sysfs-driver-hid-roccat*
+
+ROCKER DRIVER
+M: Jiri Pirko <jiri@resnulli.us>
+L: netdev@vger.kernel.org
+S: Supported
+F: drivers/net/ethernet/rocker/
+
+ROCKETPORT DRIVER
+P: Comtrol Corp.
+W: http://www.comtrol.com
+S: Maintained
+F: Documentation/serial/rocket.txt
+F: drivers/tty/rocket*
+
+ROCKETPORT EXPRESS/INFINITY DRIVER
+M: Kevin Cernekee <cernekee@gmail.com>
+L: linux-serial@vger.kernel.org
+S: Odd Fixes
+F: drivers/tty/serial/rp2.*
+
+ROSE NETWORK LAYER
+M: Ralf Baechle <ralf@linux-mips.org>
+L: linux-hams@vger.kernel.org
+W: http://www.linux-ax25.org/
+S: Maintained
+F: include/net/rose.h
+F: include/uapi/linux/rose.h
+F: net/rose/
+
+RTL2830 MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+W: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/media/dvb-frontends/rtl2830*
+
+RTL2832 MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+W: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/media/dvb-frontends/rtl2832*
+
+RTL2832_SDR MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+W: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/media/dvb-frontends/rtl2832_sdr*
+
+RTL8180 WIRELESS DRIVER
+L: linux-wireless@vger.kernel.org
+W: http://wireless.kernel.org/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-testing.git
+S: Orphan
+F: drivers/net/wireless/realtek/rtl818x/rtl8180/
+
+RTL8187 WIRELESS DRIVER
+M: Herton Ronaldo Krzesinski <herton@canonical.com>
+M: Hin-Tak Leung <htl10@users.sourceforge.net>
+M: Larry Finger <Larry.Finger@lwfinger.net>
+L: linux-wireless@vger.kernel.org
+W: http://wireless.kernel.org/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-testing.git
+S: Maintained
+F: drivers/net/wireless/realtek/rtl818x/rtl8187/
+
+RTL8192CE WIRELESS DRIVER
+M: Larry Finger <Larry.Finger@lwfinger.net>
+M: Chaoming Li <chaoming_li@realsil.com.cn>
+L: linux-wireless@vger.kernel.org
+W: http://wireless.kernel.org/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-testing.git
+S: Maintained
+F: drivers/net/wireless/realtek/rtlwifi/
+F: drivers/net/wireless/realtek/rtlwifi/rtl8192ce/
+
+RTL8XXXU WIRELESS DRIVER (rtl8xxxu)
+M: Jes Sorensen <Jes.Sorensen@gmail.com>
+L: linux-wireless@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/jes/linux.git rtl8xxxu-devel
+S: Maintained
+F: drivers/net/wireless/realtek/rtl8xxxu/
+
+S3 SAVAGE FRAMEBUFFER DRIVER
+M: Antonino Daplas <adaplas@gmail.com>
+L: linux-fbdev@vger.kernel.org
+S: Maintained
+F: drivers/video/fbdev/savage/
+
+S390
+M: Martin Schwidefsky <schwidefsky@de.ibm.com>
+M: Heiko Carstens <heiko.carstens@de.ibm.com>
+L: linux-s390@vger.kernel.org
+W: http://www.ibm.com/developerworks/linux/linux390/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux.git
+S: Supported
+F: arch/s390/
+F: drivers/s390/
+F: Documentation/s390/
+F: Documentation/DocBook/s390*
+
+S390 COMMON I/O LAYER
+M: Sebastian Ott <sebott@linux.vnet.ibm.com>
+M: Peter Oberparleiter <oberpar@linux.vnet.ibm.com>
+L: linux-s390@vger.kernel.org
+W: http://www.ibm.com/developerworks/linux/linux390/
+S: Supported
+F: drivers/s390/cio/
+
+S390 DASD DRIVER
+M: Stefan Haberland <sth@linux.vnet.ibm.com>
+M: Jan Hoeppner <hoeppner@linux.vnet.ibm.com>
+L: linux-s390@vger.kernel.org
+W: http://www.ibm.com/developerworks/linux/linux390/
+S: Supported
+F: drivers/s390/block/dasd*
+F: block/partitions/ibm.c
+
+S390 NETWORK DRIVERS
+M: Julian Wiedmann <jwi@linux.vnet.ibm.com>
+M: Ursula Braun <ubraun@linux.vnet.ibm.com>
+L: linux-s390@vger.kernel.org
+W: http://www.ibm.com/developerworks/linux/linux390/
+S: Supported
+F: drivers/s390/net/
+
+S390 PCI SUBSYSTEM
+M: Sebastian Ott <sebott@linux.vnet.ibm.com>
+M: Gerald Schaefer <gerald.schaefer@de.ibm.com>
+L: linux-s390@vger.kernel.org
+W: http://www.ibm.com/developerworks/linux/linux390/
+S: Supported
+F: arch/s390/pci/
+F: drivers/pci/hotplug/s390_pci_hpc.c
+
+S390 ZCRYPT DRIVER
+M: Harald Freudenberger <freude@de.ibm.com>
+L: linux-s390@vger.kernel.org
+W: http://www.ibm.com/developerworks/linux/linux390/
+S: Supported
+F: drivers/s390/crypto/
+
+S390 ZFCP DRIVER
+M: Steffen Maier <maier@linux.vnet.ibm.com>
+L: linux-s390@vger.kernel.org
+W: http://www.ibm.com/developerworks/linux/linux390/
+S: Supported
+F: drivers/s390/scsi/zfcp_*
+
+S390 IUCV NETWORK LAYER
+M: Julian Wiedmann <jwi@linux.vnet.ibm.com>
+M: Ursula Braun <ubraun@linux.vnet.ibm.com>
+L: linux-s390@vger.kernel.org
+W: http://www.ibm.com/developerworks/linux/linux390/
+S: Supported
+F: drivers/s390/net/*iucv*
+F: include/net/iucv/
+F: net/iucv/
+
+S390 IOMMU (PCI)
+M: Gerald Schaefer <gerald.schaefer@de.ibm.com>
+L: linux-s390@vger.kernel.org
+W: http://www.ibm.com/developerworks/linux/linux390/
+S: Supported
+F: drivers/iommu/s390-iommu.c
+
+S390 VFIO-CCW DRIVER
+M: Cornelia Huck <cornelia.huck@de.ibm.com>
+M: Dong Jia Shi <bjsdjshi@linux.vnet.ibm.com>
+L: linux-s390@vger.kernel.org
+L: kvm@vger.kernel.org
+S: Supported
+F: drivers/s390/cio/vfio_ccw*
+F: Documentation/s390/vfio-ccw.txt
+F: include/uapi/linux/vfio_ccw.h
+
+S3C24XX SD/MMC Driver
+M: Ben Dooks <ben-linux@fluff.org>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Supported
+F: drivers/mmc/host/s3cmci.*
+
+SAA6588 RDS RECEIVER DRIVER
+M: Hans Verkuil <hverkuil@xs4all.nl>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+W: https://linuxtv.org
+S: Odd Fixes
+F: drivers/media/i2c/saa6588*
+
+SAA7134 VIDEO4LINUX DRIVER
+M: Mauro Carvalho Chehab <mchehab@s-opensource.com>
+M: Mauro Carvalho Chehab <mchehab@kernel.org>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+T: git git://linuxtv.org/media_tree.git
+S: Odd fixes
+F: Documentation/media/v4l-drivers/saa7134*
+F: drivers/media/pci/saa7134/
+
+SAA7146 VIDEO4LINUX-2 DRIVER
+M: Hans Verkuil <hverkuil@xs4all.nl>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+S: Maintained
+F: drivers/media/common/saa7146/
+F: drivers/media/pci/saa7146/
+F: include/media/saa7146*
+
+SAMSUNG LAPTOP DRIVER
+M: Corentin Chary <corentin.chary@gmail.com>
+L: platform-driver-x86@vger.kernel.org
+S: Maintained
+F: drivers/platform/x86/samsung-laptop.c
+
+SAMSUNG AUDIO (ASoC) DRIVERS
+M: Krzysztof Kozlowski <krzk@kernel.org>
+M: Sangbeom Kim <sbkim73@samsung.com>
+M: Sylwester Nawrocki <s.nawrocki@samsung.com>
+L: alsa-devel@alsa-project.org (moderated for non-subscribers)
+S: Supported
+F: sound/soc/samsung/
+
+SAMSUNG EXYNOS PSEUDO RANDOM NUMBER GENERATOR (RNG) DRIVER
+M: Krzysztof Kozlowski <krzk@kernel.org>
+L: linux-crypto@vger.kernel.org
+L: linux-samsung-soc@vger.kernel.org
+S: Maintained
+F: drivers/crypto/exynos-rng.c
+F: Documentation/devicetree/bindings/rng/samsung,exynos-rng4.txt
+
+SAMSUNG FRAMEBUFFER DRIVER
+M: Jingoo Han <jingoohan1@gmail.com>
+L: linux-fbdev@vger.kernel.org
+S: Maintained
+F: drivers/video/fbdev/s3c-fb.c
+
+SAMSUNG MULTIFUNCTION PMIC DEVICE DRIVERS
+M: Sangbeom Kim <sbkim73@samsung.com>
+M: Krzysztof Kozlowski <krzk@kernel.org>
+M: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
+L: linux-kernel@vger.kernel.org
+L: linux-samsung-soc@vger.kernel.org
+S: Supported
+F: drivers/mfd/sec*.c
+F: drivers/regulator/s2m*.c
+F: drivers/regulator/s5m*.c
+F: drivers/clk/clk-s2mps11.c
+F: drivers/rtc/rtc-s5m.c
+F: include/linux/mfd/samsung/
+F: Documentation/devicetree/bindings/mfd/samsung,sec-core.txt
+F: Documentation/devicetree/bindings/regulator/samsung,s2m*.txt
+F: Documentation/devicetree/bindings/regulator/samsung,s5m*.txt
+F: Documentation/devicetree/bindings/clock/samsung,s2mps11.txt
+
+SAMSUNG S5P Security SubSystem (SSS) DRIVER
+M: Krzysztof Kozlowski <krzk@kernel.org>
+M: Vladimir Zapolskiy <vz@mleia.com>
+L: linux-crypto@vger.kernel.org
+L: linux-samsung-soc@vger.kernel.org
+S: Maintained
+F: drivers/crypto/s5p-sss.c
+
+SAMSUNG S5P/EXYNOS4 SOC SERIES CAMERA SUBSYSTEM DRIVERS
+M: Kyungmin Park <kyungmin.park@samsung.com>
+M: Sylwester Nawrocki <s.nawrocki@samsung.com>
+L: linux-media@vger.kernel.org
+Q: https://patchwork.linuxtv.org/project/linux-media/list/
+S: Supported
+F: drivers/media/platform/exynos4-is/
+
+SAMSUNG S3C24XX/S3C64XX SOC SERIES CAMIF DRIVER
+M: Sylwester Nawrocki <sylvester.nawrocki@gmail.com>
+L: linux-media@vger.kernel.org
+L: linux-samsung-soc@vger.kernel.org (moderated for non-subscribers)
+S: Maintained
+F: drivers/media/platform/s3c-camif/
+F: include/media/drv-intf/s3c_camif.h
+
+SAMSUNG S5C73M3 CAMERA DRIVER
+M: Kyungmin Park <kyungmin.park@samsung.com>
+M: Andrzej Hajda <a.hajda@samsung.com>
+L: linux-media@vger.kernel.org
+S: Supported
+F: drivers/media/i2c/s5c73m3/*
+
+SAMSUNG S5K5BAF CAMERA DRIVER
+M: Kyungmin Park <kyungmin.park@samsung.com>
+M: Andrzej Hajda <a.hajda@samsung.com>
+L: linux-media@vger.kernel.org
+S: Supported
+F: drivers/media/i2c/s5k5baf.c
+
+SAMSUNG S3FWRN5 NFC DRIVER
+M: Robert Baldyga <r.baldyga@samsung.com>
+M: Krzysztof Opasiak <k.opasiak@samsung.com>
+L: linux-nfc@lists.01.org (moderated for non-subscribers)
+S: Supported
+F: drivers/nfc/s3fwrn5
+
+SAMSUNG SOC CLOCK DRIVERS
+M: Sylwester Nawrocki <s.nawrocki@samsung.com>
+M: Tomasz Figa <tomasz.figa@gmail.com>
+M: Chanwoo Choi <cw00.choi@samsung.com>
+S: Supported
+L: linux-samsung-soc@vger.kernel.org (moderated for non-subscribers)
+F: drivers/clk/samsung/
+F: include/dt-bindings/clock/exynos*.h
+F: Documentation/devicetree/bindings/clock/exynos*.txt
+
+SAMSUNG SPI DRIVERS
+M: Kukjin Kim <kgene@kernel.org>
+M: Krzysztof Kozlowski <krzk@kernel.org>
+M: Andi Shyti <andi.shyti@samsung.com>
+L: linux-spi@vger.kernel.org
+L: linux-samsung-soc@vger.kernel.org (moderated for non-subscribers)
+S: Maintained
+F: Documentation/devicetree/bindings/spi/spi-samsung.txt
+F: drivers/spi/spi-s3c*
+F: include/linux/platform_data/spi-s3c64xx.h
+
+SAMSUNG SXGBE DRIVERS
+M: Byungho An <bh74.an@samsung.com>
+M: Girish K S <ks.giri@samsung.com>
+M: Vipul Pandya <vipul.pandya@samsung.com>
+S: Supported
+L: netdev@vger.kernel.org
+F: drivers/net/ethernet/samsung/sxgbe/
+
+SAMSUNG THERMAL DRIVER
+M: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
+L: linux-pm@vger.kernel.org
+L: linux-samsung-soc@vger.kernel.org
+S: Supported
+T: git https://github.com/lmajewski/linux-samsung-thermal.git
+F: drivers/thermal/samsung/
+
+SAMSUNG USB2 PHY DRIVER
+M: Kamil Debski <kamil@wypas.org>
+M: Sylwester Nawrocki <s.nawrocki@samsung.com>
+L: linux-kernel@vger.kernel.org
+S: Supported
+F: Documentation/devicetree/bindings/phy/samsung-phy.txt
+F: Documentation/phy/samsung-usb2.txt
+F: drivers/phy/phy-exynos4210-usb2.c
+F: drivers/phy/phy-exynos4x12-usb2.c
+F: drivers/phy/phy-exynos5250-usb2.c
+F: drivers/phy/phy-s5pv210-usb2.c
+F: drivers/phy/phy-samsung-usb2.c
+F: drivers/phy/phy-samsung-usb2.h
+
+SERIAL DRIVERS
+M: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+L: linux-serial@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/serial/
+F: drivers/tty/serial/
+
+SERIAL DEVICE BUS
+M: Rob Herring <robh@kernel.org>
+L: linux-serial@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/serial/slave-device.txt
+F: drivers/tty/serdev/
+F: include/linux/serdev.h
+
+SERIAL IR RECEIVER
+M: Sean Young <sean@mess.org>
+L: linux-media@vger.kernel.org
+S: Maintained
+F: drivers/media/rc/serial_ir.c
+
+STI CEC DRIVER
+M: Benjamin Gaignard <benjamin.gaignard@linaro.org>
+S: Maintained
+F: drivers/staging/media/st-cec/
+F: Documentation/devicetree/bindings/media/stih-cec.txt
+
+SHARED MEMORY COMMUNICATIONS (SMC) SOCKETS
+M: Ursula Braun <ubraun@linux.vnet.ibm.com>
+L: linux-s390@vger.kernel.org
+W: http://www.ibm.com/developerworks/linux/linux390/
+S: Supported
+F: net/smc/
+
+SYNOPSYS DESIGNWARE DMAC DRIVER
+M: Viresh Kumar <vireshk@kernel.org>
+M: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+S: Maintained
+F: include/linux/dma/dw.h
+F: include/linux/platform_data/dma-dw.h
+F: drivers/dma/dw/
+
+SYNOPSYS DESIGNWARE ENTERPRISE ETHERNET DRIVER
+M: Jie Deng <jiedeng@synopsys.com>
+L: netdev@vger.kernel.org
+S: Supported
+F: drivers/net/ethernet/synopsys/
+
+SYNOPSYS DESIGNWARE I2C DRIVER
+M: Jarkko Nikula <jarkko.nikula@linux.intel.com>
+R: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+R: Mika Westerberg <mika.westerberg@linux.intel.com>
+L: linux-i2c@vger.kernel.org
+S: Maintained
+F: drivers/i2c/busses/i2c-designware-*
+F: include/linux/platform_data/i2c-designware.h
+
+SYNOPSYS DESIGNWARE MMC/SD/SDIO DRIVER
+M: Jaehoon Chung <jh80.chung@samsung.com>
+L: linux-mmc@vger.kernel.org
+S: Maintained
+F: drivers/mmc/host/dw_mmc*
+
+SYSTEM TRACE MODULE CLASS
+M: Alexander Shishkin <alexander.shishkin@linux.intel.com>
+S: Maintained
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/ash/stm.git
+F: Documentation/trace/stm.txt
+F: drivers/hwtracing/stm/
+F: include/linux/stm.h
+F: include/uapi/linux/stm.h
+
+TEE SUBSYSTEM
+M: Jens Wiklander <jens.wiklander@linaro.org>
+S: Maintained
+F: include/linux/tee_drv.h
+F: include/uapi/linux/tee.h
+F: drivers/tee/
+F: Documentation/tee.txt
+
+THUNDERBOLT DRIVER
+M: Andreas Noever <andreas.noever@gmail.com>
+S: Maintained
+F: drivers/thunderbolt/
+
+TI BQ27XXX POWER SUPPLY DRIVER
+R: Andrew F. Davis <afd@ti.com>
+F: include/linux/power/bq27xxx_battery.h
+F: drivers/power/supply/bq27xxx_battery.c
+F: drivers/power/supply/bq27xxx_battery_i2c.c
+
+TIMEKEEPING, CLOCKSOURCE CORE, NTP, ALARMTIMER
+M: John Stultz <john.stultz@linaro.org>
+M: Thomas Gleixner <tglx@linutronix.de>
+R: Stephen Boyd <sboyd@codeaurora.org>
+L: linux-kernel@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git timers/core
+S: Supported
+F: include/linux/clocksource.h
+F: include/linux/time.h
+F: include/linux/timex.h
+F: include/uapi/linux/time.h
+F: include/uapi/linux/timex.h
+F: kernel/time/clocksource.c
+F: kernel/time/time*.c
+F: kernel/time/alarmtimer.c
+F: kernel/time/ntp.c
+F: tools/testing/selftests/timers/
+
+SC1200 WDT DRIVER
+M: Zwane Mwaikambo <zwanem@gmail.com>
+S: Maintained
+F: drivers/watchdog/sc1200wdt.c
+
+SCHEDULER
+M: Ingo Molnar <mingo@redhat.com>
+M: Peter Zijlstra <peterz@infradead.org>
+L: linux-kernel@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git sched/core
+S: Maintained
+F: kernel/sched/
+F: include/linux/sched.h
+F: include/uapi/linux/sched.h
+F: include/linux/wait.h
+
+SCORE ARCHITECTURE
+M: Chen Liqin <liqin.linux@gmail.com>
+M: Lennox Wu <lennox.wu@gmail.com>
+W: http://www.sunplus.com
+S: Supported
+F: arch/score/
+
+SCR24X CHIP CARD INTERFACE DRIVER
+M: Lubomir Rintel <lkundrak@v3.sk>
+S: Supported
+F: drivers/char/pcmcia/scr24x_cs.c
+
+SYSTEM CONTROL & POWER INTERFACE (SCPI) Message Protocol drivers
+M: Sudeep Holla <sudeep.holla@arm.com>
+L: linux-arm-kernel@lists.infradead.org
+S: Maintained
+F: Documentation/devicetree/bindings/arm/arm,scpi.txt
+F: drivers/clk/clk-scpi.c
+F: drivers/cpufreq/scpi-cpufreq.c
+F: drivers/firmware/arm_scpi.c
+F: include/linux/scpi_protocol.h
+
+SCSI CDROM DRIVER
+M: Jens Axboe <axboe@kernel.dk>
+L: linux-scsi@vger.kernel.org
+W: http://www.kernel.dk
+S: Maintained
+F: drivers/scsi/sr*
+
+SCSI RDMA PROTOCOL (SRP) INITIATOR
+M: Bart Van Assche <bart.vanassche@sandisk.com>
+L: linux-rdma@vger.kernel.org
+S: Supported
+W: http://www.openfabrics.org
+Q: http://patchwork.kernel.org/project/linux-rdma/list/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/dad/srp-initiator.git
+F: drivers/infiniband/ulp/srp/
+F: include/scsi/srp.h
+
+SCSI SG DRIVER
+M: Doug Gilbert <dgilbert@interlog.com>
+L: linux-scsi@vger.kernel.org
+W: http://sg.danny.cz/sg
+S: Maintained
+F: Documentation/scsi/scsi-generic.txt
+F: drivers/scsi/sg.c
+F: include/scsi/sg.h
+
+SCSI SUBSYSTEM
+M: "James E.J. Bottomley" <jejb@linux.vnet.ibm.com>
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi.git
+M: "Martin K. Petersen" <martin.petersen@oracle.com>
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/mkp/scsi.git
+L: linux-scsi@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/scsi/
+F: drivers/scsi/
+F: include/scsi/
+
+SCSI TAPE DRIVER
+M: Kai Mäkisara <Kai.Makisara@kolumbus.fi>
+L: linux-scsi@vger.kernel.org
+S: Maintained
+F: Documentation/scsi/st.txt
+F: drivers/scsi/st.*
+F: drivers/scsi/st_*.h
+
+SCTP PROTOCOL
+M: Vlad Yasevich <vyasevich@gmail.com>
+M: Neil Horman <nhorman@tuxdriver.com>
+L: linux-sctp@vger.kernel.org
+W: http://lksctp.sourceforge.net
+S: Maintained
+F: Documentation/networking/sctp.txt
+F: include/linux/sctp.h
+F: include/uapi/linux/sctp.h
+F: include/net/sctp/
+F: net/sctp/
+
+SCx200 CPU SUPPORT
+M: Jim Cromie <jim.cromie@gmail.com>
+S: Odd Fixes
+F: Documentation/i2c/busses/scx200_acb
+F: arch/x86/platform/scx200/
+F: drivers/watchdog/scx200_wdt.c
+F: drivers/i2c/busses/scx200*
+F: drivers/mtd/maps/scx200_docflash.c
+F: include/linux/scx200.h
+
+SCx200 GPIO DRIVER
+M: Jim Cromie <jim.cromie@gmail.com>
+S: Maintained
+F: drivers/char/scx200_gpio.c
+F: include/linux/scx200_gpio.h
+
+SCx200 HRT CLOCKSOURCE DRIVER
+M: Jim Cromie <jim.cromie@gmail.com>
+S: Maintained
+F: drivers/clocksource/scx200_hrt.c
+
+SDRICOH_CS MMC/SD HOST CONTROLLER INTERFACE DRIVER
+M: Sascha Sommer <saschasommer@freenet.de>
+L: sdricohcs-devel@lists.sourceforge.net (subscribers-only)
+S: Maintained
+F: drivers/mmc/host/sdricoh_cs.c
+
+SECURE DIGITAL HOST CONTROLLER INTERFACE (SDHCI) DRIVER
+M: Adrian Hunter <adrian.hunter@intel.com>
+L: linux-mmc@vger.kernel.org
+T: git git://git.infradead.org/users/ahunter/linux-sdhci.git
+S: Maintained
+F: drivers/mmc/host/sdhci*
+F: include/linux/mmc/sdhci*
+
+SECURE COMPUTING
+M: Kees Cook <keescook@chromium.org>
+R: Andy Lutomirski <luto@amacapital.net>
+R: Will Drewry <wad@chromium.org>
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git seccomp
+S: Supported
+F: kernel/seccomp.c
+F: include/uapi/linux/seccomp.h
+F: include/linux/seccomp.h
+F: tools/testing/selftests/seccomp/*
+K: \bsecure_computing
+K: \bTIF_SECCOMP\b
+
+SECURE DIGITAL HOST CONTROLLER INTERFACE (SDHCI) Broadcom BRCMSTB DRIVER
+M: Al Cooper <alcooperx@gmail.com>
+L: linux-mmc@vger.kernel.org
+L: bcm-kernel-feedback-list@broadcom.com
+S: Maintained
+F: drivers/mmc/host/sdhci-brcmstb*
+
+SECURE DIGITAL HOST CONTROLLER INTERFACE (SDHCI) SAMSUNG DRIVER
+M: Ben Dooks <ben-linux@fluff.org>
+M: Jaehoon Chung <jh80.chung@samsung.com>
+L: linux-mmc@vger.kernel.org
+S: Maintained
+F: drivers/mmc/host/sdhci-s3c*
+
+SECURE DIGITAL HOST CONTROLLER INTERFACE (SDHCI) ST SPEAR DRIVER
+M: Viresh Kumar <vireshk@kernel.org>
+L: linux-mmc@vger.kernel.org
+S: Maintained
+F: drivers/mmc/host/sdhci-spear.c
+
+SECURE ENCRYPTING DEVICE (SED) OPAL DRIVER
+M: Scott Bauer <scott.bauer@intel.com>
+M: Jonathan Derrick <jonathan.derrick@intel.com>
+M: Rafael Antognolli <rafael.antognolli@intel.com>
+L: linux-block@vger.kernel.org
+S: Supported
+F: block/sed*
+F: block/opal_proto.h
+F: include/linux/sed*
+F: include/uapi/linux/sed*
+
+SECURITY SUBSYSTEM
+M: James Morris <james.l.morris@oracle.com>
+M: "Serge E. Hallyn" <serge@hallyn.com>
+L: linux-security-module@vger.kernel.org (suggested Cc:)
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security.git
+W: http://kernsec.org/
+S: Supported
+F: security/
+
+SECURITY CONTACT
+M: Security Officers <security@kernel.org>
+S: Supported
+
+SELINUX SECURITY MODULE
+M: Paul Moore <paul@paul-moore.com>
+M: Stephen Smalley <sds@tycho.nsa.gov>
+M: Eric Paris <eparis@parisplace.org>
+L: selinux@tycho.nsa.gov (moderated for non-subscribers)
+W: http://selinuxproject.org
+T: git git://git.infradead.org/users/pcmoore/selinux
+S: Supported
+F: include/linux/selinux*
+F: security/selinux/
+F: scripts/selinux/
+
+APPARMOR SECURITY MODULE
+M: John Johansen <john.johansen@canonical.com>
+L: apparmor@lists.ubuntu.com (subscribers-only, general discussion)
+W: apparmor.wiki.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/jj/apparmor-dev.git
+S: Supported
+F: security/apparmor/
+
+LOADPIN SECURITY MODULE
+M: Kees Cook <keescook@chromium.org>
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git lsm/loadpin
+S: Supported
+F: security/loadpin/
+
+YAMA SECURITY MODULE
+M: Kees Cook <keescook@chromium.org>
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git yama/tip
+S: Supported
+F: security/yama/
+
+SENSABLE PHANTOM
+M: Jiri Slaby <jirislaby@gmail.com>
+S: Maintained
+F: drivers/misc/phantom.c
+F: include/uapi/linux/phantom.h
+
+Emulex 10Gbps iSCSI - OneConnect DRIVER
+M: Subbu Seetharaman <subbu.seetharaman@broadcom.com>
+M: Ketan Mukadam <ketan.mukadam@broadcom.com>
+M: Jitendra Bhivare <jitendra.bhivare@broadcom.com>
+L: linux-scsi@vger.kernel.org
+W: http://www.broadcom.com
+S: Supported
+F: drivers/scsi/be2iscsi/
+
+Emulex 10Gbps NIC BE2, BE3-R, Lancer, Skyhawk-R DRIVER (be2net)
+M: Sathya Perla <sathya.perla@broadcom.com>
+M: Ajit Khaparde <ajit.khaparde@broadcom.com>
+M: Sriharsha Basavapatna <sriharsha.basavapatna@broadcom.com>
+M: Somnath Kotur <somnath.kotur@broadcom.com>
+L: netdev@vger.kernel.org
+W: http://www.emulex.com
+S: Supported
+F: drivers/net/ethernet/emulex/benet/
+
+EMULEX ONECONNECT ROCE DRIVER
+M: Selvin Xavier <selvin.xavier@broadcom.com>
+M: Devesh Sharma <devesh.sharma@broadcom.com>
+L: linux-rdma@vger.kernel.org
+W: http://www.broadcom.com
+S: Odd Fixes
+F: drivers/infiniband/hw/ocrdma/
+F: include/uapi/rdma/ocrdma-abi.h
+
+SFC NETWORK DRIVER
+M: Solarflare linux maintainers <linux-net-drivers@solarflare.com>
+M: Edward Cree <ecree@solarflare.com>
+M: Bert Kenward <bkenward@solarflare.com>
+L: netdev@vger.kernel.org
+S: Supported
+F: drivers/net/ethernet/sfc/
+
+SGI GRU DRIVER
+M: Dimitri Sivanich <sivanich@sgi.com>
+S: Maintained
+F: drivers/misc/sgi-gru/
+
+SGI SN-IA64 (Altix) SERIAL CONSOLE DRIVER
+M: Pat Gefre <pfg@sgi.com>
+L: linux-ia64@vger.kernel.org
+S: Supported
+F: Documentation/ia64/serial.txt
+F: drivers/tty/serial/ioc?_serial.c
+F: include/linux/ioc?.h
+
+SGI XP/XPC/XPNET DRIVER
+M: Cliff Whickman <cpw@sgi.com>
+M: Robin Holt <robinmholt@gmail.com>
+S: Maintained
+F: drivers/misc/sgi-xp/
+
+SI2157 MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+W: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/media/tuners/si2157*
+
+SI2168 MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+W: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/media/dvb-frontends/si2168*
+
+SI470X FM RADIO RECEIVER I2C DRIVER
+M: Hans Verkuil <hverkuil@xs4all.nl>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+W: https://linuxtv.org
+S: Odd Fixes
+F: drivers/media/radio/si470x/radio-si470x-i2c.c
+
+SI470X FM RADIO RECEIVER USB DRIVER
+M: Hans Verkuil <hverkuil@xs4all.nl>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+W: https://linuxtv.org
+S: Maintained
+F: drivers/media/radio/si470x/radio-si470x-common.c
+F: drivers/media/radio/si470x/radio-si470x.h
+F: drivers/media/radio/si470x/radio-si470x-usb.c
+
+SI4713 FM RADIO TRANSMITTER I2C DRIVER
+M: Eduardo Valentin <edubezval@gmail.com>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+W: https://linuxtv.org
+S: Odd Fixes
+F: drivers/media/radio/si4713/si4713.?
+
+SI4713 FM RADIO TRANSMITTER PLATFORM DRIVER
+M: Eduardo Valentin <edubezval@gmail.com>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+W: https://linuxtv.org
+S: Odd Fixes
+F: drivers/media/radio/si4713/radio-platform-si4713.c
+
+SI4713 FM RADIO TRANSMITTER USB DRIVER
+M: Hans Verkuil <hverkuil@xs4all.nl>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+W: https://linuxtv.org
+S: Maintained
+F: drivers/media/radio/si4713/radio-usb-si4713.c
+
+SIANO DVB DRIVER
+M: Mauro Carvalho Chehab <mchehab@s-opensource.com>
+M: Mauro Carvalho Chehab <mchehab@kernel.org>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+T: git git://linuxtv.org/media_tree.git
+S: Odd fixes
+F: drivers/media/common/siano/
+F: drivers/media/usb/siano/
+F: drivers/media/usb/siano/
+F: drivers/media/mmc/siano/
+
+SILEAD TOUCHSCREEN DRIVER
+M: Hans de Goede <hdegoede@redhat.com>
+L: linux-input@vger.kernel.org
+L: platform-driver-x86@vger.kernel.org
+S: Maintained
+F: drivers/input/touchscreen/silead.c
+F: drivers/platform/x86/silead_dmi.c
+
+SIMPLEFB FB DRIVER
+M: Hans de Goede <hdegoede@redhat.com>
+L: linux-fbdev@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/display/simple-framebuffer.txt
+F: drivers/video/fbdev/simplefb.c
+F: include/linux/platform_data/simplefb.h
+
+SH_VEU V4L2 MEM2MEM DRIVER
+L: linux-media@vger.kernel.org
+S: Orphan
+F: drivers/media/platform/sh_veu.c
+
+SH_VOU V4L2 OUTPUT DRIVER
+L: linux-media@vger.kernel.org
+S: Orphan
+F: drivers/media/platform/sh_vou.c
+F: include/media/drv-intf/sh_vou.h
+
+SIMPLE FIRMWARE INTERFACE (SFI)
+M: Len Brown <lenb@kernel.org>
+L: sfi-devel@simplefirmware.org
+W: http://simplefirmware.org/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/lenb/linux-sfi-2.6.git
+S: Supported
+F: arch/x86/platform/sfi/
+F: drivers/sfi/
+F: include/linux/sfi*.h
+
+SIMTEC EB110ATX (Chalice CATS)
+P: Ben Dooks
+P: Vincent Sanders <vince@simtec.co.uk>
+M: Simtec Linux Team <linux@simtec.co.uk>
+W: http://www.simtec.co.uk/products/EB110ATX/
+S: Supported
+
+SIMTEC EB2410ITX (BAST)
+P: Ben Dooks
+P: Vincent Sanders <vince@simtec.co.uk>
+M: Simtec Linux Team <linux@simtec.co.uk>
+W: http://www.simtec.co.uk/products/EB2410ITX/
+S: Supported
+F: arch/arm/mach-s3c24xx/mach-bast.c
+F: arch/arm/mach-s3c24xx/bast-ide.c
+F: arch/arm/mach-s3c24xx/bast-irq.c
+
+SIPHASH PRF ROUTINES
+M: Jason A. Donenfeld <Jason@zx2c4.com>
+S: Maintained
+F: lib/siphash.c
+F: lib/test_siphash.c
+F: include/linux/siphash.h
+
+TI DAVINCI MACHINE SUPPORT
+M: Sekhar Nori <nsekhar@ti.com>
+M: Kevin Hilman <khilman@kernel.org>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/nsekhar/linux-davinci.git
+S: Supported
+F: arch/arm/mach-davinci/
+F: drivers/i2c/busses/i2c-davinci.c
+F: arch/arm/boot/dts/da850*
+
+TI DAVINCI SERIES MEDIA DRIVER
+M: "Lad, Prabhakar" <prabhakar.csengg@gmail.com>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/mhadli/v4l-dvb-davinci_devices.git
+S: Maintained
+F: drivers/media/platform/davinci/
+F: include/media/davinci/
+
+TI AM437X VPFE DRIVER
+M: "Lad, Prabhakar" <prabhakar.csengg@gmail.com>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/mhadli/v4l-dvb-davinci_devices.git
+S: Maintained
+F: drivers/media/platform/am437x/
+
+OV2659 OMNIVISION SENSOR DRIVER
+M: "Lad, Prabhakar" <prabhakar.csengg@gmail.com>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/mhadli/v4l-dvb-davinci_devices.git
+S: Maintained
+F: drivers/media/i2c/ov2659.c
+F: include/media/i2c/ov2659.h
+
+SILICON MOTION SM712 FRAME BUFFER DRIVER
+M: Sudip Mukherjee <sudipm.mukherjee@gmail.com>
+M: Teddy Wang <teddy.wang@siliconmotion.com>
+M: Sudip Mukherjee <sudip.mukherjee@codethink.co.uk>
+L: linux-fbdev@vger.kernel.org
+S: Maintained
+F: drivers/video/fbdev/sm712*
+F: Documentation/fb/sm712fb.txt
+
+SIS 190 ETHERNET DRIVER
+M: Francois Romieu <romieu@fr.zoreil.com>
+L: netdev@vger.kernel.org
+S: Maintained
+F: drivers/net/ethernet/sis/sis190.c
+
+SIS 900/7016 FAST ETHERNET DRIVER
+M: Daniele Venzano <venza@brownhat.org>
+W: http://www.brownhat.org/sis900.html
+L: netdev@vger.kernel.org
+S: Maintained
+F: drivers/net/ethernet/sis/sis900.*
+
+SIS FRAMEBUFFER DRIVER
+M: Thomas Winischhofer <thomas@winischhofer.net>
+W: http://www.winischhofer.net/linuxsisvga.shtml
+S: Maintained
+F: Documentation/fb/sisfb.txt
+F: drivers/video/fbdev/sis/
+F: include/video/sisfb.h
+
+SIS USB2VGA DRIVER
+M: Thomas Winischhofer <thomas@winischhofer.net>
+W: http://www.winischhofer.at/linuxsisusbvga.shtml
+S: Maintained
+F: drivers/usb/misc/sisusbvga/
+
+SLAB ALLOCATOR
+M: Christoph Lameter <cl@linux.com>
+M: Pekka Enberg <penberg@kernel.org>
+M: David Rientjes <rientjes@google.com>
+M: Joonsoo Kim <iamjoonsoo.kim@lge.com>
+M: Andrew Morton <akpm@linux-foundation.org>
+L: linux-mm@kvack.org
+S: Maintained
+F: include/linux/sl?b*.h
+F: mm/sl?b*
+
+SLEEPABLE READ-COPY UPDATE (SRCU)
+M: Lai Jiangshan <jiangshanlai@gmail.com>
+M: "Paul E. McKenney" <paulmck@linux.vnet.ibm.com>
+M: Josh Triplett <josh@joshtriplett.org>
+R: Steven Rostedt <rostedt@goodmis.org>
+R: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+L: linux-kernel@vger.kernel.org
+W: http://www.rdrop.com/users/paulmck/RCU/
+S: Supported
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/paulmck/linux-rcu.git
+F: include/linux/srcu.h
+F: kernel/rcu/srcu.c
+
+SMACK SECURITY MODULE
+M: Casey Schaufler <casey@schaufler-ca.com>
+L: linux-security-module@vger.kernel.org
+W: http://schaufler-ca.com
+T: git git://github.com/cschaufler/smack-next
+S: Maintained
+F: Documentation/security/Smack.txt
+F: security/smack/
+
+DRIVERS FOR ADAPTIVE VOLTAGE SCALING (AVS)
+M: Kevin Hilman <khilman@kernel.org>
+M: Nishanth Menon <nm@ti.com>
+S: Maintained
+F: drivers/power/avs/
+F: include/linux/power/smartreflex.h
+L: linux-pm@vger.kernel.org
+
+SMC91x ETHERNET DRIVER
+M: Nicolas Pitre <nico@fluxnic.net>
+S: Odd Fixes
+F: drivers/net/ethernet/smsc/smc91x.*
+
+SMIA AND SMIA++ IMAGE SENSOR DRIVER
+M: Sakari Ailus <sakari.ailus@iki.fi>
+L: linux-media@vger.kernel.org
+S: Maintained
+F: drivers/media/i2c/smiapp/
+F: include/media/i2c/smiapp.h
+F: drivers/media/i2c/smiapp-pll.c
+F: drivers/media/i2c/smiapp-pll.h
+F: include/uapi/linux/smiapp.h
+F: Documentation/devicetree/bindings/media/i2c/nokia,smia.txt
+
+SMM665 HARDWARE MONITOR DRIVER
+M: Guenter Roeck <linux@roeck-us.net>
+L: linux-hwmon@vger.kernel.org
+S: Maintained
+F: Documentation/hwmon/smm665
+F: drivers/hwmon/smm665.c
+
+SMSC EMC2103 HARDWARE MONITOR DRIVER
+M: Steve Glendinning <steve.glendinning@shawell.net>
+L: linux-hwmon@vger.kernel.org
+S: Maintained
+F: Documentation/hwmon/emc2103
+F: drivers/hwmon/emc2103.c
+
+SMSC SCH5627 HARDWARE MONITOR DRIVER
+M: Hans de Goede <hdegoede@redhat.com>
+L: linux-hwmon@vger.kernel.org
+S: Supported
+F: Documentation/hwmon/sch5627
+F: drivers/hwmon/sch5627.c
+
+SMSC47B397 HARDWARE MONITOR DRIVER
+M: Jean Delvare <jdelvare@suse.com>
+L: linux-hwmon@vger.kernel.org
+S: Maintained
+F: Documentation/hwmon/smsc47b397
+F: drivers/hwmon/smsc47b397.c
+
+SMSC911x ETHERNET DRIVER
+M: Steve Glendinning <steve.glendinning@shawell.net>
+L: netdev@vger.kernel.org
+S: Maintained
+F: include/linux/smsc911x.h
+F: drivers/net/ethernet/smsc/smsc911x.*
+
+SMSC9420 PCI ETHERNET DRIVER
+M: Steve Glendinning <steve.glendinning@shawell.net>
+L: netdev@vger.kernel.org
+S: Maintained
+F: drivers/net/ethernet/smsc/smsc9420.*
+
+SMSC UFX6000 and UFX7000 USB to VGA DRIVER
+M: Steve Glendinning <steve.glendinning@shawell.net>
+L: linux-fbdev@vger.kernel.org
+S: Maintained
+F: drivers/video/fbdev/smscufx.c
+
+SOC-CAMERA V4L2 SUBSYSTEM
+M: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+S: Maintained
+F: include/media/soc*
+F: drivers/media/i2c/soc_camera/
+F: drivers/media/platform/soc_camera/
+
+SOEKRIS NET48XX LED SUPPORT
+M: Chris Boot <bootc@bootc.net>
+S: Maintained
+F: drivers/leds/leds-net48xx.c
+
+SOFTLOGIC 6x10 MPEG CODEC
+M: Bluecherry Maintainers <maintainers@bluecherrydvr.com>
+M: Andrey Utkin <andrey.utkin@corp.bluecherry.net>
+M: Andrey Utkin <andrey.krieger.utkin@gmail.com>
+M: Ismael Luceno <ismael@iodev.co.uk>
+L: linux-media@vger.kernel.org
+S: Supported
+F: drivers/media/pci/solo6x10/
+
+SOFTWARE RAID (Multiple Disks) SUPPORT
+M: Shaohua Li <shli@kernel.org>
+L: linux-raid@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/shli/md.git
+S: Supported
+F: drivers/md/
+F: include/linux/raid/
+F: include/uapi/linux/raid/
+
+SONIC NETWORK DRIVER
+M: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
+L: netdev@vger.kernel.org
+S: Maintained
+F: drivers/net/ethernet/natsemi/sonic.*
+
+SONICS SILICON BACKPLANE DRIVER (SSB)
+M: Michael Buesch <m@bues.ch>
+L: linux-wireless@vger.kernel.org
+S: Maintained
+F: drivers/ssb/
+F: include/linux/ssb/
+
+SONY VAIO CONTROL DEVICE DRIVER
+M: Mattia Dongili <malattia@linux.it>
+L: platform-driver-x86@vger.kernel.org
+W: http://www.linux.it/~malattia/wiki/index.php/Sony_drivers
+S: Maintained
+F: Documentation/laptops/sony-laptop.txt
+F: drivers/char/sonypi.c
+F: drivers/platform/x86/sony-laptop.c
+F: include/linux/sony-laptop.h
+
+SONY MEMORYSTICK CARD SUPPORT
+M: Alex Dubov <oakad@yahoo.com>
+W: http://tifmxx.berlios.de/
+S: Maintained
+F: drivers/memstick/host/tifm_ms.c
+
+SONY MEMORYSTICK STANDARD SUPPORT
+M: Maxim Levitsky <maximlevitsky@gmail.com>
+S: Maintained
+F: drivers/memstick/core/ms_block.*
+
+SOUND
+M: Jaroslav Kysela <perex@perex.cz>
+M: Takashi Iwai <tiwai@suse.com>
+L: alsa-devel@alsa-project.org (moderated for non-subscribers)
+W: http://www.alsa-project.org/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git
+T: git git://git.alsa-project.org/alsa-kernel.git
+Q: http://patchwork.kernel.org/project/alsa-devel/list/
+S: Maintained
+F: Documentation/sound/
+F: include/sound/
+F: include/uapi/sound/
+F: sound/
+
+SOUND - COMPRESSED AUDIO
+M: Vinod Koul <vinod.koul@intel.com>
+L: alsa-devel@alsa-project.org (moderated for non-subscribers)
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git
+S: Supported
+F: Documentation/sound/alsa/compress_offload.txt
+F: include/sound/compress_driver.h
+F: include/uapi/sound/compress_*
+F: sound/core/compress_offload.c
+F: sound/soc/soc-compress.c
+
+SOUND - SOC LAYER / DYNAMIC AUDIO POWER MANAGEMENT (ASoC)
+M: Liam Girdwood <lgirdwood@gmail.com>
+M: Mark Brown <broonie@kernel.org>
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git
+L: alsa-devel@alsa-project.org (moderated for non-subscribers)
+W: http://alsa-project.org/main/index.php/ASoC
+S: Supported
+F: Documentation/devicetree/bindings/sound/
+F: Documentation/sound/alsa/soc/
+F: sound/soc/
+F: include/sound/soc*
+
+SOUND - DMAENGINE HELPERS
+M: Lars-Peter Clausen <lars@metafoo.de>
+S: Supported
+F: include/sound/dmaengine_pcm.h
+F: sound/core/pcm_dmaengine.c
+F: sound/soc/soc-generic-dmaengine-pcm.c
+
+SP2 MEDIA DRIVER
+M: Olli Salonen <olli.salonen@iki.fi>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+S: Maintained
+F: drivers/media/dvb-frontends/sp2*
+
+SPARC + UltraSPARC (sparc/sparc64)
+M: "David S. Miller" <davem@davemloft.net>
+L: sparclinux@vger.kernel.org
+Q: http://patchwork.ozlabs.org/project/sparclinux/list/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/davem/sparc.git
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/davem/sparc-next.git
+S: Maintained
+F: arch/sparc/
+F: drivers/sbus/
+
+SPARC SERIAL DRIVERS
+M: "David S. Miller" <davem@davemloft.net>
+L: sparclinux@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/davem/sparc.git
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/davem/sparc-next.git
+S: Maintained
+F: include/linux/sunserialcore.h
+F: drivers/tty/serial/suncore.c
+F: drivers/tty/serial/sunhv.c
+F: drivers/tty/serial/sunsab.c
+F: drivers/tty/serial/sunsab.h
+F: drivers/tty/serial/sunsu.c
+F: drivers/tty/serial/sunzilog.c
+F: drivers/tty/serial/sunzilog.h
+
+SPARSE CHECKER
+M: "Christopher Li" <sparse@chrisli.org>
+L: linux-sparse@vger.kernel.org
+W: https://sparse.wiki.kernel.org/
+T: git git://git.kernel.org/pub/scm/devel/sparse/sparse.git
+T: git git://git.kernel.org/pub/scm/devel/sparse/chrisl/sparse.git
+S: Maintained
+F: include/linux/compiler.h
+
+SPEAR PLATFORM SUPPORT
+M: Viresh Kumar <vireshk@kernel.org>
+M: Shiraz Hashim <shiraz.linux.kernel@gmail.com>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+W: http://www.st.com/spear
+S: Maintained
+F: arch/arm/boot/dts/spear*
+F: arch/arm/mach-spear/
+
+SPEAR CLOCK FRAMEWORK SUPPORT
+M: Viresh Kumar <vireshk@kernel.org>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+W: http://www.st.com/spear
+S: Maintained
+F: drivers/clk/spear/
+
+SPI NOR SUBSYSTEM
+M: Cyrille Pitchen <cyrille.pitchen@wedev4u.fr>
+M: Marek Vasut <marek.vasut@gmail.com>
+L: linux-mtd@lists.infradead.org
+W: http://www.linux-mtd.infradead.org/
+Q: http://patchwork.ozlabs.org/project/linux-mtd/list/
+T: git git://github.com/spi-nor/linux.git
+S: Maintained
+F: drivers/mtd/spi-nor/
+F: include/linux/mtd/spi-nor.h
+
+SPI SUBSYSTEM
+M: Mark Brown <broonie@kernel.org>
+L: linux-spi@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git
+Q: http://patchwork.kernel.org/project/spi-devel-general/list/
+S: Maintained
+F: Documentation/devicetree/bindings/spi/
+F: Documentation/spi/
+F: drivers/spi/
+F: include/linux/spi/
+F: include/uapi/linux/spi/
+F: tools/spi/
+
+SPIDERNET NETWORK DRIVER for CELL
+M: Ishizaki Kou <kou.ishizaki@toshiba.co.jp>
+L: netdev@vger.kernel.org
+S: Supported
+F: Documentation/networking/spider_net.txt
+F: drivers/net/ethernet/toshiba/spider_net*
+
+SPU FILE SYSTEM
+M: Jeremy Kerr <jk@ozlabs.org>
+L: linuxppc-dev@lists.ozlabs.org
+W: http://www.ibm.com/developerworks/power/cell/
+S: Supported
+F: Documentation/filesystems/spufs.txt
+F: arch/powerpc/platforms/cell/spufs/
+
+SQUASHFS FILE SYSTEM
+M: Phillip Lougher <phillip@squashfs.org.uk>
+L: squashfs-devel@lists.sourceforge.net (subscribers-only)
+W: http://squashfs.org.uk
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/pkl/squashfs-next.git
+S: Maintained
+F: Documentation/filesystems/squashfs.txt
+F: fs/squashfs/
+
+SRM (Alpha) environment access
+M: Jan-Benedict Glaw <jbglaw@lug-owl.de>
+S: Maintained
+F: arch/alpha/kernel/srm_env.c
+
+STABLE BRANCH
+M: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+L: stable@vger.kernel.org
+S: Supported
+F: Documentation/process/stable-kernel-rules.rst
+
+STAGING SUBSYSTEM
+M: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging.git
+L: devel@driverdev.osuosl.org
+S: Supported
+F: drivers/staging/
+
+STAGING - COMEDI
+M: Ian Abbott <abbotti@mev.co.uk>
+M: H Hartley Sweeten <hsweeten@visionengravers.com>
+S: Odd Fixes
+F: drivers/staging/comedi/
+
+STAGING - FLARION FT1000 DRIVERS
+M: Marek Belisko <marek.belisko@gmail.com>
+S: Odd Fixes
+F: drivers/staging/ft1000/
+
+STAGING - INDUSTRIAL IO
+M: Jonathan Cameron <jic23@kernel.org>
+L: linux-iio@vger.kernel.org
+S: Odd Fixes
+F: Documentation/devicetree/bindings/staging/iio/
+F: drivers/staging/iio/
+
+STAGING - LIRC (LINUX INFRARED REMOTE CONTROL) DRIVERS
+M: Jarod Wilson <jarod@wilsonet.com>
+W: http://www.lirc.org/
+S: Odd Fixes
+F: drivers/staging/media/lirc/
+
+STAGING - LUSTRE PARALLEL FILESYSTEM
+M: Oleg Drokin <oleg.drokin@intel.com>
+M: Andreas Dilger <andreas.dilger@intel.com>
+M: James Simmons <jsimmons@infradead.org>
+L: lustre-devel@lists.lustre.org (moderated for non-subscribers)
+W: http://wiki.lustre.org/
+S: Maintained
+F: drivers/staging/lustre
+
+STAGING - NVIDIA COMPLIANT EMBEDDED CONTROLLER INTERFACE (nvec)
+M: Marc Dietrich <marvin24@gmx.de>
+L: ac100@lists.launchpad.net (moderated for non-subscribers)
+L: linux-tegra@vger.kernel.org
+S: Maintained
+F: drivers/staging/nvec/
+
+STAGING - OLPC SECONDARY DISPLAY CONTROLLER (DCON)
+M: Jens Frederich <jfrederich@gmail.com>
+M: Daniel Drake <dsd@laptop.org>
+M: Jon Nettleton <jon.nettleton@gmail.com>
+W: http://wiki.laptop.org/go/DCON
+S: Maintained
+F: drivers/staging/olpc_dcon/
+
+STAGING - REALTEK RTL8712U DRIVERS
+M: Larry Finger <Larry.Finger@lwfinger.net>
+M: Florian Schilhabel <florian.c.schilhabel@googlemail.com>.
+S: Odd Fixes
+F: drivers/staging/rtl8712/
+
+STAGING - SILICON MOTION SM750 FRAME BUFFER DRIVER
+M: Sudip Mukherjee <sudipm.mukherjee@gmail.com>
+M: Teddy Wang <teddy.wang@siliconmotion.com>
+M: Sudip Mukherjee <sudip.mukherjee@codethink.co.uk>
+L: linux-fbdev@vger.kernel.org
+S: Maintained
+F: drivers/staging/sm750fb/
+
+STAGING - SPEAKUP CONSOLE SPEECH DRIVER
+M: William Hubbs <w.d.hubbs@gmail.com>
+M: Chris Brannon <chris@the-brannons.com>
+M: Kirk Reiser <kirk@reisers.ca>
+M: Samuel Thibault <samuel.thibault@ens-lyon.org>
+L: speakup@linux-speakup.org
+W: http://www.linux-speakup.org/
+S: Odd Fixes
+F: drivers/staging/speakup/
+
+STAGING - VIA VT665X DRIVERS
+M: Forest Bond <forest@alittletooquiet.net>
+S: Odd Fixes
+F: drivers/staging/vt665?/
+
+STAGING - WILC1000 WIFI DRIVER
+M: Aditya Shankar <aditya.shankar@microchip.com>
+M: Ganesh Krishna <ganesh.krishna@microchip.com>
+L: linux-wireless@vger.kernel.org
+S: Supported
+F: drivers/staging/wilc1000/
+
+STAGING - XGI Z7,Z9,Z11 PCI DISPLAY DRIVER
+M: Arnaud Patard <arnaud.patard@rtp-net.org>
+S: Odd Fixes
+F: drivers/staging/xgifb/
+
+STARFIRE/DURALAN NETWORK DRIVER
+M: Ion Badulescu <ionut@badula.org>
+S: Odd Fixes
+F: drivers/net/ethernet/adaptec/starfire*
+
+SUN3/3X
+M: Sam Creasey <sammy@sammy.net>
+W: http://sammy.net/sun3/
+S: Maintained
+F: arch/m68k/kernel/*sun3*
+F: arch/m68k/sun3*/
+F: arch/m68k/include/asm/sun3*
+F: drivers/net/ethernet/i825xx/sun3*
+
+SUN4I LOW RES ADC ATTACHED TABLET KEYS DRIVER
+M: Hans de Goede <hdegoede@redhat.com>
+L: linux-input@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/input/sun4i-lradc-keys.txt
+F: drivers/input/keyboard/sun4i-lradc-keys.c
+
+SUNDANCE NETWORK DRIVER
+M: Denis Kirjanov <kda@linux-powerpc.org>
+L: netdev@vger.kernel.org
+S: Maintained
+F: drivers/net/ethernet/dlink/sundance.c
+
+SUPERH
+M: Yoshinori Sato <ysato@users.sourceforge.jp>
+M: Rich Felker <dalias@libc.org>
+L: linux-sh@vger.kernel.org
+Q: http://patchwork.kernel.org/project/linux-sh/list/
+S: Maintained
+F: Documentation/sh/
+F: arch/sh/
+F: drivers/sh/
+
+SUSPEND TO RAM
+M: "Rafael J. Wysocki" <rjw@rjwysocki.net>
+M: Len Brown <len.brown@intel.com>
+M: Pavel Machek <pavel@ucw.cz>
+L: linux-pm@vger.kernel.org
+B: https://bugzilla.kernel.org
+S: Supported
+F: Documentation/power/
+F: arch/x86/kernel/acpi/
+F: drivers/base/power/
+F: kernel/power/
+F: include/linux/suspend.h
+F: include/linux/freezer.h
+F: include/linux/pm.h
+
+SVGA HANDLING
+M: Martin Mares <mj@ucw.cz>
+L: linux-video@atrey.karlin.mff.cuni.cz
+S: Maintained
+F: Documentation/svga.txt
+F: arch/x86/boot/video*
+
+SWIOTLB SUBSYSTEM
+M: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
+L: linux-kernel@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/konrad/swiotlb.git
+S: Supported
+F: lib/swiotlb.c
+F: arch/*/kernel/pci-swiotlb.c
+F: include/linux/swiotlb.h
+
+SWITCHDEV
+M: Jiri Pirko <jiri@resnulli.us>
+M: Ivan Vecera <ivecera@redhat.com>
+L: netdev@vger.kernel.org
+S: Supported
+F: net/switchdev/
+F: include/net/switchdev.h
+
+SYNOPSYS ARC ARCHITECTURE
+M: Vineet Gupta <vgupta@synopsys.com>
+L: linux-snps-arc@lists.infradead.org
+S: Supported
+F: arch/arc/
+F: Documentation/devicetree/bindings/arc/*
+F: Documentation/devicetree/bindings/interrupt-controller/snps,arc*
+F: drivers/clocksource/arc_timer.c
+F: drivers/tty/serial/arc_uart.c
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/vgupta/arc.git
+
+SYNOPSYS ARC SDP platform support
+M: Alexey Brodkin <abrodkin@synopsys.com>
+S: Supported
+F: arch/arc/plat-axs10x
+F: arch/arc/boot/dts/ax*
+F: Documentation/devicetree/bindings/arc/axs10*
+
+SYSTEM CONFIGURATION (SYSCON)
+M: Lee Jones <lee.jones@linaro.org>
+M: Arnd Bergmann <arnd@arndb.de>
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd.git
+S: Supported
+F: drivers/mfd/syscon.c
+
+SYSTEM RESET/SHUTDOWN DRIVERS
+M: Sebastian Reichel <sre@kernel.org>
+L: linux-pm@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply.git
+S: Maintained
+F: Documentation/devicetree/bindings/power/reset/
+F: drivers/power/reset/
+
+SYSV FILESYSTEM
+M: Christoph Hellwig <hch@infradead.org>
+S: Maintained
+F: Documentation/filesystems/sysv-fs.txt
+F: fs/sysv/
+F: include/linux/sysv_fs.h
+
+TARGET SUBSYSTEM
+M: "Nicholas A. Bellinger" <nab@linux-iscsi.org>
+L: linux-scsi@vger.kernel.org
+L: target-devel@vger.kernel.org
+W: http://www.linux-iscsi.org
+W: http://groups.google.com/group/linux-iscsi-target-dev
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/nab/target-pending.git master
+S: Supported
+F: drivers/target/
+F: include/target/
+F: Documentation/target/
+
+TASKSTATS STATISTICS INTERFACE
+M: Balbir Singh <bsingharora@gmail.com>
+S: Maintained
+F: Documentation/accounting/taskstats*
+F: include/linux/taskstats*
+F: kernel/taskstats.c
+
+TC subsystem
+M: Jamal Hadi Salim <jhs@mojatatu.com>
+M: Cong Wang <xiyou.wangcong@gmail.com>
+M: Jiri Pirko <jiri@resnulli.us>
+L: netdev@vger.kernel.org
+S: Maintained
+F: include/net/pkt_cls.h
+F: include/net/pkt_sched.h
+F: include/net/tc_act/
+F: include/uapi/linux/pkt_cls.h
+F: include/uapi/linux/pkt_sched.h
+F: include/uapi/linux/tc_act/
+F: include/uapi/linux/tc_ematch/
+F: net/sched/
+
+TCP LOW PRIORITY MODULE
+M: "Wong Hoi Sing, Edison" <hswong3i@gmail.com>
+M: "Hung Hing Lun, Mike" <hlhung3i@gmail.com>
+W: http://tcp-lp-mod.sourceforge.net/
+S: Maintained
+F: net/ipv4/tcp_lp.c
+
+TDA10071 MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+W: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/media/dvb-frontends/tda10071*
+
+TDA18212 MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+W: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/media/tuners/tda18212*
+
+TDA18218 MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+W: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/media/tuners/tda18218*
+
+TDA18271 MEDIA DRIVER
+M: Michael Krufky <mkrufky@linuxtv.org>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+W: http://github.com/mkrufky
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/mkrufky/tuners.git
+S: Maintained
+F: drivers/media/tuners/tda18271*
+
+TDA827x MEDIA DRIVER
+M: Michael Krufky <mkrufky@linuxtv.org>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+W: http://github.com/mkrufky
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/mkrufky/tuners.git
+S: Maintained
+F: drivers/media/tuners/tda8290.*
+
+TDA8290 MEDIA DRIVER
+M: Michael Krufky <mkrufky@linuxtv.org>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+W: http://github.com/mkrufky
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/mkrufky/tuners.git
+S: Maintained
+F: drivers/media/tuners/tda8290.*
+
+TDA9840 MEDIA DRIVER
+M: Hans Verkuil <hverkuil@xs4all.nl>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+W: https://linuxtv.org
+S: Maintained
+F: drivers/media/i2c/tda9840*
+
+TEA5761 TUNER DRIVER
+M: Mauro Carvalho Chehab <mchehab@s-opensource.com>
+M: Mauro Carvalho Chehab <mchehab@kernel.org>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+T: git git://linuxtv.org/media_tree.git
+S: Odd fixes
+F: drivers/media/tuners/tea5761.*
+
+TEA5767 TUNER DRIVER
+M: Mauro Carvalho Chehab <mchehab@s-opensource.com>
+M: Mauro Carvalho Chehab <mchehab@kernel.org>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+T: git git://linuxtv.org/media_tree.git
+S: Maintained
+F: drivers/media/tuners/tea5767.*
+
+TEA6415C MEDIA DRIVER
+M: Hans Verkuil <hverkuil@xs4all.nl>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+W: https://linuxtv.org
+S: Maintained
+F: drivers/media/i2c/tea6415c*
+
+TEA6420 MEDIA DRIVER
+M: Hans Verkuil <hverkuil@xs4all.nl>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+W: https://linuxtv.org
+S: Maintained
+F: drivers/media/i2c/tea6420*
+
+TEAM DRIVER
+M: Jiri Pirko <jiri@resnulli.us>
+L: netdev@vger.kernel.org
+S: Supported
+F: drivers/net/team/
+F: include/linux/if_team.h
+F: include/uapi/linux/if_team.h
+
+TECHNOLOGIC SYSTEMS TS-5500 PLATFORM SUPPORT
+M: "Savoir-faire Linux Inc." <kernel@savoirfairelinux.com>
+S: Maintained
+F: arch/x86/platform/ts5500/
+
+TECHNOTREND USB IR RECEIVER
+M: Sean Young <sean@mess.org>
+L: linux-media@vger.kernel.org
+S: Maintained
+F: drivers/media/rc/ttusbir.c
+
+TEGRA ARCHITECTURE SUPPORT
+M: Thierry Reding <thierry.reding@gmail.com>
+M: Jonathan Hunter <jonathanh@nvidia.com>
+L: linux-tegra@vger.kernel.org
+Q: http://patchwork.ozlabs.org/project/linux-tegra/list/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/tegra/linux.git
+S: Supported
+N: [^a-z]tegra
+
+TEGRA CLOCK DRIVER
+M: Peter De Schrijver <pdeschrijver@nvidia.com>
+M: Prashant Gaikwad <pgaikwad@nvidia.com>
+S: Supported
+F: drivers/clk/tegra/
+
+TEGRA DMA DRIVERS
+M: Laxman Dewangan <ldewangan@nvidia.com>
+M: Jon Hunter <jonathanh@nvidia.com>
+S: Supported
+F: drivers/dma/tegra*
+
+TEGRA I2C DRIVER
+M: Laxman Dewangan <ldewangan@nvidia.com>
+S: Supported
+F: drivers/i2c/busses/i2c-tegra.c
+
+TEGRA IOMMU DRIVERS
+M: Hiroshi Doyu <hdoyu@nvidia.com>
+S: Supported
+F: drivers/iommu/tegra*
+
+TEGRA KBC DRIVER
+M: Rakesh Iyer <riyer@nvidia.com>
+M: Laxman Dewangan <ldewangan@nvidia.com>
+S: Supported
+F: drivers/input/keyboard/tegra-kbc.c
+
+TEGRA PWM DRIVER
+M: Thierry Reding <thierry.reding@gmail.com>
+S: Supported
+F: drivers/pwm/pwm-tegra.c
+
+TEGRA SERIAL DRIVER
+M: Laxman Dewangan <ldewangan@nvidia.com>
+S: Supported
+F: drivers/tty/serial/serial-tegra.c
+
+TEGRA SPI DRIVER
+M: Laxman Dewangan <ldewangan@nvidia.com>
+S: Supported
+F: drivers/spi/spi-tegra*
+
+TEHUTI ETHERNET DRIVER
+M: Andy Gospodarek <andy@greyhouse.net>
+L: netdev@vger.kernel.org
+S: Supported
+F: drivers/net/ethernet/tehuti/*
+
+Telecom Clock Driver for MCPL0010
+M: Mark Gross <mark.gross@intel.com>
+S: Supported
+F: drivers/char/tlclk.c
+
+TENSILICA XTENSA PORT (xtensa)
+M: Chris Zankel <chris@zankel.net>
+M: Max Filippov <jcmvbkbc@gmail.com>
+L: linux-xtensa@linux-xtensa.org
+T: git git://github.com/czankel/xtensa-linux.git
+S: Maintained
+F: arch/xtensa/
+F: drivers/irqchip/irq-xtensa-*
+
+Texas Instruments' System Control Interface (TISCI) Protocol Driver
+M: Nishanth Menon <nm@ti.com>
+M: Tero Kristo <t-kristo@ti.com>
+M: Santosh Shilimkar <ssantosh@kernel.org>
+L: linux-arm-kernel@lists.infradead.org
+S: Maintained
+F: Documentation/devicetree/bindings/arm/keystone/ti,sci.txt
+F: drivers/firmware/ti_sci*
+F: include/linux/soc/ti/ti_sci_protocol.h
+F: Documentation/devicetree/bindings/soc/ti/sci-pm-domain.txt
+F: include/dt-bindings/genpd/k2g.h
+F: drivers/soc/ti/ti_sci_pm_domains.c
+
+THANKO'S RAREMONO AM/FM/SW RADIO RECEIVER USB DRIVER
+M: Hans Verkuil <hverkuil@xs4all.nl>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+W: https://linuxtv.org
+S: Maintained
+F: drivers/media/radio/radio-raremono.c
+
+THERMAL
+M: Zhang Rui <rui.zhang@intel.com>
+M: Eduardo Valentin <edubezval@gmail.com>
+L: linux-pm@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/rzhang/linux.git
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/evalenti/linux-soc-thermal.git
+Q: https://patchwork.kernel.org/project/linux-pm/list/
+S: Supported
+F: drivers/thermal/
+F: include/linux/thermal.h
+F: include/uapi/linux/thermal.h
+F: include/linux/cpu_cooling.h
+F: Documentation/devicetree/bindings/thermal/
+
+THERMAL/CPU_COOLING
+M: Amit Daniel Kachhap <amit.kachhap@gmail.com>
+M: Viresh Kumar <viresh.kumar@linaro.org>
+M: Javi Merino <javi.merino@kernel.org>
+L: linux-pm@vger.kernel.org
+S: Supported
+F: Documentation/thermal/cpu-cooling-api.txt
+F: drivers/thermal/cpu_cooling.c
+F: include/linux/cpu_cooling.h
+
+THINKPAD ACPI EXTRAS DRIVER
+M: Henrique de Moraes Holschuh <ibm-acpi@hmh.eng.br>
+L: ibm-acpi-devel@lists.sourceforge.net
+L: platform-driver-x86@vger.kernel.org
+W: http://ibm-acpi.sourceforge.net
+W: http://thinkwiki.org/wiki/Ibm-acpi
+T: git git://repo.or.cz/linux-2.6/linux-acpi-2.6/ibm-acpi-2.6.git
+S: Maintained
+F: drivers/platform/x86/thinkpad_acpi.c
+
+TI BANDGAP AND THERMAL DRIVER
+M: Eduardo Valentin <edubezval@gmail.com>
+M: Keerthy <j-keerthy@ti.com>
+L: linux-pm@vger.kernel.org
+L: linux-omap@vger.kernel.org
+S: Maintained
+F: drivers/thermal/ti-soc-thermal/
+
+TI VPE/CAL DRIVERS
+M: Benoit Parrot <bparrot@ti.com>
+L: linux-media@vger.kernel.org
+W: http://linuxtv.org/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+S: Maintained
+F: drivers/media/platform/ti-vpe/
+
+TI CDCE706 CLOCK DRIVER
+M: Max Filippov <jcmvbkbc@gmail.com>
+S: Maintained
+F: drivers/clk/clk-cdce706.c
+
+TI CLOCK DRIVER
+M: Tero Kristo <t-kristo@ti.com>
+L: linux-omap@vger.kernel.org
+S: Maintained
+F: drivers/clk/ti/
+F: include/linux/clk/ti.h
+
+TI ETHERNET SWITCH DRIVER (CPSW)
+R: Grygorii Strashko <grygorii.strashko@ti.com>
+L: linux-omap@vger.kernel.org
+L: netdev@vger.kernel.org
+S: Maintained
+F: drivers/net/ethernet/ti/cpsw*
+F: drivers/net/ethernet/ti/davinci*
+
+TI FLASH MEDIA INTERFACE DRIVER
+M: Alex Dubov <oakad@yahoo.com>
+S: Maintained
+F: drivers/misc/tifm*
+F: drivers/mmc/host/tifm_sd.c
+F: include/linux/tifm.h
+
+TI KEYSTONE MULTICORE NAVIGATOR DRIVERS
+M: Santosh Shilimkar <ssantosh@kernel.org>
+L: linux-kernel@vger.kernel.org
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+F: drivers/soc/ti/*
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/ssantosh/linux-keystone.git
+
+
+TI LM49xxx FAMILY ASoC CODEC DRIVERS
+M: M R Swami Reddy <mr.swami.reddy@ti.com>
+M: Vishwas A Deshpande <vishwas.a.deshpande@ti.com>
+L: alsa-devel@alsa-project.org (moderated for non-subscribers)
+S: Maintained
+F: sound/soc/codecs/lm49453*
+F: sound/soc/codecs/isabelle*
+
+TI LP855x BACKLIGHT DRIVER
+M: Milo Kim <milo.kim@ti.com>
+S: Maintained
+F: Documentation/backlight/lp855x-driver.txt
+F: drivers/video/backlight/lp855x_bl.c
+F: include/linux/platform_data/lp855x.h
+
+TI LP8727 CHARGER DRIVER
+M: Milo Kim <milo.kim@ti.com>
+S: Maintained
+F: drivers/power/supply/lp8727_charger.c
+F: include/linux/platform_data/lp8727.h
+
+TI LP8788 MFD DRIVER
+M: Milo Kim <milo.kim@ti.com>
+S: Maintained
+F: drivers/iio/adc/lp8788_adc.c
+F: drivers/leds/leds-lp8788.c
+F: drivers/mfd/lp8788*.c
+F: drivers/power/supply/lp8788-charger.c
+F: drivers/regulator/lp8788-*.c
+F: include/linux/mfd/lp8788*.h
+
+TI NETCP ETHERNET DRIVER
+M: Wingman Kwok <w-kwok2@ti.com>
+M: Murali Karicheri <m-karicheri2@ti.com>
+L: netdev@vger.kernel.org
+S: Maintained
+F: drivers/net/ethernet/ti/netcp*
+
+TI TAS571X FAMILY ASoC CODEC DRIVER
+M: Kevin Cernekee <cernekee@chromium.org>
+L: alsa-devel@alsa-project.org (moderated for non-subscribers)
+S: Odd Fixes
+F: sound/soc/codecs/tas571x*
+
+TI TWL4030 SERIES SOC CODEC DRIVER
+M: Peter Ujfalusi <peter.ujfalusi@ti.com>
+L: alsa-devel@alsa-project.org (moderated for non-subscribers)
+S: Maintained
+F: sound/soc/codecs/twl4030*
+
+TI WILINK WIRELESS DRIVERS
+L: linux-wireless@vger.kernel.org
+W: http://wireless.kernel.org/en/users/Drivers/wl12xx
+W: http://wireless.kernel.org/en/users/Drivers/wl1251
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/luca/wl12xx.git
+S: Orphan
+F: drivers/net/wireless/ti/
+F: include/linux/wl12xx.h
+
+TIPC NETWORK LAYER
+M: Jon Maloy <jon.maloy@ericsson.com>
+M: Ying Xue <ying.xue@windriver.com>
+L: netdev@vger.kernel.org (core kernel code)
+L: tipc-discussion@lists.sourceforge.net (user apps, general discussion)
+W: http://tipc.sourceforge.net/
+S: Maintained
+F: include/uapi/linux/tipc*.h
+F: net/tipc/
+
+TILE ARCHITECTURE
+M: Chris Metcalf <cmetcalf@mellanox.com>
+W: http://www.mellanox.com/repository/solutions/tile-scm/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/cmetcalf/linux-tile.git
+S: Supported
+F: arch/tile/
+F: drivers/char/tile-srom.c
+F: drivers/edac/tile_edac.c
+F: drivers/net/ethernet/tile/
+F: drivers/rtc/rtc-tile.c
+F: drivers/tty/hvc/hvc_tile.c
+F: drivers/tty/serial/tilegx.c
+F: drivers/usb/host/*-tilegx.c
+F: include/linux/usb/tilegx.h
+
+TLAN NETWORK DRIVER
+M: Samuel Chessman <chessman@tux.org>
+L: tlan-devel@lists.sourceforge.net (subscribers-only)
+W: http://sourceforge.net/projects/tlan/
+S: Maintained
+F: Documentation/networking/tlan.txt
+F: drivers/net/ethernet/ti/tlan.*
+
+TOMOYO SECURITY MODULE
+M: Kentaro Takeda <takedakn@nttdata.co.jp>
+M: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
+L: tomoyo-dev-en@lists.sourceforge.jp (subscribers-only, for developers in English)
+L: tomoyo-users-en@lists.sourceforge.jp (subscribers-only, for users in English)
+L: tomoyo-dev@lists.sourceforge.jp (subscribers-only, for developers in Japanese)
+L: tomoyo-users@lists.sourceforge.jp (subscribers-only, for users in Japanese)
+W: http://tomoyo.sourceforge.jp/
+T: quilt http://svn.sourceforge.jp/svnroot/tomoyo/trunk/2.5.x/tomoyo-lsm/patches/
+S: Maintained
+F: security/tomoyo/
+
+TOPSTAR LAPTOP EXTRAS DRIVER
+M: Herton Ronaldo Krzesinski <herton@canonical.com>
+L: platform-driver-x86@vger.kernel.org
+S: Maintained
+F: drivers/platform/x86/topstar-laptop.c
+
+TOSHIBA ACPI EXTRAS DRIVER
+M: Azael Avalos <coproscefalo@gmail.com>
+L: platform-driver-x86@vger.kernel.org
+S: Maintained
+F: drivers/platform/x86/toshiba_acpi.c
+
+TOSHIBA BLUETOOTH DRIVER
+M: Azael Avalos <coproscefalo@gmail.com>
+L: platform-driver-x86@vger.kernel.org
+S: Maintained
+F: drivers/platform/x86/toshiba_bluetooth.c
+
+TOSHIBA HDD ACTIVE PROTECTION SENSOR DRIVER
+M: Azael Avalos <coproscefalo@gmail.com>
+L: platform-driver-x86@vger.kernel.org
+S: Maintained
+F: drivers/platform/x86/toshiba_haps.c
+
+TOSHIBA WMI HOTKEYS DRIVER
+M: Azael Avalos <coproscefalo@gmail.com>
+L: platform-driver-x86@vger.kernel.org
+S: Maintained
+F: drivers/platform/x86/toshiba-wmi.c
+
+TOSHIBA SMM DRIVER
+M: Jonathan Buzzard <jonathan@buzzard.org.uk>
+W: http://www.buzzard.org.uk/toshiba/
+S: Maintained
+F: drivers/char/toshiba.c
+F: include/linux/toshiba.h
+F: include/uapi/linux/toshiba.h
+
+TOSHIBA TC358743 DRIVER
+M: Mats Randgaard <matrandg@cisco.com>
+L: linux-media@vger.kernel.org
+S: Maintained
+F: drivers/media/i2c/tc358743*
+F: include/media/i2c/tc358743.h
+
+TMIO/SDHI MMC DRIVER
+M: Wolfram Sang <wsa+renesas@sang-engineering.com>
+L: linux-mmc@vger.kernel.org
+S: Supported
+F: drivers/mmc/host/tmio_mmc*
+F: drivers/mmc/host/sh_mobile_sdhi.c
+F: include/linux/mfd/tmio.h
+
+TMP401 HARDWARE MONITOR DRIVER
+M: Guenter Roeck <linux@roeck-us.net>
+L: linux-hwmon@vger.kernel.org
+S: Maintained
+F: Documentation/hwmon/tmp401
+F: drivers/hwmon/tmp401.c
+
+TMPFS (SHMEM FILESYSTEM)
+M: Hugh Dickins <hughd@google.com>
+L: linux-mm@kvack.org
+S: Maintained
+F: include/linux/shmem_fs.h
+F: mm/shmem.c
+
+TM6000 VIDEO4LINUX DRIVER
+M: Mauro Carvalho Chehab <mchehab@s-opensource.com>
+M: Mauro Carvalho Chehab <mchehab@kernel.org>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+T: git git://linuxtv.org/media_tree.git
+S: Odd fixes
+F: drivers/media/usb/tm6000/
+F: Documentation/media/v4l-drivers/tm6000*
+
+TW5864 VIDEO4LINUX DRIVER
+M: Bluecherry Maintainers <maintainers@bluecherrydvr.com>
+M: Andrey Utkin <andrey.utkin@corp.bluecherry.net>
+M: Andrey Utkin <andrey_utkin@fastmail.com>
+L: linux-media@vger.kernel.org
+S: Supported
+F: drivers/media/pci/tw5864/
+
+TW68 VIDEO4LINUX DRIVER
+M: Hans Verkuil <hverkuil@xs4all.nl>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+W: https://linuxtv.org
+S: Odd Fixes
+F: drivers/media/pci/tw68/
+
+TW686X VIDEO4LINUX DRIVER
+M: Ezequiel Garcia <ezequiel@vanguardiasur.com.ar>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+W: http://linuxtv.org
+S: Maintained
+F: drivers/media/pci/tw686x/
+
+TPM DEVICE DRIVER
+M: Peter Huewe <peterhuewe@gmx.de>
+M: Marcel Selhorst <tpmdd@selhorst.net>
+M: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
+R: Jason Gunthorpe <jgunthorpe@obsidianresearch.com>
+W: http://tpmdd.sourceforge.net
+L: tpmdd-devel@lists.sourceforge.net (moderated for non-subscribers)
+Q: https://patchwork.kernel.org/project/tpmdd-devel/list/
+T: git git://git.infradead.org/users/jjs/linux-tpmdd.git
+S: Maintained
+F: drivers/char/tpm/
+
+TPM IBM_VTPM DEVICE DRIVER
+M: Ashley Lai <ashleydlai@gmail.com>
+W: http://tpmdd.sourceforge.net
+L: tpmdd-devel@lists.sourceforge.net (moderated for non-subscribers)
+S: Maintained
+F: drivers/char/tpm/tpm_ibmvtpm*
+
+TRACING
+M: Steven Rostedt <rostedt@goodmis.org>
+M: Ingo Molnar <mingo@redhat.com>
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git perf/core
+S: Maintained
+F: Documentation/trace/ftrace.txt
+F: arch/*/*/*/ftrace.h
+F: arch/*/kernel/ftrace.c
+F: include/*/ftrace.h
+F: include/linux/trace*.h
+F: include/trace/
+F: kernel/trace/
+F: tools/testing/selftests/ftrace/
+
+TRACING MMIO ACCESSES (MMIOTRACE)
+M: Steven Rostedt <rostedt@goodmis.org>
+M: Ingo Molnar <mingo@kernel.org>
+R: Karol Herbst <karolherbst@gmail.com>
+R: Pekka Paalanen <ppaalanen@gmail.com>
+S: Maintained
+L: linux-kernel@vger.kernel.org
+L: nouveau@lists.freedesktop.org
+F: kernel/trace/trace_mmiotrace.c
+F: include/linux/mmiotrace.h
+F: arch/x86/mm/kmmio.c
+F: arch/x86/mm/mmio-mod.c
+F: arch/x86/mm/testmmiotrace.c
+
+TRIVIAL PATCHES
+M: Jiri Kosina <trivial@kernel.org>
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/jikos/trivial.git
+S: Maintained
+K: ^Subject:.*(?i)trivial
+
+TTY LAYER
+M: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+M: Jiri Slaby <jslaby@suse.com>
+S: Supported
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty.git
+F: Documentation/serial/
+F: drivers/tty/
+F: drivers/tty/serial/serial_core.c
+F: include/linux/serial_core.h
+F: include/linux/serial.h
+F: include/linux/tty.h
+F: include/uapi/linux/serial_core.h
+F: include/uapi/linux/serial.h
+F: include/uapi/linux/tty.h
+
+TUA9001 MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+W: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/media/tuners/tua9001*
+
+TULIP NETWORK DRIVERS
+L: netdev@vger.kernel.org
+L: linux-parisc@vger.kernel.org
+S: Orphan
+F: drivers/net/ethernet/dec/tulip/
+
+TUN/TAP driver
+M: Maxim Krasnyansky <maxk@qti.qualcomm.com>
+W: http://vtun.sourceforge.net/tun
+S: Maintained
+F: Documentation/networking/tuntap.txt
+F: arch/um/os-Linux/drivers/
+
+TURBOCHANNEL SUBSYSTEM
+M: "Maciej W. Rozycki" <macro@linux-mips.org>
+M: Ralf Baechle <ralf@linux-mips.org>
+L: linux-mips@linux-mips.org
+Q: http://patchwork.linux-mips.org/project/linux-mips/list/
+S: Maintained
+F: drivers/tc/
+F: include/linux/tc.h
+
+UBI FILE SYSTEM (UBIFS)
+M: Richard Weinberger <richard@nod.at>
+M: Artem Bityutskiy <dedekind1@gmail.com>
+M: Adrian Hunter <adrian.hunter@intel.com>
+L: linux-mtd@lists.infradead.org
+T: git git://git.infradead.org/ubifs-2.6.git
+W: http://www.linux-mtd.infradead.org/doc/ubifs.html
+S: Supported
+F: Documentation/filesystems/ubifs.txt
+F: fs/ubifs/
+
+UCLINUX (M68KNOMMU AND COLDFIRE)
+M: Greg Ungerer <gerg@linux-m68k.org>
+W: http://www.linux-m68k.org/
+W: http://www.uclinux.org/
+L: linux-m68k@lists.linux-m68k.org
+L: uclinux-dev@uclinux.org (subscribers-only)
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/gerg/m68knommu.git
+S: Maintained
+F: arch/m68k/coldfire/
+F: arch/m68k/68*/
+F: arch/m68k/*/*_no.*
+F: arch/m68k/include/asm/*_no.*
+
+UDF FILESYSTEM
+M: Jan Kara <jack@suse.com>
+S: Maintained
+F: Documentation/filesystems/udf.txt
+F: fs/udf/
+
+UDRAW TABLET
+M: Bastien Nocera <hadess@hadess.net>
+L: linux-input@vger.kernel.org
+S: Maintained
+F: drivers/hid/hid-udraw.c
+
+UFS FILESYSTEM
+M: Evgeniy Dushistov <dushistov@mail.ru>
+S: Maintained
+F: Documentation/filesystems/ufs.txt
+F: fs/ufs/
+
+UHID USERSPACE HID IO DRIVER:
+M: David Herrmann <dh.herrmann@googlemail.com>
+L: linux-input@vger.kernel.org
+S: Maintained
+F: drivers/hid/uhid.c
+F: include/uapi/linux/uhid.h
+
+ULTRA-WIDEBAND (UWB) SUBSYSTEM:
+L: linux-usb@vger.kernel.org
+S: Orphan
+F: drivers/uwb/
+F: include/linux/uwb.h
+F: include/linux/uwb/
+
+UNICORE32 ARCHITECTURE:
+M: Guan Xuetao <gxt@mprc.pku.edu.cn>
+W: http://mprc.pku.edu.cn/~guanxuetao/linux
+S: Maintained
+T: git git://github.com/gxt/linux.git
+F: arch/unicore32/
+
+UNIFDEF
+M: Tony Finch <dot@dotat.at>
+W: http://dotat.at/prog/unifdef
+S: Maintained
+F: scripts/unifdef.c
+
+UNIFORM CDROM DRIVER
+M: Jens Axboe <axboe@kernel.dk>
+W: http://www.kernel.dk
+S: Maintained
+F: Documentation/cdrom/
+F: drivers/cdrom/cdrom.c
+F: include/linux/cdrom.h
+F: include/uapi/linux/cdrom.h
+
+UNISYS S-PAR DRIVERS
+M: David Kershner <david.kershner@unisys.com>
+L: sparmaintainer@unisys.com (Unisys internal)
+S: Supported
+F: drivers/staging/unisys/
+
+UNIVERSAL FLASH STORAGE HOST CONTROLLER DRIVER
+M: Vinayak Holikatti <vinholikatti@gmail.com>
+L: linux-scsi@vger.kernel.org
+S: Supported
+F: Documentation/scsi/ufs.txt
+F: drivers/scsi/ufs/
+
+UNIVERSAL FLASH STORAGE HOST CONTROLLER DRIVER DWC HOOKS
+M: Manjunath M Bettegowda <manjumb@synopsys.com>
+M: Prabu Thangamuthu <prabut@synopsys.com>
+L: linux-scsi@vger.kernel.org
+S: Supported
+F: drivers/scsi/ufs/*dwc*
+
+UNSORTED BLOCK IMAGES (UBI)
+M: Artem Bityutskiy <dedekind1@gmail.com>
+M: Richard Weinberger <richard@nod.at>
+W: http://www.linux-mtd.infradead.org/
+L: linux-mtd@lists.infradead.org
+T: git git://git.infradead.org/ubifs-2.6.git
+S: Supported
+F: drivers/mtd/ubi/
+F: include/linux/mtd/ubi.h
+F: include/uapi/mtd/ubi-user.h
+
+USB ACM DRIVER
+M: Oliver Neukum <oneukum@suse.com>
+L: linux-usb@vger.kernel.org
+S: Maintained
+F: Documentation/usb/acm.txt
+F: drivers/usb/class/cdc-acm.*
+
+USB AR5523 WIRELESS DRIVER
+M: Pontus Fuchs <pontus.fuchs@gmail.com>
+L: linux-wireless@vger.kernel.org
+S: Maintained
+F: drivers/net/wireless/ath/ar5523/
+
+USB ATTACHED SCSI
+M: Oliver Neukum <oneukum@suse.com>
+L: linux-usb@vger.kernel.org
+L: linux-scsi@vger.kernel.org
+S: Maintained
+F: drivers/usb/storage/uas.c
+
+USB CDC ETHERNET DRIVER
+M: Oliver Neukum <oliver@neukum.org>
+L: linux-usb@vger.kernel.org
+S: Maintained
+F: drivers/net/usb/cdc_*.c
+F: include/uapi/linux/usb/cdc.h
+
+USB CHAOSKEY DRIVER
+M: Keith Packard <keithp@keithp.com>
+L: linux-usb@vger.kernel.org
+S: Maintained
+F: drivers/usb/misc/chaoskey.c
+
+USB CYPRESS C67X00 DRIVER
+M: Peter Korsgaard <jacmet@sunsite.dk>
+L: linux-usb@vger.kernel.org
+S: Maintained
+F: drivers/usb/c67x00/
+
+USB DAVICOM DM9601 DRIVER
+M: Peter Korsgaard <jacmet@sunsite.dk>
+L: netdev@vger.kernel.org
+W: http://www.linux-usb.org/usbnet
+S: Maintained
+F: drivers/net/usb/dm9601.c
+
+USB DIAMOND RIO500 DRIVER
+M: Cesar Miquel <miquel@df.uba.ar>
+L: rio500-users@lists.sourceforge.net
+W: http://rio500.sourceforge.net
+S: Maintained
+F: drivers/usb/misc/rio500*
+
+USB EHCI DRIVER
+M: Alan Stern <stern@rowland.harvard.edu>
+L: linux-usb@vger.kernel.org
+S: Maintained
+F: Documentation/usb/ehci.txt
+F: drivers/usb/host/ehci*
+
+USB GADGET/PERIPHERAL SUBSYSTEM
+M: Felipe Balbi <balbi@kernel.org>
+L: linux-usb@vger.kernel.org
+W: http://www.linux-usb.org/gadget
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb.git
+S: Maintained
+F: drivers/usb/gadget/
+F: include/linux/usb/gadget*
+
+USB HID/HIDBP DRIVERS (USB KEYBOARDS, MICE, REMOTE CONTROLS, ...)
+M: Jiri Kosina <jikos@kernel.org>
+R: Benjamin Tissoires <benjamin.tissoires@redhat.com>
+L: linux-usb@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid.git
+S: Maintained
+F: Documentation/hid/hiddev.txt
+F: drivers/hid/usbhid/
+
+USB ISP116X DRIVER
+M: Olav Kongas <ok@artecdesign.ee>
+L: linux-usb@vger.kernel.org
+S: Maintained
+F: drivers/usb/host/isp116x*
+F: include/linux/usb/isp116x.h
+
+USB LAN78XX ETHERNET DRIVER
+M: Woojung Huh <woojung.huh@microchip.com>
+M: Microchip Linux Driver Support <UNGLinuxDriver@microchip.com>
+L: netdev@vger.kernel.org
+S: Maintained
+F: drivers/net/usb/lan78xx.*
+
+USB MASS STORAGE DRIVER
+M: Alan Stern <stern@rowland.harvard.edu>
+L: linux-usb@vger.kernel.org
+L: usb-storage@lists.one-eyed-alien.net
+S: Maintained
+W: http://www.one-eyed-alien.net/~mdharm/linux-usb/
+F: drivers/usb/storage/
+
+USB MIDI DRIVER
+M: Clemens Ladisch <clemens@ladisch.de>
+L: alsa-devel@alsa-project.org (moderated for non-subscribers)
+T: git git://git.alsa-project.org/alsa-kernel.git
+S: Maintained
+F: sound/usb/midi.*
+
+USB NETWORKING DRIVERS
+L: linux-usb@vger.kernel.org
+S: Odd Fixes
+F: drivers/net/usb/
+
+USB OHCI DRIVER
+M: Alan Stern <stern@rowland.harvard.edu>
+L: linux-usb@vger.kernel.org
+S: Maintained
+F: Documentation/usb/ohci.txt
+F: drivers/usb/host/ohci*
+
+USB OTG FSM (Finite State Machine)
+M: Peter Chen <Peter.Chen@nxp.com>
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/peter.chen/usb.git
+L: linux-usb@vger.kernel.org
+S: Maintained
+F: drivers/usb/common/usb-otg-fsm.c
+
+USB OVER IP DRIVER
+M: Valentina Manea <valentina.manea.m@gmail.com>
+M: Shuah Khan <shuahkh@osg.samsung.com>
+M: Shuah Khan <shuah@kernel.org>
+L: linux-usb@vger.kernel.org
+S: Maintained
+F: Documentation/usb/usbip_protocol.txt
+F: drivers/usb/usbip/
+F: tools/usb/usbip/
+
+USB PEGASUS DRIVER
+M: Petko Manolov <petkan@nucleusys.com>
+L: linux-usb@vger.kernel.org
+L: netdev@vger.kernel.org
+T: git git://github.com/petkan/pegasus.git
+W: https://github.com/petkan/pegasus
+S: Maintained
+F: drivers/net/usb/pegasus.*
+
+USB PHY LAYER
+M: Felipe Balbi <balbi@kernel.org>
+L: linux-usb@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb.git
+S: Maintained
+F: drivers/usb/phy/
+
+USB PRINTER DRIVER (usblp)
+M: Pete Zaitcev <zaitcev@redhat.com>
+L: linux-usb@vger.kernel.org
+S: Supported
+F: drivers/usb/class/usblp.c
+
+USB QMI WWAN NETWORK DRIVER
+M: Bjørn Mork <bjorn@mork.no>
+L: netdev@vger.kernel.org
+S: Maintained
+F: Documentation/ABI/testing/sysfs-class-net-qmi
+F: drivers/net/usb/qmi_wwan.c
+
+USB RTL8150 DRIVER
+M: Petko Manolov <petkan@nucleusys.com>
+L: linux-usb@vger.kernel.org
+L: netdev@vger.kernel.org
+T: git git://github.com/petkan/rtl8150.git
+W: https://github.com/petkan/rtl8150
+S: Maintained
+F: drivers/net/usb/rtl8150.c
+
+USB SERIAL SUBSYSTEM
+M: Johan Hovold <johan@kernel.org>
+L: linux-usb@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/johan/usb-serial.git
+S: Maintained
+F: Documentation/usb/usb-serial.txt
+F: drivers/usb/serial/
+F: include/linux/usb/serial.h
+
+USB SMSC75XX ETHERNET DRIVER
+M: Steve Glendinning <steve.glendinning@shawell.net>
+L: netdev@vger.kernel.org
+S: Maintained
+F: drivers/net/usb/smsc75xx.*
+
+USB SMSC95XX ETHERNET DRIVER
+M: Steve Glendinning <steve.glendinning@shawell.net>
+M: Microchip Linux Driver Support <UNGLinuxDriver@microchip.com>
+L: netdev@vger.kernel.org
+S: Maintained
+F: drivers/net/usb/smsc95xx.*
+
+USB SUBSYSTEM
+M: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+L: linux-usb@vger.kernel.org
+W: http://www.linux-usb.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git
+S: Supported
+F: Documentation/devicetree/bindings/usb/
+F: Documentation/usb/
+F: drivers/usb/
+F: include/linux/usb.h
+F: include/linux/usb/
+
+USB TYPEC SUBSYSTEM
+M: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+L: linux-usb@vger.kernel.org
+S: Maintained
+F: Documentation/ABI/testing/sysfs-class-typec
+F: Documentation/usb/typec.rst
+F: drivers/usb/typec/
+F: include/linux/usb/typec.h
+
+USB UHCI DRIVER
+M: Alan Stern <stern@rowland.harvard.edu>
+L: linux-usb@vger.kernel.org
+S: Maintained
+F: drivers/usb/host/uhci*
+
+USB "USBNET" DRIVER FRAMEWORK
+M: Oliver Neukum <oneukum@suse.com>
+L: netdev@vger.kernel.org
+W: http://www.linux-usb.org/usbnet
+S: Maintained
+F: drivers/net/usb/usbnet.c
+F: include/linux/usb/usbnet.h
+
+USB VIDEO CLASS
+M: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+L: linux-uvc-devel@lists.sourceforge.net (subscribers-only)
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+W: http://www.ideasonboard.org/uvc/
+S: Maintained
+F: drivers/media/usb/uvc/
+F: include/uapi/linux/uvcvideo.h
+
+USB VISION DRIVER
+M: Hans Verkuil <hverkuil@xs4all.nl>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+W: https://linuxtv.org
+S: Odd Fixes
+F: drivers/media/usb/usbvision/
+
+USB WEBCAM GADGET
+M: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+L: linux-usb@vger.kernel.org
+S: Maintained
+F: drivers/usb/gadget/function/*uvc*
+F: drivers/usb/gadget/legacy/webcam.c
+
+USB WIRELESS RNDIS DRIVER (rndis_wlan)
+M: Jussi Kivilinna <jussi.kivilinna@iki.fi>
+L: linux-wireless@vger.kernel.org
+S: Maintained
+F: drivers/net/wireless/rndis_wlan.c
+
+USB XHCI DRIVER
+M: Mathias Nyman <mathias.nyman@intel.com>
+L: linux-usb@vger.kernel.org
+S: Supported
+F: drivers/usb/host/xhci*
+F: drivers/usb/host/pci-quirks*
+
+USB ZD1201 DRIVER
+L: linux-wireless@vger.kernel.org
+W: http://linux-lc100020.sourceforge.net
+S: Orphan
+F: drivers/net/wireless/zydas/zd1201.*
+
+USB ZR364XX DRIVER
+M: Antoine Jacquet <royale@zerezo.com>
+L: linux-usb@vger.kernel.org
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+W: http://royale.zerezo.com/zr364xx/
+S: Maintained
+F: Documentation/media/v4l-drivers/zr364xx*
+F: drivers/media/usb/zr364xx/
+
+ULPI BUS
+M: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+L: linux-usb@vger.kernel.org
+S: Maintained
+F: drivers/usb/common/ulpi.c
+F: include/linux/ulpi/
+
+USER-MODE LINUX (UML)
+M: Jeff Dike <jdike@addtoit.com>
+M: Richard Weinberger <richard@nod.at>
+L: user-mode-linux-devel@lists.sourceforge.net
+L: user-mode-linux-user@lists.sourceforge.net
+W: http://user-mode-linux.sourceforge.net
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/rw/uml.git
+S: Maintained
+F: Documentation/virtual/uml/
+F: arch/um/
+F: arch/x86/um/
+F: fs/hostfs/
+F: fs/hppfs/
+
+USERSPACE I/O (UIO)
+M: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+S: Maintained
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc.git
+F: Documentation/driver-api/uio-howto.rst
+F: drivers/uio/
+F: include/linux/uio*.h
+
+UTIL-LINUX PACKAGE
+M: Karel Zak <kzak@redhat.com>
+L: util-linux@vger.kernel.org
+W: http://en.wikipedia.org/wiki/Util-linux
+T: git git://git.kernel.org/pub/scm/utils/util-linux/util-linux.git
+S: Maintained
+
+UVESAFB DRIVER
+M: Michal Januszewski <spock@gentoo.org>
+L: linux-fbdev@vger.kernel.org
+W: http://dev.gentoo.org/~spock/projects/uvesafb/
+S: Maintained
+F: Documentation/fb/uvesafb.txt
+F: drivers/video/fbdev/uvesafb.*
+
+VF610 NAND DRIVER
+M: Stefan Agner <stefan@agner.ch>
+L: linux-mtd@lists.infradead.org
+S: Supported
+F: drivers/mtd/nand/vf610_nfc.c
+
+VFAT/FAT/MSDOS FILESYSTEM
+M: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
+S: Maintained
+F: Documentation/filesystems/vfat.txt
+F: fs/fat/
+
+VFIO DRIVER
+M: Alex Williamson <alex.williamson@redhat.com>
+L: kvm@vger.kernel.org
+T: git git://github.com/awilliam/linux-vfio.git
+S: Maintained
+F: Documentation/vfio.txt
+F: drivers/vfio/
+F: include/linux/vfio.h
+F: include/uapi/linux/vfio.h
+
+VFIO MEDIATED DEVICE DRIVERS
+M: Kirti Wankhede <kwankhede@nvidia.com>
+L: kvm@vger.kernel.org
+S: Maintained
+F: Documentation/vfio-mediated-device.txt
+F: drivers/vfio/mdev/
+F: include/linux/mdev.h
+F: samples/vfio-mdev/
+
+VFIO PLATFORM DRIVER
+M: Baptiste Reynal <b.reynal@virtualopensystems.com>
+L: kvm@vger.kernel.org
+S: Maintained
+F: drivers/vfio/platform/
+
+VGA_SWITCHEROO
+R: Lukas Wunner <lukas@wunner.de>
+S: Maintained
+F: Documentation/gpu/vga-switcheroo.rst
+F: drivers/gpu/vga/vga_switcheroo.c
+F: include/linux/vga_switcheroo.h
+T: git git://anongit.freedesktop.org/drm/drm-misc
+
+VIDEOBUF2 FRAMEWORK
+M: Pawel Osciak <pawel@osciak.com>
+M: Marek Szyprowski <m.szyprowski@samsung.com>
+M: Kyungmin Park <kyungmin.park@samsung.com>
+L: linux-media@vger.kernel.org
+S: Maintained
+F: drivers/media/v4l2-core/videobuf2-*
+F: include/media/videobuf2-*
+
+VIRTIO AND VHOST VSOCK DRIVER
+M: Stefan Hajnoczi <stefanha@redhat.com>
+L: kvm@vger.kernel.org
+L: virtualization@lists.linux-foundation.org
+L: netdev@vger.kernel.org
+S: Maintained
+F: include/linux/virtio_vsock.h
+F: include/uapi/linux/virtio_vsock.h
+F: include/uapi/linux/vsockmon.h
+F: net/vmw_vsock/af_vsock_tap.c
+F: net/vmw_vsock/virtio_transport_common.c
+F: net/vmw_vsock/virtio_transport.c
+F: drivers/net/vsockmon.c
+F: drivers/vhost/vsock.c
+F: drivers/vhost/vsock.h
+
+VIRTUAL SERIO DEVICE DRIVER
+M: Stephen Chandler Paul <thatslyude@gmail.com>
+S: Maintained
+F: drivers/input/serio/userio.c
+F: include/uapi/linux/userio.h
+
+VIRTIO CONSOLE DRIVER
+M: Amit Shah <amit@kernel.org>
+L: virtualization@lists.linux-foundation.org
+S: Maintained
+F: drivers/char/virtio_console.c
+F: include/linux/virtio_console.h
+F: include/uapi/linux/virtio_console.h
+
+VIRTIO CORE, NET AND BLOCK DRIVERS
+M: "Michael S. Tsirkin" <mst@redhat.com>
+M: Jason Wang <jasowang@redhat.com>
+L: virtualization@lists.linux-foundation.org
+S: Maintained
+F: Documentation/devicetree/bindings/virtio/
+F: drivers/virtio/
+F: tools/virtio/
+F: drivers/net/virtio_net.c
+F: drivers/block/virtio_blk.c
+F: include/linux/virtio*.h
+F: include/uapi/linux/virtio_*.h
+F: drivers/crypto/virtio/
+
+VIRTIO DRIVERS FOR S390
+M: Cornelia Huck <cornelia.huck@de.ibm.com>
+M: Halil Pasic <pasic@linux.vnet.ibm.com>
+L: linux-s390@vger.kernel.org
+L: virtualization@lists.linux-foundation.org
+L: kvm@vger.kernel.org
+S: Supported
+F: drivers/s390/virtio/
+
+VIRTIO GPU DRIVER
+M: David Airlie <airlied@linux.ie>
+M: Gerd Hoffmann <kraxel@redhat.com>
+L: dri-devel@lists.freedesktop.org
+L: virtualization@lists.linux-foundation.org
+T: git git://anongit.freedesktop.org/drm/drm-misc
+S: Maintained
+F: drivers/gpu/drm/virtio/
+F: include/uapi/linux/virtio_gpu.h
+
+VIRTIO HOST (VHOST)
+M: "Michael S. Tsirkin" <mst@redhat.com>
+M: Jason Wang <jasowang@redhat.com>
+L: kvm@vger.kernel.org
+L: virtualization@lists.linux-foundation.org
+L: netdev@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/mst/vhost.git
+S: Maintained
+F: drivers/vhost/
+F: include/uapi/linux/vhost.h
+
+VIRTIO INPUT DRIVER
+M: Gerd Hoffmann <kraxel@redhat.com>
+S: Maintained
+F: drivers/virtio/virtio_input.c
+F: include/uapi/linux/virtio_input.h
+
+VIRTIO CRYPTO DRIVER
+M: Gonglei <arei.gonglei@huawei.com>
+L: virtualization@lists.linux-foundation.org
+L: linux-crypto@vger.kernel.org
+S: Maintained
+F: drivers/crypto/virtio/
+F: include/uapi/linux/virtio_crypto.h
+
+VIA RHINE NETWORK DRIVER
+S: Orphan
+F: drivers/net/ethernet/via/via-rhine.c
+
+VIA SD/MMC CARD CONTROLLER DRIVER
+M: Bruce Chang <brucechang@via.com.tw>
+M: Harald Welte <HaraldWelte@viatech.com>
+S: Maintained
+F: drivers/mmc/host/via-sdmmc.c
+
+VIA UNICHROME(PRO)/CHROME9 FRAMEBUFFER DRIVER
+M: Florian Tobias Schandinat <FlorianSchandinat@gmx.de>
+L: linux-fbdev@vger.kernel.org
+S: Maintained
+F: include/linux/via-core.h
+F: include/linux/via-gpio.h
+F: include/linux/via_i2c.h
+F: drivers/video/fbdev/via/
+
+VIA VELOCITY NETWORK DRIVER
+M: Francois Romieu <romieu@fr.zoreil.com>
+L: netdev@vger.kernel.org
+S: Maintained
+F: drivers/net/ethernet/via/via-velocity.*
+
+VIRT LIB
+M: Alex Williamson <alex.williamson@redhat.com>
+M: Paolo Bonzini <pbonzini@redhat.com>
+L: kvm@vger.kernel.org
+S: Supported
+F: virt/lib/
+
+VIVID VIRTUAL VIDEO DRIVER
+M: Hans Verkuil <hverkuil@xs4all.nl>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+W: https://linuxtv.org
+S: Maintained
+F: drivers/media/platform/vivid/*
+
+VIMC VIRTUAL MEDIA CONTROLLER DRIVER
+M: Helen Koike <helen.koike@collabora.com>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+W: https://linuxtv.org
+S: Maintained
+F: drivers/media/platform/vimc/*
+
+VLYNQ BUS
+M: Florian Fainelli <f.fainelli@gmail.com>
+L: openwrt-devel@lists.openwrt.org (subscribers-only)
+S: Maintained
+F: drivers/vlynq/vlynq.c
+F: include/linux/vlynq.h
+
+VME SUBSYSTEM
+M: Martyn Welch <martyn@welchs.me.uk>
+M: Manohar Vanga <manohar.vanga@gmail.com>
+M: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+L: devel@driverdev.osuosl.org
+S: Maintained
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core.git
+F: Documentation/driver-api/vme.rst
+F: drivers/staging/vme/
+F: drivers/vme/
+F: include/linux/vme*
+
+VMWARE HYPERVISOR INTERFACE
+M: Alok Kataria <akataria@vmware.com>
+L: virtualization@lists.linux-foundation.org
+S: Supported
+F: arch/x86/kernel/cpu/vmware.c
+
+VMWARE BALLOON DRIVER
+M: Xavier Deguillard <xdeguillard@vmware.com>
+M: Philip Moltmann <moltmann@vmware.com>
+M: "VMware, Inc." <pv-drivers@vmware.com>
+L: linux-kernel@vger.kernel.org
+S: Maintained
+F: drivers/misc/vmw_balloon.c
+
+VMWARE VMMOUSE SUBDRIVER
+M: "VMware Graphics" <linux-graphics-maintainer@vmware.com>
+M: "VMware, Inc." <pv-drivers@vmware.com>
+L: linux-input@vger.kernel.org
+S: Maintained
+F: drivers/input/mouse/vmmouse.c
+F: drivers/input/mouse/vmmouse.h
+
+VMWARE VMXNET3 ETHERNET DRIVER
+M: Shrikrishna Khare <skhare@vmware.com>
+M: "VMware, Inc." <pv-drivers@vmware.com>
+L: netdev@vger.kernel.org
+S: Maintained
+F: drivers/net/vmxnet3/
+
+VMware PVSCSI driver
+M: Jim Gill <jgill@vmware.com>
+M: VMware PV-Drivers <pv-drivers@vmware.com>
+L: linux-scsi@vger.kernel.org
+S: Maintained
+F: drivers/scsi/vmw_pvscsi.c
+F: drivers/scsi/vmw_pvscsi.h
+
+VMWARE PVRDMA DRIVER
+M: Adit Ranadive <aditr@vmware.com>
+M: VMware PV-Drivers <pv-drivers@vmware.com>
+L: linux-rdma@vger.kernel.org
+S: Maintained
+F: drivers/infiniband/hw/vmw_pvrdma/
+
+VOLTAGE AND CURRENT REGULATOR FRAMEWORK
+M: Liam Girdwood <lgirdwood@gmail.com>
+M: Mark Brown <broonie@kernel.org>
+L: linux-kernel@vger.kernel.org
+W: http://www.slimlogic.co.uk/?p=48
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regulator.git
+S: Supported
+F: Documentation/devicetree/bindings/regulator/
+F: drivers/regulator/
+F: include/dt-bindings/regulator/
+F: include/linux/regulator/
+
+VRF
+M: David Ahern <dsa@cumulusnetworks.com>
+M: Shrijeet Mukherjee <shm@cumulusnetworks.com>
+L: netdev@vger.kernel.org
+S: Maintained
+F: drivers/net/vrf.c
+F: Documentation/networking/vrf.txt
+
+VT1211 HARDWARE MONITOR DRIVER
+M: Juerg Haefliger <juergh@gmail.com>
+L: linux-hwmon@vger.kernel.org
+S: Maintained
+F: Documentation/hwmon/vt1211
+F: drivers/hwmon/vt1211.c
+
+VT8231 HARDWARE MONITOR DRIVER
+M: Roger Lucas <vt8231@hiddenengine.co.uk>
+L: linux-hwmon@vger.kernel.org
+S: Maintained
+F: drivers/hwmon/vt8231.c
+
+VUB300 USB to SDIO/SD/MMC bridge chip
+M: Tony Olech <tony.olech@elandigitalsystems.com>
+L: linux-mmc@vger.kernel.org
+L: linux-usb@vger.kernel.org
+S: Supported
+F: drivers/mmc/host/vub300.c
+
+W1 DALLAS'S 1-WIRE BUS
+M: Evgeniy Polyakov <zbr@ioremap.net>
+S: Maintained
+F: Documentation/w1/
+F: drivers/w1/
+
+W83791D HARDWARE MONITORING DRIVER
+M: Marc Hulsman <m.hulsman@tudelft.nl>
+L: linux-hwmon@vger.kernel.org
+S: Maintained
+F: Documentation/hwmon/w83791d
+F: drivers/hwmon/w83791d.c
+
+W83793 HARDWARE MONITORING DRIVER
+M: Rudolf Marek <r.marek@assembler.cz>
+L: linux-hwmon@vger.kernel.org
+S: Maintained
+F: Documentation/hwmon/w83793
+F: drivers/hwmon/w83793.c
+
+W83795 HARDWARE MONITORING DRIVER
+M: Jean Delvare <jdelvare@suse.com>
+L: linux-hwmon@vger.kernel.org
+S: Maintained
+F: drivers/hwmon/w83795.c
+
+W83L51xD SD/MMC CARD INTERFACE DRIVER
+M: Pierre Ossman <pierre@ossman.eu>
+S: Maintained
+F: drivers/mmc/host/wbsd.*
+
+WACOM PROTOCOL 4 SERIAL TABLETS
+M: Julian Squires <julian@cipht.net>
+M: Hans de Goede <hdegoede@redhat.com>
+L: linux-input@vger.kernel.org
+S: Maintained
+F: drivers/input/tablet/wacom_serial4.c
+
+WATCHDOG DEVICE DRIVERS
+M: Wim Van Sebroeck <wim@iguana.be>
+R: Guenter Roeck <linux@roeck-us.net>
+L: linux-watchdog@vger.kernel.org
+W: http://www.linux-watchdog.org/
+T: git git://www.linux-watchdog.org/linux-watchdog.git
+S: Maintained
+F: Documentation/devicetree/bindings/watchdog/
+F: Documentation/watchdog/
+F: drivers/watchdog/
+F: include/linux/watchdog.h
+F: include/uapi/linux/watchdog.h
+
+WIIMOTE HID DRIVER
+M: David Herrmann <dh.herrmann@googlemail.com>
+L: linux-input@vger.kernel.org
+S: Maintained
+F: drivers/hid/hid-wiimote*
+
+WINBOND CIR DRIVER
+M: David Härdeman <david@hardeman.nu>
+S: Maintained
+F: drivers/media/rc/winbond-cir.c
+
+WINSYSTEMS EBC-C384 WATCHDOG DRIVER
+M: William Breathitt Gray <vilhelm.gray@gmail.com>
+L: linux-watchdog@vger.kernel.org
+S: Maintained
+F: drivers/watchdog/ebc-c384_wdt.c
+
+WINSYSTEMS WS16C48 GPIO DRIVER
+M: William Breathitt Gray <vilhelm.gray@gmail.com>
+L: linux-gpio@vger.kernel.org
+S: Maintained
+F: drivers/gpio/gpio-ws16c48.c
+
+WIMAX STACK
+M: Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+M: linux-wimax@intel.com
+L: wimax@linuxwimax.org (subscribers-only)
+S: Supported
+W: http://linuxwimax.org
+F: Documentation/wimax/README.wimax
+F: include/linux/wimax/debug.h
+F: include/net/wimax.h
+F: include/uapi/linux/wimax.h
+F: net/wimax/
+
+WISTRON LAPTOP BUTTON DRIVER
+M: Miloslav Trmac <mitr@volny.cz>
+S: Maintained
+F: drivers/input/misc/wistron_btns.c
+
+WL3501 WIRELESS PCMCIA CARD DRIVER
+L: linux-wireless@vger.kernel.org
+S: Odd fixes
+F: drivers/net/wireless/wl3501*
+
+WOLFSON MICROELECTRONICS DRIVERS
+L: patches@opensource.cirrus.com
+T: git https://github.com/CirrusLogic/linux-drivers.git
+W: https://github.com/CirrusLogic/linux-drivers/wiki
+S: Supported
+F: Documentation/hwmon/wm83??
+F: Documentation/devicetree/bindings/extcon/extcon-arizona.txt
+F: Documentation/devicetree/bindings/regulator/arizona-regulator.txt
+F: Documentation/devicetree/bindings/mfd/arizona.txt
+F: Documentation/devicetree/bindings/mfd/wm831x.txt
+F: arch/arm/mach-s3c64xx/mach-crag6410*
+F: drivers/clk/clk-wm83*.c
+F: drivers/extcon/extcon-arizona.c
+F: drivers/leds/leds-wm83*.c
+F: drivers/gpio/gpio-*wm*.c
+F: drivers/gpio/gpio-arizona.c
+F: drivers/hwmon/wm83??-hwmon.c
+F: drivers/input/misc/wm831x-on.c
+F: drivers/input/touchscreen/wm831x-ts.c
+F: drivers/input/touchscreen/wm97*.c
+F: drivers/mfd/arizona*
+F: drivers/mfd/wm*.c
+F: drivers/mfd/cs47l24*
+F: drivers/power/supply/wm83*.c
+F: drivers/rtc/rtc-wm83*.c
+F: drivers/regulator/wm8*.c
+F: drivers/regulator/arizona*
+F: drivers/video/backlight/wm83*_bl.c
+F: drivers/watchdog/wm83*_wdt.c
+F: include/linux/mfd/arizona/
+F: include/linux/mfd/wm831x/
+F: include/linux/mfd/wm8350/
+F: include/linux/mfd/wm8400*
+F: include/linux/regulator/arizona*
+F: include/linux/wm97xx.h
+F: include/sound/wm????.h
+F: sound/soc/codecs/arizona.?
+F: sound/soc/codecs/wm*
+F: sound/soc/codecs/cs47l24*
+
+WORKQUEUE
+M: Tejun Heo <tj@kernel.org>
+R: Lai Jiangshan <jiangshanlai@gmail.com>
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/tj/wq.git
+S: Maintained
+F: include/linux/workqueue.h
+F: kernel/workqueue.c
+F: Documentation/core-api/workqueue.rst
+
+X-POWERS MULTIFUNCTION PMIC DEVICE DRIVERS
+M: Chen-Yu Tsai <wens@csie.org>
+L: linux-kernel@vger.kernel.org
+S: Maintained
+N: axp[128]
+
+X.25 NETWORK LAYER
+M: Andrew Hendry <andrew.hendry@gmail.com>
+L: linux-x25@vger.kernel.org
+S: Odd Fixes
+F: Documentation/networking/x25*
+F: include/net/x25*
+F: net/x25/
+
+X86 ARCHITECTURE (32-BIT AND 64-BIT)
+M: Thomas Gleixner <tglx@linutronix.de>
+M: Ingo Molnar <mingo@redhat.com>
+M: "H. Peter Anvin" <hpa@zytor.com>
+M: x86@kernel.org
+L: linux-kernel@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git x86/core
+S: Maintained
+F: Documentation/x86/
+F: arch/x86/
+
+X86 PLATFORM DRIVERS
+M: Darren Hart <dvhart@infradead.org>
+M: Andy Shevchenko <andy@infradead.org>
+L: platform-driver-x86@vger.kernel.org
+T: git git://git.infradead.org/users/dvhart/linux-platform-drivers-x86.git
+S: Maintained
+F: drivers/platform/x86/
+F: drivers/platform/olpc/
+
+X86 MCE INFRASTRUCTURE
+M: Tony Luck <tony.luck@intel.com>
+M: Borislav Petkov <bp@alien8.de>
+L: linux-edac@vger.kernel.org
+S: Maintained
+F: arch/x86/kernel/cpu/mcheck/*
+
+X86 MICROCODE UPDATE SUPPORT
+M: Borislav Petkov <bp@alien8.de>
+S: Maintained
+F: arch/x86/kernel/cpu/microcode/*
+
+X86 VDSO
+M: Andy Lutomirski <luto@amacapital.net>
+L: linux-kernel@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git x86/vdso
+S: Maintained
+F: arch/x86/entry/vdso/
+
+XC2028/3028 TUNER DRIVER
+M: Mauro Carvalho Chehab <mchehab@s-opensource.com>
+M: Mauro Carvalho Chehab <mchehab@kernel.org>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org
+T: git git://linuxtv.org/media_tree.git
+S: Maintained
+F: drivers/media/tuners/tuner-xc2028.*
+
+XEN HYPERVISOR INTERFACE
+M: Boris Ostrovsky <boris.ostrovsky@oracle.com>
+M: Juergen Gross <jgross@suse.com>
+L: xen-devel@lists.xenproject.org (moderated for non-subscribers)
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/xen/tip.git
+S: Supported
+F: arch/x86/xen/
+F: drivers/*/xen-*front.c
+F: drivers/xen/
+F: arch/x86/include/asm/xen/
+F: include/xen/
+F: include/uapi/xen/
+
+XEN HYPERVISOR ARM
+M: Stefano Stabellini <sstabellini@kernel.org>
+L: xen-devel@lists.xenproject.org (moderated for non-subscribers)
+S: Maintained
+F: arch/arm/xen/
+F: arch/arm/include/asm/xen/
+
+XEN HYPERVISOR ARM64
+M: Stefano Stabellini <sstabellini@kernel.org>
+L: xen-devel@lists.xenproject.org (moderated for non-subscribers)
+S: Maintained
+F: arch/arm64/xen/
+F: arch/arm64/include/asm/xen/
+
+XEN NETWORK BACKEND DRIVER
+M: Wei Liu <wei.liu2@citrix.com>
+M: Paul Durrant <paul.durrant@citrix.com>
+L: xen-devel@lists.xenproject.org (moderated for non-subscribers)
+L: netdev@vger.kernel.org
+S: Supported
+F: drivers/net/xen-netback/*
+
+XEN PCI SUBSYSTEM
+M: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
+L: xen-devel@lists.xenproject.org (moderated for non-subscribers)
+S: Supported
+F: arch/x86/pci/*xen*
+F: drivers/pci/*xen*
+
+XEN BLOCK SUBSYSTEM
+M: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
+M: Roger Pau Monné <roger.pau@citrix.com>
+L: xen-devel@lists.xenproject.org (moderated for non-subscribers)
+S: Supported
+F: drivers/block/xen-blkback/*
+F: drivers/block/xen*
+
+XEN PVSCSI DRIVERS
+M: Juergen Gross <jgross@suse.com>
+L: xen-devel@lists.xenproject.org (moderated for non-subscribers)
+L: linux-scsi@vger.kernel.org
+S: Supported
+F: drivers/scsi/xen-scsifront.c
+F: drivers/xen/xen-scsiback.c
+F: include/xen/interface/io/vscsiif.h
+
+XEN SWIOTLB SUBSYSTEM
+M: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
+L: xen-devel@lists.xenproject.org (moderated for non-subscribers)
+S: Supported
+F: arch/x86/xen/*swiotlb*
+F: drivers/xen/*swiotlb*
+
+XFS FILESYSTEM
+M: Darrick J. Wong <darrick.wong@oracle.com>
+M: linux-xfs@vger.kernel.org
+L: linux-xfs@vger.kernel.org
+W: http://xfs.org/
+T: git git://git.kernel.org/pub/scm/fs/xfs/xfs-linux.git
+S: Supported
+F: Documentation/filesystems/xfs.txt
+F: fs/xfs/
+
+XILINX AXI ETHERNET DRIVER
+M: Anirudha Sarangi <anirudh@xilinx.com>
+M: John Linn <John.Linn@xilinx.com>
+S: Maintained
+F: drivers/net/ethernet/xilinx/xilinx_axienet*
+
+XILINX UARTLITE SERIAL DRIVER
+M: Peter Korsgaard <jacmet@sunsite.dk>
+L: linux-serial@vger.kernel.org
+S: Maintained
+F: drivers/tty/serial/uartlite.c
+
+XILINX VIDEO IP CORES
+M: Hyun Kwon <hyun.kwon@xilinx.com>
+M: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+S: Supported
+F: Documentation/devicetree/bindings/media/xilinx/
+F: drivers/media/platform/xilinx/
+F: include/uapi/linux/xilinx-v4l2-controls.h
+
+XILLYBUS DRIVER
+M: Eli Billauer <eli.billauer@gmail.com>
+L: linux-kernel@vger.kernel.org
+S: Supported
+F: drivers/char/xillybus/
+
+XTENSA XTFPGA PLATFORM SUPPORT
+M: Max Filippov <jcmvbkbc@gmail.com>
+L: linux-xtensa@linux-xtensa.org
+S: Maintained
+F: drivers/spi/spi-xtensa-xtfpga.c
+F: sound/soc/xtensa/xtfpga-i2s.c
+
+YAM DRIVER FOR AX.25
+M: Jean-Paul Roubelat <jpr@f6fbb.org>
+L: linux-hams@vger.kernel.org
+S: Maintained
+F: drivers/net/hamradio/yam*
+F: include/linux/yam.h
+
+YEALINK PHONE DRIVER
+M: Henk Vergonet <Henk.Vergonet@gmail.com>
+L: usbb2k-api-dev@nongnu.org
+S: Maintained
+F: Documentation/input/yealink.rst
+F: drivers/input/misc/yealink.*
+
+Z8530 DRIVER FOR AX.25
+M: Joerg Reuter <jreuter@yaina.de>
+W: http://yaina.de/jreuter/
+W: http://www.qsl.net/dl1bke/
+L: linux-hams@vger.kernel.org
+S: Maintained
+F: Documentation/networking/z8530drv.txt
+F: drivers/net/hamradio/*scc.c
+F: drivers/net/hamradio/z8530.h
+
+ZBUD COMPRESSED PAGE ALLOCATOR
+M: Seth Jennings <sjenning@redhat.com>
+M: Dan Streetman <ddstreet@ieee.org>
+L: linux-mm@kvack.org
+S: Maintained
+F: mm/zbud.c
+F: include/linux/zbud.h
+
+ZD1211RW WIRELESS DRIVER
+M: Daniel Drake <dsd@gentoo.org>
+M: Ulrich Kunitz <kune@deine-taler.de>
+W: http://zd1211.ath.cx/wiki/DriverRewrite
+L: linux-wireless@vger.kernel.org
+L: zd1211-devs@lists.sourceforge.net (subscribers-only)
+S: Maintained
+F: drivers/net/wireless/zydas/zd1211rw/
+
+ZD1301_DEMOD MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org/
+W: http://palosaari.fi/linux/
+Q: https://patchwork.linuxtv.org/project/linux-media/list/
+S: Maintained
+F: drivers/media/dvb-frontends/zd1301_demod*
+
+ZD1301 MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: https://linuxtv.org/
+W: http://palosaari.fi/linux/
+Q: https://patchwork.linuxtv.org/project/linux-media/list/
+S: Maintained
+F: drivers/media/usb/dvb-usb-v2/zd1301*
+
+ZPOOL COMPRESSED PAGE STORAGE API
+M: Dan Streetman <ddstreet@ieee.org>
+L: linux-mm@kvack.org
+S: Maintained
+F: mm/zpool.c
+F: include/linux/zpool.h
+
+ZR36067 VIDEO FOR LINUX DRIVER
+L: mjpeg-users@lists.sourceforge.net
+L: linux-media@vger.kernel.org
+W: http://mjpeg.sourceforge.net/driver-zoran/
+T: hg https://linuxtv.org/hg/v4l-dvb
+S: Odd Fixes
+F: drivers/media/pci/zoran/
+
+ZRAM COMPRESSED RAM BLOCK DEVICE DRVIER
+M: Minchan Kim <minchan@kernel.org>
+M: Nitin Gupta <ngupta@vflare.org>
+R: Sergey Senozhatsky <sergey.senozhatsky.work@gmail.com>
+L: linux-kernel@vger.kernel.org
+S: Maintained
+F: drivers/block/zram/
+F: Documentation/blockdev/zram.txt
+
+ZS DECSTATION Z85C30 SERIAL DRIVER
+M: "Maciej W. Rozycki" <macro@linux-mips.org>
+S: Maintained
+F: drivers/tty/serial/zs.*
+
+ZSMALLOC COMPRESSED SLAB MEMORY ALLOCATOR
+M: Minchan Kim <minchan@kernel.org>
+M: Nitin Gupta <ngupta@vflare.org>
+R: Sergey Senozhatsky <sergey.senozhatsky.work@gmail.com>
+L: linux-mm@kvack.org
+S: Maintained
+F: mm/zsmalloc.c
+F: include/linux/zsmalloc.h
+F: Documentation/vm/zsmalloc.txt
+
+ZSWAP COMPRESSED SWAP CACHING
+M: Seth Jennings <sjenning@redhat.com>
+M: Dan Streetman <ddstreet@ieee.org>
+L: linux-mm@kvack.org
+S: Maintained
+F: mm/zswap.c
+
+THE REST
+M: Linus Torvalds <torvalds@linux-foundation.org>
+L: linux-kernel@vger.kernel.org
+Q: http://patchwork.kernel.org/project/LKML/list/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
+S: Buried alive in reporters
+F: *
+F: */
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..1b8ef5d
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,168 @@
+#
+# Makefile for the output source package
+#
+
+ifeq ($(KERNELRELEASE),)
+
+MAKEFLAGS += --no-print-directory
+SHELL := /bin/bash
+BACKPORT_DIR := $(shell pwd)
+
+KMODDIR ?= updates
+ifneq ($(origin KLIB), undefined)
+KMODPATH_ARG := "INSTALL_MOD_PATH=$(KLIB)"
+else
+KLIB := /lib/modules/$(shell uname -r)/
+KMODPATH_ARG :=
+endif
+KLIB_BUILD ?= $(KLIB)/build/
+KERNEL_CONFIG := $(KLIB_BUILD)/.config
+KERNEL_MAKEFILE := $(KLIB_BUILD)/Makefile
+CONFIG_MD5 := $(shell md5sum $(KERNEL_CONFIG) 2>/dev/null | sed 's/\s.*//')
+
+export KLIB KLIB_BUILD BACKPORT_DIR KMODDIR KMODPATH_ARG
+
+# disable built-in rules for this file
+.SUFFIXES:
+
+.PHONY: default
+default:
+ @$(MAKE) modules
+
+.PHONY: mrproper
+mrproper:
+ @test -f .config && $(MAKE) clean || true
+ @rm -f .config
+ @rm -f .kernel_config_md5 Kconfig.versions Kconfig.kernel
+ @rm -f backport-include/backport/autoconf.h
+
+.DEFAULT:
+ @set -e ; test -f local-symbols || ( \
+ echo "/--------------" ;\
+ echo "| You shouldn't run make in the backports tree, but only in" ;\
+ echo "| the generated output. This here is only the skeleton code" ;\
+ echo "| copied into the output directory. To use the backport system" ;\
+ echo "| from scratch, go into the top-level directory and run" ;\
+ echo "| ./gentree.py /path/to/linux-next/ /tmp/output" ;\
+ echo "| and then make menuconfig/... in the output directory. See" ;\
+ echo "| ./gentree.py --help" ;\
+ echo "| for more options." ;\
+ echo "\\--" ;\
+ false)
+ @set -e ; test -f $(KERNEL_CONFIG) || ( \
+ echo "/--------------" ;\
+ echo "| Your kernel headers are incomplete/not installed." ;\
+ echo "| Please install kernel headers, including a .config" ;\
+ echo "| file or use the KLIB/KLIB_BUILD make variables to" ;\
+ echo "| set the kernel to build against, e.g." ;\
+ echo "| make KLIB=/lib/modules/3.1.7/" ;\
+ echo "| to compile/install for the installed kernel 3.1.7" ;\
+ echo "| (that isn't currently running.)" ;\
+ echo "\\--" ;\
+ false)
+ @set -e ; if [ "$$(cat .kernel_config_md5 2>/dev/null)" != "$(CONFIG_MD5)" ] ;\
+ then \
+ echo -n "Generating local configuration database from kernel ..." ;\
+ grep -v -f local-symbols $(KERNEL_CONFIG) | grep = | ( \
+ while read l ; do \
+ if [ "$${l:0:7}" != "CONFIG_" ] ; then \
+ continue ;\
+ fi ;\
+ l=$${l:7} ;\
+ n=$${l%%=*} ;\
+ v=$${l#*=} ;\
+ if [ "$$v" = "m" ] ; then \
+ echo config $$n ;\
+ echo ' tristate' ;\
+ elif [ "$$v" = "y" ] ; then \
+ echo config $$n ;\
+ echo ' bool' ;\
+ else \
+ continue ;\
+ fi ;\
+ echo " default $$v" ;\
+ echo "" ;\
+ done \
+ ) > Kconfig.kernel ;\
+ kver=$$($(MAKE) --no-print-directory -C $(KLIB_BUILD) kernelversion | \
+ sed 's/^\(\([3-4]\|2\.6\)\.[0-9]\+\).*/\1/;t;d') ;\
+ test "$$kver" != "" || echo "Kernel version parse failed!" ;\
+ test "$$kver" != "" ;\
+ kvers="$$(seq 14 39 | sed 's/^/2.6./')" ;\
+ kvers="$$kvers $$(seq 0 19 | sed 's/^/3./')" ;\
+ kvers="$$kvers $$(seq 0 99 | sed 's/^/4./')" ;\
+ print=0 ;\
+ for v in $$kvers ; do \
+ if [ "$$print" = "1" ] ; then \
+ echo config KERNEL_$$(echo $$v | tr . _) ;\
+ echo " def_bool y" ;\
+ fi ;\
+ if [ "$$v" = "$$kver" ] ; then print=1 ; fi ;\
+ done > Kconfig.versions ;\
+ # RHEL as well, sadly we need to grep for it ;\
+ RHEL_MAJOR=$$(grep '^RHEL_MAJOR' $(KERNEL_MAKEFILE) | \
+ sed 's/.*=\s*\([0-9]*\)/\1/;t;d') ;\
+ RHEL_MINOR=$$(grep '^RHEL_MINOR' $(KERNEL_MAKEFILE) | \
+ sed 's/.*=\s*\([0-9]*\)/\1/;t;d') ;\
+ for v in $$(seq 0 $$RHEL_MINOR) ; do \
+ echo config BACKPORT_RHEL_KERNEL_$${RHEL_MAJOR}_$$v ;\
+ echo " def_bool y" ;\
+ done >> Kconfig.versions ;\
+ echo " done." ;\
+ fi ;\
+ echo "$(CONFIG_MD5)" > .kernel_config_md5
+ @$(MAKE) -f Makefile.real "$@"
+
+.PHONY: defconfig-help
+defconfig-help:
+ @echo "Driver or subsystem configuration targets:"
+ @set -e ;\
+ bk_configs="$$(ls defconfigs/*)" ;\
+ for cfg in $$bk_configs; do \
+ echo " defconfig-$${cfg##defconfigs/}" ;\
+ done
+ @echo ""
+
+.PHONY: help
+help: defconfig-help
+ @echo "Cleaning targets:"
+ @echo " clean - Remove most generated files but keep the config and"
+ @echo " enough build support to build external modules"
+ @echo " mrproper - Remove all generated files + config + various backup files"
+ @echo ""
+ @echo "Driver configuration help:"
+ @echo " defconfig-help - List all prearranged defconfig-targets we have"
+ @echo " designed for you. You can use this to find"
+ @echo " driver specific configs in case all you really"
+ @echo " need is to just compile one or a small group "
+ @echo " of drivers."
+ @echo ""
+ @echo "Configuration targets:"
+ @echo " menuconfig - Update current config utilising a menu based program"
+ @echo " oldconfig - Update current config utilising a provided .config as base"
+ @echo " oldaskconfig - ??"
+ @echo " silentoldconfig - Same as oldconfig, but quietly, additionally update deps"
+ @echo " allnoconfig - New config where all options are answered with no"
+ @echo " allyesconfig - New config where all options are accepted with yes"
+ @echo " allmodconfig - New config selecting modules when possible"
+ @echo " alldefconfig - New config with all symbols set to default"
+ @echo " randconfig - New config with random answer to all options"
+ @echo " listnewconfig - List new options"
+ @echo " olddefconfig - Same as silentoldconfig but sets new symbols to their default value"
+ @echo ""
+ @echo "Other generic targets:"
+ @echo " all - Build all targets marked with [*]"
+ @echo "* modules - Build all modules"
+ @echo ""
+ @echo "Architecture specific targets:"
+ @echo " install - Install modules"
+ @echo " uninstall - Uninstall modules"
+ @echo ""
+ @echo "Execute "make" or "make all" to build all targets marked with [*]"
+else
+ifneq ($(ANDROID_BUILD),)
+-include $(BACKPORT_DIR)/.config
+export $(filter CPTCFG_%,$(.VARIABLES))
+endif
+include $(BACKPORT_DIR)/Makefile.kernel
+endif
diff --git a/Makefile.build b/Makefile.build
new file mode 100644
index 0000000..a848b37
--- /dev/null
+++ b/Makefile.build
@@ -0,0 +1,10 @@
+-include .config
+export
+
+.PHONY: modules
+modules:
+ @$(MAKE) -C $(KLIB_BUILD) M=$(BACKPORT_DIR) modules
+
+.PHONY: clean
+clean:
+ @$(MAKE) -C $(KLIB_BUILD) M=$(BACKPORT_DIR) clean
diff --git a/Makefile.kernel b/Makefile.kernel
new file mode 100644
index 0000000..1f65484
--- /dev/null
+++ b/Makefile.kernel
@@ -0,0 +1,69 @@
+ifeq ($(CONFIG_BACKPORT_INTEGRATE),)
+abs_m := $(if $(filter /%,$(M)),$(M),$(KBUILD_SRC)/$(M))
+
+# Since 2.6.21, try-run is available, but cc-disable-warning
+# was only added later, so we add it here ourselves:
+backport-cc-disable-warning = $(call try-run,\
+ $(CC) $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) -W$(strip $(1)) -c -x c /dev/null -o "$$TMP",-Wno-$(strip $(1)))
+
+# Compile the package with debug info
+BACKPORTS_CFLAGS := $(CFLAGS)
+ifeq ($(CPTCFG_DEBUG_INFO)$(CPTCFG_BACKPORTED_DEBUG_INFO),)
+BACKPORTS_CFLAGS := $(filter-out -g, $(CFLAGS))
+else
+ifeq ($(call cc-option-yn, -g), n)
+BACKPORTS_CFLAGS += -g
+endif
+endif
+
+NOSTDINC_FLAGS := \
+ -I$(abs_m)/backport-include/ \
+ -I$(abs_m)/backport-include/uapi \
+ -I$(abs_m)/include/ \
+ -I$(abs_m)/include/uapi \
+ -include $(abs_m)/backport-include/backport/backport.h \
+ $(call backport-cc-disable-warning, unused-but-set-variable) \
+ -DCPTCFG_VERSION=\"$(BACKPORTS_VERSION)\" \
+ -DCPTCFG_KERNEL_VERSION=\"$(BACKPORTED_KERNEL_VERSION)\" \
+ -DCPTCFG_KERNEL_NAME=\"$(BACKPORTED_KERNEL_NAME)\" \
+ $(BACKPORTS_GIT_TRACKER_DEF) \
+ $(BACKPORTS_CFLAGS)
+
+export backport_srctree = $(abs_m)
+else
+export BACKPORT_DIR = backports/
+export backport_srctree = $(BACKPORT_DIR)
+NOSTDINC_FLAGS := \
+ -I$(BACKPORT_DIR)/backport-include/ \
+ -I$(BACKPORT_DIR)/backport-include/uapi \
+ -I$(BACKPORT_DIR)/include/ \
+ -I$(BACKPORT_DIR)/include/uapi \
+ -include $(BACKPORT_DIR)/backport-include/backport/backport.h \
+ $(KBUILD_CFLAGS)
+endif
+
+subdir-ccflags-y += $(call cc-option, -fno-pie) $(call cc-option, -no-pie)
+
+obj-y += compat/
+
+obj-$(CPTCFG_CFG80211) += net/wireless/
+#obj-$(CPTCFG_MAC80211) += net/mac80211/
+obj-$(CPTCFG_WLAN) += drivers/net/wireless/
+#obj-$(CPTCFG_BT) += net/bluetooth/
+#obj-$(CPTCFG_BT) += drivers/bluetooth/
+#obj-$(CPTCFG_SSB) += drivers/ssb/
+#obj-$(CPTCFG_BCMA) += drivers/bcma/
+#obj-$(CPTCFG_ETHERNET) += drivers/net/ethernet/
+#obj-$(CPTCFG_USB_NET_RNDIS_WLAN) += drivers/net/usb/
+#obj-$(CPTCFG_NFC) += net/nfc/
+#obj-$(CPTCFG_NFC) += drivers/nfc/
+#obj-$(CPTCFG_MEDIA_SUPPORT) += drivers/media/
+#
+#obj-$(CPTCFG_6LOWPAN) += net/6lowpan/
+#obj-$(CPTCFG_IEEE802154) += net/ieee802154/
+#obj-$(CPTCFG_BT) += net/ieee802154/
+#obj-$(CPTCFG_MAC802154) += net/mac802154/
+#obj-$(CPTCFG_IEEE802154) += drivers/net/ieee802154/
+#
+#obj-$(CPTCFG_USB_WDM) += drivers/usb/class/
+#obj-$(CPTCFG_USB_USBNET) += drivers/net/usb/
diff --git a/Makefile.real b/Makefile.real
new file mode 100644
index 0000000..7edc445
--- /dev/null
+++ b/Makefile.real
@@ -0,0 +1,111 @@
+include versions
+export BACKPORTS_VERSION BACKPORTED_KERNEL_VERSION BACKPORTED_KERNEL_NAME
+ifdef BACKPORTS_GIT_TRACKED
+export BACKPORTS_GIT_TRACKER_DEF=-DBACKPORTS_GIT_TRACKED=\"$(BACKPORTS_GIT_TRACKED)\"
+else
+export BACKPORTS_GIT_TRACKER_DEF=
+endif
+
+# disable built-in rules for this file
+.SUFFIXES:
+
+export CONFIG_=CPTCFG_
+
+.PHONY: menuconfig
+menuconfig:
+ @$(MAKE) -C kconf mconf
+ @./kconf/mconf Kconfig
+
+.PHONY: listnewconfig oldaskconfig oldconfig \
+ silentoldconfig olddefconfig oldnoconfig \
+ allnoconfig allyesconfig allmodconfig \
+ alldefconfig randconfig
+listnewconfig oldaskconfig oldconfig \
+silentoldconfig olddefconfig oldnoconfig \
+allnoconfig allyesconfig allmodconfig \
+alldefconfig randconfig:
+ @$(MAKE) -C kconf conf
+ @./kconf/conf --$@ Kconfig
+
+.PHONY: usedefconfig
+usedefconfig:
+ @$(MAKE) -C kconf conf
+ @./kconf/conf --defconfig=defconfig Kconfig
+
+.PHONY: savedefconfig
+savedefconfig:
+ @$(MAKE) -C kconf conf
+ @./kconf/conf --savedefconfig=defconfig Kconfig
+
+defconfig-%::
+ @$(MAKE) -C kconf conf
+ @./kconf/conf --defconfig=defconfigs/$(@:defconfig-%=%) Kconfig
+
+.config:
+ @test -f defconfig && $(MAKE) usedefconfig || ( \
+ echo "/--------------" ;\
+ echo "| Your backport package isn't configured, please configure it" ;\
+ echo "| using one of the following options:" ;\
+ echo "| To configure manually:" ;\
+ echo "| make oldconfig" ;\
+ echo "| make menuconfig" ;\
+ echo "|" ;\
+ echo "| To get defaults for certain drivers:" ;\
+ (cd defconfigs ; for f in $$(ls) ; do \
+ echo "| make defconfig-$$f" ;\
+ done ) ;\
+ echo "\--" ;\
+ false )
+
+backport-include/backport/autoconf.h: .config Kconfig.versions Kconfig.kernel
+ @$(MAKE) oldconfig
+ @echo -n "Building backport-include/backport/autoconf.h ..."
+ @grep -f local-symbols .config | ( \
+ echo "#ifndef COMPAT_AUTOCONF_INCLUDED" ;\
+ echo "#define COMPAT_AUTOCONF_INCLUDED" ;\
+ echo "/*" ;\
+ echo " * Automatically generated file, don't edit!" ;\
+ echo " * Changes will be overwritten" ;\
+ echo " */" ;\
+ echo "" ;\
+ while read l ; do \
+ n=$${l%%=*} ;\
+ v=$${l#*=} ;\
+ case $$v in \
+ y) echo "#define $$n 1" ;; \
+ m) echo "#define $${n}_MODULE 1" ;; \
+ \"*) echo "#define $$n $$v" ;; \
+ [0-9]*) echo "#define $$n $$v" ;; \
+ *) echo "#warning unknown value for $$n";;\
+ esac ;\
+ done ;\
+ echo "#endif /* COMPAT_AUTOCONF_INCLUDED */" ;\
+ ) > backport-include/backport/autoconf.h
+ @echo " done."
+
+.PHONY: modules
+modules: backport-include/backport/autoconf.h
+ @$(MAKE) -f Makefile.build modules
+
+.PHONY: install
+install: modules
+ @$(MAKE) -C $(KLIB_BUILD) M=$(BACKPORT_DIR) \
+ INSTALL_MOD_DIR=$(KMODDIR) $(KMODPATH_ARG) \
+ modules_install
+
+.PHONY: modules_install
+modules_install: install
+
+.PHONY: uninstall
+uninstall:
+ @./scripts/uninstall.sh
+ @/sbin/depmod -a
+ @./scripts/update-initramfs.sh $(KLIB)
+ @echo
+ @echo Your backported driver modules should be uninstalled now.
+ @echo Reboot.
+ @echo
+
+.PHONY: clean
+clean:
+ @$(MAKE) -f Makefile.build clean
diff --git a/backport-include/asm-generic/bug.h b/backport-include/asm-generic/bug.h
new file mode 100644
index 0000000..4e9e05f
--- /dev/null
+++ b/backport-include/asm-generic/bug.h
@@ -0,0 +1,39 @@
+#ifndef __BACKPORT_ASM_GENERIC_BUG_H
+#define __BACKPORT_ASM_GENERIC_BUG_H
+#include_next <asm-generic/bug.h>
+
+#ifndef __WARN
+#define __WARN(foo) dump_stack()
+#endif
+
+#ifndef WARN_ONCE
+#define WARN_ONCE(condition, format...) ({ \
+ static int __warned; \
+ int __ret_warn_once = !!(condition); \
+ \
+ if (unlikely(__ret_warn_once)) \
+ if (WARN(!__warned, format)) \
+ __warned = 1; \
+ unlikely(__ret_warn_once); \
+})
+#endif
+
+#ifndef __WARN_printf
+/*
+ * To port this properly we'd have to port warn_slowpath_null(),
+ * which I'm lazy to do so just do a regular print for now. If you
+ * want to port this read kernel/panic.c
+ */
+#define __WARN_printf(arg...) do { printk(arg); __WARN(); } while (0)
+#endif
+
+#ifndef WARN
+#define WARN(condition, format...) ({ \
+ int __ret_warn_on = !!(condition); \
+ if (unlikely(__ret_warn_on)) \
+ __WARN_printf(format); \
+ unlikely(__ret_warn_on); \
+})
+#endif
+
+#endif /* __BACKPORT_ASM_GENERIC_BUG_H */
diff --git a/backport-include/asm-generic/pci-dma-compat.h b/backport-include/asm-generic/pci-dma-compat.h
new file mode 100644
index 0000000..1b1433e
--- /dev/null
+++ b/backport-include/asm-generic/pci-dma-compat.h
@@ -0,0 +1,17 @@
+#ifndef __BACKPORT_ASM_GENERIC_PCI_DMA_COMPAT_H
+#define __BACKPORT_ASM_GENERIC_PCI_DMA_COMPAT_H
+#include_next <asm-generic/pci-dma-compat.h>
+
+#if LINUX_VERSION_IS_LESS(3,17,0)
+#define pci_zalloc_consistent LINUX_BACKPORT(pci_zalloc_consistent)
+static inline void *pci_zalloc_consistent(struct pci_dev *hwdev, size_t size,
+ dma_addr_t *dma_handle)
+{
+ void *ret = pci_alloc_consistent(hwdev, size, dma_handle);
+ if (ret)
+ memset(ret, 0, size);
+ return ret;
+}
+#endif
+
+#endif /* __BACKPORT_ASM_GENERIC_PCI_DMA_COMPAT_H */
diff --git a/backport-include/asm/atomic.h b/backport-include/asm/atomic.h
new file mode 100644
index 0000000..31bddc6
--- /dev/null
+++ b/backport-include/asm/atomic.h
@@ -0,0 +1,19 @@
+#ifndef __BACKPORT_ASM_ATOMIC_H
+#define __BACKPORT_ASM_ATOMIC_H
+#include_next <asm/atomic.h>
+#include <linux/version.h>
+#include <asm/barrier.h>
+
+#if LINUX_VERSION_IS_LESS(3,1,0)
+/*
+ * In many versions, several architectures do not seem to include an
+ * atomic64_t implementation, and do not include the software emulation from
+ * asm-generic/atomic64_t.
+ * Detect and handle this here.
+ */
+#if (!defined(ATOMIC64_INIT) && !defined(CONFIG_X86) && !(defined(CONFIG_ARM) && !defined(CONFIG_GENERIC_ATOMIC64)))
+#include <asm-generic/atomic64.h>
+#endif
+#endif
+
+#endif /* __BACKPORT_ASM_ATOMIC_H */
diff --git a/backport-include/asm/barrier.h b/backport-include/asm/barrier.h
new file mode 100644
index 0000000..9147fb0
--- /dev/null
+++ b/backport-include/asm/barrier.h
@@ -0,0 +1,18 @@
+#ifndef __BACKPORT_ASM_BARRIER_H
+#define __BACKPORT_ASM_BARRIER_H
+
+#include <linux/version.h>
+#if LINUX_VERSION_IS_GEQ(3,4,0) || \
+ defined(CONFIG_ALPHA) || defined(CONFIG_MIPS)
+#include_next <asm/barrier.h>
+#endif /* >= 3.4 */
+
+#ifndef dma_rmb
+#define dma_rmb() rmb()
+#endif
+
+#ifndef smp_mb__after_atomic
+#define smp_mb__after_atomic smp_mb__after_clear_bit
+#endif
+
+#endif /* __BACKPORT_ASM_BARRIER_H */
diff --git a/backport-include/asm/errno.h b/backport-include/asm/errno.h
new file mode 100644
index 0000000..0a730b7
--- /dev/null
+++ b/backport-include/asm/errno.h
@@ -0,0 +1,23 @@
+#ifndef __BACKPORT_ASM_ERRNO_H
+#define __BACKPORT_ASM_ERRNO_H
+#include_next <asm/errno.h>
+
+#ifndef ERFKILL
+#if !defined(CONFIG_ALPHA) && !defined(CONFIG_MIPS) && !defined(CONFIG_PARISC) && !defined(CONFIG_SPARC)
+#define ERFKILL 132 /* Operation not possible due to RF-kill */
+#endif
+#ifdef CONFIG_ALPHA
+#define ERFKILL 138 /* Operation not possible due to RF-kill */
+#endif
+#ifdef CONFIG_MIPS
+#define ERFKILL 167 /* Operation not possible due to RF-kill */
+#endif
+#ifdef CONFIG_PARISC
+#define ERFKILL 256 /* Operation not possible due to RF-kill */
+#endif
+#ifdef CONFIG_SPARC
+#define ERFKILL 134 /* Operation not possible due to RF-kill */
+#endif
+#endif
+
+#endif /* __BACKPORT_ASM_ERRNO_H */
diff --git a/backport-include/asm/ioctls.h b/backport-include/asm/ioctls.h
new file mode 100644
index 0000000..72c2f0a
--- /dev/null
+++ b/backport-include/asm/ioctls.h
@@ -0,0 +1,9 @@
+#ifndef __BACKPORT_ASM_IOCTLS_H
+#define __BACKPORT_ASM_IOCTLS_H
+#include_next <asm/ioctls.h>
+
+#ifndef TIOCPKT_IOCTL
+#define TIOCPKT_IOCTL 64
+#endif
+
+#endif /* __BACKPORT_ASM_IOCTLS_H */
diff --git a/backport-include/backport/backport.h b/backport-include/backport/backport.h
new file mode 100644
index 0000000..d1d3b10
--- /dev/null
+++ b/backport-include/backport/backport.h
@@ -0,0 +1,16 @@
+#ifndef __BACKPORT_H
+#define __BACKPORT_H
+#include <generated/autoconf.h>
+#ifndef CONFIG_BACKPORT_INTEGRATE
+#include <backport/autoconf.h>
+#endif
+#include <linux/kconfig.h>
+
+#ifndef __ASSEMBLY__
+#define LINUX_BACKPORT(__sym) backport_ ##__sym
+#ifndef CONFIG_BACKPORT_INTEGRATE
+#include <backport/checks.h>
+#endif
+#endif
+
+#endif /* __BACKPORT_H */
diff --git a/backport-include/backport/checks.h b/backport-include/backport/checks.h
new file mode 100644
index 0000000..6e9ba9c
--- /dev/null
+++ b/backport-include/backport/checks.h
@@ -0,0 +1,12 @@
+#ifndef __BACKPORT_CHECKS
+#define __BACKPORT_CHECKS
+
+#if defined(CONFIG_MAC80211) && defined(CPTCFG_MAC80211)
+#error "You must not have mac80211 built into your kernel if you want to enable it"
+#endif
+
+#if defined(CPTCFG_CFG80211) && defined(CPTCFG_CFG80211)
+#error "You must not have cfg80211 built into your kernel if you want to enable it"
+#endif
+
+#endif /* __BACKPORT_CHECKS */
diff --git a/backport-include/backport/leds-disabled.h b/backport-include/backport/leds-disabled.h
new file mode 100644
index 0000000..5ab82e5
--- /dev/null
+++ b/backport-include/backport/leds-disabled.h
@@ -0,0 +1,198 @@
+#ifndef __BACKPORT_LED_DISABLED_SUPPORT
+#define __BACKPORT_LED_DISABLED_SUPPORT
+
+/*
+ * LED support is strange, with the NEW_LEDS, LEDS_CLASS and LEDS_TRIGGERS
+ * Kconfig symbols ... If any of them are not defined, we build our
+ * "compatibility" code that really just makes it all non-working but
+ * allows compilation.
+ */
+
+#ifdef CPTCFG_BPAUTO_BUILD_LEDS
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/rwsem.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+
+#define led_classdev LINUX_BACKPORT(led_classdev)
+#define led_trigger LINUX_BACKPORT(led_trigger)
+
+struct led_classdev {
+ const char *name;
+ enum led_brightness brightness;
+ enum led_brightness max_brightness;
+ int flags;
+
+ /* Lower 16 bits reflect status */
+#ifndef LED_SUSPENDED
+#define LED_SUSPENDED (1 << 0)
+ /* Upper 16 bits reflect control information */
+#define LED_CORE_SUSPENDRESUME (1 << 16)
+#define LED_BLINK_ONESHOT (1 << 17)
+#define LED_BLINK_ONESHOT_STOP (1 << 18)
+#define LED_BLINK_INVERT (1 << 19)
+#define LED_SYSFS_DISABLE (1 << 20)
+#define SET_BRIGHTNESS_ASYNC (1 << 21)
+#define SET_BRIGHTNESS_SYNC (1 << 22)
+#define LED_DEV_CAP_FLASH (1 << 23)
+#endif
+
+ /* Set LED brightness level */
+ /* Must not sleep, use a workqueue if needed */
+ void (*brightness_set)(struct led_classdev *led_cdev,
+ enum led_brightness brightness);
+ /*
+ * Set LED brightness level immediately - it can block the caller for
+ * the time required for accessing a LED device register.
+ */
+ int (*brightness_set_sync)(struct led_classdev *led_cdev,
+ enum led_brightness brightness);
+ /* Get LED brightness level */
+ enum led_brightness (*brightness_get)(struct led_classdev *led_cdev);
+
+ /*
+ * Activate hardware accelerated blink, delays are in milliseconds
+ * and if both are zero then a sensible default should be chosen.
+ * The call should adjust the timings in that case and if it can't
+ * match the values specified exactly.
+ * Deactivate blinking again when the brightness is set to a fixed
+ * value via the brightness_set() callback.
+ */
+ int (*blink_set)(struct led_classdev *led_cdev,
+ unsigned long *delay_on,
+ unsigned long *delay_off);
+
+ struct device *dev;
+ const struct attribute_group **groups;
+
+ struct list_head node; /* LED Device list */
+ const char *default_trigger; /* Trigger to use */
+
+ unsigned long blink_delay_on, blink_delay_off;
+ struct timer_list blink_timer;
+ int blink_brightness;
+ void (*flash_resume)(struct led_classdev *led_cdev);
+
+ struct work_struct set_brightness_work;
+ int delayed_set_value;
+
+ /* Protects the trigger data below */
+ struct rw_semaphore trigger_lock;
+
+ struct led_trigger *trigger;
+ struct list_head trig_list;
+ void *trigger_data;
+ /* true if activated - deactivate routine uses it to do cleanup */
+ bool activated;
+
+ /* Ensures consistent access to the LED Flash Class device */
+ struct mutex led_access;
+};
+
+struct led_trigger {
+ const char *name;
+ void (*activate)(struct led_classdev *led_cdev);
+ void (*deactivate)(struct led_classdev *led_cdev);
+ rwlock_t leddev_list_lock;
+ struct list_head led_cdevs;
+ struct list_head next_trig;
+};
+
+#undef led_classdev_register
+#define led_classdev_register LINUX_BACKPORT(led_classdev_register)
+#undef led_classdev_unregister
+#define led_classdev_unregister LINUX_BACKPORT(led_classdev_unregister)
+#undef led_blink_set
+#define led_blink_set LINUX_BACKPORT(led_blink_set)
+#undef led_set_brightness
+#define led_set_brightness LINUX_BACKPORT(led_set_brightness)
+#undef led_classdev_suspend
+#define led_classdev_suspend LINUX_BACKPORT(led_classdev_suspend)
+#undef led_classdev_resume
+#define led_classdev_resume LINUX_BACKPORT(led_classdev_resume)
+
+#undef led_trigger_register
+#define led_trigger_register LINUX_BACKPORT(led_trigger_register)
+#undef led_trigger_unregister
+#define led_trigger_unregister LINUX_BACKPORT(led_trigger_unregister)
+#undef led_trigger_register_simple
+#define led_trigger_register_simple LINUX_BACKPORT(led_trigger_register_simple)
+#undef led_trigger_unregister_simple
+#define led_trigger_unregister_simple LINUX_BACKPORT(led_trigger_unregister_simple)
+#undef led_trigger_event
+#define led_trigger_event LINUX_BACKPORT(led_trigger_event)
+
+#undef DEFINE_LED_TRIGGER
+#define DEFINE_LED_TRIGGER(x) static struct led_trigger *x;
+
+static inline int led_classdev_register(struct device *parent,
+ struct led_classdev *led_cdev)
+{
+ return 0;
+}
+
+static inline void led_classdev_unregister(struct led_classdev *led_cdev)
+{
+}
+
+static inline void led_trigger_register_simple(const char *name,
+ struct led_trigger **trigger)
+{
+}
+
+static inline void led_trigger_unregister_simple(struct led_trigger *trigger)
+{
+}
+
+static inline void led_blink_set(struct led_classdev *led_cdev,
+ unsigned long *delay_on,
+ unsigned long *delay_off)
+{
+}
+
+static inline void led_set_brightness(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+}
+
+static inline void led_classdev_suspend(struct led_classdev *led_cdev)
+{
+}
+
+static inline void led_classdev_resume(struct led_classdev *led_cdev)
+{
+}
+
+static inline int led_trigger_register(struct led_trigger *trigger)
+{
+ INIT_LIST_HEAD(&trigger->led_cdevs);
+ INIT_LIST_HEAD(&trigger->next_trig);
+ rwlock_init(&trigger->leddev_list_lock);
+ return 0;
+}
+
+static inline void led_trigger_unregister(struct led_trigger *trigger)
+{
+}
+
+static inline void led_trigger_event(struct led_trigger *trigger,
+ enum led_brightness event)
+{
+}
+
+static inline void led_trigger_blink(struct led_trigger *trigger,
+ unsigned long *delay_on,
+ unsigned long *delay_off)
+{
+}
+
+static inline void led_trigger_blink_oneshot(struct led_trigger *trigger,
+ unsigned long *delay_on,
+ unsigned long *delay_off,
+ int invert)
+{
+}
+#endif
+
+#endif /* __BACKPORT_LED_DISABLED_SUPPORT */
diff --git a/backport-include/backport/magic.h b/backport-include/backport/magic.h
new file mode 100644
index 0000000..d7e7cc9
--- /dev/null
+++ b/backport-include/backport/magic.h
@@ -0,0 +1,16 @@
+/*
+ * These tricks are taken from
+ * http://efesx.com/2010/07/17/variadic-macro-to-count-number-of-arguments/
+ * and
+ * http://efesx.com/2010/08/31/overloading-macros/
+ */
+
+#define VA_NUM_ARGS(...) VA_NUM_ARGS_IMPL(__VA_ARGS__, 6,5,4,3,2,1)
+#define VA_NUM_ARGS_IMPL(_1,_2,_3,_4,_5,_6,N,...) N
+
+#define macro_dispatcher(func, ...) \
+ macro_dispatcher_(func, VA_NUM_ARGS(__VA_ARGS__))
+#define macro_dispatcher_(func, nargs) \
+ macro_dispatcher__(func, nargs)
+#define macro_dispatcher__(func, nargs) \
+ func ## nargs
diff --git a/backport-include/crypto/aead.h b/backport-include/crypto/aead.h
new file mode 100644
index 0000000..26b1355
--- /dev/null
+++ b/backport-include/crypto/aead.h
@@ -0,0 +1,33 @@
+#ifndef __BACKPORT_CRYPTO_AEAD_H
+#define __BACKPORT_CRYPTO_AEAD_H
+#include_next <crypto/aead.h>
+#include <linux/version.h>
+
+#if LINUX_VERSION_IS_LESS(4,2,0)
+#define aead_request_set_ad LINUX_BACKPORT(aead_request_set_ad)
+static inline void aead_request_set_ad(struct aead_request *req,
+ unsigned int assoclen)
+{
+ req->assoclen = assoclen;
+}
+
+#define crypto_aead_reqsize LINUX_BACKPORT(crypto_aead_reqsize)
+unsigned int crypto_aead_reqsize(struct crypto_aead *tfm);
+
+struct aead_request *crypto_backport_convert(struct aead_request *req);
+
+static inline int backport_crypto_aead_encrypt(struct aead_request *req)
+{
+ return crypto_aead_encrypt(crypto_backport_convert(req));
+}
+#define crypto_aead_encrypt LINUX_BACKPORT(crypto_aead_encrypt)
+
+static inline int backport_crypto_aead_decrypt(struct aead_request *req)
+{
+ return crypto_aead_decrypt(crypto_backport_convert(req));
+}
+#define crypto_aead_decrypt LINUX_BACKPORT(crypto_aead_decrypt)
+
+#endif /* LINUX_VERSION_IS_LESS(4,2,0) */
+
+#endif /* __BACKPORT_CRYPTO_AEAD_H */
diff --git a/backport-include/crypto/hash.h b/backport-include/crypto/hash.h
new file mode 100644
index 0000000..96ae799
--- /dev/null
+++ b/backport-include/crypto/hash.h
@@ -0,0 +1,38 @@
+#ifndef _BACKPORT_CRYPTO_HASH_H
+#define _BACKPORT_CRYPTO_HASH_H
+#include_next <crypto/hash.h>
+#include <linux/version.h>
+
+#if LINUX_VERSION_IS_LESS(4,6,0)
+#define shash_desc_zero LINUX_BACKPORT(shash_desc_zero)
+static inline void shash_desc_zero(struct shash_desc *desc)
+{
+ memzero_explicit(desc,
+ sizeof(*desc) + crypto_shash_descsize(desc->tfm));
+}
+#endif
+
+#if LINUX_VERSION_IS_LESS(4,6,0)
+#define ahash_request_zero LINUX_BACKPORT(ahash_request_zero)
+static inline void ahash_request_zero(struct ahash_request *req)
+{
+ memzero_explicit(req, sizeof(*req) +
+ crypto_ahash_reqsize(crypto_ahash_reqtfm(req)));
+}
+#endif
+
+#ifndef AHASH_REQUEST_ON_STACK
+#define AHASH_REQUEST_ON_STACK(name, ahash) \
+ char __##name##_desc[sizeof(struct ahash_request) + \
+ crypto_ahash_reqsize(ahash)] CRYPTO_MINALIGN_ATTR; \
+ struct ahash_request *name = (void *)__##name##_desc
+#endif
+
+#ifndef SHASH_DESC_ON_STACK
+#define SHASH_DESC_ON_STACK(shash, ctx) \
+ char __##shash##_desc[sizeof(struct shash_desc) + \
+ crypto_shash_descsize(ctx)] CRYPTO_MINALIGN_ATTR; \
+ struct shash_desc *shash = (struct shash_desc *)__##shash##_desc
+#endif
+
+#endif /* _BACKPORT_CRYPTO_HASH_H */
diff --git a/backport-include/crypto/skcipher.h b/backport-include/crypto/skcipher.h
new file mode 100644
index 0000000..73ba783
--- /dev/null
+++ b/backport-include/crypto/skcipher.h
@@ -0,0 +1,454 @@
+#ifndef _BACKPORT_CRYPTO_SKCIPHER_H
+#define _BACKPORT_CRYPTO_SKCIPHER_H
+#include_next <crypto/skcipher.h>
+#include <linux/version.h>
+
+#if LINUX_VERSION_IS_LESS(4,3,0)
+/**
+ * struct skcipher_request - Symmetric key cipher request
+ * @cryptlen: Number of bytes to encrypt or decrypt
+ * @iv: Initialisation Vector
+ * @src: Source SG list
+ * @dst: Destination SG list
+ * @base: Underlying async request request
+ * @__ctx: Start of private context data
+ */
+#define skcipher_request LINUX_BACKPORT(skcipher_request)
+struct skcipher_request {
+ unsigned int cryptlen;
+
+ u8 *iv;
+
+ struct scatterlist *src;
+ struct scatterlist *dst;
+
+ struct crypto_async_request base;
+
+ void *__ctx[] CRYPTO_MINALIGN_ATTR;
+};
+
+#define crypto_skcipher LINUX_BACKPORT(crypto_skcipher)
+struct crypto_skcipher {
+ int (*setkey)(struct crypto_skcipher *tfm, const u8 *key,
+ unsigned int keylen);
+ int (*encrypt)(struct skcipher_request *req);
+ int (*decrypt)(struct skcipher_request *req);
+
+ unsigned int ivsize;
+ unsigned int reqsize;
+ unsigned int keysize;
+
+ struct crypto_tfm base;
+};
+
+#ifndef SKCIPHER_REQUEST_ON_STACK
+#define SKCIPHER_REQUEST_ON_STACK(name, tfm) \
+ char __##name##_desc[sizeof(struct skcipher_request) + \
+ crypto_skcipher_reqsize(tfm)] CRYPTO_MINALIGN_ATTR; \
+ struct skcipher_request *name = (void *)__##name##_desc
+#endif
+
+/**
+ * DOC: Symmetric Key Cipher API
+ *
+ * Symmetric key cipher API is used with the ciphers of type
+ * CRYPTO_ALG_TYPE_SKCIPHER (listed as type "skcipher" in /proc/crypto).
+ *
+ * Asynchronous cipher operations imply that the function invocation for a
+ * cipher request returns immediately before the completion of the operation.
+ * The cipher request is scheduled as a separate kernel thread and therefore
+ * load-balanced on the different CPUs via the process scheduler. To allow
+ * the kernel crypto API to inform the caller about the completion of a cipher
+ * request, the caller must provide a callback function. That function is
+ * invoked with the cipher handle when the request completes.
+ *
+ * To support the asynchronous operation, additional information than just the
+ * cipher handle must be supplied to the kernel crypto API. That additional
+ * information is given by filling in the skcipher_request data structure.
+ *
+ * For the symmetric key cipher API, the state is maintained with the tfm
+ * cipher handle. A single tfm can be used across multiple calls and in
+ * parallel. For asynchronous block cipher calls, context data supplied and
+ * only used by the caller can be referenced the request data structure in
+ * addition to the IV used for the cipher request. The maintenance of such
+ * state information would be important for a crypto driver implementer to
+ * have, because when calling the callback function upon completion of the
+ * cipher operation, that callback function may need some information about
+ * which operation just finished if it invoked multiple in parallel. This
+ * state information is unused by the kernel crypto API.
+ */
+
+#define __crypto_skcipher_cast LINUX_BACKPORT(__crypto_skcipher_cast)
+static inline struct crypto_skcipher *__crypto_skcipher_cast(
+ struct crypto_tfm *tfm)
+{
+ return container_of(tfm, struct crypto_skcipher, base);
+}
+
+/**
+ * crypto_alloc_skcipher() - allocate symmetric key cipher handle
+ * @alg_name: is the cra_name / name or cra_driver_name / driver name of the
+ * skcipher cipher
+ * @type: specifies the type of the cipher
+ * @mask: specifies the mask for the cipher
+ *
+ * Allocate a cipher handle for an skcipher. The returned struct
+ * crypto_skcipher is the cipher handle that is required for any subsequent
+ * API invocation for that skcipher.
+ *
+ * Return: allocated cipher handle in case of success; IS_ERR() is true in case
+ * of an error, PTR_ERR() returns the error code.
+ */
+#define crypto_alloc_skcipher LINUX_BACKPORT(crypto_alloc_skcipher)
+struct crypto_skcipher *crypto_alloc_skcipher(const char *alg_name,
+ u32 type, u32 mask);
+
+#define crypto_skcipher_tfm LINUX_BACKPORT(crypto_skcipher_tfm)
+static inline struct crypto_tfm *crypto_skcipher_tfm(
+ struct crypto_skcipher *tfm)
+{
+ return &tfm->base;
+}
+
+/**
+ * crypto_free_skcipher() - zeroize and free cipher handle
+ * @tfm: cipher handle to be freed
+ */
+#define crypto_free_skcipher LINUX_BACKPORT(crypto_free_skcipher)
+static inline void crypto_free_skcipher(struct crypto_skcipher *tfm)
+{
+ crypto_destroy_tfm(tfm, crypto_skcipher_tfm(tfm));
+}
+
+/**
+ * crypto_has_skcipher() - Search for the availability of an skcipher.
+ * @alg_name: is the cra_name / name or cra_driver_name / driver name of the
+ * skcipher
+ * @type: specifies the type of the cipher
+ * @mask: specifies the mask for the cipher
+ *
+ * Return: true when the skcipher is known to the kernel crypto API; false
+ * otherwise
+ */
+#define crypto_has_skcipher LINUX_BACKPORT(crypto_has_skcipher)
+static inline int crypto_has_skcipher(const char *alg_name, u32 type,
+ u32 mask)
+{
+ return crypto_has_alg(alg_name, crypto_skcipher_type(type),
+ crypto_skcipher_mask(mask));
+}
+
+#define crypto_skcipher_driver_name LINUX_BACKPORT(crypto_skcipher_driver_name)
+static inline const char *crypto_skcipher_driver_name(
+ struct crypto_skcipher *tfm)
+{
+ return crypto_tfm_alg_driver_name(crypto_skcipher_tfm(tfm));
+}
+
+/**
+ * crypto_skcipher_ivsize() - obtain IV size
+ * @tfm: cipher handle
+ *
+ * The size of the IV for the skcipher referenced by the cipher handle is
+ * returned. This IV size may be zero if the cipher does not need an IV.
+ *
+ * Return: IV size in bytes
+ */
+#define crypto_skcipher_ivsize LINUX_BACKPORT(crypto_skcipher_ivsize)
+static inline unsigned int crypto_skcipher_ivsize(struct crypto_skcipher *tfm)
+{
+ return tfm->ivsize;
+}
+
+/**
+ * crypto_skcipher_blocksize() - obtain block size of cipher
+ * @tfm: cipher handle
+ *
+ * The block size for the skcipher referenced with the cipher handle is
+ * returned. The caller may use that information to allocate appropriate
+ * memory for the data returned by the encryption or decryption operation
+ *
+ * Return: block size of cipher
+ */
+#define crypto_skcipher_blocksize LINUX_BACKPORT(crypto_skcipher_blocksize)
+static inline unsigned int crypto_skcipher_blocksize(
+ struct crypto_skcipher *tfm)
+{
+ return crypto_tfm_alg_blocksize(crypto_skcipher_tfm(tfm));
+}
+
+#define crypto_skcipher_alignmask LINUX_BACKPORT(crypto_skcipher_alignmask)
+static inline unsigned int crypto_skcipher_alignmask(
+ struct crypto_skcipher *tfm)
+{
+ return crypto_tfm_alg_alignmask(crypto_skcipher_tfm(tfm));
+}
+
+#define crypto_skcipher_get_flags LINUX_BACKPORT(crypto_skcipher_get_flags)
+static inline u32 crypto_skcipher_get_flags(struct crypto_skcipher *tfm)
+{
+ return crypto_tfm_get_flags(crypto_skcipher_tfm(tfm));
+}
+
+#define crypto_skcipher_set_flags LINUX_BACKPORT(crypto_skcipher_set_flags)
+static inline void crypto_skcipher_set_flags(struct crypto_skcipher *tfm,
+ u32 flags)
+{
+ crypto_tfm_set_flags(crypto_skcipher_tfm(tfm), flags);
+}
+
+#define crypto_skcipher_clear_flags LINUX_BACKPORT(crypto_skcipher_clear_flags)
+static inline void crypto_skcipher_clear_flags(struct crypto_skcipher *tfm,
+ u32 flags)
+{
+ crypto_tfm_clear_flags(crypto_skcipher_tfm(tfm), flags);
+}
+
+/**
+ * crypto_skcipher_setkey() - set key for cipher
+ * @tfm: cipher handle
+ * @key: buffer holding the key
+ * @keylen: length of the key in bytes
+ *
+ * The caller provided key is set for the skcipher referenced by the cipher
+ * handle.
+ *
+ * Note, the key length determines the cipher type. Many block ciphers implement
+ * different cipher modes depending on the key size, such as AES-128 vs AES-192
+ * vs. AES-256. When providing a 16 byte key for an AES cipher handle, AES-128
+ * is performed.
+ *
+ * Return: 0 if the setting of the key was successful; < 0 if an error occurred
+ */
+#define crypto_skcipher_setkey LINUX_BACKPORT(crypto_skcipher_setkey)
+static inline int crypto_skcipher_setkey(struct crypto_skcipher *tfm,
+ const u8 *key, unsigned int keylen)
+{
+ return tfm->setkey(tfm, key, keylen);
+}
+
+#define crypto_skcipher_has_setkey LINUX_BACKPORT(crypto_skcipher_has_setkey)
+static inline bool crypto_skcipher_has_setkey(struct crypto_skcipher *tfm)
+{
+ return tfm->keysize;
+}
+
+#define crypto_skcipher_default_keysize LINUX_BACKPORT(crypto_skcipher_default_keysize)
+static inline unsigned int crypto_skcipher_default_keysize(
+ struct crypto_skcipher *tfm)
+{
+ return tfm->keysize;
+}
+
+/**
+ * crypto_skcipher_reqtfm() - obtain cipher handle from request
+ * @req: skcipher_request out of which the cipher handle is to be obtained
+ *
+ * Return the crypto_skcipher handle when furnishing an skcipher_request
+ * data structure.
+ *
+ * Return: crypto_skcipher handle
+ */
+#define crypto_skcipher_reqtfm LINUX_BACKPORT(crypto_skcipher_reqtfm)
+static inline struct crypto_skcipher *crypto_skcipher_reqtfm(
+ struct skcipher_request *req)
+{
+ return __crypto_skcipher_cast(req->base.tfm);
+}
+
+/**
+ * crypto_skcipher_encrypt() - encrypt plaintext
+ * @req: reference to the skcipher_request handle that holds all information
+ * needed to perform the cipher operation
+ *
+ * Encrypt plaintext data using the skcipher_request handle. That data
+ * structure and how it is filled with data is discussed with the
+ * skcipher_request_* functions.
+ *
+ * Return: 0 if the cipher operation was successful; < 0 if an error occurred
+ */
+#define crypto_skcipher_encrypt LINUX_BACKPORT(crypto_skcipher_encrypt)
+static inline int crypto_skcipher_encrypt(struct skcipher_request *req)
+{
+ struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
+
+ return tfm->encrypt(req);
+}
+
+/**
+ * crypto_skcipher_decrypt() - decrypt ciphertext
+ * @req: reference to the skcipher_request handle that holds all information
+ * needed to perform the cipher operation
+ *
+ * Decrypt ciphertext data using the skcipher_request handle. That data
+ * structure and how it is filled with data is discussed with the
+ * skcipher_request_* functions.
+ *
+ * Return: 0 if the cipher operation was successful; < 0 if an error occurred
+ */
+#define crypto_skcipher_decrypt LINUX_BACKPORT(crypto_skcipher_decrypt)
+static inline int crypto_skcipher_decrypt(struct skcipher_request *req)
+{
+ struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
+
+ return tfm->decrypt(req);
+}
+
+/**
+ * DOC: Symmetric Key Cipher Request Handle
+ *
+ * The skcipher_request data structure contains all pointers to data
+ * required for the symmetric key cipher operation. This includes the cipher
+ * handle (which can be used by multiple skcipher_request instances), pointer
+ * to plaintext and ciphertext, asynchronous callback function, etc. It acts
+ * as a handle to the skcipher_request_* API calls in a similar way as
+ * skcipher handle to the crypto_skcipher_* API calls.
+ */
+
+/**
+ * crypto_skcipher_reqsize() - obtain size of the request data structure
+ * @tfm: cipher handle
+ *
+ * Return: number of bytes
+ */
+#define crypto_skcipher_reqsize LINUX_BACKPORT(crypto_skcipher_reqsize)
+static inline unsigned int crypto_skcipher_reqsize(struct crypto_skcipher *tfm)
+{
+ return tfm->reqsize;
+}
+
+/**
+ * skcipher_request_set_tfm() - update cipher handle reference in request
+ * @req: request handle to be modified
+ * @tfm: cipher handle that shall be added to the request handle
+ *
+ * Allow the caller to replace the existing skcipher handle in the request
+ * data structure with a different one.
+ */
+#define skcipher_request_set_tfm LINUX_BACKPORT(skcipher_request_set_tfm)
+static inline void skcipher_request_set_tfm(struct skcipher_request *req,
+ struct crypto_skcipher *tfm)
+{
+ req->base.tfm = crypto_skcipher_tfm(tfm);
+}
+
+#define skcipher_request_cast LINUX_BACKPORT(skcipher_request_cast)
+static inline struct skcipher_request *skcipher_request_cast(
+ struct crypto_async_request *req)
+{
+ return container_of(req, struct skcipher_request, base);
+}
+
+/**
+ * skcipher_request_alloc() - allocate request data structure
+ * @tfm: cipher handle to be registered with the request
+ * @gfp: memory allocation flag that is handed to kmalloc by the API call.
+ *
+ * Allocate the request data structure that must be used with the skcipher
+ * encrypt and decrypt API calls. During the allocation, the provided skcipher
+ * handle is registered in the request data structure.
+ *
+ * Return: allocated request handle in case of success; IS_ERR() is true in case
+ * of an error, PTR_ERR() returns the error code.
+ */
+#define skcipher_request LINUX_BACKPORT(skcipher_request)
+static inline struct skcipher_request *skcipher_request_alloc(
+ struct crypto_skcipher *tfm, gfp_t gfp)
+{
+ struct skcipher_request *req;
+
+ req = kmalloc(sizeof(struct skcipher_request) +
+ crypto_skcipher_reqsize(tfm), gfp);
+
+ if (likely(req))
+ skcipher_request_set_tfm(req, tfm);
+
+ return req;
+}
+
+/**
+ * skcipher_request_free() - zeroize and free request data structure
+ * @req: request data structure cipher handle to be freed
+ */
+#define skcipher_request_free LINUX_BACKPORT(skcipher_request_free)
+static inline void skcipher_request_free(struct skcipher_request *req)
+{
+ kzfree(req);
+}
+
+/**
+ * skcipher_request_set_callback() - set asynchronous callback function
+ * @req: request handle
+ * @flags: specify zero or an ORing of the flags
+ * CRYPTO_TFM_REQ_MAY_BACKLOG the request queue may back log and
+ * increase the wait queue beyond the initial maximum size;
+ * CRYPTO_TFM_REQ_MAY_SLEEP the request processing may sleep
+ * @compl: callback function pointer to be registered with the request handle
+ * @data: The data pointer refers to memory that is not used by the kernel
+ * crypto API, but provided to the callback function for it to use. Here,
+ * the caller can provide a reference to memory the callback function can
+ * operate on. As the callback function is invoked asynchronously to the
+ * related functionality, it may need to access data structures of the
+ * related functionality which can be referenced using this pointer. The
+ * callback function can access the memory via the "data" field in the
+ * crypto_async_request data structure provided to the callback function.
+ *
+ * This function allows setting the callback function that is triggered once the
+ * cipher operation completes.
+ *
+ * The callback function is registered with the skcipher_request handle and
+ * must comply with the following template
+ *
+ * void callback_function(struct crypto_async_request *req, int error)
+ */
+#define skcipher_request_set_callback LINUX_BACKPORT(skcipher_request_set_callback)
+static inline void skcipher_request_set_callback(struct skcipher_request *req,
+ u32 flags,
+ crypto_completion_t compl,
+ void *data)
+{
+ req->base.complete = compl;
+ req->base.data = data;
+ req->base.flags = flags;
+}
+
+/**
+ * skcipher_request_set_crypt() - set data buffers
+ * @req: request handle
+ * @src: source scatter / gather list
+ * @dst: destination scatter / gather list
+ * @cryptlen: number of bytes to process from @src
+ * @iv: IV for the cipher operation which must comply with the IV size defined
+ * by crypto_skcipher_ivsize
+ *
+ * This function allows setting of the source data and destination data
+ * scatter / gather lists.
+ *
+ * For encryption, the source is treated as the plaintext and the
+ * destination is the ciphertext. For a decryption operation, the use is
+ * reversed - the source is the ciphertext and the destination is the plaintext.
+ */
+#define skcipher_request_set_crypt LINUX_BACKPORT(skcipher_request_set_crypt)
+static inline void skcipher_request_set_crypt(
+ struct skcipher_request *req,
+ struct scatterlist *src, struct scatterlist *dst,
+ unsigned int cryptlen, void *iv)
+{
+ req->src = src;
+ req->dst = dst;
+ req->cryptlen = cryptlen;
+ req->iv = iv;
+}
+#endif /* < 4.3 */
+
+#if LINUX_VERSION_IS_LESS(4,6,0)
+#define skcipher_request_zero LINUX_BACKPORT(skcipher_request_zero)
+static inline void skcipher_request_zero(struct skcipher_request *req)
+{
+ struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
+
+ memzero_explicit(req, sizeof(*req) + crypto_skcipher_reqsize(tfm));
+}
+#endif /* < 4.6 */
+
+#endif /* _BACKPORT_CRYPTO_SKCIPHER_H */
diff --git a/backport-include/generated/utsrelease.h b/backport-include/generated/utsrelease.h
new file mode 100644
index 0000000..a149b7a
--- /dev/null
+++ b/backport-include/generated/utsrelease.h
@@ -0,0 +1,21 @@
+#ifndef __BACKPORT_GENERATED_UTS_RELEASE_H
+#define __BACKPORT_GENERATED_UTS_RELEASE_H
+#include_next <generated/utsrelease.h>
+
+/*
+ * We only want the UTS_UBUNTU_RELEASE_ABI var when we are on a normal
+ * Ubuntu distribution kernel and not when we are on a Ubuntu mainline
+ * kernel. Some of the Ubuntu mainline kernel do have an invalid octal
+ * number in this field like 031418 and we do not want to evaluate this
+ * at all on the Ubuntu mainline kernels. All Ubuntu distribution
+ * kernel have CONFIG_VERSION_SIGNATURE set so this way we can detect
+ * the which type of kernel we are on.
+ */
+#ifndef UTS_UBUNTU_RELEASE_ABI
+#define UTS_UBUNTU_RELEASE_ABI 0
+#elif !defined(CONFIG_VERSION_SIGNATURE)
+#undef UTS_UBUNTU_RELEASE_ABI
+#define UTS_UBUNTU_RELEASE_ABI 0
+#endif
+
+#endif /* __BACKPORT_GENERATED_UTS_RELEASE_H */
diff --git a/backport-include/linux/acpi.h b/backport-include/linux/acpi.h
new file mode 100644
index 0000000..c63648b
--- /dev/null
+++ b/backport-include/linux/acpi.h
@@ -0,0 +1,63 @@
+#ifndef __BACKPORT_LINUX_ACPI_H
+#define __BACKPORT_LINUX_ACPI_H
+#include_next <linux/acpi.h>
+#include <linux/version.h>
+
+#if LINUX_VERSION_IS_LESS(3,8,0)
+/*
+ * Backports
+ *
+ * commit 95f8a082b9b1ead0c2859f2a7b1ac91ff63d8765
+ * Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+ * Date: Wed Nov 21 00:21:50 2012 +0100
+ *
+ * ACPI / driver core: Introduce struct acpi_dev_node and related macros
+ *
+ * To avoid adding an ACPI handle pointer to struct device on
+ * architectures that don't use ACPI, or generally when CONFIG_ACPI is
+ * not set, in which cases that pointer is useless, define struct
+ * acpi_dev_node that will contain the handle pointer if CONFIG_ACPI is
+ * set and will be empty otherwise and use it to represent the ACPI
+ * device node field in struct device.
+ *
+ * In addition to that define macros for reading and setting the ACPI
+ * handle of a device that don't generate code when CONFIG_ACPI is
+ * unset. Modify the ACPI subsystem to use those macros instead of
+ * referring to the given device's ACPI handle directly.
+ *
+ * Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+ * Reviewed-by: Mika Westerberg <mika.westerberg@linux.intel.com>
+ * Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+ */
+#ifdef CONFIG_ACPI
+#define ACPI_HANDLE(dev) DEVICE_ACPI_HANDLE(dev)
+#else
+#define ACPI_HANDLE(dev) (NULL)
+#endif /* CONFIG_ACPI */
+#endif /* LINUX_VERSION_IS_LESS(3,8,0) */
+
+#ifndef ACPI_COMPANION
+#ifdef CONFIG_ACPI
+static inline struct acpi_device *_acpi_get_companion(struct device *dev)
+{
+ struct acpi_device *adev;
+ int ret;
+
+ ret = acpi_bus_get_device(ACPI_HANDLE(dev), &adev);
+ if (ret < 0)
+ adev = NULL;
+
+ return adev;
+}
+#define ACPI_COMPANION(dev) _acpi_get_companion(dev)
+#else
+#define ACPI_COMPANION(dev) (NULL)
+#endif /* CONFIG_ACPI */
+#endif /* ACPI_COMPANION */
+
+#if LINUX_VERSION_IS_LESS(3,19,0)
+#define acpi_dev_remove_driver_gpios LINUX_BACKPORT(acpi_dev_remove_driver_gpios)
+static inline void acpi_dev_remove_driver_gpios(struct acpi_device *adev) {}
+#endif /* LINUX_VERSION_IS_LESS(3, 19, 0) */
+
+#endif /* __BACKPORT_LINUX_ACPI_H */
diff --git a/backport-include/linux/bcm47xx_nvram.h b/backport-include/linux/bcm47xx_nvram.h
new file mode 100644
index 0000000..5295a02
--- /dev/null
+++ b/backport-include/linux/bcm47xx_nvram.h
@@ -0,0 +1,25 @@
+#ifndef __BACKPORTS_BCM47XX_NVRAM_H
+#define __BACKPORTS_BCM47XX_NVRAM_H
+#include <linux/version.h>
+
+#if LINUX_VERSION_IS_GEQ(4,1,0)
+#include_next <linux/bcm47xx_nvram.h>
+#else
+#include <linux/types.h>
+#include <linux/kernel.h>
+#endif
+
+#if LINUX_VERSION_IS_LESS(4,2,0)
+#define bcm47xx_nvram_get_contents LINUX_BACKPORT(bcm47xx_nvram_get_contents)
+static inline char *bcm47xx_nvram_get_contents(size_t *val_len)
+{
+ return NULL;
+}
+
+#define bcm47xx_nvram_release_contents LINUX_BACKPORT(bcm47xx_nvram_release_contents)
+static inline void bcm47xx_nvram_release_contents(char *nvram)
+{
+}
+#endif /* LINUX_VERSION_IS_GEQ(4,1,0) */
+
+#endif /* __BACKPORTS_BCM47XX_NVRAM_H */
diff --git a/backport-include/linux/bitops.h b/backport-include/linux/bitops.h
new file mode 100644
index 0000000..86360d0
--- /dev/null
+++ b/backport-include/linux/bitops.h
@@ -0,0 +1,23 @@
+#ifndef __BACKPORT_BITOPS_H
+#define __BACKPORT_BITOPS_H
+#include_next <linux/bitops.h>
+#include <linux/version.h>
+#include <generated/utsrelease.h>
+
+#ifndef GENMASK
+
+/*
+ * Create a contiguous bitmask starting at bit position @l and ending at
+ * position @h. For example
+ * GENMASK_ULL(39, 21) gives us the 64bit vector 0x000000ffffe00000.
+ */
+#define GENMASK(h, l) (((U32_C(1) << ((h) - (l) + 1)) - 1) << (l))
+#define GENMASK_ULL(h, l) (((U64_C(1) << ((h) - (l) + 1)) - 1) << (l))
+
+#endif
+
+#ifndef BIT_ULL
+#define BIT_ULL(nr) (1ULL << (nr))
+#endif
+
+#endif /* __BACKPORT_BITOPS_H */
diff --git a/backport-include/linux/bp-devcoredump.h b/backport-include/linux/bp-devcoredump.h
new file mode 100644
index 0000000..1e4abf6
--- /dev/null
+++ b/backport-include/linux/bp-devcoredump.h
@@ -0,0 +1,32 @@
+#ifndef __BACKPORT_LINUX_DEVCOREDUMP_H
+#define __BACKPORT_LINUX_DEVCOREDUMP_H
+#include <linux/version.h>
+#include <linux/scatterlist.h>
+
+/* We only need to add our wrapper inside the range from 3.18 until
+ * 4.6, outside that we can let our BPAUTO mechanism handle it.
+ */
+#if (LINUX_VERSION_IS_GEQ(3,18,0) && \
+ LINUX_VERSION_IS_LESS(4,7,0))
+static inline
+void backport_dev_coredumpm(struct device *dev, struct module *owner,
+ void *data, size_t datalen, gfp_t gfp,
+ ssize_t (*read_fn)(char *buffer, loff_t offset,
+ size_t count, void *data,
+ size_t datalen),
+ void (*free_fn)(void *data))
+{
+ return dev_coredumpm(dev, owner, (const void *)data, datalen, gfp,
+ (void *)read_fn, (void *)free_fn);
+}
+
+#define dev_coredumpm LINUX_BACKPORT(dev_coredumpm)
+
+#define dev_coredumpsg LINUX_BACKPORT(dev_coredumpsg)
+void dev_coredumpsg(struct device *dev, struct scatterlist *table,
+ size_t datalen, gfp_t gfp);
+
+#endif /* (LINUX_VERSION_IS_GEQ(3,18,0) && \
+ LINUX_VERSION_IS_LESS(4,7,0)) */
+
+#endif /* __BACKPORT_LINUX_DEVCOREDUMP_H */
diff --git a/backport-include/linux/bug.h b/backport-include/linux/bug.h
new file mode 100644
index 0000000..8595fd2
--- /dev/null
+++ b/backport-include/linux/bug.h
@@ -0,0 +1,18 @@
+#ifndef __BP_BUG_H
+#define __BP_BUG_H
+#include_next <linux/bug.h>
+
+#ifndef __BUILD_BUG_ON_NOT_POWER_OF_2
+#ifdef __CHECKER__
+#define __BUILD_BUG_ON_NOT_POWER_OF_2(n) (0)
+#else
+#define __BUILD_BUG_ON_NOT_POWER_OF_2(n) \
+ BUILD_BUG_ON(((n) & ((n) - 1)) != 0)
+#endif /* __CHECKER__ */
+#endif /* __BUILD_BUG_ON_NOT_POWER_OF_2 */
+
+#ifndef BUILD_BUG_ON_MSG
+#define BUILD_BUG_ON_MSG(x, msg) BUILD_BUG_ON(x)
+#endif
+
+#endif /* __BP_BUG_H */
diff --git a/backport-include/linux/cache.h b/backport-include/linux/cache.h
new file mode 100644
index 0000000..bd7cdcf
--- /dev/null
+++ b/backport-include/linux/cache.h
@@ -0,0 +1,10 @@
+#ifndef _BACKPORT_CACHE_H
+#define _BACKPORT_CACHE_H
+
+#include_next <linux/cache.h>
+
+#ifndef __ro_after_init
+#define __ro_after_init
+#endif
+
+#endif /* _BACKPORT_CACHE_H */
diff --git a/backport-include/linux/clk.h b/backport-include/linux/clk.h
new file mode 100644
index 0000000..62d9218
--- /dev/null
+++ b/backport-include/linux/clk.h
@@ -0,0 +1,116 @@
+#ifndef __BACKPORT_LINUX_CLK_H
+#define __BACKPORT_LINUX_CLK_H
+#include_next <linux/clk.h>
+#include <linux/version.h>
+
+/*
+ * commit 93abe8e4 - we only backport the non CONFIG_COMMON_CLK
+ * case as the CONFIG_COMMON_CLK case requires arch support. By
+ * using the backport_ namespace for older kernels we force usage
+ * of these helpers and that's required given that 3.5 added some
+ * of these helpers expecting a few exported symbols for the non
+ * CONFIG_COMMON_CLK case. The 3.5 kernel is not supported as
+ * per kernel.org so we don't send a fix upstream for that.
+ */
+#if LINUX_VERSION_IS_LESS(3,6,0)
+
+#ifndef CONFIG_COMMON_CLK
+
+/*
+ * Whoopsie!
+ *
+ * clk_enable() and clk_disable() have been left without
+ * a nop export symbols when !CONFIG_COMMON_CLK since its
+ * introduction on v2.6.16, but fixed until 3.6.
+ */
+#if 0
+#define clk_enable LINUX_BACKPORT(clk_enable)
+static inline int clk_enable(struct clk *clk)
+{
+ return 0;
+}
+
+#define clk_disable LINUX_BACKPORT(clk_disable)
+static inline void clk_disable(struct clk *clk) {}
+#endif
+
+
+#define clk_get LINUX_BACKPORT(clk_get)
+static inline struct clk *clk_get(struct device *dev, const char *id)
+{
+ return NULL;
+}
+
+#define devm_clk_get LINUX_BACKPORT(devm_clk_get)
+static inline struct clk *devm_clk_get(struct device *dev, const char *id)
+{
+ return NULL;
+}
+
+#define clk_put LINUX_BACKPORT(clk_put)
+static inline void clk_put(struct clk *clk) {}
+
+#define devm_clk_put LINUX_BACKPORT(devm_clk_put)
+static inline void devm_clk_put(struct device *dev, struct clk *clk) {}
+
+#define clk_get_rate LINUX_BACKPORT(clk_get_rate)
+static inline unsigned long clk_get_rate(struct clk *clk)
+{
+ return 0;
+}
+
+#define clk_set_rate LINUX_BACKPORT(clk_set_rate)
+static inline int clk_set_rate(struct clk *clk, unsigned long rate)
+{
+ return 0;
+}
+
+#define clk_round_rate LINUX_BACKPORT(clk_round_rate)
+static inline long clk_round_rate(struct clk *clk, unsigned long rate)
+{
+ return 0;
+}
+
+#define clk_set_parent LINUX_BACKPORT(clk_set_parent)
+static inline int clk_set_parent(struct clk *clk, struct clk *parent)
+{
+ return 0;
+}
+
+#define clk_get_parent LINUX_BACKPORT(clk_get_parent)
+static inline struct clk *clk_get_parent(struct clk *clk)
+{
+ return NULL;
+}
+#endif /* CONFIG_COMMON_CLK */
+
+#endif /* #if LINUX_VERSION_IS_LESS(3,0,0) */
+
+#if LINUX_VERSION_IS_LESS(3,3,0) && \
+ LINUX_VERSION_IS_GEQ(3,2,0)
+#define clk_prepare_enable LINUX_BACKPORT(clk_prepare_enable)
+/* clk_prepare_enable helps cases using clk_enable in non-atomic context. */
+static inline int clk_prepare_enable(struct clk *clk)
+{
+ int ret;
+
+ ret = clk_prepare(clk);
+ if (ret)
+ return ret;
+ ret = clk_enable(clk);
+ if (ret)
+ clk_unprepare(clk);
+
+ return ret;
+}
+
+#define clk_disable_unprepare LINUX_BACKPORT(clk_disable_unprepare)
+/* clk_disable_unprepare helps cases using clk_disable in non-atomic context. */
+static inline void clk_disable_unprepare(struct clk *clk)
+{
+ clk_disable(clk);
+ clk_unprepare(clk);
+}
+#endif /* < 3,3,0 && >= 3,2,0 */
+
+#endif /* __LINUX_CLK_H */
diff --git a/backport-include/linux/compat.h b/backport-include/linux/compat.h
new file mode 100644
index 0000000..f41ee5e
--- /dev/null
+++ b/backport-include/linux/compat.h
@@ -0,0 +1,21 @@
+#ifndef __BACKPORT_COMPAT_H
+#define __BACKPORT_COMPAT_H
+
+#include_next <linux/compat.h>
+#include <linux/version.h>
+
+#if LINUX_VERSION_IS_LESS(3,4,0)
+#ifdef CONFIG_X86_X32_ABI
+#define COMPAT_USE_64BIT_TIME \
+ (!!(task_pt_regs(current)->orig_ax & __X32_SYSCALL_BIT))
+#else
+#define COMPAT_USE_64BIT_TIME 0
+#endif
+#endif
+
+#if LINUX_VERSION_IS_LESS(3,4,0)
+#define compat_put_timespec LINUX_BACKPORT(compat_put_timespec)
+extern int compat_put_timespec(const struct timespec *, void __user *);
+#endif
+
+#endif /* __BACKPORT_COMPAT_H */
diff --git a/backport-include/linux/compiler-gcc5.h b/backport-include/linux/compiler-gcc5.h
new file mode 100644
index 0000000..9ff99f0
--- /dev/null
+++ b/backport-include/linux/compiler-gcc5.h
@@ -0,0 +1 @@
+#include <linux/compiler-gccN.h>
diff --git a/backport-include/linux/compiler-gcc6.h b/backport-include/linux/compiler-gcc6.h
new file mode 100644
index 0000000..9ff99f0
--- /dev/null
+++ b/backport-include/linux/compiler-gcc6.h
@@ -0,0 +1 @@
+#include <linux/compiler-gccN.h>
diff --git a/backport-include/linux/compiler-gcc7.h b/backport-include/linux/compiler-gcc7.h
new file mode 100644
index 0000000..9ff99f0
--- /dev/null
+++ b/backport-include/linux/compiler-gcc7.h
@@ -0,0 +1 @@
+#include <linux/compiler-gccN.h>
diff --git a/backport-include/linux/compiler-gccN.h b/backport-include/linux/compiler-gccN.h
new file mode 100644
index 0000000..23ad803
--- /dev/null
+++ b/backport-include/linux/compiler-gccN.h
@@ -0,0 +1,122 @@
+/* gcc version specific checks */
+
+#ifndef GCC_VERSION
+#define GCC_VERSION (__GNUC__ * 10000 \
+ + __GNUC_MINOR__ * 100 \
+ + __GNUC_PATCHLEVEL__)
+#endif
+
+#if GCC_VERSION < 30200
+# error Sorry, your compiler is too old - please upgrade it.
+#endif
+
+#if GCC_VERSION < 30300
+# define __used __attribute__((__unused__))
+#else
+# define __used __attribute__((__used__))
+#endif
+
+#ifdef CONFIG_GCOV_KERNEL
+# if GCC_VERSION < 30400
+# error "GCOV profiling support for gcc versions below 3.4 not included"
+# endif /* __GNUC_MINOR__ */
+#endif /* CONFIG_GCOV_KERNEL */
+
+#if GCC_VERSION >= 30400
+#define __must_check __attribute__((warn_unused_result))
+#endif
+
+#if GCC_VERSION >= 40000
+
+/* GCC 4.1.[01] miscompiles __weak */
+#ifdef __KERNEL__
+# if GCC_VERSION >= 40100 && GCC_VERSION <= 40101
+# error Your version of gcc miscompiles the __weak directive
+# endif
+#endif
+
+#define __used __attribute__((__used__))
+#define __compiler_offsetof(a, b) \
+ __builtin_offsetof(a, b)
+
+#if GCC_VERSION >= 40100 && GCC_VERSION < 40600
+# define __compiletime_object_size(obj) __builtin_object_size(obj, 0)
+#endif
+
+#if GCC_VERSION >= 40300
+/* Mark functions as cold. gcc will assume any path leading to a call
+ * to them will be unlikely. This means a lot of manual unlikely()s
+ * are unnecessary now for any paths leading to the usual suspects
+ * like BUG(), printk(), panic() etc. [but let's keep them for now for
+ * older compilers]
+ *
+ * Early snapshots of gcc 4.3 don't support this and we can't detect this
+ * in the preprocessor, but we can live with this because they're unreleased.
+ * Maketime probing would be overkill here.
+ *
+ * gcc also has a __attribute__((__hot__)) to move hot functions into
+ * a special section, but I don't see any sense in this right now in
+ * the kernel context
+ */
+#define __cold __attribute__((__cold__))
+
+#define __UNIQUE_ID(prefix) __PASTE(__PASTE(__UNIQUE_ID_, prefix), __COUNTER__)
+
+#ifndef __CHECKER__
+# define __compiletime_warning(message) __attribute__((warning(message)))
+# define __compiletime_error(message) __attribute__((error(message)))
+#endif /* __CHECKER__ */
+#endif /* GCC_VERSION >= 40300 */
+
+#if GCC_VERSION >= 40500
+/*
+ * Mark a position in code as unreachable. This can be used to
+ * suppress control flow warnings after asm blocks that transfer
+ * control elsewhere.
+ *
+ * Early snapshots of gcc 4.5 don't support this and we can't detect
+ * this in the preprocessor, but we can live with this because they're
+ * unreleased. Really, we need to have autoconf for the kernel.
+ */
+#define unreachable() __builtin_unreachable()
+
+/* Mark a function definition as prohibited from being cloned. */
+#define __noclone __attribute__((__noclone__))
+
+#endif /* GCC_VERSION >= 40500 */
+
+#if GCC_VERSION >= 40600
+/*
+ * Tell the optimizer that something else uses this function or variable.
+ */
+#define __visible __attribute__((externally_visible))
+#endif
+
+/*
+ * GCC 'asm goto' miscompiles certain code sequences:
+ *
+ * http://gcc.gnu.org/bugzilla/show_bug.cgi?id=58670
+ *
+ * Work it around via a compiler barrier quirk suggested by Jakub Jelinek.
+ *
+ * (asm goto is automatically volatile - the naming reflects this.)
+ */
+#define asm_volatile_goto(x...) do { asm goto(x); asm (""); } while (0)
+
+#ifdef CONFIG_ARCH_USE_BUILTIN_BSWAP
+#if GCC_VERSION >= 40400
+#define __HAVE_BUILTIN_BSWAP32__
+#define __HAVE_BUILTIN_BSWAP64__
+#endif
+#if GCC_VERSION >= 40800 || (defined(__powerpc__) && GCC_VERSION >= 40600)
+#define __HAVE_BUILTIN_BSWAP16__
+#endif
+#endif /* CONFIG_ARCH_USE_BUILTIN_BSWAP */
+
+#if GCC_VERSION >= 50000
+#define KASAN_ABI_VERSION 4
+#elif GCC_VERSION >= 40902
+#define KASAN_ABI_VERSION 3
+#endif
+
+#endif /* gcc version >= 40000 specific checks */
diff --git a/backport-include/linux/compiler.h b/backport-include/linux/compiler.h
new file mode 100644
index 0000000..e5af565
--- /dev/null
+++ b/backport-include/linux/compiler.h
@@ -0,0 +1,91 @@
+#ifndef __BACKPORT_LINUX_COMPILER_H
+#define __BACKPORT_LINUX_COMPILER_H
+#include_next <linux/compiler.h>
+
+#ifndef __rcu
+#define __rcu
+#endif
+
+#ifndef __always_unused
+#ifdef __GNUC__
+#define __always_unused __attribute__((unused))
+#else
+#define __always_unused /* unimplemented */
+#endif
+#endif
+
+#ifndef __PASTE
+/* Indirect macros required for expanded argument pasting, eg. __LINE__. */
+#define ___PASTE(a,b) a##b
+#define __PASTE(a,b) ___PASTE(a,b)
+#endif
+
+/* Not-quite-unique ID. */
+#ifndef __UNIQUE_ID
+# define __UNIQUE_ID(prefix) __PASTE(__PASTE(__UNIQUE_ID_, prefix), __LINE__)
+#endif
+
+#ifndef barrier_data
+#ifdef __GNUC__
+#define barrier_data(ptr) __asm__ __volatile__("": :"r"(ptr) :"memory")
+#else /* __GNUC__ */
+# define barrier_data(ptr) barrier()
+#endif /* __GNUC__ */
+#endif
+
+#ifndef READ_ONCE
+#include <linux/types.h>
+
+#define __READ_ONCE_SIZE \
+({ \
+ switch (size) { \
+ case 1: *(__u8 *)res = *(volatile __u8 *)p; break; \
+ case 2: *(__u16 *)res = *(volatile __u16 *)p; break; \
+ case 4: *(__u32 *)res = *(volatile __u32 *)p; break; \
+ case 8: *(__u64 *)res = *(volatile __u64 *)p; break; \
+ default: \
+ barrier(); \
+ __builtin_memcpy((void *)res, (const void *)p, size); \
+ barrier(); \
+ } \
+})
+
+static __always_inline
+void __read_once_size(const volatile void *p, void *res, int size)
+{
+ __READ_ONCE_SIZE;
+}
+
+#define __READ_ONCE(x, check) \
+({ \
+ union { typeof(x) __val; char __c[1]; } __u; \
+ __read_once_size(&(x), __u.__c, sizeof(x)); \
+ __u.__val; \
+})
+
+#define READ_ONCE(x) __READ_ONCE(x, 1)
+
+static __always_inline void __write_once_size(volatile void *p, void *res, int size)
+{
+ switch (size) {
+ case 1: *(volatile __u8 *)p = *(__u8 *)res; break;
+ case 2: *(volatile __u16 *)p = *(__u16 *)res; break;
+ case 4: *(volatile __u32 *)p = *(__u32 *)res; break;
+ case 8: *(volatile __u64 *)p = *(__u64 *)res; break;
+ default:
+ barrier();
+ __builtin_memcpy((void *)p, (const void *)res, size);
+ barrier();
+ }
+}
+
+#define WRITE_ONCE(x, val) \
+({ \
+ union { typeof(x) __val; char __c[1]; } __u = \
+ { .__val = (__force typeof(x)) (val) }; \
+ __write_once_size(&(x), __u.__c, sizeof(x)); \
+ __u.__val; \
+})
+#endif
+
+#endif /* __BACKPORT_LINUX_COMPILER_H */
diff --git a/backport-include/linux/completion.h b/backport-include/linux/completion.h
new file mode 100644
index 0000000..f8ce5b1
--- /dev/null
+++ b/backport-include/linux/completion.h
@@ -0,0 +1,21 @@
+#ifndef __BACKPORT_COMPLETION_H
+#define __BACKPORT_COMPLETION_H
+#include_next <linux/completion.h>
+#include <linux/version.h>
+
+#if LINUX_VERSION_IS_LESS(3,13,0)
+/**
+ * reinit_completion - reinitialize a completion structure
+ * @x: pointer to completion structure that is to be reinitialized
+ *
+ * This inline function should be used to reinitialize a completion structure so it can
+ * be reused. This is especially important after complete_all() is used.
+ */
+#define reinit_completion LINUX_BACKPORT(reinit_completion)
+static inline void reinit_completion(struct completion *x)
+{
+ x->done = 0;
+}
+#endif /* LINUX_VERSION_IS_LESS(3,13,0) */
+
+#endif /* __BACKPORT_COMPLETION_H */
diff --git a/backport-include/linux/cordic.h b/backport-include/linux/cordic.h
new file mode 100644
index 0000000..7f27b00
--- /dev/null
+++ b/backport-include/linux/cordic.h
@@ -0,0 +1,60 @@
+#ifndef _BACKPORT_LINUX_CORDIC_H
+#define _BACKPORT_LINUX_CORDIC_H 1
+
+#include <linux/version.h>
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(3,1,0))
+#include_next <linux/cordic.h>
+#else
+
+/*
+ * Copyright (c) 2011 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#ifndef __CORDIC_H_
+#define __CORDIC_H_
+
+#include <linux/types.h>
+
+/**
+ * struct cordic_iq - i/q coordinate.
+ *
+ * @i: real part of coordinate (in phase).
+ * @q: imaginary part of coordinate (quadrature).
+ */
+struct cordic_iq {
+ s32 i;
+ s32 q;
+};
+
+/**
+ * cordic_calc_iq() - calculates the i/q coordinate for given angle.
+ *
+ * @theta: angle in degrees for which i/q coordinate is to be calculated.
+ * @coord: function output parameter holding the i/q coordinate.
+ *
+ * The function calculates the i/q coordinate for a given angle using
+ * cordic algorithm. The coordinate consists of a real (i) and an
+ * imaginary (q) part. The real part is essentially the cosine of the
+ * angle and the imaginary part is the sine of the angle. The returned
+ * values are scaled by 2^16 for precision. The range for theta is
+ * for -180 degrees to +180 degrees. Passed values outside this range are
+ * converted before doing the actual calculation.
+ */
+#define cordic_calc_iq LINUX_BACKPORT(cordic_calc_iq)
+struct cordic_iq cordic_calc_iq(s32 theta);
+
+#endif /* __CORDIC_H_ */
+#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(3,1,0)) */
+#endif /* _BACKPORT_LINUX_CORDIC_H */
diff --git a/backport-include/linux/crc7.h b/backport-include/linux/crc7.h
new file mode 100644
index 0000000..50706ea
--- /dev/null
+++ b/backport-include/linux/crc7.h
@@ -0,0 +1,14 @@
+#ifndef _BACKPORT_LINUX_CRC7_H
+#define _BACKPORT_LINUX_CRC7_H
+#include_next <linux/crc7.h>
+#include <linux/version.h>
+
+#if LINUX_VERSION_IS_LESS(3,16,0)
+#define crc7_be LINUX_BACKPORT(crc7_be)
+static inline u8 crc7_be(u8 crc, const u8 *buffer, size_t len)
+{
+ return crc7(crc, buffer, len) << 1;
+}
+#endif /* < 3.16 */
+
+#endif /* _BACKPORT_LINUX_CRC7_H */
diff --git a/backport-include/linux/debugfs.h b/backport-include/linux/debugfs.h
new file mode 100644
index 0000000..1167038
--- /dev/null
+++ b/backport-include/linux/debugfs.h
@@ -0,0 +1,56 @@
+#ifndef __BACKPORT_DEBUGFS_H_
+#define __BACKPORT_DEBUGFS_H_
+#include_next <linux/debugfs.h>
+#include <linux/version.h>
+#include <linux/device.h>
+#include <generated/utsrelease.h>
+
+#if LINUX_VERSION_IS_LESS(3,19,0)
+#define debugfs_create_devm_seqfile LINUX_BACKPORT(debugfs_create_devm_seqfile)
+#if defined(CONFIG_DEBUG_FS)
+struct dentry *debugfs_create_devm_seqfile(struct device *dev, const char *name,
+ struct dentry *parent,
+ int (*read_fn)(struct seq_file *s,
+ void *data));
+#else
+static inline struct dentry *debugfs_create_devm_seqfile(struct device *dev,
+ const char *name,
+ struct dentry *parent,
+ int (*read_fn)(struct seq_file *s,
+ void *data))
+{
+ return ERR_PTR(-ENODEV);
+}
+#endif /* CONFIG_DEBUG_FS */
+#endif /* LINUX_VERSION_IS_LESS(3,19,0) */
+
+#if LINUX_VERSION_IS_LESS(4,4,0)
+#define debugfs_create_bool LINUX_BACKPORT(debugfs_create_bool)
+#ifdef CONFIG_DEBUG_FS
+struct dentry *debugfs_create_bool(const char *name, umode_t mode,
+ struct dentry *parent, bool *value);
+#else
+static inline struct dentry *
+debugfs_create_bool(const char *name, umode_t mode,
+ struct dentry *parent, bool *value)
+{
+ return ERR_PTR(-ENODEV);
+}
+#endif
+#endif /* LINUX_VERSION_IS_LESS(4,4,0) */
+
+#if LINUX_VERSION_IS_LESS(4,9,0) && \
+ !LINUX_VERSION_IN_RANGE(4,8,4, 4,9,0) && \
+ !LINUX_VERSION_IN_RANGE(4,7,10, 4,8,0)
+static inline const struct file_operations *
+debugfs_real_fops(const struct file *filp)
+{
+ /*
+ * Neither the pointer to the struct file_operations, nor its
+ * contents ever change -- srcu_dereference() is not needed here.
+ */
+ return filp->f_path.dentry->d_fsdata;
+}
+#endif /* <4.9.0 but not >= 4.8.4, 4.7.10 */
+
+#endif /* __BACKPORT_DEBUGFS_H_ */
diff --git a/backport-include/linux/device.h b/backport-include/linux/device.h
new file mode 100644
index 0000000..e0886f5
--- /dev/null
+++ b/backport-include/linux/device.h
@@ -0,0 +1,276 @@
+#ifndef __BACKPORT_DEVICE_H
+#define __BACKPORT_DEVICE_H
+#include <linux/export.h>
+#include_next <linux/device.h>
+
+#include <linux/version.h>
+
+/*
+ * string.h is usually included from the asm/ folder in most configuration,
+ * but on some older kernels it doesn't. As we're using memcpy() in the code
+ * below, we need to be safe and make sure string.h is indeed there.
+ */
+#include <linux/string.h>
+
+#if LINUX_VERSION_IS_LESS(3,9,0)
+/* backport
+ * commit 9f3b795a626ee79574595e06d1437fe0c7d51d29
+ * Author: Michał Mirosław <mirq-linux@rere.qmqm.pl>
+ * Date: Fri Feb 1 20:40:17 2013 +0100
+ *
+ * driver-core: constify data for class_find_device()
+ */
+typedef int (backport_device_find_function_t)(struct device *, void *);
+#define class_find_device(cls, start, idx, fun) \
+ class_find_device((cls), (start), (void *)(idx),\
+ (backport_device_find_function_t *)(fun))
+#endif
+
+#ifndef module_driver
+/**
+ * module_driver() - Helper macro for drivers that don't do anything
+ * special in module init/exit. This eliminates a lot of boilerplate.
+ * Each module may only use this macro once, and calling it replaces
+ * module_init() and module_exit().
+ *
+ * Use this macro to construct bus specific macros for registering
+ * drivers, and do not use it on its own.
+ */
+#define module_driver(__driver, __register, __unregister) \
+static int __init __driver##_init(void) \
+{ \
+ return __register(&(__driver)); \
+} \
+module_init(__driver##_init); \
+static void __exit __driver##_exit(void) \
+{ \
+ __unregister(&(__driver)); \
+} \
+module_exit(__driver##_exit);
+#endif
+
+#if LINUX_VERSION_IS_LESS(3,9,0)
+#define devm_ioremap_resource LINUX_BACKPORT(devm_ioremap_resource)
+void __iomem *devm_ioremap_resource(struct device *dev, struct resource *res);
+#endif
+
+#if LINUX_VERSION_IS_LESS(3,5,0) && \
+ LINUX_VERSION_IS_GEQ(3,2,0)
+#define devres_release LINUX_BACKPORT(devres_release)
+extern int devres_release(struct device *dev, dr_release_t release,
+ dr_match_t match, void *match_data);
+#endif
+
+#if LINUX_VERSION_IS_LESS(3,5,0)
+#include <linux/ratelimit.h>
+
+#define dev_level_ratelimited(dev_level, dev, fmt, ...) \
+do { \
+ static DEFINE_RATELIMIT_STATE(_rs, \
+ DEFAULT_RATELIMIT_INTERVAL, \
+ DEFAULT_RATELIMIT_BURST); \
+ if (__ratelimit(&_rs)) \
+ dev_level(dev, fmt, ##__VA_ARGS__); \
+} while (0)
+
+#define dev_emerg_ratelimited(dev, fmt, ...) \
+ dev_level_ratelimited(dev_emerg, dev, fmt, ##__VA_ARGS__)
+#define dev_alert_ratelimited(dev, fmt, ...) \
+ dev_level_ratelimited(dev_alert, dev, fmt, ##__VA_ARGS__)
+#define dev_crit_ratelimited(dev, fmt, ...) \
+ dev_level_ratelimited(dev_crit, dev, fmt, ##__VA_ARGS__)
+#define dev_err_ratelimited(dev, fmt, ...) \
+ dev_level_ratelimited(dev_err, dev, fmt, ##__VA_ARGS__)
+#define dev_warn_ratelimited(dev, fmt, ...) \
+ dev_level_ratelimited(dev_warn, dev, fmt, ##__VA_ARGS__)
+#define dev_notice_ratelimited(dev, fmt, ...) \
+ dev_level_ratelimited(dev_notice, dev, fmt, ##__VA_ARGS__)
+#define dev_info_ratelimited(dev, fmt, ...) \
+ dev_level_ratelimited(dev_info, dev, fmt, ##__VA_ARGS__)
+
+
+#if defined(CONFIG_DYNAMIC_DEBUG) || defined(DEBUG)
+#define dev_dbg_ratelimited(dev, fmt, ...) \
+do { \
+ static DEFINE_RATELIMIT_STATE(_rs, \
+ DEFAULT_RATELIMIT_INTERVAL, \
+ DEFAULT_RATELIMIT_BURST); \
+ DEFINE_DYNAMIC_DEBUG_METADATA(descriptor, fmt); \
+ if (unlikely(descriptor.flags & _DPRINTK_FLAGS_PRINT) && \
+ __ratelimit(&_rs)) \
+ __dynamic_pr_debug(&descriptor, pr_fmt(fmt), \
+ ##__VA_ARGS__); \
+} while (0)
+#else
+#define dev_dbg_ratelimited(dev, fmt, ...) \
+ no_printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
+#endif /* dynamic debug */
+#endif /* <= 3.5 */
+
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(3,6,0)
+static inline void
+backport_device_release_driver(struct device *dev)
+{
+ device_release_driver(dev);
+ device_lock(dev);
+ dev_set_drvdata(dev, NULL);
+ device_unlock(dev);
+}
+#define device_release_driver LINUX_BACKPORT(device_release_driver)
+
+#define kobj_to_dev LINUX_BACKPORT(kobj_to_dev)
+static inline struct device *kobj_to_dev(struct kobject *kobj)
+{
+ return container_of(kobj, struct device, kobj);
+}
+#endif /* LINUX_VERSION_CODE <= KERNEL_VERSION(3,6,0) */
+
+#if LINUX_VERSION_IS_LESS(3,11,0) && RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(7,0)
+#ifndef DEVICE_ATTR_RO
+#define DEVICE_ATTR_RO(_name) \
+struct device_attribute dev_attr_ ## _name = __ATTR_RO(_name);
+#endif
+#ifndef DEVICE_ATTR_RW
+#define DEVICE_ATTR_RW(_name) \
+struct device_attribute dev_attr_ ## _name = __ATTR_RW(_name)
+#endif
+#endif
+
+#ifndef CLASS_ATTR_RW
+#define CLASS_ATTR_RW(_name) \
+ struct class_attribute class_attr_##_name = __ATTR_RW(_name)
+#endif
+#ifndef CLASS_ATTR_RO
+#define CLASS_ATTR_RO(_name) \
+ struct class_attribute class_attr_##_name = __ATTR_RO(_name)
+#endif
+
+#define ATTRIBUTE_GROUPS_BACKPORT(_name) \
+static struct BP_ATTR_GRP_STRUCT _name##_dev_attrs[ARRAY_SIZE(_name##_attrs)];\
+static void init_##_name##_attrs(void) \
+{ \
+ int i; \
+ for (i = 0; _name##_attrs[i]; i++) \
+ _name##_dev_attrs[i] = \
+ *container_of(_name##_attrs[i], \
+ struct BP_ATTR_GRP_STRUCT, \
+ attr); \
+}
+
+#ifndef __ATTRIBUTE_GROUPS
+#define __ATTRIBUTE_GROUPS(_name) \
+static const struct attribute_group *_name##_groups[] = { \
+ &_name##_group, \
+ NULL, \
+}
+#endif /* __ATTRIBUTE_GROUPS */
+
+#undef ATTRIBUTE_GROUPS
+#define ATTRIBUTE_GROUPS(_name) \
+static const struct attribute_group _name##_group = { \
+ .attrs = _name##_attrs, \
+}; \
+static inline void init_##_name##_attrs(void) {} \
+__ATTRIBUTE_GROUPS(_name)
+
+#if LINUX_VERSION_IS_LESS(3,13,0)
+#define devm_kmalloc(dev, size, flags) devm_kzalloc(dev, size, flags)
+#endif
+
+#if LINUX_VERSION_IS_LESS(3,15,0)
+#define devm_kstrdup LINUX_BACKPORT(devm_kstrdup)
+extern char *devm_kstrdup(struct device *dev, const char *s, gfp_t gfp);
+#endif
+
+#if LINUX_VERSION_IS_LESS(3,13,0)
+#define devm_kmalloc_array LINUX_BACKPORT(devm_kmalloc_array)
+static inline void *devm_kmalloc_array(struct device *dev,
+ size_t n, size_t size, gfp_t flags)
+{
+ if (size != 0 && n > SIZE_MAX / size)
+ return NULL;
+ return devm_kmalloc(dev, n * size, flags);
+}
+
+#define devm_kcalloc LINUX_BACKPORT(devm_kcalloc)
+static inline void *devm_kcalloc(struct device *dev,
+ size_t n, size_t size, gfp_t flags)
+{
+ return devm_kmalloc_array(dev, n, size, flags);
+}
+#endif
+
+#if LINUX_VERSION_IS_LESS(3,16,0)
+#define devm_kmemdup LINUX_BACKPORT(devm_kmemdup)
+static inline void *devm_kmemdup(struct device *dev, const void *src,
+ size_t len, gfp_t gfp)
+{
+ void *p;
+
+ p = devm_kmalloc(dev, len, gfp);
+ if (p)
+ memcpy(p, src, len);
+
+ return p;
+}
+#endif
+
+#ifndef dev_level_once
+#ifdef CONFIG_PRINTK
+#define dev_level_once(dev_level, dev, fmt, ...) \
+do { \
+ static bool __print_once __read_mostly; \
+ \
+ if (!__print_once) { \
+ __print_once = true; \
+ dev_level(dev, fmt, ##__VA_ARGS__); \
+ } \
+} while (0)
+#else
+#define dev_level_once(dev_level, dev, fmt, ...) \
+do { \
+ if (0) \
+ dev_level(dev, fmt, ##__VA_ARGS__); \
+} while (0)
+#endif
+
+#define dev_emerg_once(dev, fmt, ...) \
+ dev_level_once(dev_emerg, dev, fmt, ##__VA_ARGS__)
+#define dev_alert_once(dev, fmt, ...) \
+ dev_level_once(dev_alert, dev, fmt, ##__VA_ARGS__)
+#define dev_crit_once(dev, fmt, ...) \
+ dev_level_once(dev_crit, dev, fmt, ##__VA_ARGS__)
+#define dev_err_once(dev, fmt, ...) \
+ dev_level_once(dev_err, dev, fmt, ##__VA_ARGS__)
+#define dev_warn_once(dev, fmt, ...) \
+ dev_level_once(dev_warn, dev, fmt, ##__VA_ARGS__)
+#define dev_notice_once(dev, fmt, ...) \
+ dev_level_once(dev_notice, dev, fmt, ##__VA_ARGS__)
+#define dev_info_once(dev, fmt, ...) \
+ dev_level_once(dev_info, dev, fmt, ##__VA_ARGS__)
+#define dev_dbg_once(dev, fmt, ...) \
+ dev_level_once(dev_dbg, dev, fmt, ##__VA_ARGS__)
+#endif /* dev_level_once */
+
+#if LINUX_VERSION_IS_LESS(3,17,0)
+#define devm_kvasprintf LINUX_BACKPORT(devm_kvasprintf)
+extern char *devm_kvasprintf(struct device *dev, gfp_t gfp, const char *fmt,
+ va_list ap);
+#define devm_kasprintf LINUX_BACKPORT(devm_kasprintf)
+extern char *devm_kasprintf(struct device *dev, gfp_t gfp,
+ const char *fmt, ...);
+#endif /* < 3.17 */
+
+#if LINUX_VERSION_IS_LESS(4, 1, 0)
+#define dev_of_node LINUX_BACKPORT(dev_of_node)
+static inline struct device_node *dev_of_node(struct device *dev)
+{
+#ifndef CONFIG_OF
+ return NULL;
+#else
+ return dev->of_node;
+#endif
+}
+#endif
+
+#endif /* __BACKPORT_DEVICE_H */
diff --git a/backport-include/linux/dma-buf.h b/backport-include/linux/dma-buf.h
new file mode 100644
index 0000000..ef1d0d3
--- /dev/null
+++ b/backport-include/linux/dma-buf.h
@@ -0,0 +1,54 @@
+#ifndef _BACKPORT_DMA_BUF_H__
+#define _BACKPORT_DMA_BUF_H__
+#include <linux/version.h>
+#if LINUX_VERSION_IS_GEQ(3,3,0)
+#include_next <linux/dma-buf.h>
+#endif /* LINUX_VERSION_IS_GEQ(3,3,0) */
+#include <linux/dma-direction.h>
+#include <linux/dma-attrs.h>
+#include <linux/dma-mapping.h>
+
+#if !defined(DEFINE_DMA_BUF_EXPORT_INFO) && LINUX_VERSION_IS_GEQ(3,3,0)
+/**
+ * helper macro for exporters; zeros and fills in most common values
+ */
+#define DEFINE_DMA_BUF_EXPORT_INFO(a) \
+ struct dma_buf_export_info a = { .exp_name = KBUILD_MODNAME }
+
+struct dma_buf_export_info {
+ const char *exp_name;
+ const struct dma_buf_ops *ops;
+ size_t size;
+ int flags;
+ struct reservation_object *resv;
+ void *priv;
+};
+
+#ifdef dma_buf_export
+#undef dma_buf_export
+#endif
+
+static inline
+struct dma_buf *backport_dma_buf_export(const struct dma_buf_export_info *exp_info)
+{
+#if LINUX_VERSION_IS_LESS(3,4,0)
+ return dma_buf_export(exp_info->priv,
+ (struct dma_buf_ops *)exp_info->ops,
+ exp_info->size, exp_info->flags);
+#elif LINUX_VERSION_IS_LESS(3,10,0)
+ return dma_buf_export(exp_info->priv, exp_info->ops,
+ exp_info->size, exp_info->flags);
+#elif LINUX_VERSION_IS_LESS(3,17,0)
+ return dma_buf_export_named(exp_info->priv, exp_info->ops,
+ exp_info->size, exp_info->flags,
+ exp_info->exp_name);
+#else
+ return dma_buf_export_named(exp_info->priv, exp_info->ops,
+ exp_info->size, exp_info->flags,
+ exp_info->exp_name, exp_info->resv);
+#endif
+}
+#define dma_buf_export LINUX_BACKPORT(dma_buf_export)
+#endif /* !defined(DEFINE_DMA_BUF_EXPORT_INFO) */
+
+#endif /* _BACKPORT_DMA_BUF_H__ */
diff --git a/backport-include/linux/dma-mapping.h b/backport-include/linux/dma-mapping.h
new file mode 100644
index 0000000..16b0e3c
--- /dev/null
+++ b/backport-include/linux/dma-mapping.h
@@ -0,0 +1,35 @@
+#ifndef __BACKPORT_LINUX_DMA_MAPPING_H
+#define __BACKPORT_LINUX_DMA_MAPPING_H
+#include_next <linux/dma-mapping.h>
+#include <linux/version.h>
+
+#if LINUX_VERSION_IS_LESS(3,2,0)
+#define dma_zalloc_coherent LINUX_BACKPORT(dma_zalloc_coherent)
+static inline void *dma_zalloc_coherent(struct device *dev, size_t size,
+ dma_addr_t *dma_handle, gfp_t flag)
+{
+ void *ret = dma_alloc_coherent(dev, size, dma_handle, flag);
+ if (ret)
+ memset(ret, 0, size);
+ return ret;
+}
+#endif
+
+#if LINUX_VERSION_IS_LESS(3,13,0)
+/*
+ * Set both the DMA mask and the coherent DMA mask to the same thing.
+ * Note that we don't check the return value from dma_set_coherent_mask()
+ * as the DMA API guarantees that the coherent DMA mask can be set to
+ * the same or smaller than the streaming DMA mask.
+ */
+#define dma_set_mask_and_coherent LINUX_BACKPORT(dma_set_mask_and_coherent)
+static inline int dma_set_mask_and_coherent(struct device *dev, u64 mask)
+{
+ int rc = dma_set_mask(dev, mask);
+ if (rc == 0)
+ dma_set_coherent_mask(dev, mask);
+ return rc;
+}
+#endif /* LINUX_VERSION_IS_LESS(3,13,0) */
+
+#endif /* __BACKPORT_LINUX_DMA_MAPPING_H */
diff --git a/backport-include/linux/dynamic_debug.h b/backport-include/linux/dynamic_debug.h
new file mode 100644
index 0000000..1ff204b
--- /dev/null
+++ b/backport-include/linux/dynamic_debug.h
@@ -0,0 +1,36 @@
+#ifndef __BACKPORT_LINUX_DYNAMIC_DEBUG_H
+#define __BACKPORT_LINUX_DYNAMIC_DEBUG_H
+#include <linux/version.h>
+#include_next <linux/dynamic_debug.h>
+
+#if LINUX_VERSION_IS_LESS(3,2,0)
+/* backports 07613b0b */
+#if defined(CONFIG_DYNAMIC_DEBUG)
+#if (RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(6,4))
+#define DEFINE_DYNAMIC_DEBUG_METADATA(name, fmt) \
+ static struct _ddebug __used __aligned(8) \
+ __attribute__((section("__verbose"))) name = { \
+ .modname = KBUILD_MODNAME, \
+ .function = __func__, \
+ .filename = __FILE__, \
+ .format = (fmt), \
+ .lineno = __LINE__, \
+ .flags = _DPRINTK_FLAGS_DEFAULT, \
+ .enabled = false, \
+ }
+#else
+#define DEFINE_DYNAMIC_DEBUG_METADATA(name, fmt) \
+ static struct _ddebug __used __aligned(8) \
+ __attribute__((section("__verbose"))) name = { \
+ .modname = KBUILD_MODNAME, \
+ .function = __func__, \
+ .filename = __FILE__, \
+ .format = (fmt), \
+ .lineno = __LINE__, \
+ .flags = _DPRINTK_FLAGS_DEFAULT, \
+ }
+#endif /* RHEL_RELEASE_CODE < 6.4 */
+#endif /* defined(CONFIG_DYNAMIC_DEBUG) */
+#endif /* < 3.2 */
+
+#endif /* __BACKPORT_LINUX_DYNAMIC_DEBUG_H */
diff --git a/backport-include/linux/eeprom_93cx6.h b/backport-include/linux/eeprom_93cx6.h
new file mode 100644
index 0000000..3385a3f
--- /dev/null
+++ b/backport-include/linux/eeprom_93cx6.h
@@ -0,0 +1,10 @@
+#ifndef _COMPAT_LINUX_EEPROM_93CX6_H
+#define _COMPAT_LINUX_EEPROM_93CX6_H 1
+
+#include_next <linux/eeprom_93cx6.h>
+
+#ifndef PCI_EEPROM_WIDTH_93C86
+#define PCI_EEPROM_WIDTH_93C86 8
+#endif /* PCI_EEPROM_WIDTH_93C86 */
+
+#endif /* _COMPAT_LINUX_EEPROM_93CX6_H */
diff --git a/backport-include/linux/err.h b/backport-include/linux/err.h
new file mode 100644
index 0000000..d08968e
--- /dev/null
+++ b/backport-include/linux/err.h
@@ -0,0 +1,10 @@
+#ifndef __BACKPORT_LINUX_ERR_H
+#define __BACKPORT_LINUX_ERR_H
+#include_next <linux/err.h>
+#include <linux/version.h>
+
+#if LINUX_VERSION_IS_LESS(3,12,0)
+#define PTR_ERR_OR_ZERO(p) PTR_RET(p)
+#endif
+
+#endif /* __BACKPORT_LINUX_ERR_H */
diff --git a/backport-include/linux/etherdevice.h b/backport-include/linux/etherdevice.h
new file mode 100644
index 0000000..92256e0
--- /dev/null
+++ b/backport-include/linux/etherdevice.h
@@ -0,0 +1,196 @@
+#ifndef _BACKPORT_LINUX_ETHERDEVICE_H
+#define _BACKPORT_LINUX_ETHERDEVICE_H
+#include_next <linux/etherdevice.h>
+#include <linux/version.h>
+/*
+ * newer kernels include this already and some
+ * users rely on getting this indirectly
+ */
+#include <asm/unaligned.h>
+
+#if LINUX_VERSION_IS_LESS(3,4,0)
+#define eth_hw_addr_random LINUX_BACKPORT(eth_hw_addr_random)
+static inline void eth_hw_addr_random(struct net_device *dev)
+{
+ dev->addr_assign_type |= NET_ADDR_RANDOM;
+ random_ether_addr(dev->dev_addr);
+}
+#endif
+
+#if LINUX_VERSION_IS_LESS(3,6,0)
+#include <linux/random.h>
+/**
+ * eth_broadcast_addr - Assign broadcast address
+ * @addr: Pointer to a six-byte array containing the Ethernet address
+ *
+ * Assign the broadcast address to the given address array.
+ */
+#define eth_broadcast_addr LINUX_BACKPORT(eth_broadcast_addr)
+static inline void eth_broadcast_addr(u8 *addr)
+{
+ memset(addr, 0xff, ETH_ALEN);
+}
+
+/**
+ * eth_random_addr - Generate software assigned random Ethernet address
+ * @addr: Pointer to a six-byte array containing the Ethernet address
+ *
+ * Generate a random Ethernet address (MAC) that is not multicast
+ * and has the local assigned bit set.
+ */
+#define eth_random_addr LINUX_BACKPORT(eth_random_addr)
+static inline void eth_random_addr(u8 *addr)
+{
+ get_random_bytes(addr, ETH_ALEN);
+ addr[0] &= 0xfe; /* clear multicast bit */
+ addr[0] |= 0x02; /* set local assignment bit (IEEE802) */
+}
+#endif /* LINUX_VERSION_IS_LESS(3,6,0) */
+
+#if LINUX_VERSION_IS_LESS(3,7,0)
+
+/* This backports:
+ *
+ * commit 6d57e9078e880a3dd232d579f42ac437a8f1ef7b
+ * Author: Duan Jiong <djduanjiong@gmail.com>
+ * Date: Sat Sep 8 16:32:28 2012 +0000
+ *
+ * etherdevice: introduce help function eth_zero_addr()
+ */
+#define eth_zero_addr LINUX_BACKPORT(eth_zero_addr)
+static inline void eth_zero_addr(u8 *addr)
+{
+ memset(addr, 0x00, ETH_ALEN);
+}
+#endif
+
+#if LINUX_VERSION_IS_LESS(3,5,0)
+#define ether_addr_equal LINUX_BACKPORT(ether_addr_equal)
+static inline bool ether_addr_equal(const u8 *addr1, const u8 *addr2)
+{
+ return !compare_ether_addr(addr1, addr2);
+}
+#endif
+
+#if LINUX_VERSION_IS_LESS(3,9,0)
+#define eth_prepare_mac_addr_change LINUX_BACKPORT(eth_prepare_mac_addr_change)
+extern int eth_prepare_mac_addr_change(struct net_device *dev, void *p);
+
+#define eth_commit_mac_addr_change LINUX_BACKPORT(eth_commit_mac_addr_change)
+extern void eth_commit_mac_addr_change(struct net_device *dev, void *p);
+#endif /* < 3.9 */
+
+#if LINUX_VERSION_IS_LESS(3,12,0)
+/**
+ * eth_hw_addr_inherit - Copy dev_addr from another net_device
+ * @dst: pointer to net_device to copy dev_addr to
+ * @src: pointer to net_device to copy dev_addr from
+ *
+ * Copy the Ethernet address from one net_device to another along with
+ * the address attributes (addr_assign_type).
+ */
+static inline void eth_hw_addr_inherit(struct net_device *dst,
+ struct net_device *src)
+{
+ dst->addr_assign_type = src->addr_assign_type;
+ memcpy(dst->dev_addr, src->dev_addr, ETH_ALEN);
+}
+#endif /* LINUX_VERSION_IS_LESS(3,13,0) */
+
+#if LINUX_VERSION_IS_LESS(3,5,0)
+/**
+ * ether_addr_equal_64bits - Compare two Ethernet addresses
+ * @addr1: Pointer to an array of 8 bytes
+ * @addr2: Pointer to an other array of 8 bytes
+ *
+ * Compare two Ethernet addresses, returns true if equal, false otherwise.
+ *
+ * The function doesn't need any conditional branches and possibly uses
+ * word memory accesses on CPU allowing cheap unaligned memory reads.
+ * arrays = { byte1, byte2, byte3, byte4, byte5, byte6, pad1, pad2 }
+ *
+ * Please note that alignment of addr1 & addr2 are only guaranteed to be 16 bits.
+ */
+#define ether_addr_equal_64bits LINUX_BACKPORT(ether_addr_equal_64bits)
+static inline bool ether_addr_equal_64bits(const u8 addr1[6+2],
+ const u8 addr2[6+2])
+{
+#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) && BITS_PER_LONG == 64
+ u64 fold = (*(const u64 *)addr1) ^ (*(const u64 *)addr2);
+
+#ifdef __BIG_ENDIAN
+ return (fold >> 16) == 0;
+#else
+ return (fold << 16) == 0;
+#endif
+#else
+ return ether_addr_equal(addr1, addr2);
+#endif
+}
+#endif /* LINUX_VERSION_IS_LESS(3,5,0) */
+
+#if LINUX_VERSION_IS_LESS(3,14,0)
+/**
+ * ether_addr_equal_unaligned - Compare two not u16 aligned Ethernet addresses
+ * @addr1: Pointer to a six-byte array containing the Ethernet address
+ * @addr2: Pointer other six-byte array containing the Ethernet address
+ *
+ * Compare two Ethernet addresses, returns true if equal
+ *
+ * Please note: Use only when any Ethernet address may not be u16 aligned.
+ */
+#define ether_addr_equal_unaligned LINUX_BACKPORT(ether_addr_equal_unaligned)
+static inline bool ether_addr_equal_unaligned(const u8 *addr1, const u8 *addr2)
+{
+#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
+ return ether_addr_equal(addr1, addr2);
+#else
+ return memcmp(addr1, addr2, ETH_ALEN) == 0;
+#endif
+}
+
+/**
+ * ether_addr_copy - Copy an Ethernet address
+ * @dst: Pointer to a six-byte array Ethernet address destination
+ * @src: Pointer to a six-byte array Ethernet address source
+ *
+ * Please note: dst & src must both be aligned to u16.
+ */
+#define ether_addr_copy LINUX_BACKPORT(ether_addr_copy)
+static inline void ether_addr_copy(u8 *dst, const u8 *src)
+{
+#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
+ *(u32 *)dst = *(const u32 *)src;
+ *(u16 *)(dst + 4) = *(const u16 *)(src + 4);
+#else
+ u16 *a = (u16 *)dst;
+ const u16 *b = (const u16 *)src;
+
+ a[0] = b[0];
+ a[1] = b[1];
+ a[2] = b[2];
+#endif
+}
+#endif /* LINUX_VERSION_IS_LESS(3,14,0) */
+
+#if LINUX_VERSION_IS_LESS(3,18,0)
+#define eth_get_headlen LINUX_BACKPORT(eth_get_headlen)
+int eth_get_headlen(unsigned char *data, unsigned int max_len);
+#endif /* LINUX_VERSION_IS_LESS(3,18,0) */
+
+#if LINUX_VERSION_IS_LESS(3,19,0)
+#define eth_skb_pad LINUX_BACKPORT(eth_skb_pad)
+/**
+ * eth_skb_pad - Pad buffer to mininum number of octets for Ethernet frame
+ * @skb: Buffer to pad
+ *
+ * An Ethernet frame should have a minimum size of 60 bytes. This function
+ * takes short frames and pads them with zeros up to the 60 byte limit.
+ */
+static inline int eth_skb_pad(struct sk_buff *skb)
+{
+ return skb_put_padto(skb, ETH_ZLEN);
+}
+#endif /* LINUX_VERSION_IS_LESS(3,19,0) */
+
+#endif /* _BACKPORT_LINUX_ETHERDEVICE_H */
diff --git a/backport-include/linux/ethtool.h b/backport-include/linux/ethtool.h
new file mode 100644
index 0000000..7a78bb5
--- /dev/null
+++ b/backport-include/linux/ethtool.h
@@ -0,0 +1,18 @@
+#ifndef __BACKPORT_LINUX_ETHTOOL_H
+#define __BACKPORT_LINUX_ETHTOOL_H
+#include_next <linux/ethtool.h>
+#include <linux/version.h>
+
+#ifndef SPEED_UNKNOWN
+#define SPEED_UNKNOWN -1
+#endif /* SPEED_UNKNOWN */
+
+#ifndef DUPLEX_UNKNOWN
+#define DUPLEX_UNKNOWN 0xff
+#endif /* DUPLEX_UNKNOWN */
+
+#ifndef ETHTOOL_FWVERS_LEN
+#define ETHTOOL_FWVERS_LEN 32
+#endif
+
+#endif /* __BACKPORT_LINUX_ETHTOOL_H */
diff --git a/backport-include/linux/export.h b/backport-include/linux/export.h
new file mode 100644
index 0000000..6f6cb6a
--- /dev/null
+++ b/backport-include/linux/export.h
@@ -0,0 +1,19 @@
+#ifndef _COMPAT_LINUX_EXPORT_H
+#define _COMPAT_LINUX_EXPORT_H 1
+
+#include <linux/version.h>
+
+#if LINUX_VERSION_IS_GEQ(3,2,0)
+#include_next <linux/export.h>
+#else
+#ifndef pr_fmt
+#define backport_undef_pr_fmt
+#endif
+#include <linux/module.h>
+#ifdef backport_undef_pr_fmt
+#undef pr_fmt
+#undef backport_undef_pr_fmt
+#endif
+#endif /* LINUX_VERSION_IS_GEQ(3,2,0) */
+
+#endif /* _COMPAT_LINUX_EXPORT_H */
diff --git a/backport-include/linux/firmware.h b/backport-include/linux/firmware.h
new file mode 100644
index 0000000..186eec3
--- /dev/null
+++ b/backport-include/linux/firmware.h
@@ -0,0 +1,9 @@
+#ifndef __BACKPORT_LINUX_FIRMWARE_H
+#define __BACKPORT_LINUX_FIRMWARE_H
+#include_next <linux/firmware.h>
+
+#if LINUX_VERSION_IS_LESS(3,14,0)
+#define request_firmware_direct(fw, name, device) request_firmware(fw, name, device)
+#endif
+
+#endif /* __BACKPORT_LINUX_FIRMWARE_H */
diff --git a/backport-include/linux/freezer.h b/backport-include/linux/freezer.h
new file mode 100644
index 0000000..c6053f3
--- /dev/null
+++ b/backport-include/linux/freezer.h
@@ -0,0 +1,32 @@
+#ifndef __BACKPORT_FREEZER_H_INCLUDED
+#define __BACKPORT_FREEZER_H_INCLUDED
+#include_next <linux/freezer.h>
+
+#ifdef CONFIG_FREEZER
+#if LINUX_VERSION_IS_LESS(3,11,0)
+/*
+ * Like schedule_hrtimeout_range(), but should not block the freezer. Do not
+ * call this with locks held.
+ */
+#define freezable_schedule_hrtimeout_range LINUX_BACKPORT(freezable_schedule_hrtimeout_range)
+static inline int freezable_schedule_hrtimeout_range(ktime_t *expires,
+ unsigned long delta, const enum hrtimer_mode mode)
+{
+ int __retval;
+ freezer_do_not_count();
+ __retval = schedule_hrtimeout_range(expires, delta, mode);
+ freezer_count();
+ return __retval;
+}
+#endif /* LINUX_VERSION_IS_LESS(3,11,0) */
+
+#else /* !CONFIG_FREEZER */
+
+#ifndef freezable_schedule_hrtimeout_range
+#define freezable_schedule_hrtimeout_range(expires, delta, mode) \
+ schedule_hrtimeout_range(expires, delta, mode)
+#endif
+
+#endif /* !CONFIG_FREEZER */
+
+#endif /* __BACKPORT_FREEZER_H_INCLUDED */
diff --git a/backport-include/linux/fs.h b/backport-include/linux/fs.h
new file mode 100644
index 0000000..6e4d4a5
--- /dev/null
+++ b/backport-include/linux/fs.h
@@ -0,0 +1,52 @@
+#ifndef _COMPAT_LINUX_FS_H
+#define _COMPAT_LINUX_FS_H
+#include_next <linux/fs.h>
+#include <linux/version.h>
+/*
+ * some versions don't have this and thus don't
+ * include it from the original fs.h
+ */
+#include <linux/uidgid.h>
+
+#if LINUX_VERSION_IS_LESS(3,4,0)
+#define simple_open LINUX_BACKPORT(simple_open)
+extern int simple_open(struct inode *inode, struct file *file);
+#endif
+
+#if LINUX_VERSION_IS_LESS(3,9,0)
+/**
+ * backport of:
+ *
+ * commit 496ad9aa8ef448058e36ca7a787c61f2e63f0f54
+ * Author: Al Viro <viro@zeniv.linux.org.uk>
+ * Date: Wed Jan 23 17:07:38 2013 -0500
+ *
+ * new helper: file_inode(file)
+ */
+static inline struct inode *file_inode(struct file *f)
+{
+ return f->f_path.dentry->d_inode;
+}
+#endif
+
+#ifndef replace_fops
+/*
+ * This one is to be used *ONLY* from ->open() instances.
+ * fops must be non-NULL, pinned down *and* module dependencies
+ * should be sufficient to pin the caller down as well.
+ */
+#define replace_fops(f, fops) \
+ do { \
+ struct file *__file = (f); \
+ fops_put(__file->f_op); \
+ BUG_ON(!(__file->f_op = (fops))); \
+ } while(0)
+#endif /* replace_fops */
+
+#if (LINUX_VERSION_IS_LESS(4,5,0) && \
+ LINUX_VERSION_IS_GEQ(3,2,0))
+#define no_seek_end_llseek LINUX_BACKPORT(no_seek_end_llseek)
+extern loff_t no_seek_end_llseek(struct file *, loff_t, int);
+#endif /* < 4.5 && >= 3.2 */
+
+#endif /* _COMPAT_LINUX_FS_H */
diff --git a/backport-include/linux/ftrace_event.h b/backport-include/linux/ftrace_event.h
new file mode 100644
index 0000000..edea21e
--- /dev/null
+++ b/backport-include/linux/ftrace_event.h
@@ -0,0 +1,10 @@
+#ifndef __BACKPORT_LINUX_FTRACE_EVENT_H
+#define __BACKPORT_LINUX_FTRACE_EVENT_H
+#include_next <linux/ftrace_event.h>
+
+#if LINUX_VERSION_IS_LESS(4,0,0)
+const char *ftrace_print_array_seq(struct trace_seq *p,
+ const void *buf, int buf_len,
+ size_t el_size);
+#endif
+#endif /* __BACKPORT_LINUX_FTRACE_EVENT_H */
diff --git a/backport-include/linux/genetlink.h b/backport-include/linux/genetlink.h
new file mode 100644
index 0000000..afd1f67
--- /dev/null
+++ b/backport-include/linux/genetlink.h
@@ -0,0 +1,18 @@
+#ifndef __BACKPORT_LINUX_GENETLINK_H
+#define __BACKPORT_LINUX_GENETLINK_H
+#include_next <linux/genetlink.h>
+
+/* This backports:
+ *
+ * commit e9412c37082b5c932e83364aaed0c38c2ce33acb
+ * Author: Neil Horman <nhorman@tuxdriver.com>
+ * Date: Tue May 29 09:30:41 2012 +0000
+ *
+ * genetlink: Build a generic netlink family module alias
+ */
+#ifndef MODULE_ALIAS_GENL_FAMILY
+#define MODULE_ALIAS_GENL_FAMILY(family)\
+ MODULE_ALIAS_NET_PF_PROTO_NAME(PF_NETLINK, NETLINK_GENERIC, "-family-" family)
+#endif
+
+#endif /* __BACKPORT_LINUX_GENETLINK_H */
diff --git a/backport-include/linux/gfp.h b/backport-include/linux/gfp.h
new file mode 100644
index 0000000..b4db7bb
--- /dev/null
+++ b/backport-include/linux/gfp.h
@@ -0,0 +1,13 @@
+#ifndef __BACKPORT_LINUX_GFP_H
+#define __BACKPORT_LINUX_GFP_H
+#include_next <linux/gfp.h>
+
+#ifndef ___GFP_KSWAPD_RECLAIM
+#define ___GFP_KSWAPD_RECLAIM 0x0u
+#endif
+
+#ifndef __GFP_KSWAPD_RECLAIM
+#define __GFP_KSWAPD_RECLAIM ((__force gfp_t)___GFP_KSWAPD_RECLAIM) /* kswapd can wake */
+#endif
+
+#endif /* __BACKPORT_TIMKEEPING_H */
diff --git a/backport-include/linux/gpio.h b/backport-include/linux/gpio.h
new file mode 100644
index 0000000..ef4a8f1
--- /dev/null
+++ b/backport-include/linux/gpio.h
@@ -0,0 +1,35 @@
+#ifndef __BACKPORT_LINUX_GPIO_H
+#define __BACKPORT_LINUX_GPIO_H
+#include_next <linux/gpio.h>
+
+#if LINUX_VERSION_IS_LESS(3,5,0)
+#define devm_gpio_request_one LINUX_BACKPORT(devm_gpio_request_one)
+#define devm_gpio_request LINUX_BACKPORT(devm_gpio_request)
+#ifdef CONFIG_GPIOLIB
+int devm_gpio_request(struct device *dev, unsigned gpio, const char *label);
+int devm_gpio_request_one(struct device *dev, unsigned gpio,
+ unsigned long flags, const char *label);
+void devm_gpio_free(struct device *dev, unsigned int gpio);
+#else
+static inline int devm_gpio_request(struct device *dev, unsigned gpio,
+ const char *label)
+{
+ WARN_ON(1);
+ return -EINVAL;
+}
+
+static inline int devm_gpio_request_one(struct device *dev, unsigned gpio,
+ unsigned long flags, const char *label)
+{
+ WARN_ON(1);
+ return -EINVAL;
+}
+
+static inline void devm_gpio_free(struct device *dev, unsigned int gpio)
+{
+ WARN_ON(1);
+}
+#endif /* CONFIG_GPIOLIB */
+#endif
+
+#endif /* __BACKPORT_LINUX_GPIO_H */
diff --git a/backport-include/linux/gpio/driver.h b/backport-include/linux/gpio/driver.h
new file mode 100644
index 0000000..8df5c29
--- /dev/null
+++ b/backport-include/linux/gpio/driver.h
@@ -0,0 +1,10 @@
+#ifndef __BP_GPIO_DRIVER_H
+#define __BP_GPIO_DRIVER_H
+#include <linux/version.h>
+#if LINUX_VERSION_IS_LESS(3,13,0)
+#include <asm-generic/gpio.h>
+#else
+#include_next <linux/gpio/driver.h>
+#endif
+
+#endif /* __BP_GPIO_DRIVER_H */
diff --git a/backport-include/linux/hashtable.h b/backport-include/linux/hashtable.h
new file mode 100644
index 0000000..33314dc
--- /dev/null
+++ b/backport-include/linux/hashtable.h
@@ -0,0 +1,42 @@
+#ifndef __BACKPORT_HASHTABLE_H
+#define __BACKPORT_HASHTABLE_H
+#include_next <linux/hashtable.h>
+#include <linux/version.h>
+
+#if LINUX_VERSION_IS_LESS(3,9,0)
+/**
+ * backport:
+ *
+ * commit 0bbacca7c3911451cea923b0ad6389d58e3d9ce9
+ * Author: Sasha Levin <sasha.levin@oracle.com>
+ * Date: Thu Feb 7 12:32:18 2013 +1100
+ *
+ * hlist: drop the node parameter from iterators
+ */
+#include <linux/list.h>
+#include <backport/magic.h>
+
+#undef hash_for_each
+#define hash_for_each(name, bkt, obj, member) \
+ for ((bkt) = 0, obj = NULL; obj == NULL && (bkt) < HASH_SIZE(name);\
+ (bkt)++)\
+ hlist_for_each_entry(obj, &name[bkt], member)
+
+#undef hash_for_each_safe
+#define hash_for_each_safe(name, bkt, tmp, obj, member) \
+ for ((bkt) = 0, obj = NULL; obj == NULL && (bkt) < HASH_SIZE(name);\
+ (bkt)++)\
+ hlist_for_each_entry_safe(obj, tmp, &name[bkt], member)
+
+#undef hash_for_each_possible
+#define hash_for_each_possible(name, obj, member, key) \
+ hlist_for_each_entry(obj, &name[hash_min(key, HASH_BITS(name))], member)
+
+#undef hash_for_each_possible_safe
+#define hash_for_each_possible_safe(name, obj, tmp, member, key) \
+ hlist_for_each_entry_safe(obj, tmp,\
+ &name[hash_min(key, HASH_BITS(name))], member)
+
+#endif
+
+#endif /* __BACKPORT_HASHTABLE_H */
diff --git a/backport-include/linux/hid.h b/backport-include/linux/hid.h
new file mode 100644
index 0000000..887c6b1
--- /dev/null
+++ b/backport-include/linux/hid.h
@@ -0,0 +1,87 @@
+#ifndef __BACKPORT_HID_H
+#define __BACKPORT_HID_H
+#include_next <linux/hid.h>
+#include <linux/version.h>
+
+#if LINUX_VERSION_IS_LESS(3,8,0)
+#define hid_ignore LINUX_BACKPORT(hid_ignore)
+extern bool hid_ignore(struct hid_device *);
+#endif
+
+#if LINUX_VERSION_IS_LESS(3,1,0)
+#define HID_TYPE_USBNONE 2
+#endif
+
+#ifndef HID_QUIRK_NO_IGNORE
+#define HID_QUIRK_NO_IGNORE 0x40000000
+#endif
+
+#ifndef HID_QUIRK_HIDDEV_FORCE
+#define HID_QUIRK_HIDDEV_FORCE 0x00000010
+#endif
+
+#ifndef HID_QUIRK_IGNORE
+#define HID_QUIRK_IGNORE 0x00000004
+#endif
+
+#ifndef HID_USB_DEVICE
+#define HID_USB_DEVICE(ven, prod) \
+ .bus = BUS_USB, .vendor = (ven), .product = (prod)
+#endif
+
+#ifndef HID_BLUETOOTH_DEVICE
+#define HID_BLUETOOTH_DEVICE(ven, prod) \
+ .bus = BUS_BLUETOOTH, .vendor = (ven), .product = (prod)
+#endif
+
+#ifndef hid_printk
+#define hid_printk(level, hid, fmt, arg...) \
+ dev_printk(level, &(hid)->dev, fmt, ##arg)
+#endif
+
+#ifndef hid_emerg
+#define hid_emerg(hid, fmt, arg...) \
+ dev_emerg(&(hid)->dev, fmt, ##arg)
+#endif
+
+#ifndef hid_crit
+#define hid_crit(hid, fmt, arg...) \
+ dev_crit(&(hid)->dev, fmt, ##arg)
+#endif
+
+#ifndef hid_alert
+#define hid_alert(hid, fmt, arg...) \
+ dev_alert(&(hid)->dev, fmt, ##arg)
+#endif
+
+#ifndef hid_err
+#define hid_err(hid, fmt, arg...) \
+ dev_err(&(hid)->dev, fmt, ##arg)
+#endif
+
+#ifndef hid_notice
+#define hid_notice(hid, fmt, arg...) \
+ dev_notice(&(hid)->dev, fmt, ##arg)
+#endif
+
+#ifndef hid_warn
+#define hid_warn(hid, fmt, arg...) \
+ dev_warn(&(hid)->dev, fmt, ##arg)
+#endif
+
+#ifndef hid_info
+#define hid_info(hid, fmt, arg...) \
+ dev_info(&(hid)->dev, fmt, ##arg)
+#endif
+
+#ifndef hid_dbg
+#define hid_dbg(hid, fmt, arg...) \
+ dev_dbg(&(hid)->dev, fmt, ##arg)
+#endif
+
+#if LINUX_VERSION_IS_LESS(3,12,0)
+#define hid_alloc_report_buf LINUX_BACKPORT(hid_alloc_report_buf)
+u8 *hid_alloc_report_buf(struct hid_report *report, gfp_t flags);
+#endif
+
+#endif /* __BACKPORT_HID_H */
diff --git a/backport-include/linux/hrtimer.h b/backport-include/linux/hrtimer.h
new file mode 100644
index 0000000..bdcf106
--- /dev/null
+++ b/backport-include/linux/hrtimer.h
@@ -0,0 +1,17 @@
+#ifndef _BP_HRTIMER_H
+#define _BP_HRTIMER_H
+#include <linux/version.h>
+#include_next <linux/hrtimer.h>
+#include <linux/interrupt.h>
+
+#if LINUX_VERSION_IS_LESS(4,10,0)
+static inline void backport_hrtimer_start(struct hrtimer *timer, s64 time,
+ const enum hrtimer_mode mode)
+{
+ ktime_t _time = { .tv64 = time };
+ hrtimer_start(timer, _time, mode);
+}
+#define hrtimer_start LINUX_BACKPORT(hrtimer_start)
+#endif
+
+#endif /* _BP_HRTIMER_H */
diff --git a/backport-include/linux/hwmon.h b/backport-include/linux/hwmon.h
new file mode 100644
index 0000000..3b61b26
--- /dev/null
+++ b/backport-include/linux/hwmon.h
@@ -0,0 +1,34 @@
+#ifndef __BACKPORT_LINUX_HWMON_H
+#define __BACKPORT_LINUX_HWMON_H
+#include_next <linux/hwmon.h>
+#include <linux/version.h>
+
+#if LINUX_VERSION_IS_LESS(3,13,0)
+/*
+ * Backports
+ *
+ * commit bab2243ce1897865e31ea6d59b0478391f51812b
+ * Author: Guenter Roeck <linux@roeck-us.net>
+ * Date: Sat Jul 6 13:57:23 2013 -0700
+ *
+ * hwmon: Introduce hwmon_device_register_with_groups
+ *
+ * hwmon_device_register_with_groups() lets callers register a hwmon device
+ * together with all sysfs attributes in a single call.
+ *
+ * When using hwmon_device_register_with_groups(), hwmon attributes are attached
+ * to the hwmon device directly and no longer with its parent device.
+ *
+ * Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+ */
+struct device *
+hwmon_device_register_with_groups(struct device *dev, const char *name,
+ void *drvdata,
+ const struct attribute_group **groups);
+struct device *
+devm_hwmon_device_register_with_groups(struct device *dev, const char *name,
+ void *drvdata,
+ const struct attribute_group **groups);
+#endif /* LINUX_VERSION_IS_LESS(3,13,0) */
+
+#endif /* __BACKPORT_LINUX_HWMON_H */
diff --git a/backport-include/linux/i2c-mux.h b/backport-include/linux/i2c-mux.h
new file mode 100644
index 0000000..d12658d
--- /dev/null
+++ b/backport-include/linux/i2c-mux.h
@@ -0,0 +1,14 @@
+#ifndef __BACKPORT_LINUX_I2C_MUX_H
+#define __BACKPORT_LINUX_I2C_MUX_H
+#include_next <linux/i2c-mux.h>
+#include <linux/version.h>
+
+#if LINUX_VERSION_IS_LESS(3,5,0)
+#define i2c_add_mux_adapter(parent, mux_dev, mux_priv, force_nr, chan_id, class, select, deselect) \
+ i2c_add_mux_adapter(parent, mux_priv, force_nr, chan_id, select, deselect)
+#elif LINUX_VERSION_IS_LESS(3,7,0)
+#define i2c_add_mux_adapter(parent, mux_dev, mux_priv, force_nr, chan_id, class, select, deselect) \
+ i2c_add_mux_adapter(parent, mux_dev, mux_priv, force_nr, chan_id, select, deselect)
+#endif
+
+#endif /* __BACKPORT_LINUX_I2C_MUX_H */
diff --git a/backport-include/linux/i2c.h b/backport-include/linux/i2c.h
new file mode 100644
index 0000000..c8f7a8d
--- /dev/null
+++ b/backport-include/linux/i2c.h
@@ -0,0 +1,37 @@
+#ifndef __BACKPORT_LINUX_I2C_H
+#define __BACKPORT_LINUX_I2C_H
+#include_next <linux/i2c.h>
+#include <linux/version.h>
+
+/* This backports
+ *
+ * commit 14674e70119ea01549ce593d8901a797f8a90f74
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ * Date: Wed May 30 10:55:34 2012 +0200
+ *
+ * i2c: Split I2C_M_NOSTART support out of I2C_FUNC_PROTOCOL_MANGLING
+ */
+#ifndef I2C_FUNC_NOSTART
+#define I2C_FUNC_NOSTART 0x00000010 /* I2C_M_NOSTART */
+#endif
+
+/* This backports:
+ *
+ * commit 7c92784a546d2945b6d6973a30f7134be78eb7a4
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ * Date: Wed Nov 16 10:13:36 2011 +0100
+ *
+ * I2C: Add helper macro for i2c_driver boilerplate
+ */
+#ifndef module_i2c_driver
+#define module_i2c_driver(__i2c_driver) \
+ module_driver(__i2c_driver, i2c_add_driver, \
+ i2c_del_driver)
+#endif
+
+#ifndef I2C_CLIENT_SCCB
+#define I2C_CLIENT_SCCB 0x9000 /* Use Omnivision SCCB protocol */
+ /* Must match I2C_M_STOP|IGNORE_NAK */
+#endif
+
+#endif /* __BACKPORT_LINUX_I2C_H */
diff --git a/backport-include/linux/idr.h b/backport-include/linux/idr.h
new file mode 100644
index 0000000..01aa2c2
--- /dev/null
+++ b/backport-include/linux/idr.h
@@ -0,0 +1,74 @@
+#ifndef __BACKPORT_IDR_H
+#define __BACKPORT_IDR_H
+/* some versions have a broken idr header */
+#include <linux/spinlock.h>
+#include_next <linux/idr.h>
+#include <linux/version.h>
+
+#if LINUX_VERSION_IS_LESS(3,1,0)
+#define ida_simple_get LINUX_BACKPORT(ida_simple_get)
+int ida_simple_get(struct ida *ida, unsigned int start, unsigned int end,
+ gfp_t gfp_mask);
+
+#define ida_simple_remove LINUX_BACKPORT(ida_simple_remove)
+void ida_simple_remove(struct ida *ida, unsigned int id);
+#endif
+
+#if LINUX_VERSION_IS_LESS(3,9,0)
+#include <linux/errno.h>
+/**
+ * backport of idr idr_alloc() usage
+ *
+ * This backports a patch series send by Tejun Heo:
+ * https://lkml.org/lkml/2013/2/2/159
+ */
+static inline void compat_idr_destroy(struct idr *idp)
+{
+ idr_remove_all(idp);
+ idr_destroy(idp);
+}
+#define idr_destroy(idp) compat_idr_destroy(idp)
+
+static inline int idr_alloc(struct idr *idr, void *ptr, int start, int end,
+ gfp_t gfp_mask)
+{
+ int id, ret;
+
+ do {
+ if (!idr_pre_get(idr, gfp_mask))
+ return -ENOMEM;
+ ret = idr_get_new_above(idr, ptr, start, &id);
+ if (!ret && id > end) {
+ idr_remove(idr, id);
+ ret = -ENOSPC;
+ }
+ } while (ret == -EAGAIN);
+
+ return ret ? ret : id;
+}
+
+static inline void idr_preload(gfp_t gfp_mask)
+{
+}
+
+static inline void idr_preload_end(void)
+{
+}
+#endif
+
+#ifndef idr_for_each_entry
+#define idr_for_each_entry(idp, entry, id) \
+ for (id = 0; ((entry) = idr_get_next(idp, &(id))) != NULL; ++id)
+#endif
+
+#if LINUX_VERSION_IS_LESS(4, 11, 0)
+static inline void *backport_idr_remove(struct idr *idr, int id)
+{
+ void *item = idr_find(idr, id);
+ idr_remove(idr, id);
+ return item;
+}
+#define idr_remove backport_idr_remove
+#endif
+
+#endif /* __BACKPORT_IDR_H */
diff --git a/backport-include/linux/if_arp.h b/backport-include/linux/if_arp.h
new file mode 100644
index 0000000..b542423
--- /dev/null
+++ b/backport-include/linux/if_arp.h
@@ -0,0 +1,14 @@
+#ifndef _BACKPORTS_LINUX_AF_ARP_H
+#define _BACKPORTS_LINUX_AF_ARP_H 1
+
+#include_next <linux/if_arp.h>
+
+#ifndef ARPHRD_IEEE802154_MONITOR
+#define ARPHRD_IEEE802154_MONITOR 805 /* IEEE 802.15.4 network monitor */
+#endif
+
+#ifndef ARPHRD_6LOWPAN
+#define ARPHRD_6LOWPAN 825 /* IPv6 over LoWPAN */
+#endif
+
+#endif /* _BACKPORTS_LINUX_AF_ARP_H */
diff --git a/backport-include/linux/if_ether.h b/backport-include/linux/if_ether.h
new file mode 100644
index 0000000..55862ce
--- /dev/null
+++ b/backport-include/linux/if_ether.h
@@ -0,0 +1,50 @@
+#ifndef __BACKPORT_IF_ETHER_H
+#define __BACKPORT_IF_ETHER_H
+#include_next <linux/if_ether.h>
+
+/* See commit b62faf3c in next-20140311 */
+#ifndef ETH_P_80221
+#define ETH_P_80221 0x8917 /* IEEE 802.21 Media Independent Handover Protocol */
+#endif
+
+/*
+ * backport of:
+ * commit e5c5d22e8dcf7c2d430336cbf8e180bd38e8daf1
+ * Author: Simon Horman <horms@verge.net.au>
+ * Date: Thu Mar 28 13:38:25 2013 +0900
+ *
+ * net: add ETH_P_802_3_MIN
+ */
+#ifndef ETH_P_802_3_MIN
+#define ETH_P_802_3_MIN 0x0600
+#endif
+
+#ifndef ETH_P_TDLS
+#define ETH_P_TDLS 0x890D /* TDLS */
+#endif
+
+#ifndef ETH_P_LINK_CTL
+#define ETH_P_LINK_CTL 0x886c
+#endif
+
+#ifndef ETH_P_PAE
+#define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */
+#endif
+
+#ifndef ETH_P_TEB
+#define ETH_P_TEB 0x6558 /* Trans Ether Bridging */
+#endif
+
+#ifndef ETH_P_8021AD
+#define ETH_P_8021AD 0x88A8 /* 802.1ad Service VLAN */
+#endif
+
+#ifndef ETH_MIN_MTU
+#define ETH_MIN_MTU 68
+#endif
+
+#ifndef ETH_MAX_MTU
+#define ETH_MAX_MTU 0xFFFFU
+#endif
+
+#endif /* __BACKPORT_IF_ETHER_H */
diff --git a/backport-include/linux/if_vlan.h b/backport-include/linux/if_vlan.h
new file mode 100644
index 0000000..18f63e8
--- /dev/null
+++ b/backport-include/linux/if_vlan.h
@@ -0,0 +1,39 @@
+#ifndef __BACKPORT_LINUX_IF_VLAN_H_
+#define __BACKPORT_LINUX_IF_VLAN_H_
+#include_next <linux/if_vlan.h>
+
+#if LINUX_VERSION_IS_LESS(3,10,0)
+#define vlan_insert_tag(__skb, __vlan_proto, __vlan_tci) vlan_insert_tag(__skb, __vlan_tci)
+#define __vlan_put_tag(__skb, __vlan_proto, __vlan_tci) __vlan_put_tag(__skb, __vlan_tci)
+#define vlan_put_tag(__skb, __vlan_proto, __vlan_tci) vlan_put_tag(__skb, __vlan_tci)
+#define __vlan_hwaccel_put_tag(__skb, __vlan_proto, __vlan_tag) __vlan_hwaccel_put_tag(__skb, __vlan_tag)
+
+#define __vlan_find_dev_deep(__real_dev, __vlan_proto, __vlan_id) __vlan_find_dev_deep(__real_dev, __vlan_id)
+
+#endif
+
+#ifndef VLAN_PRIO_MASK
+#define VLAN_PRIO_MASK 0xe000 /* Priority Code Point */
+#endif
+
+#ifndef VLAN_PRIO_SHIFT
+#define VLAN_PRIO_SHIFT 13
+#endif
+
+#if LINUX_VERSION_IS_LESS(3,16,0)
+#define __vlan_find_dev_deep_rcu(real_dev, vlan_proto, vlan_id) __vlan_find_dev_deep(real_dev, vlan_proto, vlan_id)
+#endif
+
+#ifndef skb_vlan_tag_present
+#define skb_vlan_tag_present(__skb) ((__skb)->vlan_tci & VLAN_TAG_PRESENT)
+#endif
+
+#ifndef skb_vlan_tag_get
+#define skb_vlan_tag_get(__skb) ((__skb)->vlan_tci & ~VLAN_TAG_PRESENT)
+#endif
+
+#ifndef skb_vlan_tag_get_id
+#define skb_vlan_tag_get_id(__skb) ((__skb)->vlan_tci & VLAN_VID_MASK)
+#endif
+
+#endif /* __BACKPORT_LINUX_IF_VLAN_H_ */
diff --git a/backport-include/linux/init.h b/backport-include/linux/init.h
new file mode 100644
index 0000000..3a12282
--- /dev/null
+++ b/backport-include/linux/init.h
@@ -0,0 +1,19 @@
+#ifndef __BACKPORT_INIT_H
+#define __BACKPORT_INIT_H
+#include_next <linux/init.h>
+
+/*
+ * Backports 312b1485fb509c9bc32eda28ad29537896658cb8
+ * Author: Sam Ravnborg <sam@ravnborg.org>
+ * Date: Mon Jan 28 20:21:15 2008 +0100
+ *
+ * Introduce new section reference annotations tags: __ref, __refdata, __refconst
+ */
+#ifndef __ref
+#define __ref __init_refok
+#endif
+#ifndef __refdata
+#define __refdata __initdata_refok
+#endif
+
+#endif /* __BACKPORT_INIT_H */
diff --git a/backport-include/linux/input.h b/backport-include/linux/input.h
new file mode 100644
index 0000000..3442db0
--- /dev/null
+++ b/backport-include/linux/input.h
@@ -0,0 +1,21 @@
+#ifndef __BACKPORT_INPUT_H
+#define __BACKPORT_INPUT_H
+#include_next <linux/input.h>
+
+#ifndef KEY_WIMAX
+#define KEY_WIMAX 246
+#endif
+
+#ifndef KEY_WPS_BUTTON
+#define KEY_WPS_BUTTON 0x211
+#endif
+
+#ifndef KEY_RFKILL
+#define KEY_RFKILL 247
+#endif
+
+#ifndef SW_RFKILL_ALL
+#define SW_RFKILL_ALL 0x03
+#endif
+
+#endif /* __BACKPORT_INPUT_H */
diff --git a/backport-include/linux/interrupt.h b/backport-include/linux/interrupt.h
new file mode 100644
index 0000000..0e243ba
--- /dev/null
+++ b/backport-include/linux/interrupt.h
@@ -0,0 +1,2 @@
+#include <linux/hrtimer.h>
+#include_next <linux/interrupt.h>
diff --git a/backport-include/linux/ioport.h b/backport-include/linux/ioport.h
new file mode 100644
index 0000000..3424401
--- /dev/null
+++ b/backport-include/linux/ioport.h
@@ -0,0 +1,9 @@
+#ifndef __BACKPORT_LINUX_IOPORT_H
+#define __BACKPORT_LINUX_IOPORT_H
+#include_next <linux/ioport.h>
+
+#ifndef IORESOURCE_REG
+#define IORESOURCE_REG 0x00000300
+#endif
+
+#endif /* __BACKPORT_LINUX_IOPORT_H */
diff --git a/backport-include/linux/irq.h b/backport-include/linux/irq.h
new file mode 100644
index 0000000..e216f4f
--- /dev/null
+++ b/backport-include/linux/irq.h
@@ -0,0 +1,16 @@
+#ifndef __BACKPORT_LINUX_IRQ_H
+#define __BACKPORT_LINUX_IRQ_H
+#include_next <linux/irq.h>
+
+#ifdef CONFIG_HAVE_GENERIC_HARDIRQS
+#if LINUX_VERSION_IS_LESS(3,11,0)
+#define irq_get_trigger_type LINUX_BACKPORT(irq_get_trigger_type)
+static inline u32 irq_get_trigger_type(unsigned int irq)
+{
+ struct irq_data *d = irq_get_irq_data(irq);
+ return d ? irqd_get_trigger_type(d) : 0;
+}
+#endif
+#endif /* CONFIG_HAVE_GENERIC_HARDIRQS */
+
+#endif /* __BACKPORT_LINUX_IRQ_H */
diff --git a/backport-include/linux/irqdomain.h b/backport-include/linux/irqdomain.h
new file mode 100644
index 0000000..1878a29
--- /dev/null
+++ b/backport-include/linux/irqdomain.h
@@ -0,0 +1,9 @@
+#ifndef __BACKPORT_LINUX_IRQDOMAIN_H
+#define __BACKPORT_LINUX_IRQDOMAIN_H
+#include <linux/version.h>
+
+#if LINUX_VERSION_IS_GEQ(3,1,0)
+#include_next <linux/irqdomain.h>
+#endif
+
+#endif /* __BACKPORT_LINUX_IRQDOMAIN_H */
diff --git a/backport-include/linux/jiffies.h b/backport-include/linux/jiffies.h
new file mode 100644
index 0000000..bbadcc8
--- /dev/null
+++ b/backport-include/linux/jiffies.h
@@ -0,0 +1,30 @@
+#ifndef __BACKPORT_LNIUX_JIFFIES_H
+#define __BACKPORT_LNIUX_JIFFIES_H
+#include_next <linux/jiffies.h>
+
+#ifndef time_is_before_jiffies
+#define time_is_before_jiffies(a) time_after(jiffies, a)
+#endif
+
+#ifndef time_is_after_jiffies
+#define time_is_after_jiffies(a) time_before(jiffies, a)
+#endif
+
+#ifndef time_is_before_eq_jiffies
+#define time_is_before_eq_jiffies(a) time_after_eq(jiffies, a)
+#endif
+
+#ifndef time_is_after_eq_jiffies
+#define time_is_after_eq_jiffies(a) time_before_eq(jiffies, a)
+#endif
+
+/*
+ * This function is available, but not exported in kernel < 3.17, add
+ * an own version.
+ */
+#if LINUX_VERSION_IS_LESS(3,17,0)
+#define nsecs_to_jiffies LINUX_BACKPORT(nsecs_to_jiffies)
+extern unsigned long nsecs_to_jiffies(u64 n);
+#endif /* 3.17 */
+
+#endif /* __BACKPORT_LNIUX_JIFFIES_H */
diff --git a/backport-include/linux/kconfig.h b/backport-include/linux/kconfig.h
new file mode 100644
index 0000000..d1faad9
--- /dev/null
+++ b/backport-include/linux/kconfig.h
@@ -0,0 +1,45 @@
+#ifndef __BACKPORT_LINUX_KCONFIG_H
+#define __BACKPORT_LINUX_KCONFIG_H
+#include <linux/version.h>
+#if LINUX_VERSION_IS_GEQ(3,1,0)
+#include_next <linux/kconfig.h>
+#endif
+
+#ifndef __ARG_PLACEHOLDER_1
+#define __ARG_PLACEHOLDER_1 0,
+#define config_enabled(cfg) _config_enabled(cfg)
+#define _config_enabled(value) __config_enabled(__ARG_PLACEHOLDER_##value)
+#define __config_enabled(arg1_or_junk) ___config_enabled(arg1_or_junk 1, 0)
+#define ___config_enabled(__ignored, val, ...) val
+
+/*
+ * 3.1 - 3.3 had a broken version of this, so undef
+ * (they didn't have __ARG_PLACEHOLDER_1)
+ */
+#undef IS_ENABLED
+#define IS_ENABLED(option) \
+ (config_enabled(option) || config_enabled(option##_MODULE))
+#endif
+
+/*
+ * Since 4.9 config_enabled has been removed in favor of __is_defined.
+ */
+#ifndef config_enabled
+#define config_enabled(cfg) __is_defined(cfg)
+#endif
+
+#undef IS_BUILTIN
+#define IS_BUILTIN(option) config_enabled(option)
+
+#ifndef IS_REACHABLE
+/*
+ * IS_REACHABLE(CONFIG_FOO) evaluates to 1 if the currently compiled
+ * code can call a function defined in code compiled based on CONFIG_FOO.
+ * This is similar to IS_ENABLED(), but returns false when invoked from
+ * built-in code when CONFIG_FOO is set to 'm'.
+ */
+#define IS_REACHABLE(option) (config_enabled(option) || \
+ (config_enabled(option##_MODULE) && config_enabled(MODULE)))
+#endif
+
+#endif
diff --git a/backport-include/linux/kernel.h b/backport-include/linux/kernel.h
new file mode 100644
index 0000000..ea55d7d
--- /dev/null
+++ b/backport-include/linux/kernel.h
@@ -0,0 +1,225 @@
+#ifndef __BACKPORT_KERNEL_H
+#define __BACKPORT_KERNEL_H
+#include_next <linux/kernel.h>
+#include <linux/version.h>
+/*
+ * some older kernels don't have this and thus don't
+ * include it from kernel.h like new kernels
+ */
+#include <linux/printk.h>
+
+/*
+ * This backports:
+ *
+ * From a3860c1c5dd1137db23d7786d284939c5761d517 Mon Sep 17 00:00:00 2001
+ * From: Xi Wang <xi.wang@gmail.com>
+ * Date: Thu, 31 May 2012 16:26:04 -0700
+ * Subject: [PATCH] introduce SIZE_MAX
+ */
+#ifndef SIZE_MAX
+#define SIZE_MAX (~(size_t)0)
+#endif
+
+/* This backports:
+ *
+ * commit 36a26c69b4c70396ef569c3452690fba0c1dec08
+ * Author: Nicholas Bellinger <nab@linux-iscsi.org>
+ * Date: Tue Jul 26 00:35:26 2011 -0700
+ *
+ * kernel.h: Add DIV_ROUND_UP_ULL and DIV_ROUND_UP_SECTOR_T macro usage
+ */
+#ifndef DIV_ROUND_UP_ULL
+#define DIV_ROUND_UP_ULL(ll,d) \
+ ({ unsigned long long _tmp = (ll)+(d)-1; do_div(_tmp, d); _tmp; })
+#endif
+
+#ifndef USHRT_MAX
+#define USHRT_MAX ((u16)(~0U))
+#endif
+
+#ifndef SHRT_MAX
+#define SHRT_MAX ((s16)(USHRT_MAX>>1))
+#endif
+
+#ifndef SHRT_MIN
+#define SHRT_MIN ((s16)(-SHRT_MAX - 1))
+#endif
+
+#ifndef U8_MAX
+#define U8_MAX ((u8)~0U)
+#endif
+
+#ifndef S8_MAX
+#define S8_MAX ((s8)(U8_MAX>>1))
+#endif
+
+#ifndef S8_MIN
+#define S8_MIN ((s8)(-S8_MAX - 1))
+#endif
+
+#ifndef U16_MAX
+#define U16_MAX ((u16)~0U)
+#endif
+
+#ifndef S16_MAX
+#define S16_MAX ((s16)(U16_MAX>>1))
+#endif
+
+#ifndef S16_MIN
+#define S16_MIN ((s16)(-S16_MAX - 1))
+#endif
+
+#ifndef U32_MAX
+#define U32_MAX ((u32)~0U)
+#endif
+
+#ifndef S32_MAX
+#define S32_MAX ((s32)(U32_MAX>>1))
+#endif
+
+#ifndef S32_MIN
+#define S32_MIN ((s32)(-S32_MAX - 1))
+#endif
+
+#ifndef __round_mask
+#define __round_mask(x, y) ((__typeof__(x))((y)-1))
+#define round_up(x, y) ((((x)-1) | __round_mask(x, y))+1)
+#define round_down(x, y) ((x) & ~__round_mask(x, y))
+#endif
+
+#ifndef DIV_ROUND_CLOSEST
+#define DIV_ROUND_CLOSEST(x, divisor)( \
+{ \
+ typeof(x) __x = x; \
+ typeof(divisor) __d = divisor; \
+ (((typeof(x))-1) > 0 || \
+ ((typeof(divisor))-1) > 0 || (__x) > 0) ? \
+ (((__x) + ((__d) / 2)) / (__d)) : \
+ (((__x) - ((__d) / 2)) / (__d)); \
+} \
+)
+#endif
+
+#ifndef DIV_ROUND_CLOSEST_ULL
+#define DIV_ROUND_CLOSEST_ULL(x, divisor)( \
+{ \
+ typeof(divisor) __d = divisor; \
+ unsigned long long _tmp = (x) + (__d) / 2; \
+ do_div(_tmp, __d); \
+ _tmp; \
+} \
+)
+#endif
+
+#ifndef swap
+#define swap(a, b) \
+ do { typeof(a) __tmp = (a); (a) = (b); (b) = __tmp; } while (0)
+#endif
+
+#ifndef lower_32_bits
+#define lower_32_bits(n) ((u32)(n))
+#endif
+
+#ifndef clamp
+#define clamp(val, min, max) ({ \
+ typeof(val) __val = (val); \
+ typeof(min) __min = (min); \
+ typeof(max) __max = (max); \
+ (void) (&__val == &__min); \
+ (void) (&__val == &__max); \
+ __val = __val < __min ? __min: __val; \
+ __val > __max ? __max: __val; })
+#endif
+
+#ifndef clamp_t
+#define clamp_t(type, val, min, max) ({ \
+ type __val = (val); \
+ type __min = (min); \
+ type __max = (max); \
+ __val = __val < __min ? __min: __val; \
+ __val > __max ? __max: __val; })
+#endif
+
+#ifndef clamp_val
+#define clamp_val(val, min, max) ({ \
+ typeof(val) __val = (val); \
+ typeof(val) __min = (min); \
+ typeof(val) __max = (max); \
+ __val = __val < __min ? __min: __val; \
+ __val > __max ? __max: __val; })
+#endif
+
+#ifndef rounddown
+#define rounddown(x, y) ( \
+{ \
+ typeof(x) __x = (x); \
+ __x - (__x % (y)); \
+} \
+)
+#endif /* rounddown */
+
+#if LINUX_VERSION_IS_LESS(3,2,0)
+/* kernels before 3.2 didn't have error checking for the function */
+#define hex2bin LINUX_BACKPORT(hex2bin)
+int __must_check hex2bin(u8 *dst, const char *src, size_t count);
+#endif /* < 3.2 */
+
+#if LINUX_VERSION_IS_LESS(3,18,0)
+#undef clamp
+#define clamp(val, lo, hi) min((typeof(val))max(val, lo), hi)
+#endif /* < 3.18 */
+
+#if LINUX_VERSION_IS_LESS(4,6,0)
+#define kstrtobool LINUX_BACKPORT(kstrtobool)
+int __must_check kstrtobool(const char *s, bool *res);
+#define kstrtobool_from_user LINUX_BACKPORT(kstrtobool_from_user)
+int __must_check kstrtobool_from_user(const char __user *s, size_t count, bool *res);
+#endif
+
+#if LINUX_VERSION_IS_LESS(4,5,0)
+
+#undef abs
+/**
+ * abs - return absolute value of an argument
+ * @x: the value. If it is unsigned type, it is converted to signed type first.
+ * char is treated as if it was signed (regardless of whether it really is)
+ * but the macro's return type is preserved as char.
+ *
+ * Return: an absolute value of x.
+ */
+#define abs(x) __abs_choose_expr(x, long long, \
+ __abs_choose_expr(x, long, \
+ __abs_choose_expr(x, int, \
+ __abs_choose_expr(x, short, \
+ __abs_choose_expr(x, char, \
+ __builtin_choose_expr( \
+ __builtin_types_compatible_p(typeof(x), char), \
+ (char)({ signed char __x = (x); __x<0?-__x:__x; }), \
+ ((void)0)))))))
+
+#define __abs_choose_expr(x, type, other) __builtin_choose_expr( \
+ __builtin_types_compatible_p(typeof(x), signed type) || \
+ __builtin_types_compatible_p(typeof(x), unsigned type), \
+ ({ signed type __x = (x); __x < 0 ? -__x : __x; }), other)
+
+#endif
+
+#if LINUX_VERSION_IS_LESS(3,14,0)
+static inline u32 reciprocal_scale(u32 val, u32 ep_ro)
+{
+ return (u32)(((u64) val * ep_ro) >> 32);
+}
+#endif /* LINUX_VERSION_IS_LESS(3,14,0) */
+
+#endif /* __BACKPORT_KERNEL_H */
+
+/*
+ * We have to do this outside the include guard, because
+ * out own header (linux/export.h) has to include kernel.h
+ * indirectly (through module.h) and then undef's pr_fmt.
+ * Then, when the real kernel.h gets included again, it's
+ * not defined and we get problems ...
+ */
+#ifndef pr_fmt
+#define pr_fmt(msg) msg
+#endif
diff --git a/backport-include/linux/kfifo.h b/backport-include/linux/kfifo.h
new file mode 100644
index 0000000..b4fda3c
--- /dev/null
+++ b/backport-include/linux/kfifo.h
@@ -0,0 +1,51 @@
+#ifndef BACKPORT_LINUX_KFIFO_H
+#define BACKPORT_LINUX_KFIFO_H
+
+#include <linux/version.h>
+#include_next <linux/kfifo.h>
+
+#if LINUX_VERSION_IS_LESS(3,13,0)
+#undef kfifo_put
+/**
+ * kfifo_put - put data into the fifo
+ * @fifo: address of the fifo to be used
+ * @val: the data to be added
+ *
+ * This macro copies the given value into the fifo.
+ * It returns 0 if the fifo was full. Otherwise it returns the number
+ * processed elements.
+ *
+ * Note that with only one concurrent reader and one concurrent
+ * writer, you don't need extra locking to use these macro.
+ */
+#define kfifo_put(fifo, val) \
+({ \
+ typeof((fifo) + 1) __tmp = (fifo); \
+ typeof((&val) + 1) __val = (&val); \
+ unsigned int __ret; \
+ const size_t __recsize = sizeof(*__tmp->rectype); \
+ struct __kfifo *__kfifo = &__tmp->kfifo; \
+ if (0) { \
+ typeof(__tmp->ptr_const) __dummy __attribute__ ((unused)); \
+ __dummy = (typeof(__val))NULL; \
+ } \
+ if (__recsize) \
+ __ret = __kfifo_in_r(__kfifo, __val, sizeof(*__val), \
+ __recsize); \
+ else { \
+ __ret = !kfifo_is_full(__tmp); \
+ if (__ret) { \
+ (__is_kfifo_ptr(__tmp) ? \
+ ((typeof(__tmp->type))__kfifo->data) : \
+ (__tmp->buf) \
+ )[__kfifo->in & __tmp->kfifo.mask] = \
+ *(typeof(__tmp->type))__val; \
+ smp_wmb(); \
+ __kfifo->in++; \
+ } \
+ } \
+ __ret; \
+})
+#endif
+
+#endif /* BACKPORT_LINUX_KFIFO_H */
diff --git a/backport-include/linux/ktime.h b/backport-include/linux/ktime.h
new file mode 100644
index 0000000..9427296
--- /dev/null
+++ b/backport-include/linux/ktime.h
@@ -0,0 +1,18 @@
+#ifndef __BACKPORT_LINUX_KTIME_H
+#define __BACKPORT_LINUX_KTIME_H
+#include_next <linux/ktime.h>
+#include <linux/timekeeping.h>
+#include <linux/version.h>
+
+#if LINUX_VERSION_IS_LESS(3,17,0)
+#define ktime_get_raw LINUX_BACKPORT(ktime_get_raw)
+extern ktime_t ktime_get_raw(void);
+
+#endif /* < 3.17 */
+
+#ifndef ktime_to_timespec64
+/* Map the ktime_t to timespec conversion to ns_to_timespec function */
+#define ktime_to_timespec64(kt) ns_to_timespec64((kt).tv64)
+#endif
+
+#endif /* __BACKPORT_LINUX_KTIME_H */
diff --git a/backport-include/linux/leds.h b/backport-include/linux/leds.h
new file mode 100644
index 0000000..bc077e4
--- /dev/null
+++ b/backport-include/linux/leds.h
@@ -0,0 +1,73 @@
+#ifndef __BACKPORT_LINUX_LEDS_H
+#define __BACKPORT_LINUX_LEDS_H
+#include_next <linux/leds.h>
+#include <linux/version.h>
+
+#include <backport/leds-disabled.h>
+
+#ifndef CPTCFG_BPAUTO_BUILD_LEDS
+#if LINUX_VERSION_IS_LESS(3,6,0)
+/*
+ * Backports
+ *
+ * commit 959d62fa865d2e616b61a509e1cc5b88741f065e
+ * Author: Shuah Khan <shuahkhan@gmail.com>
+ * Date: Thu Jun 14 04:34:30 2012 +0800
+ *
+ * leds: Rename led_brightness_set() to led_set_brightness()
+ *
+ * Rename leds external interface led_brightness_set() to led_set_brightness().
+ * This is the second phase of the change to reduce confusion between the
+ * leds internal and external interfaces that set brightness. With this change,
+ * now the external interface is led_set_brightness(). The first phase renamed
+ * the internal interface led_set_brightness() to __led_set_brightness().
+ * There are no changes to the interface implementations.
+ *
+ * Signed-off-by: Shuah Khan <shuahkhan@gmail.com>
+ * Signed-off-by: Bryan Wu <bryan.wu@canonical.com>
+ */
+#define led_set_brightness(_dev, _switch) led_brightness_set(_dev, _switch)
+#endif /* LINUX_VERSION_IS_LESS(3,6,0) */
+#endif /* CPTCFG_BPAUTO_BUILD_LEDS */
+
+#if LINUX_VERSION_IS_LESS(4,2,0)
+/*
+ * There is no LINUX_BACKPORT() guard here because we want it to point to
+ * the original function which is exported normally.
+ */
+#ifdef CONFIG_LEDS_TRIGGERS
+extern void led_trigger_remove(struct led_classdev *led_cdev);
+extern int led_trigger_register(struct led_trigger *trigger);
+extern void led_trigger_unregister(struct led_trigger *trigger);
+#else
+static inline void led_trigger_remove(struct led_classdev *led_cdev) {}
+static inline int led_trigger_register(struct led_trigger *trigger) {return 0;}
+static inline void led_trigger_unregister(struct led_trigger *trigger) {}
+#endif
+#endif
+
+#if LINUX_VERSION_IS_LESS(4,5,0) && \
+ LINUX_VERSION_IS_GEQ(3,19,0)
+#define led_set_brightness_sync LINUX_BACKPORT(led_set_brightness_sync)
+/**
+ * led_set_brightness_sync - set LED brightness synchronously
+ * @led_cdev: the LED to set
+ * @brightness: the brightness to set it to
+ *
+ * Set an LED's brightness immediately. This function will block
+ * the caller for the time required for accessing device registers,
+ * and it can sleep.
+ *
+ * Returns: 0 on success or negative error value on failure
+ */
+extern int led_set_brightness_sync(struct led_classdev *led_cdev,
+ enum led_brightness value);
+#endif /* < 4.5 && >= 3.19 */
+
+#if LINUX_VERSION_IS_LESS(4,5,0)
+#define devm_led_trigger_register LINUX_BACKPORT(devm_led_trigger_register)
+extern int devm_led_trigger_register(struct device *dev,
+ struct led_trigger *trigger);
+#endif /* < 4.5 */
+
+#endif /* __BACKPORT_LINUX_LEDS_H */
diff --git a/backport-include/linux/list.h b/backport-include/linux/list.h
new file mode 100644
index 0000000..13ab976
--- /dev/null
+++ b/backport-include/linux/list.h
@@ -0,0 +1,91 @@
+#ifndef __BACKPORT_LIST_H
+#define __BACKPORT_LIST_H
+#include_next <linux/list.h>
+#include <linux/version.h>
+
+#if LINUX_VERSION_IS_LESS(3,9,0)
+/**
+ * backport:
+ *
+ * commit 0bbacca7c3911451cea923b0ad6389d58e3d9ce9
+ * Author: Sasha Levin <sasha.levin@oracle.com>
+ * Date: Thu Feb 7 12:32:18 2013 +1100
+ *
+ * hlist: drop the node parameter from iterators
+ */
+#include <backport/magic.h>
+
+#undef hlist_entry_safe
+#define hlist_entry_safe(ptr, type, member) \
+ ({ typeof(ptr) ____ptr = (ptr); \
+ ____ptr ? hlist_entry(____ptr, type, member) : NULL; \
+ })
+
+#define hlist_for_each_entry4(tpos, pos, head, member) \
+ for (pos = (head)->first; \
+ pos && \
+ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;});\
+ pos = pos->next)
+
+#define hlist_for_each_entry_safe5(tpos, pos, n, head, member) \
+ for (pos = (head)->first; \
+ pos && ({ n = pos->next; 1; }) && \
+ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;});\
+ pos = n)
+
+#define hlist_for_each_entry3(pos, head, member) \
+ for (pos = hlist_entry_safe((head)->first, typeof(*(pos)), member); \
+ pos; \
+ pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member))
+
+#define hlist_for_each_entry_safe4(pos, n, head, member) \
+ for (pos = hlist_entry_safe((head)->first, typeof(*pos), member); \
+ pos && ({ n = pos->member.next; 1; }); \
+ pos = hlist_entry_safe(n, typeof(*pos), member))
+
+#undef hlist_for_each_entry
+#define hlist_for_each_entry(...) \
+ macro_dispatcher(hlist_for_each_entry, __VA_ARGS__)(__VA_ARGS__)
+#undef hlist_for_each_entry_safe
+#define hlist_for_each_entry_safe(...) \
+ macro_dispatcher(hlist_for_each_entry_safe, __VA_ARGS__)(__VA_ARGS__)
+
+#endif
+
+#ifndef list_first_entry_or_null
+/**
+ * list_first_entry_or_null - get the first element from a list
+ * @ptr: the list head to take the element from.
+ * @type: the type of the struct this is embedded in.
+ * @member: the name of the list_struct within the struct.
+ *
+ * Note that if the list is empty, it returns NULL.
+ */
+#define list_first_entry_or_null(ptr, type, member) \
+ (!list_empty(ptr) ? list_first_entry(ptr, type, member) : NULL)
+#endif /* list_first_entry_or_null */
+
+#ifndef list_next_entry
+/**
+ * list_next_entry - get the next element in list
+ * @pos: the type * to cursor
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_next_entry(pos, member) \
+ list_entry((pos)->member.next, typeof(*(pos)), member)
+#endif /* list_next_entry */
+
+#ifndef list_last_entry
+/**
+ * list_last_entry - get the last element from a list
+ * @ptr: the list head to take the element from.
+ * @type: the type of the struct this is embedded in.
+ * @member: the name of the list_struct within the struct.
+ *
+ * Note, that list is expected to be not empty.
+ */
+#define list_last_entry(ptr, type, member) \
+ list_entry((ptr)->prev, type, member)
+#endif
+
+#endif /* __BACKPORT_LIST_H */
diff --git a/backport-include/linux/list_nulls.h b/backport-include/linux/list_nulls.h
new file mode 100644
index 0000000..ba28834
--- /dev/null
+++ b/backport-include/linux/list_nulls.h
@@ -0,0 +1,9 @@
+#ifndef __BACKPORT_LIST_NULLS
+#define __BACKPORT_LIST_NULLS
+#include_next <linux/list_nulls.h>
+
+#ifndef NULLS_MARKER
+#define NULLS_MARKER(value) (1UL | (((long)value) << 1))
+#endif
+
+#endif /* __BACKPORT_LIST_NULLS */
diff --git a/backport-include/linux/lockdep.h b/backport-include/linux/lockdep.h
new file mode 100644
index 0000000..5a5d0d0
--- /dev/null
+++ b/backport-include/linux/lockdep.h
@@ -0,0 +1,17 @@
+#ifndef __BACKPORT_LINUX_LOCKDEP_H
+#define __BACKPORT_LINUX_LOCKDEP_H
+#include_next <linux/lockdep.h>
+#include <linux/version.h>
+
+#if LINUX_VERSION_IS_LESS(3,9,0)
+#undef lockdep_assert_held
+#ifdef CONFIG_LOCKDEP
+#define lockdep_assert_held(l) do { \
+ WARN_ON(debug_locks && !lockdep_is_held(l)); \
+ } while (0)
+#else
+#define lockdep_assert_held(l) do { (void)(l); } while (0)
+#endif /* CONFIG_LOCKDEP */
+#endif /* LINUX_VERSION_IS_LESS(3,9,0) */
+
+#endif /* __BACKPORT_LINUX_LOCKDEP_H */
diff --git a/backport-include/linux/math64.h b/backport-include/linux/math64.h
new file mode 100644
index 0000000..4aeb0df
--- /dev/null
+++ b/backport-include/linux/math64.h
@@ -0,0 +1,27 @@
+#ifndef __BACKPORT_LINUX_MATH64_H
+#define __BACKPORT_LINUX_MATH64_H
+#include_next <linux/math64.h>
+
+#if LINUX_VERSION_IS_LESS(3,12,0)
+
+#if BITS_PER_LONG == 64
+/**
+ * div64_u64_rem - unsigned 64bit divide with 64bit divisor and remainder
+ */
+#define div64_u64_rem LINUX_BACKPORT(div64_u64_rem)
+static inline u64 div64_u64_rem(u64 dividend, u64 divisor, u64 *remainder)
+{
+ *remainder = dividend % divisor;
+ return dividend / divisor;
+}
+#elif BITS_PER_LONG == 32
+#ifndef div64_u64_rem
+#define div64_u64_rem LINUX_BACKPORT(div64_u64_rem)
+#define backports_div64_u64_rem_add 1
+extern u64 div64_u64_rem(u64 dividend, u64 divisor, u64 *remainder);
+#endif
+
+#endif /* BITS_PER_LONG */
+#endif /* < 3.12 */
+
+#endif /* __BACKPORT_LINUX_MATH64_H */
diff --git a/backport-include/linux/mdio.h b/backport-include/linux/mdio.h
new file mode 100644
index 0000000..e12f446
--- /dev/null
+++ b/backport-include/linux/mdio.h
@@ -0,0 +1,87 @@
+#ifndef __BACKPORT_LINUX_MDIO_H
+#define __BACKPORT_LINUX_MDIO_H
+#include_next <linux/mdio.h>
+
+#ifndef MDIO_EEE_100TX
+/* EEE Supported/Advertisement/LP Advertisement registers.
+ *
+ * EEE capability Register (3.20), Advertisement (7.60) and
+ * Link partner ability (7.61) registers have and can use the same identical
+ * bit masks.
+ */
+#define MDIO_AN_EEE_ADV_100TX 0x0002 /* Advertise 100TX EEE cap */
+#define MDIO_AN_EEE_ADV_1000T 0x0004 /* Advertise 1000T EEE cap */
+/* Note: the two defines above can be potentially used by the user-land
+ * and cannot remove them now.
+ * So, we define the new generic MDIO_EEE_100TX and MDIO_EEE_1000T macros
+ * using the previous ones (that can be considered obsolete).
+ */
+#define MDIO_EEE_100TX MDIO_AN_EEE_ADV_100TX /* 100TX EEE cap */
+#define MDIO_EEE_1000T MDIO_AN_EEE_ADV_1000T /* 1000T EEE cap */
+#define MDIO_EEE_10GT 0x0008 /* 10GT EEE cap */
+#define MDIO_EEE_1000KX 0x0010 /* 1000KX EEE cap */
+#define MDIO_EEE_10GKX4 0x0020 /* 10G KX4 EEE cap */
+#define MDIO_EEE_10GKR 0x0040 /* 10G KR EEE cap */
+#endif /* MDIO_EEE_100TX */
+
+#if LINUX_VERSION_IS_LESS(3,7,0)
+/**
+ * mmd_eee_adv_to_ethtool_adv_t
+ * @eee_adv: value of the MMD EEE Advertisement/Link Partner Ability registers
+ *
+ * A small helper function that translates the MMD EEE Advertisment (7.60)
+ * and MMD EEE Link Partner Ability (7.61) bits to ethtool advertisement
+ * settings.
+ */
+#define mmd_eee_adv_to_ethtool_adv_t LINUX_BACKPORT(mmd_eee_adv_to_ethtool_adv_t)
+static inline u32 mmd_eee_adv_to_ethtool_adv_t(u16 eee_adv)
+{
+ u32 adv = 0;
+
+ if (eee_adv & MDIO_EEE_100TX)
+ adv |= ADVERTISED_100baseT_Full;
+ if (eee_adv & MDIO_EEE_1000T)
+ adv |= ADVERTISED_1000baseT_Full;
+ if (eee_adv & MDIO_EEE_10GT)
+ adv |= ADVERTISED_10000baseT_Full;
+ if (eee_adv & MDIO_EEE_1000KX)
+ adv |= ADVERTISED_1000baseKX_Full;
+ if (eee_adv & MDIO_EEE_10GKX4)
+ adv |= ADVERTISED_10000baseKX4_Full;
+ if (eee_adv & MDIO_EEE_10GKR)
+ adv |= ADVERTISED_10000baseKR_Full;
+
+ return adv;
+}
+
+#define ethtool_adv_to_mmd_eee_adv_t LINUX_BACKPORT(ethtool_adv_to_mmd_eee_adv_t)
+/**
+ * ethtool_adv_to_mmd_eee_adv_t
+ * @adv: the ethtool advertisement settings
+ *
+ * A small helper function that translates ethtool advertisement settings
+ * to EEE advertisements for the MMD EEE Advertisement (7.60) and
+ * MMD EEE Link Partner Ability (7.61) registers.
+ */
+static inline u16 ethtool_adv_to_mmd_eee_adv_t(u32 adv)
+{
+ u16 reg = 0;
+
+ if (adv & ADVERTISED_100baseT_Full)
+ reg |= MDIO_EEE_100TX;
+ if (adv & ADVERTISED_1000baseT_Full)
+ reg |= MDIO_EEE_1000T;
+ if (adv & ADVERTISED_10000baseT_Full)
+ reg |= MDIO_EEE_10GT;
+ if (adv & ADVERTISED_1000baseKX_Full)
+ reg |= MDIO_EEE_1000KX;
+ if (adv & ADVERTISED_10000baseKX4_Full)
+ reg |= MDIO_EEE_10GKX4;
+ if (adv & ADVERTISED_10000baseKR_Full)
+ reg |= MDIO_EEE_10GKR;
+
+ return reg;
+}
+#endif /* LINUX_VERSION_IS_LESS(3,7,0) */
+
+#endif /* __BACKPORT_LINUX_MDIO_H */
diff --git a/backport-include/linux/mei_cl_bus.h b/backport-include/linux/mei_cl_bus.h
new file mode 100644
index 0000000..c0e522f
--- /dev/null
+++ b/backport-include/linux/mei_cl_bus.h
@@ -0,0 +1,25 @@
+#ifndef __BACKPORT_LINUX_MEI_CL_BUS_H
+#define __BACKPORT_LINUX_MEI_CL_BUS_H
+#include_next <linux/mei_cl_bus.h>
+
+#if LINUX_VERSION_IS_LESS(4,3,0)
+#define mei_cldev_register_event_cb(cldev, event_mask, read_cb, context) \
+ mei_cl_register_event_cb(cldev, read_cb, context)
+#elif LINUX_VERSION_IS_LESS(4,4,0)
+#define mei_cldev_register_event_cb(cldev, event_mask, read_cb, context) \
+ mei_cl_register_event_cb(cldev, event_mask, read_cb, context)
+#endif
+
+#if LINUX_VERSION_IS_LESS(4,4,0)
+#define __mei_cldev_driver_register(cldrv, owner) __mei_cl_driver_register(cldrv, owner)
+#define mei_cldev_driver_register(cldrv) mei_cl_driver_register(cldrv)
+#define mei_cldev_driver_unregister(cldrv) mei_cl_driver_unregister(cldrv)
+#define mei_cldev_send(cldev, buf, length) mei_cl_send(cldev, buf, length)
+#define mei_cldev_recv(cldev, buf, length) mei_cl_recv(cldev, buf, length)
+#define mei_cldev_get_drvdata(cldev) mei_cl_get_drvdata(cldev)
+#define mei_cldev_set_drvdata(cldev, data) mei_cl_set_drvdata(cldev, data)
+#define mei_cldev_enable(cldev) mei_cl_enable_device(cldev)
+#define mei_cldev_disable(cldev) mei_cl_disable_device(cldev)
+#endif
+
+#endif /* __BACKPORT_LINUX_MEI_CL_BUS_H */
diff --git a/backport-include/linux/mii.h b/backport-include/linux/mii.h
new file mode 100644
index 0000000..5b0ecf9
--- /dev/null
+++ b/backport-include/linux/mii.h
@@ -0,0 +1,147 @@
+#ifndef __BACKPORT_LINUX_MII_H
+#define __BACKPORT_LINUX_MII_H
+#include_next <linux/mii.h>
+#include <linux/version.h>
+
+#if LINUX_VERSION_IS_LESS(3,3,0)
+#include <linux/ethtool.h>
+
+#define ethtool_adv_to_mii_adv_t LINUX_BACKPORT(ethtool_adv_to_mii_adv_t)
+static inline u32 ethtool_adv_to_mii_adv_t(u32 ethadv)
+{
+ u32 result = 0;
+
+ if (ethadv & ADVERTISED_10baseT_Half)
+ result |= ADVERTISE_10HALF;
+ if (ethadv & ADVERTISED_10baseT_Full)
+ result |= ADVERTISE_10FULL;
+ if (ethadv & ADVERTISED_100baseT_Half)
+ result |= ADVERTISE_100HALF;
+ if (ethadv & ADVERTISED_100baseT_Full)
+ result |= ADVERTISE_100FULL;
+ if (ethadv & ADVERTISED_Pause)
+ result |= ADVERTISE_PAUSE_CAP;
+ if (ethadv & ADVERTISED_Asym_Pause)
+ result |= ADVERTISE_PAUSE_ASYM;
+
+ return result;
+}
+
+#define mii_adv_to_ethtool_adv_t LINUX_BACKPORT(mii_adv_to_ethtool_adv_t)
+static inline u32 mii_adv_to_ethtool_adv_t(u32 adv)
+{
+ u32 result = 0;
+
+ if (adv & ADVERTISE_10HALF)
+ result |= ADVERTISED_10baseT_Half;
+ if (adv & ADVERTISE_10FULL)
+ result |= ADVERTISED_10baseT_Full;
+ if (adv & ADVERTISE_100HALF)
+ result |= ADVERTISED_100baseT_Half;
+ if (adv & ADVERTISE_100FULL)
+ result |= ADVERTISED_100baseT_Full;
+ if (adv & ADVERTISE_PAUSE_CAP)
+ result |= ADVERTISED_Pause;
+ if (adv & ADVERTISE_PAUSE_ASYM)
+ result |= ADVERTISED_Asym_Pause;
+
+ return result;
+}
+
+#define ethtool_adv_to_mii_ctrl1000_t LINUX_BACKPORT(ethtool_adv_to_mii_ctrl1000_t)
+static inline u32 ethtool_adv_to_mii_ctrl1000_t(u32 ethadv)
+{
+ u32 result = 0;
+
+ if (ethadv & ADVERTISED_1000baseT_Half)
+ result |= ADVERTISE_1000HALF;
+ if (ethadv & ADVERTISED_1000baseT_Full)
+ result |= ADVERTISE_1000FULL;
+
+ return result;
+}
+
+#define mii_ctrl1000_to_ethtool_adv_t LINUX_BACKPORT(mii_ctrl1000_to_ethtool_adv_t)
+static inline u32 mii_ctrl1000_to_ethtool_adv_t(u32 adv)
+{
+ u32 result = 0;
+
+ if (adv & ADVERTISE_1000HALF)
+ result |= ADVERTISED_1000baseT_Half;
+ if (adv & ADVERTISE_1000FULL)
+ result |= ADVERTISED_1000baseT_Full;
+
+ return result;
+}
+
+#define mii_lpa_to_ethtool_lpa_t LINUX_BACKPORT(mii_lpa_to_ethtool_lpa_t)
+static inline u32 mii_lpa_to_ethtool_lpa_t(u32 lpa)
+{
+ u32 result = 0;
+
+ if (lpa & LPA_LPACK)
+ result |= ADVERTISED_Autoneg;
+
+ return result | mii_adv_to_ethtool_adv_t(lpa);
+}
+
+#define mii_stat1000_to_ethtool_lpa_t LINUX_BACKPORT(mii_stat1000_to_ethtool_lpa_t)
+static inline u32 mii_stat1000_to_ethtool_lpa_t(u32 lpa)
+{
+ u32 result = 0;
+
+ if (lpa & LPA_1000HALF)
+ result |= ADVERTISED_1000baseT_Half;
+ if (lpa & LPA_1000FULL)
+ result |= ADVERTISED_1000baseT_Full;
+
+ return result;
+}
+
+#define ethtool_adv_to_mii_adv_x LINUX_BACKPORT(ethtool_adv_to_mii_adv_x)
+static inline u32 ethtool_adv_to_mii_adv_x(u32 ethadv)
+{
+ u32 result = 0;
+
+ if (ethadv & ADVERTISED_1000baseT_Half)
+ result |= ADVERTISE_1000XHALF;
+ if (ethadv & ADVERTISED_1000baseT_Full)
+ result |= ADVERTISE_1000XFULL;
+ if (ethadv & ADVERTISED_Pause)
+ result |= ADVERTISE_1000XPAUSE;
+ if (ethadv & ADVERTISED_Asym_Pause)
+ result |= ADVERTISE_1000XPSE_ASYM;
+
+ return result;
+}
+
+#define mii_adv_to_ethtool_adv_x LINUX_BACKPORT(mii_adv_to_ethtool_adv_x)
+static inline u32 mii_adv_to_ethtool_adv_x(u32 adv)
+{
+ u32 result = 0;
+
+ if (adv & ADVERTISE_1000XHALF)
+ result |= ADVERTISED_1000baseT_Half;
+ if (adv & ADVERTISE_1000XFULL)
+ result |= ADVERTISED_1000baseT_Full;
+ if (adv & ADVERTISE_1000XPAUSE)
+ result |= ADVERTISED_Pause;
+ if (adv & ADVERTISE_1000XPSE_ASYM)
+ result |= ADVERTISED_Asym_Pause;
+
+ return result;
+}
+
+#define mii_lpa_to_ethtool_lpa_x LINUX_BACKPORT(mii_lpa_to_ethtool_lpa_x)
+static inline u32 mii_lpa_to_ethtool_lpa_x(u32 lpa)
+{
+ u32 result = 0;
+
+ if (lpa & LPA_LPACK)
+ result |= ADVERTISED_Autoneg;
+
+ return result | mii_adv_to_ethtool_adv_x(lpa);
+}
+#endif
+
+#endif /* __BACKPORT_LINUX_MII_H */
diff --git a/backport-include/linux/miscdevice.h b/backport-include/linux/miscdevice.h
new file mode 100644
index 0000000..9c27334
--- /dev/null
+++ b/backport-include/linux/miscdevice.h
@@ -0,0 +1,9 @@
+#ifndef _BACKPORT_LINUX_MISCDEVICE_H
+#define _BACKPORT_LINUX_MISCDEVICE_H
+#include_next <linux/miscdevice.h>
+
+#ifndef VHCI_MINOR
+#define VHCI_MINOR 137
+#endif
+
+#endif /* _BACKPORT_LINUX_MISCDEVICE_H */
diff --git a/backport-include/linux/mm.h b/backport-include/linux/mm.h
new file mode 100644
index 0000000..3234b37
--- /dev/null
+++ b/backport-include/linux/mm.h
@@ -0,0 +1,126 @@
+#ifndef __BACKPORT_MM_H
+#define __BACKPORT_MM_H
+#include_next <linux/mm.h>
+#include <linux/page_ref.h>
+#include <linux/sched.h>
+
+#ifndef VM_NODUMP
+/*
+ * defined here to allow things to compile but technically
+ * using this for memory regions will yield in a no-op on newer
+ * kernels but on older kernels (v3.3 and older) this bit was used
+ * for VM_ALWAYSDUMP. The goal was to remove this bit moving forward
+ * and since we can't skip the core dump on old kernels we just make
+ * this bit name now a no-op.
+ *
+ * For details see commits: 909af7 accb61fe cdaaa7003
+ */
+#define VM_NODUMP 0x0
+#endif
+
+#ifndef VM_DONTDUMP
+#define VM_DONTDUMP VM_NODUMP
+#endif
+
+#if LINUX_VERSION_IS_LESS(3,15,0)
+#define kvfree LINUX_BACKPORT(kvfree)
+void kvfree(const void *addr);
+#endif /* < 3.15 */
+
+#if LINUX_VERSION_IS_LESS(3,20,0)
+#define get_user_pages_locked LINUX_BACKPORT(get_user_pages_locked)
+long get_user_pages_locked(unsigned long start, unsigned long nr_pages,
+ int write, int force, struct page **pages, int *locked);
+#define get_user_pages_unlocked LINUX_BACKPORT(get_user_pages_unlocked)
+long get_user_pages_unlocked(unsigned long start, unsigned long nr_pages,
+ int write, int force, struct page **pages);
+#elif LINUX_VERSION_IS_LESS(4,6,0)
+static inline
+long backport_get_user_pages_locked(unsigned long start, unsigned long nr_pages,
+ int write, int force, struct page **pages, int *locked)
+{
+ return get_user_pages_locked(current, current->mm, start, nr_pages,
+ write, force, pages, locked);
+}
+#define get_user_pages_locked LINUX_BACKPORT(get_user_pages_locked)
+
+static inline
+long backport_get_user_pages_unlocked(unsigned long start, unsigned long nr_pages,
+ int write, int force, struct page **pages)
+{
+ return get_user_pages_unlocked(current, current->mm, start, nr_pages,
+ write, force, pages);
+}
+#define get_user_pages_unlocked LINUX_BACKPORT(get_user_pages_unlocked)
+#endif
+
+#if LINUX_VERSION_IS_LESS(4,6,0)
+static inline
+long backport_get_user_pages(unsigned long start, unsigned long nr_pages,
+ int write, int force, struct page **pages,
+ struct vm_area_struct **vmas)
+{
+ return get_user_pages(current, current->mm, start, nr_pages,
+ write, force, pages, vmas);
+}
+#define get_user_pages LINUX_BACKPORT(get_user_pages)
+#endif
+
+#ifndef FOLL_TRIED
+#define FOLL_TRIED 0x800 /* a retry, previous pass started an IO */
+#endif
+
+#ifdef CPTCFG_BPAUTO_BUILD_FRAME_VECTOR
+/* Container for pinned pfns / pages */
+struct frame_vector {
+ unsigned int nr_allocated; /* Number of frames we have space for */
+ unsigned int nr_frames; /* Number of frames stored in ptrs array */
+ bool got_ref; /* Did we pin pages by getting page ref? */
+ bool is_pfns; /* Does array contain pages or pfns? */
+ void *ptrs[0]; /* Array of pinned pfns / pages. Use
+ * pfns_vector_pages() or pfns_vector_pfns()
+ * for access */
+};
+
+struct frame_vector *frame_vector_create(unsigned int nr_frames);
+void frame_vector_destroy(struct frame_vector *vec);
+int get_vaddr_frames(unsigned long start, unsigned int nr_pfns,
+ bool write, bool force, struct frame_vector *vec);
+void put_vaddr_frames(struct frame_vector *vec);
+int frame_vector_to_pages(struct frame_vector *vec);
+void frame_vector_to_pfns(struct frame_vector *vec);
+
+static inline unsigned int frame_vector_count(struct frame_vector *vec)
+{
+ return vec->nr_frames;
+}
+
+static inline struct page **frame_vector_pages(struct frame_vector *vec)
+{
+ if (vec->is_pfns) {
+ int err = frame_vector_to_pages(vec);
+
+ if (err)
+ return ERR_PTR(err);
+ }
+ return (struct page **)(vec->ptrs);
+}
+
+static inline unsigned long *frame_vector_pfns(struct frame_vector *vec)
+{
+ if (!vec->is_pfns)
+ frame_vector_to_pfns(vec);
+ return (unsigned long *)(vec->ptrs);
+}
+#endif
+
+#if LINUX_VERSION_IS_LESS(4,1,9) && \
+ LINUX_VERSION_IS_GEQ(3,6,0)
+#define page_is_pfmemalloc LINUX_BACKPORT(page_is_pfmemalloc)
+static inline bool page_is_pfmemalloc(struct page *page)
+{
+ return page->pfmemalloc;
+}
+#endif /* < 4.2 */
+
+#endif /* __BACKPORT_MM_H */
diff --git a/backport-include/linux/mmc/host.h b/backport-include/linux/mmc/host.h
new file mode 100644
index 0000000..2a60cde
--- /dev/null
+++ b/backport-include/linux/mmc/host.h
@@ -0,0 +1,16 @@
+#ifndef _BACKPORTLINUX_MMC_HOST_H
+#define _BACKPORTLINUX_MMC_HOST_H
+#include_next <linux/mmc/host.h>
+#include <linux/version.h>
+#include <linux/mmc/card.h>
+
+#if LINUX_VERSION_IS_LESS(3,16,0)
+#define mmc_card_hs LINUX_BACKPORT(mmc_card_hs)
+static inline int mmc_card_hs(struct mmc_card *card)
+{
+ return card->host->ios.timing == MMC_TIMING_SD_HS ||
+ card->host->ios.timing == MMC_TIMING_MMC_HS;
+}
+#endif /* LINUX_VERSION_IS_LESS(3,16,0) */
+
+#endif /* _BACKPORTLINUX_MMC_HOST_H */
diff --git a/backport-include/linux/mmc/sdio.h b/backport-include/linux/mmc/sdio.h
new file mode 100644
index 0000000..31d8833
--- /dev/null
+++ b/backport-include/linux/mmc/sdio.h
@@ -0,0 +1,21 @@
+#ifndef __BACKPORT_MMC_SDIO_H
+#define __BACKPORT_MMC_SDIO_H
+#include <linux/version.h>
+#include_next <linux/mmc/sdio.h>
+
+/* backports b4625dab */
+#ifndef SDIO_CCCR_REV_3_00
+#define SDIO_CCCR_REV_3_00 3 /* CCCR/FBR Version 3.00 */
+#endif
+#ifndef SDIO_SDIO_REV_3_00
+#define SDIO_SDIO_REV_3_00 4 /* SDIO Spec Version 3.00 */
+#endif
+
+#ifndef SDIO_BUS_ECSI
+#define SDIO_BUS_ECSI 0x20 /* Enable continuous SPI interrupt */
+#endif
+#ifndef SDIO_BUS_SCSI
+#define SDIO_BUS_SCSI 0x40 /* Support continuous SPI interrupt */
+#endif
+
+#endif /* __BACKPORT_MMC_SDIO_H */
diff --git a/backport-include/linux/mmc/sdio_func.h b/backport-include/linux/mmc/sdio_func.h
new file mode 100644
index 0000000..2d3e92b
--- /dev/null
+++ b/backport-include/linux/mmc/sdio_func.h
@@ -0,0 +1,10 @@
+#ifndef __BACKPORT_MMC_SDIO_FUNC_H
+#define __BACKPORT_MMC_SDIO_FUNC_H
+#include <linux/version.h>
+#include_next <linux/mmc/sdio_func.h>
+
+#ifndef dev_to_sdio_func
+#define dev_to_sdio_func(d) container_of(d, struct sdio_func, dev)
+#endif
+
+#endif /* __BACKPORT_MMC_SDIO_FUNC_H */
diff --git a/backport-include/linux/mmc/sdio_ids.h b/backport-include/linux/mmc/sdio_ids.h
new file mode 100644
index 0000000..64fe8ec
--- /dev/null
+++ b/backport-include/linux/mmc/sdio_ids.h
@@ -0,0 +1,14 @@
+#ifndef __BACKPORT_MMC_SDIO_IDS_H
+#define __BACKPORT_MMC_SDIO_IDS_H
+#include <linux/version.h>
+#include_next <linux/mmc/sdio_ids.h>
+
+#ifndef SDIO_CLASS_BT_AMP
+#define SDIO_CLASS_BT_AMP 0x09 /* Type-A Bluetooth AMP interface */
+#endif
+
+#ifndef SDIO_DEVICE_ID_MARVELL_8688WLAN
+#define SDIO_DEVICE_ID_MARVELL_8688WLAN 0x9104
+#endif
+
+#endif /* __BACKPORT_MMC_SDIO_IDS_H */
diff --git a/backport-include/linux/mod_devicetable.h b/backport-include/linux/mod_devicetable.h
new file mode 100644
index 0000000..ec0a3e6
--- /dev/null
+++ b/backport-include/linux/mod_devicetable.h
@@ -0,0 +1,17 @@
+#ifndef __BACKPORT_MOD_DEVICETABLE_H
+#define __BACKPORT_MOD_DEVICETABLE_H
+#include_next <linux/mod_devicetable.h>
+
+#ifndef HID_BUS_ANY
+#define HID_BUS_ANY 0xffff
+#endif
+
+#ifndef HID_GROUP_ANY
+#define HID_GROUP_ANY 0x0000
+#endif
+
+#ifndef HID_ANY_ID
+#define HID_ANY_ID (~0)
+#endif
+
+#endif /* __BACKPORT_MOD_DEVICETABLE_H */
diff --git a/backport-include/linux/module.h b/backport-include/linux/module.h
new file mode 100644
index 0000000..1a2c82f
--- /dev/null
+++ b/backport-include/linux/module.h
@@ -0,0 +1,69 @@
+#ifndef __BACKPORT_LINUX_MODULE_H
+#define __BACKPORT_LINUX_MODULE_H
+#include_next <linux/module.h>
+#include <linux/rcupdate.h>
+
+/*
+ * The define overwriting module_init is based on the original module_init
+ * which looks like this:
+ * #define module_init(initfn) \
+ * static inline initcall_t __inittest(void) \
+ * { return initfn; } \
+ * int init_module(void) __attribute__((alias(#initfn)));
+ *
+ * To the call to the initfn we added the symbol dependency on compat
+ * to make sure that compat.ko gets loaded for any compat modules.
+ */
+extern void backport_dependency_symbol(void);
+
+#ifdef BACKPORTS_GIT_TRACKED
+#define BACKPORT_MOD_VERSIONS MODULE_VERSION(BACKPORTS_GIT_TRACKED);
+#else
+#define BACKPORT_MOD_VERSIONS \
+ MODULE_VERSION("backported from " CPTCFG_KERNEL_NAME \
+ " (" CPTCFG_KERNEL_VERSION ")" \
+ " using backports " CPTCFG_VERSION);
+#endif
+
+#ifdef MODULE
+#undef module_init
+#define module_init(initfn) \
+ static int __init __init_backport(void) \
+ { \
+ backport_dependency_symbol(); \
+ return initfn(); \
+ } \
+ int init_module(void) __attribute__((alias("__init_backport")));\
+ BACKPORT_MOD_VERSIONS
+
+/*
+ * The define overwriting module_exit is based on the original module_exit
+ * which looks like this:
+ * #define module_exit(exitfn) \
+ * static inline exitcall_t __exittest(void) \
+ * { return exitfn; } \
+ * void cleanup_module(void) __attribute__((alias(#exitfn)));
+ *
+ * We replaced the call to the actual function exitfn() with a call to our
+ * function which calls the original exitfn() and then rcu_barrier()
+ *
+ * As a module will not be unloaded that ofter it should not have a big
+ * performance impact when rcu_barrier() is called on every module exit,
+ * also when no kfree_rcu() backport is used in that module.
+ */
+#undef module_exit
+#define module_exit(exitfn) \
+ static void __exit __exit_compat(void) \
+ { \
+ exitfn(); \
+ rcu_barrier(); \
+ } \
+ void cleanup_module(void) __attribute__((alias("__exit_compat")));
+#endif
+
+#if LINUX_VERSION_IS_LESS(3,3,0)
+#undef param_check_bool
+#define param_check_bool(name, p) __param_check(name, p, bool)
+#endif
+
+#endif /* __BACKPORT_LINUX_MODULE_H */
diff --git a/backport-include/linux/moduleparam.h b/backport-include/linux/moduleparam.h
new file mode 100644
index 0000000..1c7dfad
--- /dev/null
+++ b/backport-include/linux/moduleparam.h
@@ -0,0 +1,39 @@
+#ifndef __BACKPORT_LINUX_MODULEPARAM_H
+#define __BACKPORT_LINUX_MODULEPARAM_H
+#include_next <linux/moduleparam.h>
+
+#if LINUX_VERSION_IS_LESS(4,2,0)
+#define kernel_param_lock LINUX_BACKPORT(kernel_param_lock)
+static inline void kernel_param_lock(struct module *mod)
+{
+ __kernel_param_lock();
+}
+#define kernel_param_unlock LINUX_BACKPORT(kernel_param_unlock)
+static inline void kernel_param_unlock(struct module *mod)
+{
+ __kernel_param_unlock();
+}
+#endif
+
+#if LINUX_VERSION_IS_LESS(3,8,0)
+#undef __MODULE_INFO
+#ifdef MODULE
+#define __MODULE_INFO(tag, name, info) \
+static const char __UNIQUE_ID(name)[] \
+ __used __attribute__((section(".modinfo"), unused, aligned(1))) \
+ = __stringify(tag) "=" info
+#else /* !MODULE */
+/* This struct is here for syntactic coherency, it is not used */
+#define __MODULE_INFO(tag, name, info) \
+ struct __UNIQUE_ID(name) {}
+#endif
+#endif /* < 3.8 */
+
+#if LINUX_VERSION_IS_LESS(3,17,0)
+extern struct kernel_param_ops param_ops_ullong;
+extern int param_set_ullong(const char *val, const struct kernel_param *kp);
+extern int param_get_ullong(char *buffer, const struct kernel_param *kp);
+#define param_check_ullong(name, p) __param_check(name, p, unsigned long long)
+#endif
+
+#endif /* __BACKPORT_LINUX_MODULEPARAM_H */
diff --git a/backport-include/linux/net.h b/backport-include/linux/net.h
new file mode 100644
index 0000000..738ff8b
--- /dev/null
+++ b/backport-include/linux/net.h
@@ -0,0 +1,112 @@
+#ifndef __BACKPORT_LINUX_NET_H
+#define __BACKPORT_LINUX_NET_H
+#include_next <linux/net.h>
+#include <linux/static_key.h>
+
+/* This backports:
+ *
+ * commit 2033e9bf06f07e049bbc77e9452856df846714cc -- from v3.5
+ * Author: Neil Horman <nhorman@tuxdriver.com>
+ * Date: Tue May 29 09:30:40 2012 +0000
+ *
+ * net: add MODULE_ALIAS_NET_PF_PROTO_NAME
+ */
+#ifndef MODULE_ALIAS_NET_PF_PROTO_NAME
+#define MODULE_ALIAS_NET_PF_PROTO_NAME(pf, proto, name) \
+ MODULE_ALIAS("net-pf-" __stringify(pf) "-proto-" __stringify(proto) \
+ name)
+#endif
+
+#ifndef net_ratelimited_function
+#define net_ratelimited_function(function, ...) \
+do { \
+ if (net_ratelimit()) \
+ function(__VA_ARGS__); \
+} while (0)
+
+#define net_emerg_ratelimited(fmt, ...) \
+ net_ratelimited_function(pr_emerg, fmt, ##__VA_ARGS__)
+#define net_alert_ratelimited(fmt, ...) \
+ net_ratelimited_function(pr_alert, fmt, ##__VA_ARGS__)
+#define net_crit_ratelimited(fmt, ...) \
+ net_ratelimited_function(pr_crit, fmt, ##__VA_ARGS__)
+#define net_err_ratelimited(fmt, ...) \
+ net_ratelimited_function(pr_err, fmt, ##__VA_ARGS__)
+#define net_notice_ratelimited(fmt, ...) \
+ net_ratelimited_function(pr_notice, fmt, ##__VA_ARGS__)
+#define net_warn_ratelimited(fmt, ...) \
+ net_ratelimited_function(pr_warn, fmt, ##__VA_ARGS__)
+#define net_info_ratelimited(fmt, ...) \
+ net_ratelimited_function(pr_info, fmt, ##__VA_ARGS__)
+#define net_dbg_ratelimited(fmt, ...) \
+ net_ratelimited_function(pr_debug, fmt, ##__VA_ARGS__)
+#endif
+
+#ifndef DECLARE_SOCKADDR
+#define DECLARE_SOCKADDR(type, dst, src) \
+ type dst = ({ __sockaddr_check_size(sizeof(*dst)); (type) src; })
+#endif
+
+/*
+ * Avoid backporting this if a distro did the work already, this
+ * takes the check a bit further than just using LINUX_BACKPORT()
+ * namespace, curious if any distro will hit a wall with this.
+ * Also curious if any distro will be daring enough to even try
+ * to backport this to a release older than 3.5.
+ */
+#ifndef ___NET_RANDOM_STATIC_KEY_INIT
+/*
+ * Backporting this before 3.5 is extremely tricky -- I tried, due
+ * to the fact that it relies on static keys, which were refactored
+ * and optimized through a series of generation of patches from jump
+ * labels. These in turn have also been optimized through kernel revisions
+ * and have architecture specific code, which if you commit to backporting
+ * may affect tracing. My recommendation is that if you have a need for
+ * static keys you just require at least 3.5 to remain sane.
+ */
+#if LINUX_VERSION_IS_GEQ(3,5,0) && !defined(net_get_random_once)
+#define __BACKPORT_NET_GET_RANDOM_ONCE 1
+#endif
+#endif /* ___NET_RANDOM_STATIC_KEY_INIT */
+
+#ifdef __BACKPORT_NET_GET_RANDOM_ONCE
+#define __net_get_random_once LINUX_BACKPORT(__net_get_random_once)
+bool __net_get_random_once(void *buf, int nbytes, bool *done,
+ struct static_key *done_key);
+
+#ifdef HAVE_JUMP_LABEL
+#define ___NET_RANDOM_STATIC_KEY_INIT ((struct static_key) \
+ { .enabled = ATOMIC_INIT(0), .entries = (void *)1 })
+#else /* !HAVE_JUMP_LABEL */
+#define ___NET_RANDOM_STATIC_KEY_INIT STATIC_KEY_INIT_FALSE
+#endif /* HAVE_JUMP_LABEL */
+
+#define net_get_random_once(buf, nbytes) \
+ ({ \
+ bool ___ret = false; \
+ static bool ___done = false; \
+ static struct static_key ___done_key = \
+ ___NET_RANDOM_STATIC_KEY_INIT; \
+ if (!static_key_true(&___done_key)) \
+ ___ret = __net_get_random_once(buf, \
+ nbytes, \
+ &___done, \
+ &___done_key); \
+ ___ret; \
+ })
+
+#endif /* __BACKPORT_NET_GET_RANDOM_ONCE */
+
+#if LINUX_VERSION_IS_LESS(4,2,0)
+#define sock_create_kern(net, family, type, proto, res) \
+ __sock_create(net, family, type, proto, res, 1)
+#endif
+
+#ifndef SOCKWQ_ASYNC_NOSPACE
+#define SOCKWQ_ASYNC_NOSPACE SOCK_ASYNC_NOSPACE
+#endif
+#ifndef SOCKWQ_ASYNC_WAITDATA
+#define SOCKWQ_ASYNC_WAITDATA SOCK_ASYNC_WAITDATA
+#endif
+
+#endif /* __BACKPORT_LINUX_NET_H */
diff --git a/backport-include/linux/netdev_features.h b/backport-include/linux/netdev_features.h
new file mode 100644
index 0000000..09568ce
--- /dev/null
+++ b/backport-include/linux/netdev_features.h
@@ -0,0 +1,53 @@
+#ifndef __BACKPORT_NETDEV_FEATURES_H
+#define __BACKPORT_NETDEV_FEATURES_H
+
+#include <linux/version.h>
+
+#if LINUX_VERSION_IS_LESS(3,3,0)
+#include <linux/netdevice.h>
+#include <linux/types.h>
+
+/* added via 9356b8fc */
+#define NETIF_F_HW_VLAN_CTAG_RX NETIF_F_HW_VLAN_RX
+#define NETIF_F_HW_VLAN_CTAG_TX NETIF_F_HW_VLAN_TX
+
+/* added via d314774c */
+#define NETIF_F_HW_VLAN_CTAG_FILTER NETIF_F_HW_VLAN_FILTER
+
+/* c8f44aff made this u32 but later a861a8b2 changed it to u64 both on v3.3 */
+typedef u32 netdev_features_t;
+
+#else
+#include_next <linux/netdev_features.h>
+
+#if LINUX_VERSION_IS_LESS(3,10,0)
+/* See commit f646968f8f on next-20130423 */
+#define NETIF_F_HW_VLAN_CTAG_TX_BIT NETIF_F_HW_VLAN_TX_BIT
+#define NETIF_F_HW_VLAN_CTAG_RX_BIT NETIF_F_HW_VLAN_RX_BIT
+#define NETIF_F_HW_VLAN_CTAG_FILTER_BIT NETIF_F_HW_VLAN_FILTER_BIT
+
+#define NETIF_F_HW_VLAN_CTAG_FILTER NETIF_F_HW_VLAN_FILTER
+#define NETIF_F_HW_VLAN_CTAG_RX NETIF_F_HW_VLAN_RX
+#define NETIF_F_HW_VLAN_CTAG_TX NETIF_F_HW_VLAN_TX
+#endif
+
+#endif /* LINUX_VERSION_IS_LESS(3,3,0) */
+
+#if !defined(NETIF_F_RXCSUM)
+#define NETIF_F_RXCSUM 0
+#endif
+
+#if !defined(NETIF_F_RXALL)
+#define NETIF_F_RXALL 0
+#endif
+
+#if !defined(NETIF_F_RXFCS)
+#define NETIF_F_RXFCS 0
+#endif
+
+/* this was renamed in commit 53692b1de : sctp: Rename NETIF_F_SCTP_CSUM to NETIF_F_SCTP_CRC */
+#ifndef NETIF_F_SCTP_CRC
+#define NETIF_F_SCTP_CRC __NETIF_F(SCTP_CSUM)
+#endif
+
+#endif /* __BACKPORT_NETDEV_FEATURES_H */
diff --git a/backport-include/linux/netdevice.h b/backport-include/linux/netdevice.h
new file mode 100644
index 0000000..d22eec2
--- /dev/null
+++ b/backport-include/linux/netdevice.h
@@ -0,0 +1,336 @@
+#ifndef __BACKPORT_NETDEVICE_H
+#define __BACKPORT_NETDEVICE_H
+#include_next <linux/netdevice.h>
+#include <linux/netdev_features.h>
+#include <linux/version.h>
+
+/*
+ * This is declared implicitly in newer kernels by netdevice.h using
+ * this pointer in struct net_device, but declare it here anyway so
+ * pointers to it are accepted as function arguments without warning.
+ */
+struct inet6_dev;
+
+/* older kernels don't include this here, we need it */
+#include <linux/ethtool.h>
+#include <linux/rculist.h>
+/*
+ * new kernels include <net/netprio_cgroup.h> which
+ * has this ... and some drivers rely on it :-(
+ */
+#include <linux/hardirq.h>
+
+#if LINUX_VERSION_IS_LESS(3,14,0)
+/*
+ * Backports note: if in-kernel support is provided we could then just
+ * take the kernel's implementation of __dev_kfree_skb_irq() as it requires
+ * raise_softirq_irqoff() which is not exported. For the backport case we
+ * just use slightly less optimized version and we don't get the ability
+ * to distinguish the two different reasons to free the skb -- whether it
+ * was consumed or dropped.
+ *
+ * The upstream documentation for this:
+ *
+ * It is not allowed to call kfree_skb() or consume_skb() from hardware
+ * interrupt context or with hardware interrupts being disabled.
+ * (in_irq() || irqs_disabled())
+ *
+ * We provide four helpers that can be used in following contexts :
+ *
+ * dev_kfree_skb_irq(skb) when caller drops a packet from irq context,
+ * replacing kfree_skb(skb)
+ *
+ * dev_consume_skb_irq(skb) when caller consumes a packet from irq context.
+ * Typically used in place of consume_skb(skb) in TX completion path
+ *
+ * dev_kfree_skb_any(skb) when caller doesn't know its current irq context,
+ * replacing kfree_skb(skb)
+ *
+ * dev_consume_skb_any(skb) when caller doesn't know its current irq context,
+ * and consumed a packet. Used in place of consume_skb(skb)
+ */
+#define skb_free_reason LINUX_BACKPORT(skb_free_reason)
+enum skb_free_reason {
+ SKB_REASON_CONSUMED,
+ SKB_REASON_DROPPED,
+};
+
+#define __dev_kfree_skb_irq LINUX_BACKPORT(__dev_kfree_skb_irq)
+static inline void __dev_kfree_skb_irq(struct sk_buff *skb,
+ enum skb_free_reason reason)
+{
+ dev_kfree_skb_irq(skb);
+}
+
+#define __dev_kfree_skb_any LINUX_BACKPORT(__dev_kfree_skb_any)
+static inline void __dev_kfree_skb_any(struct sk_buff *skb,
+ enum skb_free_reason reason)
+{
+ dev_kfree_skb_any(skb);
+}
+
+#define dev_consume_skb_irq LINUX_BACKPORT(dev_consume_skb_irq)
+static inline void dev_consume_skb_irq(struct sk_buff *skb)
+{
+ dev_kfree_skb_irq(skb);
+}
+
+#define dev_consume_skb_any LINUX_BACKPORT(dev_consume_skb_any)
+static inline void dev_consume_skb_any(struct sk_buff *skb)
+{
+ dev_kfree_skb_any(skb);
+}
+
+#if (LINUX_VERSION_CODE != KERNEL_VERSION(3,13,11) || UTS_UBUNTU_RELEASE_ABI < 24)
+struct pcpu_sw_netstats {
+ u64 rx_packets;
+ u64 rx_bytes;
+ u64 tx_packets;
+ u64 tx_bytes;
+ struct u64_stats_sync syncp;
+};
+#endif
+
+#define netdev_tstats(dev) ((struct pcpu_sw_netstats *)dev->ml_priv)
+#define netdev_assign_tstats(dev, e) dev->ml_priv = (e);
+#else
+#define netdev_tstats(dev) dev->tstats
+#define netdev_assign_tstats(dev, e) dev->tstats = (e);
+#endif /* LINUX_VERSION_IS_LESS(3,14,0) */
+
+#if LINUX_VERSION_IS_LESS(3,7,8)
+#define netdev_set_default_ethtool_ops LINUX_BACKPORT(netdev_set_default_ethtool_ops)
+extern void netdev_set_default_ethtool_ops(struct net_device *dev,
+ const struct ethtool_ops *ops);
+#endif
+
+#if LINUX_VERSION_IS_LESS(3,3,0)
+/*
+ * BQL was added as of v3.3 but some Linux distributions
+ * have backported BQL to their v3.2 kernels or older. To
+ * address this we assume that they also enabled CONFIG_BQL
+ * and test for that here and simply avoid adding the static
+ * inlines if it was defined
+ */
+#ifndef CONFIG_BQL
+#define netdev_tx_sent_queue LINUX_BACKPORT(netdev_tx_sent_queue)
+static inline void netdev_tx_sent_queue(struct netdev_queue *dev_queue,
+ unsigned int bytes)
+{
+}
+
+#define netdev_sent_queue LINUX_BACKPORT(netdev_sent_queue)
+static inline void netdev_sent_queue(struct net_device *dev, unsigned int bytes)
+{
+}
+
+#define netdev_tx_completed_queue LINUX_BACKPORT(netdev_tx_completed_queue)
+static inline void netdev_tx_completed_queue(struct netdev_queue *dev_queue,
+ unsigned pkts, unsigned bytes)
+{
+}
+
+#define netdev_completed_queue LINUX_BACKPORT(netdev_completed_queue)
+static inline void netdev_completed_queue(struct net_device *dev,
+ unsigned pkts, unsigned bytes)
+{
+}
+
+#define netdev_tx_reset_queue LINUX_BACKPORT(netdev_tx_reset_queue)
+static inline void netdev_tx_reset_queue(struct netdev_queue *q)
+{
+}
+
+#define netdev_reset_queue LINUX_BACKPORT(netdev_reset_queue)
+static inline void netdev_reset_queue(struct net_device *dev_queue)
+{
+}
+#endif /* CONFIG_BQL */
+#endif /* < 3.3 */
+
+#ifndef NETDEV_PRE_UP
+#define NETDEV_PRE_UP 0x000D
+#endif
+
+#if LINUX_VERSION_IS_LESS(3,11,0)
+#define netdev_notifier_info_to_dev(ndev) ndev
+#endif
+
+#if LINUX_VERSION_IS_LESS(3,7,0)
+#define netdev_notify_peers(dev) netif_notify_peers(dev)
+#define napi_gro_flush(napi, old) napi_gro_flush(napi)
+#endif
+
+#ifndef IFF_LIVE_ADDR_CHANGE
+#define IFF_LIVE_ADDR_CHANGE 0x100000
+#endif
+
+#ifndef IFF_SUPP_NOFCS
+#define IFF_SUPP_NOFCS 0x80000 /* device supports sending custom FCS */
+#endif
+
+#ifndef IFF_UNICAST_FLT
+#define IFF_UNICAST_FLT 0x20000 /* Supports unicast filtering */
+#endif
+
+#ifndef QUEUE_STATE_ANY_XOFF
+#define __QUEUE_STATE_DRV_XOFF __QUEUE_STATE_XOFF
+#define __QUEUE_STATE_STACK_XOFF __QUEUE_STATE_XOFF
+#endif
+
+#if LINUX_VERSION_IS_LESS(3,17,0)
+#ifndef NET_NAME_UNKNOWN
+#define NET_NAME_UNKNOWN 0
+#endif
+#ifndef NET_NAME_ENUM
+#define NET_NAME_ENUM 1
+#endif
+#ifndef NET_NAME_PREDICTABLE
+#define NET_NAME_PREDICTABLE 2
+#endif
+#ifndef NET_NAME_USER
+#define NET_NAME_USER 3
+#endif
+#ifndef NET_NAME_RENAMED
+#define NET_NAME_RENAMED 4
+#endif
+
+#define alloc_netdev_mqs(sizeof_priv, name, name_assign_type, setup, txqs, rxqs) \
+ alloc_netdev_mqs(sizeof_priv, name, setup, txqs, rxqs)
+
+#undef alloc_netdev
+#define alloc_netdev(sizeof_priv, name, name_assign_type, setup) \
+ alloc_netdev_mqs(sizeof_priv, name, name_assign_type, setup, 1, 1)
+
+#undef alloc_netdev_mq
+#define alloc_netdev_mq(sizeof_priv, name, name_assign_type, setup, count) \
+ alloc_netdev_mqs(sizeof_priv, name, name_assign_type, setup, count, \
+ count)
+#endif /* LINUX_VERSION_IS_LESS(3,17,0) */
+
+/*
+ * This backports this commit from upstream:
+ * commit 87757a917b0b3c0787e0563c679762152be81312
+ * net: force a list_del() in unregister_netdevice_many()
+ */
+#if (!(LINUX_VERSION_IS_GEQ(3,10,45) && \
+ LINUX_VERSION_IS_LESS(3,11,0)) && \
+ !(LINUX_VERSION_IS_GEQ(3,12,23) && \
+ LINUX_VERSION_IS_LESS(3,13,0)) && \
+ !(LINUX_VERSION_IS_GEQ(3,14,9) && \
+ LINUX_VERSION_IS_LESS(3,15,0)) && \
+ !(LINUX_VERSION_IS_GEQ(3,15,2) && \
+ LINUX_VERSION_IS_LESS(3,16,0)) && \
+ LINUX_VERSION_IS_LESS(3,16,0))
+static inline void backport_unregister_netdevice_many(struct list_head *head)
+{
+ unregister_netdevice_many(head);
+
+ if (!(head->next == LIST_POISON1 && head->prev == LIST_POISON2))
+ list_del(head);
+}
+#define unregister_netdevice_many LINUX_BACKPORT(unregister_netdevice_many)
+#endif
+
+#if LINUX_VERSION_IS_LESS(3,19,0)
+#define napi_alloc_frag(fragsz) netdev_alloc_frag(fragsz)
+#endif
+
+#if LINUX_VERSION_IS_LESS(3,19,0)
+/* RSS keys are 40 or 52 bytes long */
+#define NETDEV_RSS_KEY_LEN 52
+#define netdev_rss_key_fill LINUX_BACKPORT(netdev_rss_key_fill)
+void netdev_rss_key_fill(void *buffer, size_t len);
+#endif /* LINUX_VERSION_IS_LESS(3,19,0) */
+
+#if LINUX_VERSION_IS_LESS(3,19,0)
+#define napi_alloc_skb LINUX_BACKPORT(napi_alloc_skb)
+static inline struct sk_buff *napi_alloc_skb(struct napi_struct *napi,
+ unsigned int length)
+{
+ return netdev_alloc_skb_ip_align(napi->dev, length);
+}
+#endif /* LINUX_VERSION_IS_LESS(3,19,0) */
+
+#ifndef IFF_TX_SKB_SHARING
+#define IFF_TX_SKB_SHARING 0
+#endif
+
+#if LINUX_VERSION_IS_LESS(4,1,0)
+netdev_features_t passthru_features_check(struct sk_buff *skb,
+ struct net_device *dev,
+ netdev_features_t features);
+#endif /* LINUX_VERSION_IS_LESS(4,1,0) */
+
+#ifndef netdev_alloc_pcpu_stats
+#define netdev_alloc_pcpu_stats(type) \
+({ \
+ typeof(type) __percpu *pcpu_stats = alloc_percpu(type); \
+ if (pcpu_stats) { \
+ int i; \
+ for_each_possible_cpu(i) { \
+ typeof(type) *stat; \
+ stat = per_cpu_ptr(pcpu_stats, i); \
+ u64_stats_init(&stat->syncp); \
+ } \
+ } \
+ pcpu_stats; \
+})
+#endif /* netdev_alloc_pcpu_stats */
+
+#if LINUX_VERSION_IS_LESS(3,19,0)
+#define napi_complete_done LINUX_BACKPORT(napi_complete_done)
+static inline void napi_complete_done(struct napi_struct *n, int work_done)
+{
+ napi_complete(n);
+}
+#endif /* < 3.19 */
+
+#if LINUX_VERSION_IS_LESS(4,5,0)
+#define netif_tx_napi_add LINUX_BACKPORT(netif_tx_napi_add)
+/**
+ * netif_tx_napi_add - initialize a napi context
+ * @dev: network device
+ * @napi: napi context
+ * @poll: polling function
+ * @weight: default weight
+ *
+ * This variant of netif_napi_add() should be used from drivers using NAPI
+ * to exclusively poll a TX queue.
+ * This will avoid we add it into napi_hash[], thus polluting this hash table.
+ */
+static inline void netif_tx_napi_add(struct net_device *dev,
+ struct napi_struct *napi,
+ int (*poll)(struct napi_struct *, int),
+ int weight)
+{
+ netif_napi_add(dev, napi, poll, weight);
+}
+#endif /* < 4.5 */
+
+#ifndef NETIF_F_CSUM_MASK
+#define NETIF_F_CSUM_MASK NETIF_F_ALL_CSUM
+#endif
+
+#if LINUX_VERSION_IS_LESS(4,7,0)
+#define netif_trans_update LINUX_BACKPORT(netif_trans_update)
+static inline void netif_trans_update(struct net_device *dev)
+{
+ dev->trans_start = jiffies;
+}
+#endif
+
+#if LINUX_VERSION_IS_LESS(4,12,0)
+#define netdev_set_priv_destructor(_dev, _destructor) \
+ (_dev)->destructor = __ ## _destructor
+#define netdev_set_def_destructor(_dev) \
+ (_dev)->destructor = free_netdev
+#else
+#define netdev_set_priv_destructor(_dev, _destructor) \
+ (_dev)->needs_free_netdev = true; \
+ (_dev)->priv_destructor = (_destructor);
+#define netdev_set_def_destructor(_dev) \
+ (_dev)->needs_free_netdev = true;
+#endif
+
+#endif /* __BACKPORT_NETDEVICE_H */
diff --git a/backport-include/linux/netlink.h b/backport-include/linux/netlink.h
new file mode 100644
index 0000000..58fad58
--- /dev/null
+++ b/backport-include/linux/netlink.h
@@ -0,0 +1,32 @@
+#ifndef __BACKPORT_LINUX_NETLINK_H
+#define __BACKPORT_LINUX_NETLINK_H
+#include_next <linux/netlink.h>
+#include <linux/version.h>
+
+#if LINUX_VERSION_IS_LESS(4,12,0)
+#define NETLINK_MAX_COOKIE_LEN 20
+
+struct netlink_ext_ack {
+ const char *_msg;
+ const struct nlattr *bad_attr;
+ u8 cookie[NETLINK_MAX_COOKIE_LEN];
+ u8 cookie_len;
+};
+
+#define NL_SET_ERR_MSG(extack, msg) do { \
+ static const char _msg[] = (msg); \
+ \
+ (extack)->_msg = _msg; \
+} while (0)
+#endif
+
+/* this is for patches we apply */
+#if LINUX_VERSION_IS_LESS(3,7,0)
+#define netlink_notify_portid(__notify) (__notify->pid)
+#define NETLINK_CB_PORTID(__skb) NETLINK_CB(__skb).pid
+#else
+#define netlink_notify_portid(__notify) (__notify->portid)
+#define NETLINK_CB_PORTID(__skb) NETLINK_CB(__skb).portid
+#endif
+
+#endif /* __BACKPORT_LINUX_NETLINK_H */
diff --git a/backport-include/linux/nl80211.h b/backport-include/linux/nl80211.h
new file mode 100644
index 0000000..d49057a
--- /dev/null
+++ b/backport-include/linux/nl80211.h
@@ -0,0 +1,10 @@
+#ifndef __BACKPORT_LINUX_NL80211_H
+#define __BACKPORT_LINUX_NL80211_H
+#include_next <linux/nl80211.h>
+#include <linux/version.h>
+
+#if LINUX_VERSION_IS_LESS(3,3,0)
+#define NL80211_FEATURE_SK_TX_STATUS 0
+#endif
+
+#endif /* __BACKPORT_LINUX_NL80211_H */
diff --git a/backport-include/linux/of.h b/backport-include/linux/of.h
new file mode 100644
index 0000000..bbb5acd
--- /dev/null
+++ b/backport-include/linux/of.h
@@ -0,0 +1,259 @@
+#ifndef _COMPAT_LINUX_OF_H
+#define _COMPAT_LINUX_OF_H 1
+
+#include <linux/version.h>
+#include_next <linux/of.h>
+
+#if LINUX_VERSION_IS_LESS(3,7,0)
+#ifdef CONFIG_OF
+extern struct device_node *of_get_child_by_name(const struct device_node *node,
+ const char *name);
+#else
+static inline struct device_node *of_get_child_by_name(
+ const struct device_node *node,
+ const char *name)
+{
+ return NULL;
+}
+#endif /* CONFIG_OF */
+#endif /* LINUX_VERSION_IS_LESS(3,7,0) */
+
+#if LINUX_VERSION_IS_LESS(3,7,0)
+#ifndef CONFIG_OF
+static inline struct device_node *of_find_node_by_name(struct device_node *from,
+ const char *name)
+{
+ return NULL;
+}
+#endif /* CONFIG_OF */
+#endif /* LINUX_VERSION_IS_LESS(3,7,0) */
+
+#if LINUX_VERSION_IS_LESS(3,8,0)
+#define of_property_read_u8_array LINUX_BACKPORT(of_property_read_u8_array)
+#ifdef CONFIG_OF
+extern int of_property_read_u8_array(const struct device_node *np,
+ const char *propname, u8 *out_values, size_t sz);
+#else
+static inline int of_property_read_u8_array(const struct device_node *np,
+ const char *propname, u8 *out_values, size_t sz)
+{
+ return -ENOSYS;
+}
+#endif /* CONFIG_OF */
+#endif /* LINUX_VERSION_IS_LESS(3,8,0) */
+
+#if LINUX_VERSION_IS_LESS(3,1,0)
+#define of_property_read_u32_array LINUX_BACKPORT(of_property_read_u32_array)
+#ifdef CONFIG_OF
+extern int of_property_read_u32_array(const struct device_node *np,
+ const char *propname,
+ u32 *out_values,
+ size_t sz);
+#else
+static inline int of_property_read_u32_array(const struct device_node *np,
+ const char *propname,
+ u32 *out_values, size_t sz)
+{
+ return -ENOSYS;
+}
+#endif /* CONFIG_OF */
+#define of_property_read_u32 LINUX_BACKPORT(of_property_read_u32)
+static inline int of_property_read_u32(const struct device_node *np,
+ const char *propname,
+ u32 *out_value)
+{
+ return of_property_read_u32_array(np, propname, out_value, 1);
+}
+#ifndef CONFIG_OF
+#define of_get_property LINUX_BACKPORT(of_get_property)
+static inline const void *of_get_property(const struct device_node *node,
+ const char *name,
+ int *lenp)
+{
+ return NULL;
+}
+
+#endif
+#endif /* LINUX_VERSION_IS_LESS(3,1,0) */
+
+#if LINUX_VERSION_IS_LESS(3,10,0)
+#define of_property_read_u32_index LINUX_BACKPORT(of_property_read_u32_index)
+#ifdef CONFIG_OF
+extern int of_property_read_u32_index(const struct device_node *np,
+ const char *propname,
+ u32 index, u32 *out_value);
+#else
+static inline int of_property_read_u32_index(const struct device_node *np,
+ const char *propname, u32 index, u32 *out_value)
+{
+ return -ENOSYS;
+}
+#endif /* CONFIG_OF */
+#endif /* LINUX_VERSION_IS_LESS(3,10,0) */
+
+#if LINUX_VERSION_IS_LESS(3,15,0)
+#define of_property_count_elems_of_size LINUX_BACKPORT(of_property_count_elems_of_size)
+#ifdef CONFIG_OF
+extern int of_property_count_elems_of_size(const struct device_node *np,
+ const char *propname, int elem_size);
+#else
+static inline int of_property_count_elems_of_size(const struct device_node *np,
+ const char *propname, int elem_size)
+{
+ return -ENOSYS;
+}
+#endif /* CONFIG_OF */
+#endif /* LINUX_VERSION_IS_LESS(3,15,0) */
+
+
+#if LINUX_VERSION_IS_LESS(3,15,0)
+/**
+ * of_property_count_u32_elems - Count the number of u32 elements in a property
+ *
+ * @np: device node from which the property value is to be read.
+ * @propname: name of the property to be searched.
+ *
+ * Search for a property in a device node and count the number of u32 elements
+ * in it. Returns number of elements on sucess, -EINVAL if the property does
+ * not exist or its length does not match a multiple of u32 and -ENODATA if the
+ * property does not have a value.
+ */
+#define of_property_count_u32_elems LINUX_BACKPORT(of_property_count_u32_elems)
+static inline int of_property_count_u32_elems(const struct device_node *np,
+ const char *propname)
+{
+ return of_property_count_elems_of_size(np, propname, sizeof(u32));
+}
+#endif /* LINUX_VERSION_IS_LESS(3,15,0) */
+
+#if LINUX_VERSION_IS_LESS(3,3,0)
+#ifndef CONFIG_OF
+#define of_node_get LINUX_BACKPORT(of_node_get)
+/* Dummy ref counting routines - to be implemented later */
+static inline struct device_node *of_node_get(struct device_node *node)
+{
+ return node;
+}
+static inline void of_node_put(struct device_node *node) { }
+#endif /* CONFIG_OF */
+#endif /* LINUX_VERSION_IS_LESS(3,3,0) */
+
+#ifndef of_match_ptr
+#ifdef CONFIG_OF
+#define of_match_ptr(_ptr) (_ptr)
+#else
+#define of_match_ptr(_ptr) NULL
+#endif /* CONFIG_OF */
+#endif /* of_match_ptr */
+
+#ifndef for_each_compatible_node
+#define for_each_compatible_node(dn, type, compatible) \
+ for (dn = of_find_compatible_node(NULL, type, compatible); dn; \
+ dn = of_find_compatible_node(dn, type, compatible))
+#endif /* for_each_compatible_node */
+
+#if LINUX_VERSION_IS_LESS(3,3,0)
+#ifndef CONFIG_OF
+static inline struct device_node *of_find_compatible_node(
+ struct device_node *from,
+ const char *type,
+ const char *compat)
+{
+ return NULL;
+}
+#endif
+#endif
+
+#if LINUX_VERSION_IS_LESS(3,18,0)
+#define of_property_read_u64_array LINUX_BACKPORT(of_property_read_u64_array)
+#ifdef CONFIG_OF
+/* This is static in the kernel, but we need it in multiple places */
+void *of_find_property_value_of_size(const struct device_node *np,
+ const char *propname, u32 len);
+extern int of_property_read_u64_array(const struct device_node *np,
+ const char *propname,
+ u64 *out_values,
+ size_t sz);
+#else
+static inline int of_property_read_u64_array(const struct device_node *np,
+ const char *propname,
+ u64 *out_values, size_t sz)
+{
+ return -ENOSYS;
+}
+#endif /* CONFIG_OF */
+#endif /* LINUX_VERSION_IS_LESS(3,15,0) */
+
+#if LINUX_VERSION_IS_LESS(3,6,0)
+#define of_node_full_name LINUX_BACKPORT(of_node_full_name)
+#ifdef CONFIG_OF
+static inline const char *of_node_full_name(const struct device_node *np)
+{
+ return np ? np->full_name : "<no-node>";
+}
+#else
+static inline const char* of_node_full_name(const struct device_node *np)
+{
+ return "<no-node>";
+}
+#endif /* CONFIG_OF */
+#endif /* < 3.6 */
+
+#ifndef for_each_child_of_node
+#define for_each_child_of_node(parent, child) \
+ while (0)
+#endif
+
+#ifndef CONFIG_OF
+#if LINUX_VERSION_IS_LESS(3,10,0)
+static inline int of_device_is_available(const struct device_node *device)
+{
+ return 0;
+}
+#endif
+
+#if LINUX_VERSION_IS_LESS(3,6,0) && !LINUX_VERSION_IN_RANGE(3,2,70, 3,3,0)
+static inline int of_property_match_string(struct device_node *np,
+ const char *propname,
+ const char *string)
+{
+ return -ENOSYS;
+}
+#endif
+
+#if LINUX_VERSION_IS_LESS(3,2,0)
+static inline struct property *of_find_property(const struct device_node *np,
+ const char *name, int *lenp)
+{
+ return NULL;
+}
+
+static inline int of_device_is_compatible(const struct device_node *device,
+ const char *name)
+{
+ return 0;
+}
+
+static inline struct device_node *of_parse_phandle(struct device_node *np,
+ const char *phandle_name,
+ int index)
+{
+ return NULL;
+}
+
+#define of_match_node(_matches, _node) NULL
+#endif
+
+#endif /* CONFIG_OF */
+
+#if LINUX_VERSION_IS_LESS(3,4,0) && !LINUX_VERSION_IN_RANGE(3,2,44, 3,3,0)
+static inline bool of_property_read_bool(const struct device_node *np,
+ const char *propname)
+{
+ struct property *prop = of_find_property(np, propname, NULL);
+
+ return prop ? true : false;
+}
+#endif
+
+#endif /* _COMPAT_LINUX_OF_H */
diff --git a/backport-include/linux/of_address.h b/backport-include/linux/of_address.h
new file mode 100644
index 0000000..e3a5167
--- /dev/null
+++ b/backport-include/linux/of_address.h
@@ -0,0 +1,18 @@
+#ifndef __BACKPORT_OF_ADDRESS_H
+#define __BACKPORT_OF_ADDRESS_H
+#include_next <linux/of_address.h>
+#include <linux/version.h>
+
+#if LINUX_VERSION_IS_LESS(4,4,0) && !defined(CONFIG_OF_ADDRESS)
+#ifndef OF_BAD_ADDR
+#define OF_BAD_ADDR ((u64)-1)
+#endif
+#define of_translate_address LINUX_BACKPORT(of_translate_addres)
+static inline u64 of_translate_address(struct device_node *np,
+ const __be32 *addr)
+{
+ return OF_BAD_ADDR;
+}
+#endif /* LINUX_VERSION_IS_LESS(4,4,0) */
+
+#endif /* __BACKPORT_OF_IRQ_H */
diff --git a/backport-include/linux/of_device.h b/backport-include/linux/of_device.h
new file mode 100644
index 0000000..951b253
--- /dev/null
+++ b/backport-include/linux/of_device.h
@@ -0,0 +1,11 @@
+#ifndef __BP_OF_DEVICE_H
+#define __BP_OF_DEVICE_H
+#include_next <linux/of_device.h>
+#include <linux/version.h>
+
+#if LINUX_VERSION_IS_LESS(4,1,0)
+static inline void of_dma_configure(struct device *dev, struct device_node *np)
+{}
+#endif /* < 4.1.0 */
+
+#endif /* __BP_OF_DEVICE_H */
diff --git a/backport-include/linux/of_irq.h b/backport-include/linux/of_irq.h
new file mode 100644
index 0000000..58108e2
--- /dev/null
+++ b/backport-include/linux/of_irq.h
@@ -0,0 +1,15 @@
+#ifndef __BACKPORT_OF_IRQ_H
+#define __BACKPORT_OF_IRQ_H
+#include_next <linux/of_irq.h>
+#include <linux/version.h>
+
+#if LINUX_VERSION_IS_LESS(3,5,0) && !defined(CONFIG_OF)
+#define irq_of_parse_and_map LINUX_BACKPORT(irq_of_parse_and_map)
+static inline unsigned int irq_of_parse_and_map(struct device_node *dev,
+ int index)
+{
+ return 0;
+}
+#endif /* LINUX_VERSION_IS_LESS(4,5,0) */
+
+#endif /* __BACKPORT_OF_IRQ_H */
diff --git a/backport-include/linux/of_net.h b/backport-include/linux/of_net.h
new file mode 100644
index 0000000..7f4b688
--- /dev/null
+++ b/backport-include/linux/of_net.h
@@ -0,0 +1,15 @@
+#ifndef _BP_OF_NET_H
+#define _BP_OF_NET_H
+#include_next <linux/of_net.h>
+#include <linux/version.h>
+
+#ifndef CONFIG_OF
+#if LINUX_VERSION_IS_LESS(3,10,0)
+static inline const void *of_get_mac_address(struct device_node *np)
+{
+ return NULL;
+}
+#endif
+#endif
+
+#endif /* _BP_OF_NET_H */
diff --git a/backport-include/linux/of_platform.h b/backport-include/linux/of_platform.h
new file mode 100644
index 0000000..625ee9e
--- /dev/null
+++ b/backport-include/linux/of_platform.h
@@ -0,0 +1,38 @@
+#ifndef __BACKPORT_LINUX_OF_PLATFORM_H
+#define __BACKPORT_LINUX_OF_PLATFORM_H
+#include_next <linux/of_platform.h>
+#include <linux/version.h>
+#include <linux/of.h>
+/* upstream now includes this here and some people rely on it */
+#include <linux/of_device.h>
+
+#if LINUX_VERSION_IS_LESS(3,4,0) && !defined(CONFIG_OF_DEVICE)
+struct of_dev_auxdata;
+#define of_platform_populate LINUX_BACKPORT(of_platform_populate)
+static inline int of_platform_populate(struct device_node *root,
+ const struct of_device_id *matches,
+ const struct of_dev_auxdata *lookup,
+ struct device *parent)
+{
+ return -ENODEV;
+}
+#endif /* LINUX_VERSION_IS_LESS(3,4,0) */
+
+#if LINUX_VERSION_IS_LESS(3,11,0) && !defined(CONFIG_OF_DEVICE)
+extern const struct of_device_id of_default_bus_match_table[];
+#endif /* LINUX_VERSION_IS_LESS(3,11,0) */
+
+#if LINUX_VERSION_IS_LESS(4,3,0) && !defined(CONFIG_OF_DEVICE)
+struct of_dev_auxdata;
+#define of_platform_default_populate \
+ LINUX_BACKPORT(of_platform_default_populate)
+static inline int
+of_platform_default_populate(struct device_node *root,
+ const struct of_dev_auxdata *lookup,
+ struct device *parent)
+{
+ return -ENODEV;
+}
+#endif /* LINUX_VERSION_IS_LESS(4,3,0) */
+
+#endif /* __BACKPORT_LINUX_OF_PLATFORM_H */
diff --git a/backport-include/linux/olpc-ec.h b/backport-include/linux/olpc-ec.h
new file mode 100644
index 0000000..353107f
--- /dev/null
+++ b/backport-include/linux/olpc-ec.h
@@ -0,0 +1,10 @@
+#ifndef _COMPAT_LINUX_OLPC_EC_H
+#define _COMPAT_LINUX_OLPC_EC_H
+
+#include <linux/version.h>
+
+#if LINUX_VERSION_IS_GEQ(3,6,0)
+#include_next <linux/olpc-ec.h>
+#endif /* LINUX_VERSION_IS_GEQ(3,6,0) */
+
+#endif /* _COMPAT_LINUX_OLPC_EC_H */
diff --git a/backport-include/linux/page_ref.h b/backport-include/linux/page_ref.h
new file mode 100644
index 0000000..55fc14f
--- /dev/null
+++ b/backport-include/linux/page_ref.h
@@ -0,0 +1,13 @@
+#ifndef __BP_PAGE_REF_H
+#define __BP_PAGE_REF_H
+#include <linux/version.h>
+#if LINUX_VERSION_IS_GEQ(4,6,0)
+#include_next <linux/page_ref.h>
+#else
+static inline void page_ref_inc(struct page *page)
+{
+ atomic_inc(&page->_count);
+}
+#endif
+
+#endif /* __BP_PAGE_REF_H */
diff --git a/backport-include/linux/pci.h b/backport-include/linux/pci.h
new file mode 100644
index 0000000..81c2d57
--- /dev/null
+++ b/backport-include/linux/pci.h
@@ -0,0 +1,187 @@
+#ifndef _BACKPORT_LINUX_PCI_H
+#define _BACKPORT_LINUX_PCI_H
+#include_next <linux/pci.h>
+#include <linux/version.h>
+
+#ifndef module_pci_driver
+/**
+ * module_pci_driver() - Helper macro for registering a PCI driver
+ * @__pci_driver: pci_driver struct
+ *
+ * Helper macro for PCI drivers which do not do anything special in module
+ * init/exit. This eliminates a lot of boilerplate. Each module may only
+ * use this macro once, and calling it replaces module_init() and module_exit()
+ */
+#define module_pci_driver(__pci_driver) \
+ module_driver(__pci_driver, pci_register_driver, \
+ pci_unregister_driver)
+#endif
+
+#if LINUX_VERSION_IS_LESS(3,7,0)
+#define pcie_capability_read_word LINUX_BACKPORT(pcie_capability_read_word)
+int pcie_capability_read_word(struct pci_dev *dev, int pos, u16 *val);
+#define pcie_capability_read_dword LINUX_BACKPORT(pcie_capability_read_dword)
+int pcie_capability_read_dword(struct pci_dev *dev, int pos, u32 *val);
+#define pcie_capability_write_word LINUX_BACKPORT(pcie_capability_write_word)
+int pcie_capability_write_word(struct pci_dev *dev, int pos, u16 val);
+#define pcie_capability_write_dword LINUX_BACKPORT(pcie_capability_write_dword)
+int pcie_capability_write_dword(struct pci_dev *dev, int pos, u32 val);
+#define pcie_capability_clear_and_set_word LINUX_BACKPORT(pcie_capability_clear_and_set_word)
+int pcie_capability_clear_and_set_word(struct pci_dev *dev, int pos,
+ u16 clear, u16 set);
+#define pcie_capability_clear_and_set_dword LINUX_BACKPORT(pcie_capability_clear_and_set_dword)
+int pcie_capability_clear_and_set_dword(struct pci_dev *dev, int pos,
+ u32 clear, u32 set);
+
+#define pcie_capability_set_word LINUX_BACKPORT(pcie_capability_set_word)
+static inline int pcie_capability_set_word(struct pci_dev *dev, int pos,
+ u16 set)
+{
+ return pcie_capability_clear_and_set_word(dev, pos, 0, set);
+}
+
+#define pcie_capability_set_dword LINUX_BACKPORT(pcie_capability_set_dword)
+static inline int pcie_capability_set_dword(struct pci_dev *dev, int pos,
+ u32 set)
+{
+ return pcie_capability_clear_and_set_dword(dev, pos, 0, set);
+}
+
+#define pcie_capability_clear_word LINUX_BACKPORT(pcie_capability_clear_word)
+static inline int pcie_capability_clear_word(struct pci_dev *dev, int pos,
+ u16 clear)
+{
+ return pcie_capability_clear_and_set_word(dev, pos, clear, 0);
+}
+
+#define pcie_capability_clear_dword LINUX_BACKPORT(pcie_capability_clear_dword)
+static inline int pcie_capability_clear_dword(struct pci_dev *dev, int pos,
+ u32 clear)
+{
+ return pcie_capability_clear_and_set_dword(dev, pos, clear, 0);
+}
+#endif
+
+#ifndef PCI_DEVICE_SUB
+/**
+ * PCI_DEVICE_SUB - macro used to describe a specific pci device with subsystem
+ * @vend: the 16 bit PCI Vendor ID
+ * @dev: the 16 bit PCI Device ID
+ * @subvend: the 16 bit PCI Subvendor ID
+ * @subdev: the 16 bit PCI Subdevice ID
+ *
+ * This macro is used to create a struct pci_device_id that matches a
+ * specific device with subsystem information.
+ */
+#define PCI_DEVICE_SUB(vend, dev, subvend, subdev) \
+ .vendor = (vend), .device = (dev), \
+ .subvendor = (subvend), .subdevice = (subdev)
+#endif /* PCI_DEVICE_SUB */
+
+#if LINUX_VERSION_IS_LESS(3,2,0)
+#define pci_dev_flags LINUX_BACKPORT(pci_dev_flags)
+#define PCI_DEV_FLAGS_MSI_INTX_DISABLE_BUG LINUX_BACKPORT(PCI_DEV_FLAGS_MSI_INTX_DISABLE_BUG)
+#define PCI_DEV_FLAGS_NO_D3 LINUX_BACKPORT(PCI_DEV_FLAGS_NO_D3)
+#define PCI_DEV_FLAGS_ASSIGNED LINUX_BACKPORT(PCI_DEV_FLAGS_ASSIGNED)
+enum pci_dev_flags {
+ /* INTX_DISABLE in PCI_COMMAND register disables MSI
+ * generation too.
+ */
+ PCI_DEV_FLAGS_MSI_INTX_DISABLE_BUG = (__force pci_dev_flags_t) 1,
+ /* Device configuration is irrevocably lost if disabled into D3 */
+ PCI_DEV_FLAGS_NO_D3 = (__force pci_dev_flags_t) 2,
+ /* Provide indication device is assigned by a Virtual Machine Manager */
+ PCI_DEV_FLAGS_ASSIGNED = (__force pci_dev_flags_t) 4,
+};
+#endif /* LINUX_VERSION_IS_LESS(3,2,0) */
+
+#if LINUX_VERSION_IS_LESS(3,8,0)
+#define pci_sriov_set_totalvfs LINUX_BACKPORT(pci_sriov_set_totalvfs)
+int pci_sriov_set_totalvfs(struct pci_dev *dev, u16 numvfs);
+#endif /* LINUX_VERSION_IS_LESS(3,8,0) */
+
+#if LINUX_VERSION_IS_LESS(3,10,0)
+/* Taken from drivers/pci/pci.h */
+struct pci_sriov {
+ int pos; /* capability position */
+ int nres; /* number of resources */
+ u32 cap; /* SR-IOV Capabilities */
+ u16 ctrl; /* SR-IOV Control */
+ u16 total_VFs; /* total VFs associated with the PF */
+ u16 initial_VFs; /* initial VFs associated with the PF */
+ u16 num_VFs; /* number of VFs available */
+ u16 offset; /* first VF Routing ID offset */
+ u16 stride; /* following VF stride */
+ u32 pgsz; /* page size for BAR alignment */
+ u8 link; /* Function Dependency Link */
+ u16 driver_max_VFs; /* max num VFs driver supports */
+ struct pci_dev *dev; /* lowest numbered PF */
+ struct pci_dev *self; /* this PF */
+ struct mutex lock; /* lock for VF bus */
+ struct work_struct mtask; /* VF Migration task */
+ u8 __iomem *mstate; /* VF Migration State Array */
+};
+
+#define pci_vfs_assigned LINUX_BACKPORT(pci_vfs_assigned)
+#ifdef CONFIG_PCI_IOV
+int pci_vfs_assigned(struct pci_dev *dev);
+#else
+static inline int pci_vfs_assigned(struct pci_dev *dev)
+{
+ return 0;
+}
+#endif
+
+#endif /* LINUX_VERSION_IS_LESS(3,10,0) */
+
+#if LINUX_VERSION_IS_LESS(3,14,0)
+#define pci_enable_msi_range LINUX_BACKPORT(pci_enable_msi_range)
+#ifdef CONFIG_PCI_MSI
+int pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec);
+#else
+static inline int pci_enable_msi_range(struct pci_dev *dev, int minvec,
+ int maxvec)
+{ return -ENOSYS; }
+#endif
+#endif
+
+#ifdef CONFIG_PCI
+#if LINUX_VERSION_IS_LESS(3,14,0)
+#define pci_enable_msix_range LINUX_BACKPORT(pci_enable_msix_range)
+#ifdef CONFIG_PCI_MSI
+int pci_enable_msix_range(struct pci_dev *dev, struct msix_entry *entries,
+ int minvec, int maxvec);
+#else
+static inline int pci_enable_msix_range(struct pci_dev *dev,
+ struct msix_entry *entries, int minvec, int maxvec)
+{ return -ENOSYS; }
+#endif
+#endif
+#endif
+
+#if LINUX_VERSION_IS_LESS(3,13,0)
+#define pci_device_is_present LINUX_BACKPORT(pci_device_is_present)
+bool pci_device_is_present(struct pci_dev *pdev);
+#endif
+
+#ifdef CONFIG_PCI
+#if LINUX_VERSION_IS_LESS(3,14,0)
+#define pci_enable_msix_exact LINUX_BACKPORT(pci_enable_msix_exact)
+#ifdef CONFIG_PCI_MSI
+static inline int pci_enable_msix_exact(struct pci_dev *dev,
+ struct msix_entry *entries, int nvec)
+{
+ int rc = pci_enable_msix_range(dev, entries, nvec, nvec);
+ if (rc < 0)
+ return rc;
+ return 0;
+}
+#else
+static inline int pci_enable_msix_exact(struct pci_dev *dev,
+ struct msix_entry *entries, int nvec)
+{ return -ENOSYS; }
+#endif /* CONFIG_PCI_MSI */
+#endif
+#endif /* CONFIG_PCI */
+
+#endif /* _BACKPORT_LINUX_PCI_H */
diff --git a/backport-include/linux/phy.h b/backport-include/linux/phy.h
new file mode 100644
index 0000000..f331428
--- /dev/null
+++ b/backport-include/linux/phy.h
@@ -0,0 +1,75 @@
+#ifndef __BACKPORT_LINUX_PHY_H
+#define __BACKPORT_LINUX_PHY_H
+#include_next <linux/phy.h>
+#include <linux/compiler.h>
+#include <linux/version.h>
+
+#if LINUX_VERSION_IS_LESS(3,9,0)
+#define phy_connect(dev, bus_id, handler, interface) \
+ phy_connect(dev, bus_id, handler, 0, interface)
+#endif
+
+#if LINUX_VERSION_IS_LESS(4,5,0)
+#define phydev_name LINUX_BACKPORT(phydev_name)
+static inline const char *phydev_name(const struct phy_device *phydev)
+{
+ return dev_name(&phydev->dev);
+}
+
+#define mdiobus_is_registered_device LINUX_BACKPORT(mdiobus_is_registered_device)
+static inline bool mdiobus_is_registered_device(struct mii_bus *bus, int addr)
+{
+ return bus->phy_map[addr];
+}
+
+#define phy_attached_print LINUX_BACKPORT(phy_attached_print)
+void phy_attached_print(struct phy_device *phydev, const char *fmt, ...)
+ __printf(2, 3);
+#define phy_attached_info LINUX_BACKPORT(phy_attached_info)
+void phy_attached_info(struct phy_device *phydev);
+
+static inline int backport_mdiobus_register(struct mii_bus *bus)
+{
+ bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
+ if (!bus->irq) {
+ pr_err("mii_bus irq allocation failed\n");
+ return -ENOMEM;
+ }
+
+ memset(bus->irq, PHY_POLL, sizeof(int) * PHY_MAX_ADDR);
+
+/* in kernel 4.3 a #define for mdiobus_register is added to the kernel. */
+#ifndef mdiobus_register
+ return mdiobus_register(bus);
+#else
+ return __mdiobus_register(bus, THIS_MODULE);
+#endif
+}
+#ifdef mdiobus_register
+#undef mdiobus_register
+#endif
+#define mdiobus_register LINUX_BACKPORT(mdiobus_register)
+
+static inline void backport_mdiobus_unregister(struct mii_bus *bus)
+{
+ kfree(bus->irq);
+ mdiobus_unregister(bus);
+}
+#define mdiobus_unregister LINUX_BACKPORT(mdiobus_unregister)
+#endif /* < 4.5 */
+
+#if LINUX_VERSION_IS_LESS(4,5,0)
+#define phydev_get_addr LINUX_BACKPORT(phydev_get_addr)
+static inline int phydev_get_addr(struct phy_device *phydev)
+{
+ return phydev->addr;
+}
+#else
+#define phydev_get_addr LINUX_BACKPORT(phydev_get_addr)
+static inline int phydev_get_addr(struct phy_device *phydev)
+{
+ return phydev->mdio.addr;
+}
+#endif
+
+#endif /* __BACKPORT_LINUX_PHY_H */
diff --git a/backport-include/linux/platform_data/media/si4713.h b/backport-include/linux/platform_data/media/si4713.h
new file mode 100644
index 0000000..3d49c32
--- /dev/null
+++ b/backport-include/linux/platform_data/media/si4713.h
@@ -0,0 +1,10 @@
+#ifndef __BACKPORT_SI4713_H
+#define __BACKPORT_SI4713_H
+#include <linux/version.h>
+#if LINUX_VERSION_IS_GEQ(4,5,0)
+#include_next <linux/platform_data/media/si4713.h>
+#else
+#include <media/si4713.h>
+#endif /* < 4.5 */
+
+#endif /* __BACKPORT_SI4713_H */
diff --git a/backport-include/linux/platform_data/media/soc_camera_platform.h b/backport-include/linux/platform_data/media/soc_camera_platform.h
new file mode 100644
index 0000000..927bf8f
--- /dev/null
+++ b/backport-include/linux/platform_data/media/soc_camera_platform.h
@@ -0,0 +1,10 @@
+#ifndef __BACKPORT_SOC_CAMERA_H__
+#define __BACKPORT_SOC_CAMERA_H__
+#include <linux/version.h>
+#if LINUX_VERSION_IS_GEQ(4,5,0)
+#include_next <linux/platform_data/media/soc_camera_platform.h>
+#else
+#include <media/soc_camera_platform.h>
+#endif /* < 4.5 */
+
+#endif /* __BACKPORT_SOC_CAMERA_H__ */
diff --git a/backport-include/linux/platform_data/media/timb_radio.h b/backport-include/linux/platform_data/media/timb_radio.h
new file mode 100644
index 0000000..f1fb47a
--- /dev/null
+++ b/backport-include/linux/platform_data/media/timb_radio.h
@@ -0,0 +1,10 @@
+#ifndef __BACKPORT_TIMB_RADIO_
+#define __BACKPORT_TIMB_RADIO_
+#include <linux/version.h>
+#if LINUX_VERSION_IS_GEQ(4,5,0)
+#include_next <linux/platform_data/media/timb_radio.h>
+#else
+#include <media/timb_radio.h>
+#endif /* < 4.5 */
+
+#endif /* __BACKPORT_TIMB_RADIO_ */
diff --git a/backport-include/linux/platform_data/media/timb_video.h b/backport-include/linux/platform_data/media/timb_video.h
new file mode 100644
index 0000000..82ff788
--- /dev/null
+++ b/backport-include/linux/platform_data/media/timb_video.h
@@ -0,0 +1,10 @@
+#ifndef __BACKPORT_TIMB_VIDEO_
+#define __BACKPORT_TIMB_VIDEO_
+#include <linux/version.h>
+#if LINUX_VERSION_IS_GEQ(4,5,0)
+#include_next <linux/platform_data/media/timb_video.h>
+#else
+#include <media/timb_video.h>
+#endif /* < 4.5 */
+
+#endif /* __BACKPORT_TIMB_VIDEO_ */
diff --git a/backport-include/linux/platform_device.h b/backport-include/linux/platform_device.h
new file mode 100644
index 0000000..5b821ee
--- /dev/null
+++ b/backport-include/linux/platform_device.h
@@ -0,0 +1,36 @@
+#ifndef __BACKPORT_PLATFORM_DEVICE_H
+#define __BACKPORT_PLATFORM_DEVICE_H
+
+#include_next <linux/platform_device.h>
+#include <linux/version.h>
+
+#ifndef module_platform_driver_probe
+#define module_platform_driver_probe(__platform_driver, __platform_probe) \
+static int __init __platform_driver##_init(void) \
+{ \
+ return platform_driver_probe(&(__platform_driver), \
+ __platform_probe); \
+} \
+module_init(__platform_driver##_init); \
+static void __exit __platform_driver##_exit(void) \
+{ \
+ platform_driver_unregister(&(__platform_driver)); \
+} \
+module_exit(__platform_driver##_exit);
+#endif
+
+#ifndef PLATFORM_DEVID_NONE
+#define PLATFORM_DEVID_NONE (-1)
+#endif
+
+#ifndef PLATFORM_DEVID_AUTO
+#define PLATFORM_DEVID_AUTO (-1)
+#endif
+
+#ifndef module_platform_driver
+#define module_platform_driver(__platform_driver) \
+ module_driver(__platform_driver, platform_driver_register, \
+ platform_driver_unregister)
+#endif
+
+#endif /* __BACKPORT_PLATFORM_DEVICE_H */
diff --git a/backport-include/linux/pm.h b/backport-include/linux/pm.h
new file mode 100644
index 0000000..926b0bf
--- /dev/null
+++ b/backport-include/linux/pm.h
@@ -0,0 +1,17 @@
+#ifndef __BACKPORT_PM_H
+#define __BACKPORT_PM_H
+#include_next <linux/pm.h>
+
+#ifndef PM_EVENT_AUTO
+#define PM_EVENT_AUTO 0x0400
+#endif
+
+#ifndef PM_EVENT_SLEEP
+#define PM_EVENT_SLEEP (PM_EVENT_SUSPEND)
+#endif
+
+#ifndef PMSG_IS_AUTO
+#define PMSG_IS_AUTO(msg) (((msg).event & PM_EVENT_AUTO) != 0)
+#endif
+
+#endif /* __BACKPORT_PM_H */
diff --git a/backport-include/linux/pm_qos.h b/backport-include/linux/pm_qos.h
new file mode 100644
index 0000000..d5bfc9b
--- /dev/null
+++ b/backport-include/linux/pm_qos.h
@@ -0,0 +1,16 @@
+#ifndef _COMPAT_LINUX_PM_QOS_H
+#define _COMPAT_LINUX_PM_QOS_H 1
+
+#include <linux/version.h>
+
+#if LINUX_VERSION_IS_GEQ(3,2,0)
+#include_next <linux/pm_qos.h>
+#else
+#include <linux/pm_qos_params.h>
+#endif /* LINUX_VERSION_IS_GEQ(3,2,0) */
+
+#ifndef PM_QOS_DEFAULT_VALUE
+#define PM_QOS_DEFAULT_VALUE -1
+#endif
+
+#endif /* _COMPAT_LINUX_PM_QOS_H */
diff --git a/backport-include/linux/pm_runtime.h b/backport-include/linux/pm_runtime.h
new file mode 100644
index 0000000..d9163ee
--- /dev/null
+++ b/backport-include/linux/pm_runtime.h
@@ -0,0 +1,19 @@
+#ifndef __BACKPORT_PM_RUNTIME_H
+#define __BACKPORT_PM_RUNTIME_H
+#include_next <linux/pm_runtime.h>
+
+#if LINUX_VERSION_IS_LESS(3,9,0)
+#define pm_runtime_active LINUX_BACKPORT(pm_runtime_active)
+#ifdef CONFIG_PM
+static inline bool pm_runtime_active(struct device *dev)
+{
+ return dev->power.runtime_status == RPM_ACTIVE
+ || dev->power.disable_depth;
+}
+#else
+static inline bool pm_runtime_active(struct device *dev) { return true; }
+#endif /* CONFIG_PM */
+
+#endif /* LINUX_VERSION_IS_LESS(3,9,0) */
+
+#endif /* __BACKPORT_PM_RUNTIME_H */
diff --git a/backport-include/linux/pnp.h b/backport-include/linux/pnp.h
new file mode 100644
index 0000000..0a88455
--- /dev/null
+++ b/backport-include/linux/pnp.h
@@ -0,0 +1,19 @@
+#ifndef __BACKPORT_LINUX_PNP_H
+#define __BACKPORT_LINUX_PNP_H
+#include_next <linux/pnp.h>
+
+#ifndef module_pnp_driver
+/**
+ * module_pnp_driver() - Helper macro for registering a PnP driver
+ * @__pnp_driver: pnp_driver struct
+ *
+ * Helper macro for PnP drivers which do not do anything special in module
+ * init/exit. This eliminates a lot of boilerplate. Each module may only
+ * use this macro once, and calling it replaces module_init() and module_exit()
+ */
+#define module_pnp_driver(__pnp_driver) \
+ module_driver(__pnp_driver, pnp_register_driver, \
+ pnp_unregister_driver)
+#endif
+
+#endif /* __BACKPORT_LINUX_PNP_H */
diff --git a/backport-include/linux/poll.h b/backport-include/linux/poll.h
new file mode 100644
index 0000000..3eecd81
--- /dev/null
+++ b/backport-include/linux/poll.h
@@ -0,0 +1,20 @@
+#ifndef __BACKPORT_LINUX_POLL_H
+#define __BACKPORT_LINUX_POLL_H
+#include_next <linux/poll.h>
+#include <linux/version.h>
+
+#if LINUX_VERSION_IS_LESS(3,4,0)
+#define poll_does_not_wait LINUX_BACKPORT(poll_does_not_wait)
+static inline bool poll_does_not_wait(const poll_table *p)
+{
+ return p == NULL || p->qproc == NULL;
+}
+
+#define poll_requested_events LINUX_BACKPORT(poll_requested_events)
+static inline unsigned long poll_requested_events(const poll_table *p)
+{
+ return p ? p->key : ~0UL;
+}
+#endif /* < 3.4 */
+
+#endif /* __BACKPORT_LINUX_POLL_H */
diff --git a/backport-include/linux/printk.h b/backport-include/linux/printk.h
new file mode 100644
index 0000000..22851d7
--- /dev/null
+++ b/backport-include/linux/printk.h
@@ -0,0 +1,154 @@
+#ifndef _COMPAT_LINUX_PRINTK_H
+#define _COMPAT_LINUX_PRINTK_H 1
+
+#include <linux/version.h>
+#include_next <linux/printk.h>
+
+/* see pr_fmt at end of file */
+
+#if LINUX_VERSION_IS_LESS(3,9,0)
+/* backports 7a555613 */
+#if defined(CONFIG_DYNAMIC_DEBUG)
+#define dynamic_hex_dump(prefix_str, prefix_type, rowsize, \
+ groupsize, buf, len, ascii) \
+do { \
+ DEFINE_DYNAMIC_DEBUG_METADATA(descriptor, \
+ __builtin_constant_p(prefix_str) ? prefix_str : "hexdump");\
+ if (unlikely(descriptor.flags & _DPRINTK_FLAGS_PRINT)) \
+ print_hex_dump(KERN_DEBUG, prefix_str, \
+ prefix_type, rowsize, groupsize, \
+ buf, len, ascii); \
+} while (0)
+#define print_hex_dump_debug(prefix_str, prefix_type, rowsize, \
+ groupsize, buf, len, ascii) \
+ dynamic_hex_dump(prefix_str, prefix_type, rowsize, \
+ groupsize, buf, len, ascii)
+#else
+#define print_hex_dump_debug(prefix_str, prefix_type, rowsize, \
+ groupsize, buf, len, ascii) \
+ print_hex_dump(KERN_DEBUG, prefix_str, prefix_type, rowsize, \
+ groupsize, buf, len, ascii)
+#endif /* defined(CONFIG_DYNAMIC_DEBUG) */
+
+#endif /* LINUX_VERSION_IS_LESS(3,9,0) */
+
+#ifndef pr_warn
+#define pr_warn pr_warning
+#endif
+
+#ifndef printk_once
+#define printk_once(x...) ({ \
+ static bool __print_once; \
+ \
+ if (!__print_once) { \
+ __print_once = true; \
+ printk(x); \
+ } \
+})
+#endif
+
+#ifndef printk_ratelimited
+/*
+ * ratelimited messages with local ratelimit_state,
+ * no local ratelimit_state used in the !PRINTK case
+ */
+#ifdef CONFIG_PRINTK
+#define printk_ratelimited(fmt, ...) \
+({ \
+ static DEFINE_RATELIMIT_STATE(_rs, \
+ DEFAULT_RATELIMIT_INTERVAL, \
+ DEFAULT_RATELIMIT_BURST); \
+ \
+ if (__ratelimit(&_rs)) \
+ printk(fmt, ##__VA_ARGS__); \
+})
+#else
+#define printk_ratelimited(fmt, ...) \
+ no_printk(fmt, ##__VA_ARGS__)
+#endif
+#endif /* printk_ratelimited */
+
+#ifndef pr_emerg_ratelimited
+#define pr_emerg_ratelimited(fmt, ...) \
+ printk_ratelimited(KERN_EMERG pr_fmt(fmt), ##__VA_ARGS__)
+#endif /* pr_emerg_ratelimited */
+
+#ifndef pr_alert_ratelimited
+#define pr_alert_ratelimited(fmt, ...) \
+ printk_ratelimited(KERN_ALERT pr_fmt(fmt), ##__VA_ARGS__)
+#endif /* pr_alert_ratelimited */
+
+#ifndef pr_crit_ratelimited
+#define pr_crit_ratelimited(fmt, ...) \
+ printk_ratelimited(KERN_CRIT pr_fmt(fmt), ##__VA_ARGS__)
+#endif /* pr_crit_ratelimited */
+
+#ifndef pr_err_ratelimited
+#define pr_err_ratelimited(fmt, ...) \
+ printk_ratelimited(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__)
+#endif /* pr_err_ratelimited */
+
+#ifndef pr_warn_ratelimited
+#define pr_warn_ratelimited(fmt, ...) \
+ printk_ratelimited(KERN_WARNING pr_fmt(fmt), ##__VA_ARGS__)
+#endif /* pr_warn_ratelimited */
+
+#ifndef pr_notice_ratelimited
+#define pr_notice_ratelimited(fmt, ...) \
+ printk_ratelimited(KERN_NOTICE pr_fmt(fmt), ##__VA_ARGS__)
+#endif /* pr_notice_ratelimited */
+
+#ifndef pr_info_ratelimited
+#define pr_info_ratelimited(fmt, ...) \
+ printk_ratelimited(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__)
+#endif /* pr_info_ratelimited */
+
+/* no pr_cont_ratelimited, don't do that... */
+
+#ifndef pr_devel_ratelimited
+#if defined(DEBUG)
+#define pr_devel_ratelimited(fmt, ...) \
+ printk_ratelimited(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
+#else
+#define pr_devel_ratelimited(fmt, ...) \
+ no_printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
+#endif
+#endif /* pr_devel_ratelimited */
+
+#ifndef pr_debug_ratelimited
+/* If you are writing a driver, please use dev_dbg instead */
+#if defined(CONFIG_DYNAMIC_DEBUG)
+/* descriptor check is first to prevent flooding with "callbacks suppressed" */
+#define pr_debug_ratelimited(fmt, ...) \
+do { \
+ static DEFINE_RATELIMIT_STATE(_rs, \
+ DEFAULT_RATELIMIT_INTERVAL, \
+ DEFAULT_RATELIMIT_BURST); \
+ DEFINE_DYNAMIC_DEBUG_METADATA(descriptor, fmt); \
+ if (unlikely(descriptor.flags & _DPRINTK_FLAGS_PRINT) && \
+ __ratelimit(&_rs)) \
+ __dynamic_pr_debug(&descriptor, fmt, ##__VA_ARGS__); \
+} while (0)
+#elif defined(DEBUG)
+#define pr_debug_ratelimited(fmt, ...) \
+ printk_ratelimited(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
+#else
+#define pr_debug_ratelimited(fmt, ...) \
+ no_printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
+#endif
+#endif /* pr_debug_ratelimited */
+
+/* replace hex_dump_to_buffer() with a version which returns the length */
+#if LINUX_VERSION_IS_LESS(4,0,0)
+#define hex_dump_to_buffer LINUX_BACKPORT(hex_dump_to_buffer)
+extern int hex_dump_to_buffer(const void *buf, size_t len, int rowsize,
+ int groupsize, char *linebuf, size_t linebuflen,
+ bool ascii);
+#endif
+
+#endif /* _COMPAT_LINUX_PRINTK_H */
+
+/* This must be outside -- see also kernel.h */
+#ifndef pr_fmt
+#define pr_fmt(fmt) fmt
+#endif
diff --git a/backport-include/linux/proc_fs.h b/backport-include/linux/proc_fs.h
new file mode 100644
index 0000000..be239bf
--- /dev/null
+++ b/backport-include/linux/proc_fs.h
@@ -0,0 +1,29 @@
+#ifndef __BACKPORT_PROC_FS_H
+#define __BACKPORT_PROC_FS_H
+#include_next <linux/proc_fs.h>
+#include <linux/version.h>
+
+#if LINUX_VERSION_IS_LESS(3,10,0)
+
+#ifdef CONFIG_PROC_FS
+/*
+ * backport of:
+ * procfs: new helper - PDE_DATA(inode)
+ */
+#define PDE_DATA LINUX_BACKPORT(PDE_DATA)
+static inline void *PDE_DATA(const struct inode *inode)
+{
+ return PROC_I(inode)->pde->data;
+}
+extern void proc_set_size(struct proc_dir_entry *, loff_t);
+extern void proc_set_user(struct proc_dir_entry *, kuid_t, kgid_t);
+#else
+#define PDE_DATA LINUX_BACKPORT(PDE_DATA)
+static inline void *PDE_DATA(const struct inode *inode) {BUG(); return NULL;}
+static inline void proc_set_size(struct proc_dir_entry *de, loff_t size) {}
+static inline void proc_set_user(struct proc_dir_entry *de, kuid_t uid, kgid_t gid) {}
+#endif /* CONFIG_PROC_FS */
+
+#endif /* LINUX_VERSION_IS_LESS(3,10,0) */
+
+#endif /* __BACKPORT_PROC_FS_H */
diff --git a/backport-include/linux/ptp_clock_kernel.h b/backport-include/linux/ptp_clock_kernel.h
new file mode 100644
index 0000000..e4483c0
--- /dev/null
+++ b/backport-include/linux/ptp_clock_kernel.h
@@ -0,0 +1,40 @@
+#ifndef __BACKPORT_PTP_CLOCK_KERNEL_H
+#define __BACKPORT_PTP_CLOCK_KERNEL_H
+
+#include <linux/version.h>
+#include_next <linux/ptp_clock_kernel.h>
+
+#if LINUX_VERSION_IS_LESS(3,5,0)
+#include <linux/posix-clock.h>
+
+#define PTP_MAX_TIMESTAMPS 128
+#define PTP_BUF_TIMESTAMPS 30
+
+struct timestamp_event_queue {
+ struct ptp_extts_event buf[PTP_MAX_TIMESTAMPS];
+ int head;
+ int tail;
+ spinlock_t lock;
+};
+
+struct ptp_clock {
+ struct posix_clock clock;
+ struct device *dev;
+ struct ptp_clock_info *info;
+ dev_t devid;
+ int index; /* index into clocks.map */
+ struct pps_device *pps_source;
+ struct timestamp_event_queue tsevq; /* simple fifo for time stamps */
+ struct mutex tsevq_mux; /* one process at a time reading the fifo */
+ wait_queue_head_t tsev_wq;
+ int defunct; /* tells readers to go away when clock is being removed */
+};
+
+extern int ptp_clock_index(struct ptp_clock *ptp);
+#endif /* LINUX_VERSION_IS_LESS(3,5,0) */
+
+#if LINUX_VERSION_IS_LESS(3,7,0) && !defined(CONFIG_SUSE_KERNEL)
+#define ptp_clock_register(info,parent) ptp_clock_register(info)
+#endif /* LINUX_VERSION_IS_LESS(3,7,0) */
+
+#endif /* __BACKPORT_PTP_CLOCK_KERNEL_H */
diff --git a/backport-include/linux/random.h b/backport-include/linux/random.h
new file mode 100644
index 0000000..a9dda73
--- /dev/null
+++ b/backport-include/linux/random.h
@@ -0,0 +1,45 @@
+#ifndef __BACKPORT_RANDOM_H
+#define __BACKPORT_RANDOM_H
+#include_next <linux/random.h>
+#include <linux/version.h>
+
+#if (LINUX_VERSION_IS_GEQ(3,3,0) && LINUX_VERSION_IS_LESS(3,4,10)) || \
+ (LINUX_VERSION_IS_GEQ(3,1,0) && LINUX_VERSION_IS_LESS(3,2,27)) || \
+ LINUX_VERSION_IS_LESS(3,0,41)
+#define add_device_randomness LINUX_BACKPORT(add_device_randomness)
+static inline void add_device_randomness(const void *buf, unsigned int size)
+{
+}
+#endif
+
+#if LINUX_VERSION_IS_LESS(3,8,0)
+/* backports 496f2f9 */
+#define prandom_seed(_seed) srandom32(_seed)
+#define prandom_u32() random32()
+#define prandom_u32_state(_state) prandom32(_state)
+/* backport 6582c665d6b882dad8329e05749fbcf119f1ab88 */
+#define prandom_bytes LINUX_BACKPORT(prandom_bytes)
+void prandom_bytes(void *buf, int bytes);
+#endif
+
+#if LINUX_VERSION_IS_LESS(3,14,0)
+/**
+ * prandom_u32_max - returns a pseudo-random number in interval [0, ep_ro)
+ * @ep_ro: right open interval endpoint
+ *
+ * Returns a pseudo-random number that is in interval [0, ep_ro). Note
+ * that the result depends on PRNG being well distributed in [0, ~0U]
+ * u32 space. Here we use maximally equidistributed combined Tausworthe
+ * generator, that is, prandom_u32(). This is useful when requesting a
+ * random index of an array containing ep_ro elements, for example.
+ *
+ * Returns: pseudo-random number in interval [0, ep_ro)
+ */
+#define prandom_u32_max LINUX_BACKPORT(prandom_u32_max)
+static inline u32 prandom_u32_max(u32 ep_ro)
+{
+ return (u32)(((u64) prandom_u32() * ep_ro) >> 32);
+}
+#endif /* LINUX_VERSION_IS_LESS(3,14,0) */
+
+#endif /* __BACKPORT_RANDOM_H */
diff --git a/backport-include/linux/rculist.h b/backport-include/linux/rculist.h
new file mode 100644
index 0000000..9671e7c
--- /dev/null
+++ b/backport-include/linux/rculist.h
@@ -0,0 +1,57 @@
+#ifndef __BACKPORT_RCULIST_H
+#define __BACKPORT_RCULIST_H
+#include_next <linux/rculist.h>
+
+#if LINUX_VERSION_IS_LESS(3,9,0)
+#include <backport/magic.h>
+#define hlist_for_each_entry_rcu4(tpos, pos, head, member) \
+ for (pos = rcu_dereference_raw(hlist_first_rcu(head)); \
+ pos && \
+ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1; });\
+ pos = rcu_dereference_raw(hlist_next_rcu(pos)))
+
+#define hlist_for_each_entry_rcu3(pos, head, member) \
+ for (pos = hlist_entry_safe (rcu_dereference_raw(hlist_first_rcu(head)),\
+ typeof(*(pos)), member); \
+ pos; \
+ pos = hlist_entry_safe(rcu_dereference_raw(hlist_next_rcu( \
+ &(pos)->member)), typeof(*(pos)), member))
+
+#undef hlist_for_each_entry_rcu
+#define hlist_for_each_entry_rcu(...) \
+ macro_dispatcher(hlist_for_each_entry_rcu, __VA_ARGS__)(__VA_ARGS__)
+#endif /* < 3.9 */
+
+#ifndef list_for_each_entry_continue_rcu
+#define list_for_each_entry_continue_rcu(pos, head, member) \
+ for (pos = list_entry_rcu(pos->member.next, typeof(*pos), member); \
+ prefetch(pos->member.next), &pos->member != (head); \
+ pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
+#endif
+
+#ifndef list_entry_rcu
+#define list_entry_rcu(ptr, type, member) \
+ container_of(rcu_dereference(ptr), type, member)
+#endif
+
+#ifndef list_first_or_null_rcu
+/**
+ * list_first_or_null_rcu - get the first element from a list
+ * @ptr: the list head to take the element from.
+ * @type: the type of the struct this is embedded in.
+ * @member: the name of the list_struct within the struct.
+ *
+ * Note that if the list is empty, it returns NULL.
+ *
+ * This primitive may safely run concurrently with the _rcu list-mutation
+ * primitives such as list_add_rcu() as long as it's guarded by rcu_read_lock().
+ */
+#define list_first_or_null_rcu(ptr, type, member) \
+({ \
+ struct list_head *__ptr = (ptr); \
+ struct list_head *__next = ACCESS_ONCE(__ptr->next); \
+ likely(__ptr != __next) ? list_entry_rcu(__next, type, member) : NULL; \
+})
+#endif /* list_first_or_null_rcu */
+
+#endif /* __BACKPORT_RCULIST_H */
diff --git a/backport-include/linux/rcupdate.h b/backport-include/linux/rcupdate.h
new file mode 100644
index 0000000..b96ef49
--- /dev/null
+++ b/backport-include/linux/rcupdate.h
@@ -0,0 +1,44 @@
+#ifndef __BACKPORT_LINUX_RCUPDATE_H
+#define __BACKPORT_LINUX_RCUPDATE_H
+#include_next <linux/rcupdate.h>
+
+/*
+ * This adds a nested function everywhere kfree_rcu() was called. This
+ * function frees the memory and is given as a function to call_rcu().
+ * The rcu callback could happen every time also after the module was
+ * unloaded and this will cause problems. To address that problem, we
+ * put rcu_barrier() into each module_exit() in module.h.
+ */
+#if !defined(kfree_rcu)
+#define kfree_rcu(data, rcuhead) do { \
+ void __kfree_rcu_fn(struct rcu_head *rcu_head) \
+ { \
+ void *___ptr; \
+ ___ptr = container_of(rcu_head, typeof(*(data)), rcuhead);\
+ kfree(___ptr); \
+ } \
+ call_rcu(&(data)->rcuhead, __kfree_rcu_fn); \
+ } while (0)
+#endif
+
+#ifndef RCU_INIT_POINTER
+#define RCU_INIT_POINTER(p, v) \
+ p = (typeof(*v) __force __rcu *)(v)
+#endif
+
+#ifndef rcu_dereference_check
+#define rcu_dereference_check(p, c) rcu_dereference(p)
+#endif
+
+#ifndef rcu_dereference_protected
+#define rcu_dereference_protected(p, c) (p)
+#endif
+#ifndef rcu_access_pointer
+#define rcu_access_pointer(p) ACCESS_ONCE(p)
+#endif
+
+#ifndef rcu_dereference_raw
+#define rcu_dereference_raw(p) rcu_dereference(p)
+#endif
+
+#endif /* __BACKPORT_LINUX_RCUPDATE_H */
diff --git a/backport-include/linux/regmap.h b/backport-include/linux/regmap.h
new file mode 100644
index 0000000..4b73c46
--- /dev/null
+++ b/backport-include/linux/regmap.h
@@ -0,0 +1,51 @@
+#ifndef __BACKPORT_LINUX_REGMAP_H
+#define __BACKPORT_LINUX_REGMAP_H
+#include_next <linux/regmap.h>
+#include <linux/version.h>
+
+#if LINUX_VERSION_IS_LESS(3,5,0) && \
+ LINUX_VERSION_IS_GEQ(3,2,0)
+#define dev_get_regmap LINUX_BACKPORT(dev_get_regmap)
+static inline
+struct regmap *dev_get_regmap(struct device *dev, const char *name)
+{
+ return NULL;
+}
+#endif
+
+#if LINUX_VERSION_IS_LESS(3,4,0) && \
+ LINUX_VERSION_IS_GEQ(3,2,0)
+#if defined(CONFIG_REGMAP)
+#define devm_regmap_init LINUX_BACKPORT(devm_regmap_init)
+struct regmap *devm_regmap_init(struct device *dev,
+ const struct regmap_bus *bus,
+ const struct regmap_config *config);
+#if defined(CONFIG_REGMAP_I2C)
+#define devm_regmap_init_i2c LINUX_BACKPORT(devm_regmap_init_i2c)
+struct regmap *devm_regmap_init_i2c(struct i2c_client *i2c,
+ const struct regmap_config *config);
+#endif /* defined(CONFIG_REGMAP_I2C) */
+
+/*
+ * We can't backport these unless we try to backport
+ * the full regmap into core so warn if used.
+ * No drivers are using this yet anyway.
+ */
+#define regmap_raw_write_async LINUX_BACKPORT(regmap_raw_write_async)
+static inline int regmap_raw_write_async(struct regmap *map, unsigned int reg,
+ const void *val, size_t val_len)
+{
+ WARN_ONCE(1, "regmap API is disabled");
+ return -EINVAL;
+}
+
+#define regmap_async_complete LINUX_BACKPORT(regmap_async_complete)
+static inline void regmap_async_complete(struct regmap *map)
+{
+ WARN_ONCE(1, "regmap API is disabled");
+}
+
+#endif /* defined(CONFIG_REGMAP) */
+#endif /* 3.2 <= version < 3.4 */
+
+#endif /* __BACKPORT_LINUX_REGMAP_H */
diff --git a/backport-include/linux/regulator/driver.h b/backport-include/linux/regulator/driver.h
new file mode 100644
index 0000000..f88d9be
--- /dev/null
+++ b/backport-include/linux/regulator/driver.h
@@ -0,0 +1,33 @@
+/*
+ * driver.h -- SoC Regulator driver support.
+ *
+ * Copyright (C) 2007, 2008 Wolfson Microelectronics PLC.
+ *
+ * Author: Liam Girdwood <lrg@slimlogic.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Regulator Driver Interface.
+ */
+
+#ifndef __BACKPORT_LINUX_REGULATOR_DRIVER_H_
+#define __BACKPORT_LINUX_REGULATOR_DRIVER_H_
+
+#include <linux/version.h>
+#include_next <linux/regulator/driver.h>
+
+#if LINUX_VERSION_IS_LESS(3,13,0) && \
+ LINUX_VERSION_IS_GEQ(3,5,0)
+#define devm_regulator_register LINUX_BACKPORT(devm_regulator_register)
+struct regulator_dev *
+devm_regulator_register(struct device *dev,
+ const struct regulator_desc *regulator_desc,
+ const struct regulator_config *config);
+#define devm_regulator_unregister LINUX_BACKPORT(devm_regulator_unregister)
+void devm_regulator_unregister(struct device *dev, struct regulator_dev *rdev);
+#endif /* LINUX_VERSION_IS_LESS(3,13,0) &&
+ LINUX_VERSION_IS_GEQ(3,5,0) */
+
+#endif /* __BACKPORT_LINUX_REGULATOR_DRIVER_H_ */
diff --git a/backport-include/linux/rfkill.h b/backport-include/linux/rfkill.h
new file mode 100644
index 0000000..99015af
--- /dev/null
+++ b/backport-include/linux/rfkill.h
@@ -0,0 +1,167 @@
+#ifndef __COMPAT_RFKILL_H
+#define __COMPAT_RFKILL_H
+#include <linux/version.h>
+
+#if LINUX_VERSION_IS_GEQ(3,10,0)
+#include_next <linux/rfkill.h>
+#else
+/* API only slightly changed since then */
+#define rfkill_type old_rfkill_type
+#define RFKILL_TYPE_ALL OLD_RFKILL_TYPE_ALL
+#define RFKILL_TYPE_WLAN OLD_RFKILL_TYPE_WLAN
+#define RFKILL_TYPE_BLUETOOTH OLD_RFKILL_TYPE_BLUETOOTH
+#define RFKILL_TYPE_UWB OLD_RFKILL_TYPE_UWB
+#define RFKILL_TYPE_WIMAX OLD_RFKILL_TYPE_WIMAX
+#define RFKILL_TYPE_WWAN OLD_RFKILL_TYPE_WWAN
+#define RFKILL_TYPE_GPS OLD_RFKILL_TYPE_GPS
+#define RFKILL_TYPE_FM OLD_RFKILL_TYPE_FM
+#define RFKILL_TYPE_NFC OLD_RFKILL_TYPE_NFC
+#define NUM_RFKILL_TYPES OLD_NUM_RFKILL_TYPES
+#include_next <linux/rfkill.h>
+#undef rfkill_type
+#undef RFKILL_TYPE_ALL
+#undef RFKILL_TYPE_WLAN
+#undef RFKILL_TYPE_BLUETOOTH
+#undef RFKILL_TYPE_UWB
+#undef RFKILL_TYPE_WIMAX
+#undef RFKILL_TYPE_WWAN
+#undef RFKILL_TYPE_GPS
+#undef RFKILL_TYPE_FM
+#undef RFKILL_TYPE_NFC
+#undef NUM_RFKILL_TYPES
+#define HAVE_OLD_RFKILL
+
+/* this changes infrequently, backport manually */
+enum rfkill_type {
+ RFKILL_TYPE_ALL = 0,
+ RFKILL_TYPE_WLAN,
+ RFKILL_TYPE_BLUETOOTH,
+ RFKILL_TYPE_UWB,
+ RFKILL_TYPE_WIMAX,
+ RFKILL_TYPE_WWAN,
+ RFKILL_TYPE_GPS,
+ RFKILL_TYPE_FM,
+ RFKILL_TYPE_NFC,
+ NUM_RFKILL_TYPES,
+};
+
+static inline struct rfkill * __must_check
+backport_rfkill_alloc(const char *name,
+ struct device *parent,
+ const enum rfkill_type type,
+ const struct rfkill_ops *ops,
+ void *ops_data)
+{
+#ifdef HAVE_OLD_RFKILL
+ if ((unsigned int)type >= (unsigned int)OLD_NUM_RFKILL_TYPES)
+ return ERR_PTR(-ENODEV);
+ return rfkill_alloc(name, parent, (enum old_rfkill_type)type,
+ ops, ops_data);
+#else
+ return ERR_PTR(-ENODEV);
+#endif
+}
+#define rfkill_alloc backport_rfkill_alloc
+
+static inline int __must_check backport_rfkill_register(struct rfkill *rfkill)
+{
+ if (rfkill == ERR_PTR(-ENODEV))
+ return 0;
+#ifdef HAVE_OLD_RFKILL
+ return rfkill_register(rfkill);
+#else
+ return -EINVAL;
+#endif
+}
+#define rfkill_register backport_rfkill_register
+
+static inline void backport_rfkill_pause_polling(struct rfkill *rfkill)
+{
+#ifdef HAVE_OLD_RFKILL
+ rfkill_pause_polling(rfkill);
+#endif
+}
+#define rfkill_pause_polling backport_rfkill_pause_polling
+
+static inline void backport_rfkill_resume_polling(struct rfkill *rfkill)
+{
+#ifdef HAVE_OLD_RFKILL
+ rfkill_resume_polling(rfkill);
+#endif
+}
+#define rfkill_resume_polling backport_rfkill_resume_polling
+
+static inline void backport_rfkill_unregister(struct rfkill *rfkill)
+{
+#ifdef HAVE_OLD_RFKILL
+ if (rfkill == ERR_PTR(-ENODEV))
+ return;
+ rfkill_unregister(rfkill);
+#endif
+}
+#define rfkill_unregister backport_rfkill_unregister
+
+static inline void backport_rfkill_destroy(struct rfkill *rfkill)
+{
+#ifdef HAVE_OLD_RFKILL
+ if (rfkill == ERR_PTR(-ENODEV))
+ return;
+ rfkill_destroy(rfkill);
+#endif
+}
+#define rfkill_destroy backport_rfkill_destroy
+
+static inline bool backport_rfkill_set_hw_state(struct rfkill *rfkill,
+ bool blocked)
+{
+#ifdef HAVE_OLD_RFKILL
+ if (rfkill != ERR_PTR(-ENODEV))
+ return rfkill_set_hw_state(rfkill, blocked);
+#endif
+ return blocked;
+}
+#define rfkill_set_hw_state backport_rfkill_set_hw_state
+
+static inline bool backport_rfkill_set_sw_state(struct rfkill *rfkill,
+ bool blocked)
+{
+#ifdef HAVE_OLD_RFKILL
+ if (rfkill != ERR_PTR(-ENODEV))
+ return rfkill_set_sw_state(rfkill, blocked);
+#endif
+ return blocked;
+}
+#define rfkill_set_sw_state backport_rfkill_set_sw_state
+
+static inline void backport_rfkill_init_sw_state(struct rfkill *rfkill,
+ bool blocked)
+{
+#ifdef HAVE_OLD_RFKILL
+ if (rfkill != ERR_PTR(-ENODEV))
+ rfkill_init_sw_state(rfkill, blocked);
+#endif
+}
+#define rfkill_init_sw_state backport_rfkill_init_sw_state
+
+static inline void backport_rfkill_set_states(struct rfkill *rfkill,
+ bool sw, bool hw)
+{
+#ifdef HAVE_OLD_RFKILL
+ if (rfkill != ERR_PTR(-ENODEV))
+ rfkill_set_states(rfkill, sw, hw);
+#endif
+}
+#define rfkill_set_states backport_rfkill_set_states
+
+static inline bool backport_rfkill_blocked(struct rfkill *rfkill)
+{
+#ifdef HAVE_OLD_RFKILL
+ if (rfkill != ERR_PTR(-ENODEV))
+ return rfkill_blocked(rfkill);
+#endif
+ return false;
+}
+#define rfkill_blocked backport_rfkill_blocked
+#endif
+
+#endif
diff --git a/backport-include/linux/rtnetlink.h b/backport-include/linux/rtnetlink.h
new file mode 100644
index 0000000..59beffa
--- /dev/null
+++ b/backport-include/linux/rtnetlink.h
@@ -0,0 +1,29 @@
+#ifndef __BACKPORT_LINUX_RTNETLINK_H
+#define __BACKPORT_LINUX_RTNETLINK_H
+#include_next <linux/rtnetlink.h>
+
+#ifndef rtnl_dereference
+#define rtnl_dereference(p) \
+ rcu_dereference_protected(p, lockdep_rtnl_is_held())
+#endif
+
+#ifndef rcu_dereference_rtnl
+#define rcu_dereference_rtnl(p) \
+ rcu_dereference_check(p, rcu_read_lock_held() || \
+ lockdep_rtnl_is_held())
+#endif
+
+#if LINUX_VERSION_IS_LESS(3,19,0)
+#define ndo_dflt_fdb_add(ndm, tb, dev, addr, vid, flags) \
+ ndo_dflt_fdb_add(ndm, tb, dev, addr, flags)
+#endif
+
+#if LINUX_VERSION_IS_LESS(3,13,0) && \
+ !defined(CONFIG_PROVE_LOCKING)
+static inline bool lockdep_rtnl_is_held(void)
+{
+ return true;
+}
+#endif
+
+#endif /* __BACKPORT_LINUX_RTNETLINK_H */
diff --git a/backport-include/linux/scatterlist.h b/backport-include/linux/scatterlist.h
new file mode 100644
index 0000000..bfc7449
--- /dev/null
+++ b/backport-include/linux/scatterlist.h
@@ -0,0 +1,105 @@
+#ifndef __BACKPORT_SCATTERLIST_H
+#define __BACKPORT_SCATTERLIST_H
+#include_next <linux/scatterlist.h>
+
+#if LINUX_VERSION_IS_LESS(3,7,0)
+int sg_nents(struct scatterlist *sg);
+#endif
+
+#if LINUX_VERSION_IS_LESS(3, 9, 0)
+
+/*
+ * sg page iterator
+ *
+ * Iterates over sg entries page-by-page. On each successful iteration,
+ * @piter->page points to the current page, @piter->sg to the sg holding this
+ * page and @piter->sg_pgoffset to the page's page offset within the sg. The
+ * iteration will stop either when a maximum number of sg entries was reached
+ * or a terminating sg (sg_last(sg) == true) was reached.
+ */
+struct sg_page_iter {
+ struct page *page; /* current page */
+ struct scatterlist *sg; /* sg holding the page */
+ unsigned int sg_pgoffset; /* page offset within the sg */
+
+ /* these are internal states, keep away */
+ unsigned int __nents; /* remaining sg entries */
+ int __pg_advance; /* nr pages to advance at the
+ * next step */
+};
+
+struct backport_sg_mapping_iter {
+ /* the following three fields can be accessed directly */
+ struct page *page; /* currently mapped page */
+ void *addr; /* pointer to the mapped area */
+ size_t length; /* length of the mapped area */
+ size_t consumed; /* number of consumed bytes */
+ struct sg_page_iter piter; /* page iterator */
+
+ /* these are internal states, keep away */
+ unsigned int __offset; /* offset within page */
+ unsigned int __remaining; /* remaining bytes on page */
+ unsigned int __flags;
+};
+#define sg_mapping_iter LINUX_BACKPORT(sg_mapping_iter)
+
+/**
+ * sg_page_iter_page - get the current page held by the page iterator
+ * @piter: page iterator holding the page
+ */
+static inline struct page *sg_page_iter_page(struct sg_page_iter *piter)
+{
+ return nth_page(sg_page(piter->sg), piter->sg_pgoffset);
+}
+
+bool __sg_page_iter_next(struct sg_page_iter *piter);
+void __sg_page_iter_start(struct sg_page_iter *piter,
+ struct scatterlist *sglist, unsigned int nents,
+ unsigned long pgoffset);
+
+void backport_sg_miter_start(struct sg_mapping_iter *miter, struct scatterlist *sgl,
+ unsigned int nents, unsigned int flags);
+bool backport_sg_miter_next(struct sg_mapping_iter *miter);
+void backport_sg_miter_stop(struct sg_mapping_iter *miter);
+#define sg_miter_start LINUX_BACKPORT(sg_miter_start)
+#define sg_miter_next LINUX_BACKPORT(sg_miter_next)
+#define sg_miter_stop LINUX_BACKPORT(sg_miter_stop)
+
+/**
+ * for_each_sg_page - iterate over the pages of the given sg list
+ * @sglist: sglist to iterate over
+ * @piter: page iterator to hold current page, sg, sg_pgoffset
+ * @nents: maximum number of sg entries to iterate over
+ * @pgoffset: starting page offset
+ */
+#define for_each_sg_page(sglist, piter, nents, pgoffset) \
+ for (__sg_page_iter_start((piter), (sglist), (nents), (pgoffset)); \
+ __sg_page_iter_next(piter);)
+
+#endif /* LINUX_VERSION_IS_LESS(3, 9, 0) */
+
+#if LINUX_VERSION_IS_LESS(3, 11, 0)
+size_t sg_copy_buffer(struct scatterlist *sgl, unsigned int nents, void *buf,
+ size_t buflen, off_t skip, bool to_buffer);
+
+#define sg_pcopy_to_buffer LINUX_BACKPORT(sg_pcopy_to_buffer)
+
+static inline
+size_t sg_pcopy_to_buffer(struct scatterlist *sgl, unsigned int nents,
+ void *buf, size_t buflen, off_t skip)
+{
+ return sg_copy_buffer(sgl, nents, buf, buflen, skip, true);
+}
+
+#define sg_pcopy_from_buffer LINUX_BACKPORT(sg_pcopy_from_buffer)
+
+static inline
+size_t sg_pcopy_from_buffer(struct scatterlist *sgl, unsigned int nents,
+ void *buf, size_t buflen, off_t skip)
+{
+ return sg_copy_buffer(sgl, nents, buf, buflen, skip, false);
+}
+
+#endif /* LINUX_VERSION_IS_LESS(3, 11, 0) */
+
+#endif /* __BACKPORT_SCATTERLIST_H */
diff --git a/backport-include/linux/sched/signal.h b/backport-include/linux/sched/signal.h
new file mode 100644
index 0000000..ec89925
--- /dev/null
+++ b/backport-include/linux/sched/signal.h
@@ -0,0 +1,10 @@
+#ifndef _BACKPORT_LINUX_SCHED_SIGNAL_H
+#define _BACKPORT_LINUX_SCHED_SIGNAL_H
+
+#if LINUX_VERSION_IS_LESS(4, 11, 0)
+#include <linux/sched.h>
+#else
+#include_next <linux/sched/signal.h>
+#endif
+
+#endif /* _BACKPORT_LINUX_SCHED_SIGNAL_H */
diff --git a/backport-include/linux/security.h b/backport-include/linux/security.h
new file mode 100644
index 0000000..2dfb964
--- /dev/null
+++ b/backport-include/linux/security.h
@@ -0,0 +1,18 @@
+#ifndef __BACKPORT_LINUX_SECURITY_H
+#define __BACKPORT_LINUX_SECURITY_H
+#include_next <linux/security.h>
+
+#if LINUX_VERSION_IS_LESS(3,1,0)
+/*
+ * This has been defined in include/linux/security.h for some time, but was
+ * only given an EXPORT_SYMBOL for 3.1. Add a compat_* definition to avoid
+ * breaking the compile.
+ */
+#define security_sk_clone(a, b) compat_security_sk_clone(a, b)
+
+static inline void security_sk_clone(const struct sock *sk, struct sock *newsk)
+{
+}
+#endif
+
+#endif /* __BACKPORT_LINUX_SECURITY_H */
diff --git a/backport-include/linux/seq_file.h b/backport-include/linux/seq_file.h
new file mode 100644
index 0000000..ad1bded
--- /dev/null
+++ b/backport-include/linux/seq_file.h
@@ -0,0 +1,51 @@
+#ifndef __BACKPORT_SEQ_FILE_H
+#define __BACKPORT_SEQ_FILE_H
+#include_next <linux/seq_file.h>
+#include <linux/version.h>
+
+#if LINUX_VERSION_IS_LESS(3,7,0)
+#include <linux/user_namespace.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#ifdef CONFIG_USER_NS
+static inline struct user_namespace *seq_user_ns(struct seq_file *seq)
+{
+ struct file *f = container_of((void *) seq, struct file, private_data);
+
+ return f->f_cred->user_ns;
+}
+#else
+static inline struct user_namespace *seq_user_ns(struct seq_file *seq)
+{
+ extern struct user_namespace init_user_ns;
+ return &init_user_ns;
+}
+#endif /* CONFIG_USER_NS */
+#endif /* < 3.7 */
+
+#if LINUX_VERSION_IS_LESS(3,19,0)
+#define seq_has_overflowed LINUX_BACKPORT(seq_has_overflowed)
+/**
+ * seq_has_overflowed - check if the buffer has overflowed
+ * @m: the seq_file handle
+ *
+ * seq_files have a buffer which may overflow. When this happens a larger
+ * buffer is reallocated and all the data will be printed again.
+ * The overflow state is true when m->count == m->size.
+ *
+ * Returns true if the buffer received more than it can hold.
+ */
+static inline bool seq_has_overflowed(struct seq_file *m)
+{
+ return m->count == m->size;
+}
+#endif
+
+#if LINUX_VERSION_IS_LESS(4,3,0)
+#define seq_hex_dump LINUX_BACKPORT(seq_hex_dump)
+void seq_hex_dump(struct seq_file *m, const char *prefix_str, int prefix_type,
+ int rowsize, int groupsize, const void *buf, size_t len,
+ bool ascii);
+#endif
+
+#endif /* __BACKPORT_SEQ_FILE_H */
diff --git a/backport-include/linux/skbuff.h b/backport-include/linux/skbuff.h
new file mode 100644
index 0000000..dc95748
--- /dev/null
+++ b/backport-include/linux/skbuff.h
@@ -0,0 +1,318 @@
+#ifndef __BACKPORT_SKBUFF_H
+#define __BACKPORT_SKBUFF_H
+#include_next <linux/skbuff.h>
+#include <linux/version.h>
+#include <generated/utsrelease.h>
+
+#if LINUX_VERSION_IS_LESS(3,4,0) && \
+ (RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(6,4)) && \
+ !(defined(CONFIG_SUSE_KERNEL) && LINUX_VERSION_IS_GEQ(3,0,0))
+#define skb_add_rx_frag(skb, i, page, off, size, truesize) \
+ skb_add_rx_frag(skb, i, page, off, size)
+#endif
+
+#if LINUX_VERSION_IS_LESS(3,3,0)
+#define __pskb_copy LINUX_BACKPORT(__pskb_copy)
+extern struct sk_buff *__pskb_copy(struct sk_buff *skb,
+ int headroom, gfp_t gfp_mask);
+
+#define skb_complete_wifi_ack LINUX_BACKPORT(skb_complete_wifi_ack)
+static inline void skb_complete_wifi_ack(struct sk_buff *skb, bool acked)
+{
+ WARN_ON(1);
+}
+
+/* define to 0 so checks for it are always false */
+#define SKBTX_WIFI_STATUS 0
+#elif LINUX_VERSION_IS_LESS(3,18,0)
+#define skb_complete_wifi_ack LINUX_BACKPORT(skb_complete_wifi_ack)
+void skb_complete_wifi_ack(struct sk_buff *skb, bool acked);
+#endif
+
+#if LINUX_VERSION_IS_LESS(3,2,0)
+#include <linux/dma-mapping.h>
+
+/* mask skb_frag_page as RHEL6 backports this */
+#define skb_frag_page LINUX_BACKPORT(skb_frag_page)
+static inline struct page *skb_frag_page(const skb_frag_t *frag)
+{
+ return frag->page;
+}
+
+#define skb_frag_size LINUX_BACKPORT(skb_frag_size)
+static inline unsigned int skb_frag_size(const skb_frag_t *frag)
+{
+ return frag->size;
+}
+
+/* mask skb_frag_dma_map as RHEL6 backports this */
+#define skb_frag_dma_map LINUX_BACKPORT(skb_frag_dma_map)
+static inline dma_addr_t skb_frag_dma_map(struct device *dev,
+ const skb_frag_t *frag,
+ size_t offset, size_t size,
+ enum dma_data_direction dir)
+{
+ return dma_map_page(dev, skb_frag_page(frag),
+ frag->page_offset + offset, size, dir);
+}
+#endif
+
+#if LINUX_VERSION_IS_LESS(3,1,0)
+/* mask __netdev_alloc_skb_ip_align as RHEL6 backports this */
+#define __netdev_alloc_skb_ip_align(a,b,c) compat__netdev_alloc_skb_ip_align(a,b,c)
+static inline struct sk_buff *__netdev_alloc_skb_ip_align(struct net_device *dev,
+ unsigned int length, gfp_t gfp)
+{
+ struct sk_buff *skb = __netdev_alloc_skb(dev, length + NET_IP_ALIGN, gfp);
+
+ if (NET_IP_ALIGN && skb)
+ skb_reserve(skb, NET_IP_ALIGN);
+ return skb;
+}
+#endif
+
+#ifndef skb_walk_frags
+#define skb_walk_frags(skb, iter) \
+ for (iter = skb_shinfo(skb)->frag_list; iter; iter = iter->next)
+#endif
+
+#if LINUX_VERSION_IS_LESS(3,2,0)
+#define skb_frag_size_sub LINUX_BACKPORT(skb_frag_size_sub)
+static inline void skb_frag_size_sub(skb_frag_t *frag, int delta)
+{
+ frag->size -= delta;
+}
+
+/**
+ * skb_frag_address - gets the address of the data contained in a paged fragment
+ * @frag: the paged fragment buffer
+ *
+ * Returns the address of the data within @frag. The page must already
+ * be mapped.
+ */
+#define skb_frag_address LINUX_BACKPORT(skb_frag_address)
+static inline void *skb_frag_address(const skb_frag_t *frag)
+{
+ return page_address(skb_frag_page(frag)) + frag->page_offset;
+}
+#endif /* LINUX_VERSION_IS_LESS(3,2,0) */
+
+#if LINUX_VERSION_IS_LESS(3,9,0)
+#ifndef NETDEV_FRAG_PAGE_MAX_ORDER
+#define NETDEV_FRAG_PAGE_MAX_ORDER get_order(32768)
+#endif
+#ifndef NETDEV_FRAG_PAGE_MAX_SIZE
+#define NETDEV_FRAG_PAGE_MAX_SIZE (PAGE_SIZE << NETDEV_FRAG_PAGE_MAX_ORDER)
+#endif
+#endif /* LINUX_VERSION_IS_LESS(3,9,0) */
+
+#if LINUX_VERSION_IS_LESS(3,9,0)
+#define skb_unclone LINUX_BACKPORT(skb_unclone)
+static inline int skb_unclone(struct sk_buff *skb, gfp_t pri)
+{
+ might_sleep_if(pri & __GFP_WAIT);
+ if (skb_cloned(skb))
+ return pskb_expand_head(skb, 0, 0, pri);
+ return 0;
+}
+#endif
+
+#if LINUX_VERSION_IS_LESS(3,2,0)
+
+#define skb_frag_address_safe LINUX_BACKPORT(skb_frag_address_safe)
+/**
+ * skb_frag_address_safe - gets the address of the data contained in a paged fragment
+ * @frag: the paged fragment buffer
+ *
+ * Returns the address of the data within @frag. Checks that the page
+ * is mapped and returns %NULL otherwise.
+ */
+static inline void *skb_frag_address_safe(const skb_frag_t *frag)
+{
+ void *ptr = page_address(skb_frag_page(frag));
+ if (unlikely(!ptr))
+ return NULL;
+
+ return ptr + frag->page_offset;
+}
+#endif /* LINUX_VERSION_IS_LESS(3,2,0) */
+
+#if LINUX_VERSION_IS_LESS(3,14,0) && \
+ RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(7,0) && \
+ !(LINUX_VERSION_CODE == KERNEL_VERSION(3,13,11) && UTS_UBUNTU_RELEASE_ABI > 30)
+/*
+ * Packet hash types specify the type of hash in skb_set_hash.
+ *
+ * Hash types refer to the protocol layer addresses which are used to
+ * construct a packet's hash. The hashes are used to differentiate or identify
+ * flows of the protocol layer for the hash type. Hash types are either
+ * layer-2 (L2), layer-3 (L3), or layer-4 (L4).
+ *
+ * Properties of hashes:
+ *
+ * 1) Two packets in different flows have different hash values
+ * 2) Two packets in the same flow should have the same hash value
+ *
+ * A hash at a higher layer is considered to be more specific. A driver should
+ * set the most specific hash possible.
+ *
+ * A driver cannot indicate a more specific hash than the layer at which a hash
+ * was computed. For instance an L3 hash cannot be set as an L4 hash.
+ *
+ * A driver may indicate a hash level which is less specific than the
+ * actual layer the hash was computed on. For instance, a hash computed
+ * at L4 may be considered an L3 hash. This should only be done if the
+ * driver can't unambiguously determine that the HW computed the hash at
+ * the higher layer. Note that the "should" in the second property above
+ * permits this.
+ */
+enum pkt_hash_types {
+ PKT_HASH_TYPE_NONE, /* Undefined type */
+ PKT_HASH_TYPE_L2, /* Input: src_MAC, dest_MAC */
+ PKT_HASH_TYPE_L3, /* Input: src_IP, dst_IP */
+ PKT_HASH_TYPE_L4, /* Input: src_IP, dst_IP, src_port, dst_port */
+};
+
+static inline void
+skb_set_hash(struct sk_buff *skb, __u32 hash, enum pkt_hash_types type)
+{
+#if LINUX_VERSION_IS_GEQ(3,2,0) /* 4031ae6edb */
+ skb->l4_rxhash = (type == PKT_HASH_TYPE_L4);
+#endif
+#if LINUX_VERSION_IS_GEQ(3,4,0) /* bdeab99191 */
+ skb->rxhash = hash;
+#endif
+}
+#endif /* LINUX_VERSION_IS_LESS(3,14,0) */
+
+#if LINUX_VERSION_IS_LESS(3,16,0)
+#define __pskb_copy_fclone LINUX_BACKPORT(__pskb_copy_fclone)
+static inline struct sk_buff *__pskb_copy_fclone(struct sk_buff *skb,
+ int headroom, gfp_t gfp_mask,
+ bool fclone)
+{
+ return __pskb_copy(skb, headroom, gfp_mask);
+}
+#endif
+
+#if LINUX_VERSION_IS_LESS(3,18,0)
+#define skb_clone_sk LINUX_BACKPORT(skb_clone_sk)
+struct sk_buff *skb_clone_sk(struct sk_buff *skb);
+#endif
+
+#if LINUX_VERSION_IS_LESS(3,19,0)
+/**
+ * __dev_alloc_pages - allocate page for network Rx
+ * @gfp_mask: allocation priority. Set __GFP_NOMEMALLOC if not for network Rx
+ * @order: size of the allocation
+ *
+ * Allocate a new page.
+ *
+ * %NULL is returned if there is no free memory.
+*/
+#define __dev_alloc_pages LINUX_BACKPORT(__dev_alloc_pages)
+static inline struct page *__dev_alloc_pages(gfp_t gfp_mask,
+ unsigned int order)
+{
+ /* This piece of code contains several assumptions.
+ * 1. This is for device Rx, therefor a cold page is preferred.
+ * 2. The expectation is the user wants a compound page.
+ * 3. If requesting a order 0 page it will not be compound
+ * due to the check to see if order has a value in prep_new_page
+ * 4. __GFP_MEMALLOC is ignored if __GFP_NOMEMALLOC is set due to
+ * code in gfp_to_alloc_flags that should be enforcing this.
+ */
+ gfp_mask |= __GFP_COLD | __GFP_COMP;
+#if LINUX_VERSION_IS_GEQ(3,6,0)
+ gfp_mask |= __GFP_MEMALLOC;
+#endif
+
+ return alloc_pages_node(NUMA_NO_NODE, gfp_mask, order);
+}
+
+#define dev_alloc_pages LINUX_BACKPORT(dev_alloc_pages)
+static inline struct page *dev_alloc_pages(unsigned int order)
+{
+ return __dev_alloc_pages(GFP_ATOMIC, order);
+}
+
+/**
+ * __dev_alloc_page - allocate a page for network Rx
+ * @gfp_mask: allocation priority. Set __GFP_NOMEMALLOC if not for network Rx
+ *
+ * Allocate a new page.
+ *
+ * %NULL is returned if there is no free memory.
+ */
+#define __dev_alloc_page LINUX_BACKPORT(__dev_alloc_page)
+static inline struct page *__dev_alloc_page(gfp_t gfp_mask)
+{
+ return __dev_alloc_pages(gfp_mask, 0);
+}
+
+#define dev_alloc_page LINUX_BACKPORT(dev_alloc_page)
+static inline struct page *dev_alloc_page(void)
+{
+ return __dev_alloc_page(GFP_ATOMIC);
+}
+#endif /* LINUX_VERSION_IS_LESS(3,19,0) */
+
+#if LINUX_VERSION_IS_LESS(3,19,0)
+#define skb_copy_datagram_msg LINUX_BACKPORT(skb_copy_datagram_msg)
+static inline int skb_copy_datagram_msg(const struct sk_buff *from, int offset,
+ struct msghdr *msg, int size)
+{
+ return skb_copy_datagram_iovec(from, offset, msg->msg_iov, size);
+}
+
+#define memcpy_from_msg LINUX_BACKPORT(memcpy_from_msg)
+static inline int memcpy_from_msg(void *data, struct msghdr *msg, int len)
+{
+ return memcpy_fromiovec(data, msg->msg_iov, len);
+}
+
+/**
+ * skb_put_padto - increase size and pad an skbuff up to a minimal size
+ * @skb: buffer to pad
+ * @len: minimal length
+ *
+ * Pads up a buffer to ensure the trailing bytes exist and are
+ * blanked. If the buffer already contains sufficient data it
+ * is untouched. Otherwise it is extended. Returns zero on
+ * success. The skb is freed on error.
+ */
+#define skb_put_padto LINUX_BACKPORT(skb_put_padto)
+static inline int skb_put_padto(struct sk_buff *skb, unsigned int len)
+{
+ unsigned int size = skb->len;
+
+ if (unlikely(size < len)) {
+ len -= size;
+ if (skb_pad(skb, len))
+ return -ENOMEM;
+ __skb_put(skb, len);
+ }
+ return 0;
+}
+
+#define skb_ensure_writable LINUX_BACKPORT(skb_ensure_writable)
+int skb_ensure_writable(struct sk_buff *skb, int write_len);
+
+#endif /* LINUX_VERSION_IS_LESS(3,19,0) */
+
+#if LINUX_VERSION_IS_LESS(4,2,0)
+static inline void skb_free_frag(void *data)
+{
+ put_page(virt_to_head_page(data));
+}
+
+/* iwlwifi doesn't need this function, so it's safe to just return 0 */
+static inline
+__u32 skb_get_hash_perturb(const struct sk_buff *skb, u32 perturb)
+{
+ return 0;
+}
+
+#endif
+
+#endif /* __BACKPORT_SKBUFF_H */
diff --git a/backport-include/linux/slab.h b/backport-include/linux/slab.h
new file mode 100644
index 0000000..66f0cc0
--- /dev/null
+++ b/backport-include/linux/slab.h
@@ -0,0 +1,27 @@
+#ifndef __BACKPORT_SLAB_H
+#define __BACKPORT_SLAB_H
+#include_next <linux/slab.h>
+#include <linux/version.h>
+
+#if LINUX_VERSION_IS_LESS(3,4,0)
+/* This backports:
+ *
+ * commit a8203725dfded5c1f79dca3368a4a273e24b59bb
+ * Author: Xi Wang <xi.wang@gmail.com>
+ * Date: Mon Mar 5 15:14:41 2012 -0800
+ *
+ * slab: introduce kmalloc_array()
+ */
+
+#include <linux/kernel.h> /* for SIZE_MAX */
+
+#define kmalloc_array LINUX_BACKPORT(kmalloc_array)
+static inline void *kmalloc_array(size_t n, size_t size, gfp_t flags)
+{
+ if (size != 0 && n > SIZE_MAX / size)
+ return NULL;
+ return __kmalloc(n * size, flags);
+}
+#endif
+
+#endif /* __BACKPORT_SLAB_H */
diff --git a/backport-include/linux/socket.h b/backport-include/linux/socket.h
new file mode 100644
index 0000000..8b6453e
--- /dev/null
+++ b/backport-include/linux/socket.h
@@ -0,0 +1,18 @@
+#ifndef __BACKPORT_SOCKET_H
+#define __BACKPORT_SOCKET_H
+#include_next <linux/socket.h>
+
+#ifndef SOL_NFC
+/*
+ * backport SOL_NFC -- see commit:
+ * NFC: llcp: Implement socket options
+ */
+#define SOL_NFC 280
+#endif
+
+#ifndef __sockaddr_check_size
+#define __sockaddr_check_size(size) \
+ BUILD_BUG_ON(((size) > sizeof(struct __kernel_sockaddr_storage)))
+#endif
+
+#endif /* __BACKPORT_SOCKET_H */
diff --git a/backport-include/linux/spi/spi.h b/backport-include/linux/spi/spi.h
new file mode 100644
index 0000000..6d2de15
--- /dev/null
+++ b/backport-include/linux/spi/spi.h
@@ -0,0 +1,69 @@
+#ifndef _BACKPORTS_LINUX_SPI_H
+#define _BACKPORTS_LINUX_SPI_H 1
+
+#include_next <linux/spi/spi.h>
+
+#ifndef module_spi_driver
+/**
+ * module_spi_driver() - Helper macro for registering a SPI driver
+ * @__spi_driver: spi_driver struct
+ *
+ * Helper macro for SPI drivers which do not do anything special in module
+ * init/exit. This eliminates a lot of boilerplate. Each module may only
+ * use this macro once, and calling it replaces module_init() and module_exit()
+ */
+#define module_spi_driver(__spi_driver) \
+ module_driver(__spi_driver, spi_register_driver, \
+ spi_unregister_driver)
+#endif
+
+
+#if LINUX_VERSION_IS_LESS(3,9,0)
+/**
+ * spi_message_init_with_transfers - Initialize spi_message and append transfers
+ * @m: spi_message to be initialized
+ * @xfers: An array of spi transfers
+ * @num_xfers: Number of items in the xfer array
+ *
+ * This function initializes the given spi_message and adds each spi_transfer in
+ * the given array to the message.
+ */
+#define spi_message_init_with_transfers LINUX_BACKPORT(spi_message_init_with_transfers)
+static inline void
+spi_message_init_with_transfers(struct spi_message *m,
+struct spi_transfer *xfers, unsigned int num_xfers)
+{
+ unsigned int i;
+
+ spi_message_init(m);
+ for (i = 0; i < num_xfers; ++i)
+ spi_message_add_tail(&xfers[i], m);
+}
+
+/**
+ * spi_sync_transfer - synchronous SPI data transfer
+ * @spi: device with which data will be exchanged
+ * @xfers: An array of spi_transfers
+ * @num_xfers: Number of items in the xfer array
+ * Context: can sleep
+ *
+ * Does a synchronous SPI data transfer of the given spi_transfer array.
+ *
+ * For more specific semantics see spi_sync().
+ *
+ * It returns zero on success, else a negative error code.
+ */
+#define spi_sync_transfer LINUX_BACKPORT(spi_sync_transfer)
+static inline int
+spi_sync_transfer(struct spi_device *spi, struct spi_transfer *xfers,
+ unsigned int num_xfers)
+{
+ struct spi_message msg;
+
+ spi_message_init_with_transfers(&msg, xfers, num_xfers);
+
+ return spi_sync(spi, &msg);
+}
+#endif /* < 3.9 */
+
+#endif /* _BACKPORTS_LINUX_SPI_H */
diff --git a/backport-include/linux/static_key.h b/backport-include/linux/static_key.h
new file mode 100644
index 0000000..4b82eec
--- /dev/null
+++ b/backport-include/linux/static_key.h
@@ -0,0 +1,49 @@
+#ifndef _BACKPORTS_LINUX_STATIC_KEY_H
+#define _BACKPORTS_LINUX_STATIC_KEY_H 1
+
+#include <linux/version.h>
+
+#if LINUX_VERSION_IS_GEQ(3,3,0) /* kernels >= 3.3 */
+/*
+ * XXX: NOTE!
+ *
+ * Some 3.3 kernels carry <linux/static.h> but some don't even though its
+ * its including <linux/jump_label.h>. What makes it more confusing is that
+ * later all this got shuffled. The safe thing to do then is to just assume
+ * kernels 3.3..3.4 don't have it and include <linux/jump_label.h> instead,
+ * and for newer kernels include <linux/static_key.h>.
+ */
+#if LINUX_VERSION_IS_GEQ(3,5,0)
+#include_next <linux/static_key.h>
+#else
+#include <linux/jump_label.h>
+#endif
+
+#else /* kernels < 3.3 */
+/*
+ * in between 2.6.37 - 3.5 there's a slew of changes that make
+ * it hard to backport this properly. If you are interested in
+ * trying you can use this as reference:
+ *
+ * http://drvbp1.linux-foundation.org/~mcgrof/examples/2014/04/01/backport-static-keys.patch
+ *
+ * And these notes:
+ *
+ * < v2.6.37 - No tracing support
+ * bf5438fc - v2.6.37 - Jump label support added primarily for tracing but
+ * tracing was broken, later kernels started sporting
+ * functional tracing.
+ * d430d3d7e - v3.0 - Static branch optimizations for jump labels
+ * c5905afb - v3.3 - Static keys split out, note on the below issue
+ * c5905afb - v3.5 - git describe --contains c5905afb claims but not true!
+ * c4b2c0c5f - v3.13 - Adds static_key_initialized(), STATIC_KEY_CHECK_USE()
+ *
+ * Because all of this we skip 2.6.37 - 3.3 but and adding support for older
+ * can be done by of carrying over the non-architecture optimized code.
+ * Carrying new changes into this file is a burden though so if we really
+ * find use for this we could just split the non optimized versions upstream
+ * and copy that through an automatic process.
+ */
+#endif /* kernels < 3.3 */
+
+#endif /* _BACKPORTS_LINUX_STATIC_KEY_H */
diff --git a/backport-include/linux/string.h b/backport-include/linux/string.h
new file mode 100644
index 0000000..b85d9c7
--- /dev/null
+++ b/backport-include/linux/string.h
@@ -0,0 +1,32 @@
+#ifndef __BACKPORT_LINUX_STRING_H
+#define __BACKPORT_LINUX_STRING_H
+#include_next <linux/string.h>
+#include <linux/version.h>
+
+#if LINUX_VERSION_IS_LESS(4,5,0)
+#define memdup_user_nul LINUX_BACKPORT(memdup_user_nul)
+extern void *memdup_user_nul(const void __user *, size_t);
+#endif
+
+/* this was added in v3.2.65, v3.4.106, v3.10.60, v3.12.33, v3.14.24,
+ * v3.17.3 and v3.18 */
+#if !(LINUX_VERSION_IS_GEQ(3,17,3) || \
+ (LINUX_VERSION_IS_GEQ(3,14,24) && \
+ LINUX_VERSION_IS_LESS(3,15,0)) || \
+ (LINUX_VERSION_IS_GEQ(3,12,33) && \
+ LINUX_VERSION_IS_LESS(3,13,0)) || \
+ (LINUX_VERSION_IS_GEQ(3,10,60) && \
+ LINUX_VERSION_IS_LESS(3,11,0)) || \
+ (LINUX_VERSION_IS_GEQ(3,4,106) && \
+ LINUX_VERSION_IS_LESS(3,5,0)) || \
+ (LINUX_VERSION_IS_GEQ(3,2,65) && \
+ LINUX_VERSION_IS_LESS(3,3,0)))
+#define memzero_explicit LINUX_BACKPORT(memzero_explicit)
+void memzero_explicit(void *s, size_t count);
+#endif
+
+#if LINUX_VERSION_IS_LESS(4,3,0)
+ssize_t strscpy(char *dest, const char *src, size_t count);
+#endif
+
+#endif /* __BACKPORT_LINUX_STRING_H */
diff --git a/backport-include/linux/sysfs.h b/backport-include/linux/sysfs.h
new file mode 100644
index 0000000..0b71db5
--- /dev/null
+++ b/backport-include/linux/sysfs.h
@@ -0,0 +1,11 @@
+#ifndef __BACKPORT_LINUX_SYSFS_H
+#define __BACKPORT_LINUX_SYSFS_H
+#include_next <linux/sysfs.h>
+#include <linux/version.h>
+
+#ifndef __ATTR_RW
+#define __ATTR_RW(_name) __ATTR(_name, (S_IWUSR | S_IRUGO), \
+ _name##_show, _name##_store)
+#endif
+
+#endif /* __BACKPORT_LINUX_SYSFS_H */
diff --git a/backport-include/linux/thermal.h b/backport-include/linux/thermal.h
new file mode 100644
index 0000000..108a8cf
--- /dev/null
+++ b/backport-include/linux/thermal.h
@@ -0,0 +1,114 @@
+#ifndef __BACKPORT_LINUX_THERMAL_H
+#define __BACKPORT_LINUX_THERMAL_H
+#include_next <linux/thermal.h>
+#include <linux/version.h>
+
+#if LINUX_VERSION_IS_LESS(3,8,0)
+#include <linux/errno.h>
+
+struct thermal_bind_params {
+ struct thermal_cooling_device *cdev;
+ int weight;
+ int trip_mask;
+ int (*match)(struct thermal_zone_device *tz,
+ struct thermal_cooling_device *cdev);
+};
+
+struct thermal_zone_params {
+ int num_tbps;
+ struct thermal_bind_params *tbp;
+};
+
+static inline struct thermal_zone_device *
+backport_thermal_zone_device_register(const char *type, int trips, int mask,
+ void *devdata,
+ struct thermal_zone_device_ops *ops,
+ const struct thermal_zone_params *tzp,
+ int passive_delay, int polling_delay)
+{
+ return ERR_PTR(-EOPNOTSUPP);
+}
+#define thermal_zone_device_register backport_thermal_zone_device_register
+
+static inline void thermal_notify_framework(struct thermal_zone_device *tz,
+ int trip)
+{ }
+#else /* < 3.8.0 */
+
+#if LINUX_VERSION_IS_LESS(3,10,0)
+#define thermal_notify_framework notify_thermal_framework
+#endif /* LINUX_VERSION_IS_LESS(3,10,0) */
+
+#if LINUX_VERSION_IS_LESS(4,3,0)
+
+typedef struct thermal_zone_device_ops old_thermal_zone_device_ops_t;
+
+/* also add a way to call the old register and unregister functions */
+static inline struct thermal_zone_device *old_thermal_zone_device_register(
+ const char *type, int trips, int mask, void *devdata,
+ old_thermal_zone_device_ops_t *_ops,
+ const struct thermal_zone_params *_tzp,
+ int passive_delay, int polling_delay)
+{
+ struct thermal_zone_device_ops *ops =
+ (struct thermal_zone_device_ops *) _ops;
+
+ /* cast the const away */
+ struct thermal_zone_params *tzp =
+ (struct thermal_zone_params *)_tzp;
+
+ return thermal_zone_device_register(type, trips, mask, devdata,
+ ops, tzp, passive_delay,
+ polling_delay);
+}
+
+static inline
+void old_thermal_zone_device_unregister(struct thermal_zone_device *dev)
+{
+ thermal_zone_device_unregister(dev);
+}
+
+struct backport_thermal_zone_device_ops {
+ int (*bind) (struct thermal_zone_device *,
+ struct thermal_cooling_device *);
+ int (*unbind) (struct thermal_zone_device *,
+ struct thermal_cooling_device *);
+ int (*get_temp) (struct thermal_zone_device *, int *);
+ int (*get_mode) (struct thermal_zone_device *,
+ enum thermal_device_mode *);
+ int (*set_mode) (struct thermal_zone_device *,
+ enum thermal_device_mode);
+ int (*get_trip_type) (struct thermal_zone_device *, int,
+ enum thermal_trip_type *);
+ int (*get_trip_temp) (struct thermal_zone_device *, int, int *);
+ int (*set_trip_temp) (struct thermal_zone_device *, int, int);
+ int (*get_trip_hyst) (struct thermal_zone_device *, int, int *);
+ int (*set_trip_hyst) (struct thermal_zone_device *, int, int);
+ int (*get_crit_temp) (struct thermal_zone_device *, int *);
+ int (*set_emul_temp) (struct thermal_zone_device *, int);
+ int (*get_trend) (struct thermal_zone_device *, int,
+ enum thermal_trend *);
+ int (*notify) (struct thermal_zone_device *, int,
+ enum thermal_trip_type);
+};
+#define thermal_zone_device_ops LINUX_BACKPORT(thermal_zone_device_ops)
+
+#undef thermal_zone_device_register
+struct thermal_zone_device *backport_thermal_zone_device_register(
+ const char *type, int trips, int mask, void *devdata,
+ struct thermal_zone_device_ops *ops,
+ const struct thermal_zone_params *tzp,
+ int passive_delay, int polling_delay);
+
+#define thermal_zone_device_register \
+ LINUX_BACKPORT(thermal_zone_device_register)
+
+#undef thermal_zone_device_unregister
+void backport_thermal_zone_device_unregister(struct thermal_zone_device *);
+#define thermal_zone_device_unregister \
+ LINUX_BACKPORT(thermal_zone_device_unregister)
+
+#endif /* LINUX_VERSION_IS_LESS(4,3,0) */
+#endif /* ! < 3.8.0 */
+
+#endif /* __BACKPORT_LINUX_THERMAL_H */
diff --git a/backport-include/linux/time.h b/backport-include/linux/time.h
new file mode 100644
index 0000000..3191047
--- /dev/null
+++ b/backport-include/linux/time.h
@@ -0,0 +1,7 @@
+#ifndef __BACKPORT_LINUX_TIME_H
+#define __BACKPORT_LINUX_TIME_H
+#include_next <linux/time.h>
+
+#include <linux/time64.h>
+
+#endif /* __BACKPORT_LINUX_TIME_H */
diff --git a/backport-include/linux/time64.h b/backport-include/linux/time64.h
new file mode 100644
index 0000000..609b890
--- /dev/null
+++ b/backport-include/linux/time64.h
@@ -0,0 +1,24 @@
+#ifndef __BACKPORT_LINUX_TIME64_H
+#define __BACKPORT_LINUX_TIME64_H
+#if LINUX_VERSION_IS_GEQ(3,17,0)
+#include_next <linux/time64.h>
+#else
+#include <linux/time.h>
+#endif
+
+#if LINUX_VERSION_IS_LESS(3,17,0)
+#define timespec64_equal timespec_equal
+#define timespec64_compare timespec_compare
+#define set_normalized_timespec64 set_normalized_timespec
+#define timespec64_add_safe timespec_add_safe
+#define timespec64_add timespec_add
+#define timespec64_sub timespec_sub
+#define timespec64_valid timespec_valid
+#define timespec64_valid_strict timespec_valid_strict
+#define timespec64_to_ns timespec_to_ns
+#define ns_to_timespec64 ns_to_timespec
+#define timespec64_add_ns timespec_add_ns
+#define timespec64 timespec
+#endif /* LINUX_VERSION_IS_LESS(3,17,0) */
+
+#endif /* __BACKPORT_LINUX_TIME64_H */
diff --git a/backport-include/linux/timecounter.h b/backport-include/linux/timecounter.h
new file mode 100644
index 0000000..596015b
--- /dev/null
+++ b/backport-include/linux/timecounter.h
@@ -0,0 +1,25 @@
+#ifndef __BACKPORT_LINUX_TIMECOUNTER_H
+#define __BACKPORT_LINUX_TIMECOUNTER_H
+
+#if LINUX_VERSION_IS_GEQ(3,20,0)
+#include_next <linux/timecounter.h>
+#else
+#include <linux/clocksource.h>
+
+/**
+ * timecounter_adjtime - Shifts the time of the clock.
+ * @delta: Desired change in nanoseconds.
+ */
+#define timecounter_adjtime LINUX_BACKPORT(timecounter_adjtime)
+static inline void timecounter_adjtime(struct timecounter *tc, s64 delta)
+{
+ tc->nsec += delta;
+}
+#endif
+
+#ifndef CYCLECOUNTER_MASK
+/* simplify initialization of mask field */
+#define CYCLECOUNTER_MASK(bits) (cycle_t)((bits) < 64 ? ((1ULL<<(bits))-1) : -1)
+#endif
+
+#endif /* __BACKPORT_LINUX_TIMECOUNTER_H */
diff --git a/backport-include/linux/timekeeping.h b/backport-include/linux/timekeeping.h
new file mode 100644
index 0000000..b683d12
--- /dev/null
+++ b/backport-include/linux/timekeeping.h
@@ -0,0 +1,38 @@
+#ifndef __BACKPORT_TIMKEEPING_H
+#define __BACKPORT_TIMKEEPING_H
+#include <linux/version.h>
+#include <linux/types.h>
+
+#if LINUX_VERSION_IS_GEQ(3,17,0)
+#include_next <linux/timekeeping.h>
+#endif
+
+#if LINUX_VERSION_IS_LESS(3,17,0)
+#define ktime_get_ns LINUX_BACKPORT(ktime_get_ns)
+extern ktime_t ktime_get(void);
+#define ktime_get_ns LINUX_BACKPORT(ktime_get_ns)
+static inline u64 ktime_get_ns(void)
+{
+ return ktime_to_ns(ktime_get());
+}
+
+extern ktime_t ktime_get_boottime(void);
+#define ktime_get_boot_ns LINUX_BACKPORT(ktime_get_boot_ns)
+static inline u64 ktime_get_boot_ns(void)
+{
+ return ktime_to_ns(ktime_get_boottime());
+}
+#endif /* < 3.17 */
+
+#if LINUX_VERSION_IS_LESS(3,19,0)
+static inline time64_t ktime_get_seconds(void)
+{
+ struct timespec t;
+
+ ktime_get_ts(&t);
+
+ return t.tv_sec;
+}
+#endif
+
+#endif /* __BACKPORT_TIMKEEPING_H */
diff --git a/backport-include/linux/timer.h b/backport-include/linux/timer.h
new file mode 100644
index 0000000..df560a2
--- /dev/null
+++ b/backport-include/linux/timer.h
@@ -0,0 +1,35 @@
+#ifndef _BACKPORT_TIMER_H
+#define _BACKPORT_TIMER_H
+
+#include_next <linux/timer.h>
+
+#ifndef setup_deferrable_timer
+/*
+ * The TIMER_DEFERRABLE flag has not been around since 3.0 so
+ * two different backports are needed here.
+ */
+#ifdef TIMER_DEFERRABLE
+#define setup_deferrable_timer(timer, fn, data) \
+ __setup_timer((timer), (fn), (data), TIMER_DEFERRABLE)
+#else
+static inline void setup_deferrable_timer_key(struct timer_list *timer,
+ const char *name,
+ struct lock_class_key *key,
+ void (*func)(unsigned long),
+ unsigned long data)
+{
+ timer->function = func;
+ timer->data = data;
+ init_timer_deferrable_key(timer, name, key);
+}
+#define setup_deferrable_timer(timer, fn, data) \
+ do { \
+ static struct lock_class_key __key; \
+ setup_deferrable_timer_key((timer), #timer, &__key, \
+ (fn), (data)); \
+ } while (0)
+#endif
+
+#endif
+
+#endif /* _BACKPORT_TIMER_H */
diff --git a/backport-include/linux/tracepoint.h b/backport-include/linux/tracepoint.h
new file mode 100644
index 0000000..a695c6f
--- /dev/null
+++ b/backport-include/linux/tracepoint.h
@@ -0,0 +1,10 @@
+#include_next <linux/tracepoint.h>
+
+#ifndef __BACKPORT_LINUX_TRACEPOINT_H
+#define __BACKPORT_LINUX_TRACEPOINT_H
+
+#ifndef TRACE_DEFINE_ENUM
+#define TRACE_DEFINE_ENUM(a)
+#endif
+
+#endif /* __BACKPORT_LINUX_TRACEPOINT_H */
diff --git a/backport-include/linux/tty.h b/backport-include/linux/tty.h
new file mode 100644
index 0000000..987a115
--- /dev/null
+++ b/backport-include/linux/tty.h
@@ -0,0 +1,36 @@
+#ifndef __BACKPORT_LINUX_TTY_H
+#define __BACKPORT_LINUX_TTY_H
+#include_next <linux/tty.h>
+
+/*
+ * This really belongs into uapi/asm-generic/termbits.h but
+ * that doesn't usually get included directly.
+ */
+#ifndef EXTPROC
+#define EXTPROC 0200000
+#endif
+
+#if LINUX_VERSION_IS_LESS(3,7,0)
+/* Backports tty_lock: Localise the lock */
+#define tty_lock(__tty) tty_lock()
+#define tty_unlock(__tty) tty_unlock()
+
+#define tty_port_register_device(port, driver, index, device) \
+ tty_register_device(driver, index, device)
+#endif
+
+#if LINUX_VERSION_IS_LESS(3,10,0)
+extern void tty_port_tty_wakeup(struct tty_port *port);
+extern void tty_port_tty_hangup(struct tty_port *port, bool check_clocal);
+#endif /* LINUX_VERSION_IS_LESS(3,10,0) */
+
+#if LINUX_VERSION_IS_LESS(4,1,0) && \
+ LINUX_VERSION_IS_GEQ(4,0,0)
+extern int tty_set_termios(struct tty_struct *tty, struct ktermios *kt);
+#endif /* LINUX_VERSION_IS_LESS(4,1,0) */
+
+#ifndef N_NCI
+#define N_NCI 25 /* NFC NCI UART */
+#endif
+
+#endif /* __BACKPORT_LINUX_TTY_H */
diff --git a/backport-include/linux/tty_flip.h b/backport-include/linux/tty_flip.h
new file mode 100644
index 0000000..e6c0684
--- /dev/null
+++ b/backport-include/linux/tty_flip.h
@@ -0,0 +1,11 @@
+#ifndef __BACKPORT_TTY_FLIP_H
+#define __BACKPORT_TTY_FLIP_H
+#include_next <linux/tty_flip.h>
+#include <linux/version.h>
+
+#if LINUX_VERSION_IS_LESS(3,9,0)
+#define tty_flip_buffer_push(port) tty_flip_buffer_push((port)->tty)
+#define tty_insert_flip_string(port, chars, size) tty_insert_flip_string((port)->tty, chars, size)
+#endif
+
+#endif /* __BACKPORT_TTY_FLIP_H */
diff --git a/backport-include/linux/types.h b/backport-include/linux/types.h
new file mode 100644
index 0000000..160570b
--- /dev/null
+++ b/backport-include/linux/types.h
@@ -0,0 +1,10 @@
+#ifndef __BACKPORT_TYPES
+#define __BACKPORT_TYPES
+#include <linux/version.h>
+#include_next <linux/types.h>
+
+#if LINUX_VERSION_IS_LESS(3,17,0)
+typedef __s64 time64_t;
+#endif
+
+#endif /* __BACKPORT_TYPES */
diff --git a/backport-include/linux/u64_stats_sync.h b/backport-include/linux/u64_stats_sync.h
new file mode 100644
index 0000000..112a409
--- /dev/null
+++ b/backport-include/linux/u64_stats_sync.h
@@ -0,0 +1,154 @@
+#ifndef __BACKPORT_LINUX_U64_STATS_SYNC_H
+#define __BACKPORT_LINUX_U64_STATS_SYNC_H
+
+#include <linux/version.h>
+#include <generated/utsrelease.h>
+#if LINUX_VERSION_IS_GEQ(3,6,0)
+#include_next <linux/u64_stats_sync.h>
+#else
+
+/*
+ * To properly implement 64bits network statistics on 32bit and 64bit hosts,
+ * we provide a synchronization point, that is a noop on 64bit or UP kernels.
+ *
+ * Key points :
+ * 1) Use a seqcount on SMP 32bits, with low overhead.
+ * 2) Whole thing is a noop on 64bit arches or UP kernels.
+ * 3) Write side must ensure mutual exclusion or one seqcount update could
+ * be lost, thus blocking readers forever.
+ * If this synchronization point is not a mutex, but a spinlock or
+ * spinlock_bh() or disable_bh() :
+ * 3.1) Write side should not sleep.
+ * 3.2) Write side should not allow preemption.
+ * 3.3) If applicable, interrupts should be disabled.
+ *
+ * 4) If reader fetches several counters, there is no guarantee the whole values
+ * are consistent (remember point 1) : this is a noop on 64bit arches anyway)
+ *
+ * 5) readers are allowed to sleep or be preempted/interrupted : They perform
+ * pure reads. But if they have to fetch many values, it's better to not allow
+ * preemptions/interruptions to avoid many retries.
+ *
+ * 6) If counter might be written by an interrupt, readers should block interrupts.
+ * (On UP, there is no seqcount_t protection, a reader allowing interrupts could
+ * read partial values)
+ *
+ * 7) For softirq uses, readers can use u64_stats_fetch_begin_irq() and
+ * u64_stats_fetch_retry_irq() helpers
+ *
+ * Usage :
+ *
+ * Stats producer (writer) should use following template granted it already got
+ * an exclusive access to counters (a lock is already taken, or per cpu
+ * data is used [in a non preemptable context])
+ *
+ * spin_lock_bh(...) or other synchronization to get exclusive access
+ * ...
+ * u64_stats_update_begin(&stats->syncp);
+ * stats->bytes64 += len; // non atomic operation
+ * stats->packets64++; // non atomic operation
+ * u64_stats_update_end(&stats->syncp);
+ *
+ * While a consumer (reader) should use following template to get consistent
+ * snapshot for each variable (but no guarantee on several ones)
+ *
+ * u64 tbytes, tpackets;
+ * unsigned int start;
+ *
+ * do {
+ * start = u64_stats_fetch_begin(&stats->syncp);
+ * tbytes = stats->bytes64; // non atomic operation
+ * tpackets = stats->packets64; // non atomic operation
+ * } while (u64_stats_fetch_retry(&stats->syncp, start));
+ *
+ *
+ * Example of use in drivers/net/loopback.c, using per_cpu containers,
+ * in BH disabled context.
+ */
+#include <linux/seqlock.h>
+
+struct u64_stats_sync {
+#if BITS_PER_LONG==32 && defined(CONFIG_SMP)
+ seqcount_t seq;
+#endif
+};
+
+static inline void u64_stats_update_begin(struct u64_stats_sync *syncp)
+{
+#if BITS_PER_LONG==32 && defined(CONFIG_SMP)
+ write_seqcount_begin(&syncp->seq);
+#endif
+}
+
+static inline void u64_stats_update_end(struct u64_stats_sync *syncp)
+{
+#if BITS_PER_LONG==32 && defined(CONFIG_SMP)
+ write_seqcount_end(&syncp->seq);
+#endif
+}
+
+static inline unsigned int u64_stats_fetch_begin(const struct u64_stats_sync *syncp)
+{
+#if BITS_PER_LONG==32 && defined(CONFIG_SMP)
+ return read_seqcount_begin(&syncp->seq);
+#else
+#if BITS_PER_LONG==32
+ preempt_disable();
+#endif
+ return 0;
+#endif
+}
+
+static inline bool u64_stats_fetch_retry(const struct u64_stats_sync *syncp,
+ unsigned int start)
+{
+#if BITS_PER_LONG==32 && defined(CONFIG_SMP)
+ return read_seqcount_retry(&syncp->seq, start);
+#else
+#if BITS_PER_LONG==32
+ preempt_enable();
+#endif
+ return false;
+#endif
+}
+
+#endif /* LINUX_VERSION_IS_GEQ(3,6,0) */
+
+#if LINUX_VERSION_IS_LESS(3,15,0) && \
+ !(LINUX_VERSION_CODE == KERNEL_VERSION(3,13,11) && UTS_UBUNTU_RELEASE_ABI > 30)
+static inline unsigned int u64_stats_fetch_begin_irq(const struct u64_stats_sync *syncp)
+{
+#if BITS_PER_LONG==32 && defined(CONFIG_SMP)
+ return read_seqcount_begin(&syncp->seq);
+#else
+#if BITS_PER_LONG==32
+ local_irq_disable();
+#endif
+ return 0;
+#endif
+}
+
+static inline bool u64_stats_fetch_retry_irq(const struct u64_stats_sync *syncp,
+ unsigned int start)
+{
+#if BITS_PER_LONG==32 && defined(CONFIG_SMP)
+ return read_seqcount_retry(&syncp->seq, start);
+#else
+#if BITS_PER_LONG==32
+ local_irq_enable();
+#endif
+ return false;
+#endif
+}
+
+#endif /* LINUX_VERSION_IS_GEQ(3,15,0) */
+
+#if LINUX_VERSION_IS_LESS(3,13,0)
+#if BITS_PER_LONG == 32 && defined(CONFIG_SMP)
+# define u64_stats_init(syncp) seqcount_init(syncp.seq)
+#else
+# define u64_stats_init(syncp) do { } while (0)
+#endif
+#endif /* LINUX_VERSION_IS_LESS(3,13,0) */
+
+#endif /* __BACKPORT_LINUX_U64_STATS_SYNC_H */
diff --git a/backport-include/linux/uidgid.h b/backport-include/linux/uidgid.h
new file mode 100644
index 0000000..c2f3faa
--- /dev/null
+++ b/backport-include/linux/uidgid.h
@@ -0,0 +1,221 @@
+#if LINUX_VERSION_IS_GEQ(3,5,0)
+#include_next <linux/uidgid.h>
+#else
+
+#ifndef _LINUX_UIDGID_H
+#define _LINUX_UIDGID_H
+
+/*
+ * A set of types for the internal kernel types representing uids and gids.
+ *
+ * The types defined in this header allow distinguishing which uids and gids in
+ * the kernel are values used by userspace and which uid and gid values are
+ * the internal kernel values. With the addition of user namespaces the values
+ * can be different. Using the type system makes it possible for the compiler
+ * to detect when we overlook these differences.
+ *
+ */
+#include <linux/types.h>
+#include <linux/highuid.h>
+
+struct user_namespace;
+extern struct user_namespace init_user_ns;
+
+#ifdef CONFIG_UIDGID_STRICT_TYPE_CHECKS
+
+typedef struct {
+ uid_t val;
+} kuid_t;
+
+
+typedef struct {
+ gid_t val;
+} kgid_t;
+
+#define KUIDT_INIT(value) (kuid_t){ value }
+#define KGIDT_INIT(value) (kgid_t){ value }
+
+static inline uid_t __kuid_val(kuid_t uid)
+{
+ return uid.val;
+}
+
+static inline gid_t __kgid_val(kgid_t gid)
+{
+ return gid.val;
+}
+
+#else
+
+typedef uid_t kuid_t;
+typedef gid_t kgid_t;
+
+static inline uid_t __kuid_val(kuid_t uid)
+{
+ return uid;
+}
+
+static inline gid_t __kgid_val(kgid_t gid)
+{
+ return gid;
+}
+
+#define KUIDT_INIT(value) ((kuid_t) value )
+#define KGIDT_INIT(value) ((kgid_t) value )
+
+#endif
+
+#define GLOBAL_ROOT_UID KUIDT_INIT(0)
+#define GLOBAL_ROOT_GID KGIDT_INIT(0)
+
+#define INVALID_UID KUIDT_INIT(-1)
+#define INVALID_GID KGIDT_INIT(-1)
+
+static inline bool uid_eq(kuid_t left, kuid_t right)
+{
+ return __kuid_val(left) == __kuid_val(right);
+}
+
+static inline bool gid_eq(kgid_t left, kgid_t right)
+{
+ return __kgid_val(left) == __kgid_val(right);
+}
+
+static inline bool uid_gt(kuid_t left, kuid_t right)
+{
+ return __kuid_val(left) > __kuid_val(right);
+}
+
+static inline bool gid_gt(kgid_t left, kgid_t right)
+{
+ return __kgid_val(left) > __kgid_val(right);
+}
+
+static inline bool uid_gte(kuid_t left, kuid_t right)
+{
+ return __kuid_val(left) >= __kuid_val(right);
+}
+
+static inline bool gid_gte(kgid_t left, kgid_t right)
+{
+ return __kgid_val(left) >= __kgid_val(right);
+}
+
+static inline bool uid_lt(kuid_t left, kuid_t right)
+{
+ return __kuid_val(left) < __kuid_val(right);
+}
+
+static inline bool gid_lt(kgid_t left, kgid_t right)
+{
+ return __kgid_val(left) < __kgid_val(right);
+}
+
+static inline bool uid_lte(kuid_t left, kuid_t right)
+{
+ return __kuid_val(left) <= __kuid_val(right);
+}
+
+static inline bool gid_lte(kgid_t left, kgid_t right)
+{
+ return __kgid_val(left) <= __kgid_val(right);
+}
+
+static inline bool uid_valid(kuid_t uid)
+{
+ return !uid_eq(uid, INVALID_UID);
+}
+
+static inline bool gid_valid(kgid_t gid)
+{
+ return !gid_eq(gid, INVALID_GID);
+}
+
+#ifdef CONFIG_USER_NS
+
+#define make_kuid LINUX_BACKPORT(make_kuid)
+extern kuid_t make_kuid(struct user_namespace *from, uid_t uid);
+#define make_kgid LINUX_BACKPORT(make_kgid)
+extern kgid_t make_kgid(struct user_namespace *from, gid_t gid);
+
+#define from_kuid LINUX_BACKPORT(from_kuid)
+extern uid_t from_kuid(struct user_namespace *to, kuid_t uid);
+#define from_kgid LINUX_BACKPORT(from_kgid)
+extern gid_t from_kgid(struct user_namespace *to, kgid_t gid);
+#define from_kuid_munged LINUX_BACKPORT(from_kuid_munged)
+extern uid_t from_kuid_munged(struct user_namespace *to, kuid_t uid);
+#define from_kgid_munged LINUX_BACKPORT(from_kgid_munged)
+extern gid_t from_kgid_munged(struct user_namespace *to, kgid_t gid);
+
+#define kuid_has_mapping LINUX_BACKPORT(kuid_has_mapping)
+static inline bool kuid_has_mapping(struct user_namespace *ns, kuid_t uid)
+{
+ return from_kuid(ns, uid) != (uid_t) -1;
+}
+
+#define kgid_has_mapping LINUX_BACKPORT(kgid_has_mapping)
+static inline bool kgid_has_mapping(struct user_namespace *ns, kgid_t gid)
+{
+ return from_kgid(ns, gid) != (gid_t) -1;
+}
+
+#else
+
+#define make_kuid LINUX_BACKPORT(make_kuid)
+static inline kuid_t make_kuid(struct user_namespace *from, uid_t uid)
+{
+ return KUIDT_INIT(uid);
+}
+
+#define make_kgid LINUX_BACKPORT(make_kgid)
+static inline kgid_t make_kgid(struct user_namespace *from, gid_t gid)
+{
+ return KGIDT_INIT(gid);
+}
+
+#define from_kuid LINUX_BACKPORT(from_kuid)
+static inline uid_t from_kuid(struct user_namespace *to, kuid_t kuid)
+{
+ return __kuid_val(kuid);
+}
+
+#define from_kgid LINUX_BACKPORT(from_kgid)
+static inline gid_t from_kgid(struct user_namespace *to, kgid_t kgid)
+{
+ return __kgid_val(kgid);
+}
+
+#define from_kuid_munged LINUX_BACKPORT(from_kuid_munged)
+static inline uid_t from_kuid_munged(struct user_namespace *to, kuid_t kuid)
+{
+ uid_t uid = from_kuid(to, kuid);
+ if (uid == (uid_t)-1)
+ uid = overflowuid;
+ return uid;
+}
+
+#define from_kgid_munged LINUX_BACKPORT(from_kgid_munged)
+static inline gid_t from_kgid_munged(struct user_namespace *to, kgid_t kgid)
+{
+ gid_t gid = from_kgid(to, kgid);
+ if (gid == (gid_t)-1)
+ gid = overflowgid;
+ return gid;
+}
+
+#define kuid_has_mapping LINUX_BACKPORT(kuid_has_mapping)
+static inline bool kuid_has_mapping(struct user_namespace *ns, kuid_t uid)
+{
+ return true;
+}
+
+#define kgid_has_mapping LINUX_BACKPORT(kgid_has_mapping)
+static inline bool kgid_has_mapping(struct user_namespace *ns, kgid_t gid)
+{
+ return true;
+}
+
+#endif /* CONFIG_USER_NS */
+
+#endif /* _LINUX_UIDGID_H */
+#endif /* LINUX_VERSION_IS_GEQ(3,5,0) */
diff --git a/backport-include/linux/usb.h b/backport-include/linux/usb.h
new file mode 100644
index 0000000..1873a33
--- /dev/null
+++ b/backport-include/linux/usb.h
@@ -0,0 +1,95 @@
+#ifndef __BACKPORT_USB_H
+#define __BACKPORT_USB_H
+
+#include_next <linux/usb.h>
+#include <linux/version.h>
+
+#ifndef module_usb_driver
+/**
+ * module_usb_driver() - Helper macro for registering a USB driver
+ * @__usb_driver: usb_driver struct
+ *
+ * Helper macro for USB drivers which do not do anything special in module
+ * init/exit. This eliminates a lot of boilerplate. Each module may only
+ * use this macro once, and calling it replaces module_init() and module_exit()
+ */
+#define module_usb_driver(__usb_driver) \
+ module_driver(__usb_driver, usb_register, \
+ usb_deregister)
+#endif
+
+#ifndef USB_VENDOR_AND_INTERFACE_INFO
+/**
+ * Backports
+ *
+ * commit d81a5d1956731c453b85c141458d4ff5d6cc5366
+ * Author: Gustavo Padovan <gustavo.padovan@collabora.co.uk>
+ * Date: Tue Jul 10 19:10:06 2012 -0300
+ *
+ * USB: add USB_VENDOR_AND_INTERFACE_INFO() macro
+ */
+#define USB_VENDOR_AND_INTERFACE_INFO(vend, cl, sc, pr) \
+ .match_flags = USB_DEVICE_ID_MATCH_INT_INFO \
+ | USB_DEVICE_ID_MATCH_VENDOR, \
+ .idVendor = (vend), \
+ .bInterfaceClass = (cl), \
+ .bInterfaceSubClass = (sc), \
+ .bInterfaceProtocol = (pr)
+#endif /* USB_VENDOR_AND_INTERFACE_INFO */
+
+#ifndef USB_DEVICE_INTERFACE_NUMBER
+/**
+ * USB_DEVICE_INTERFACE_NUMBER - describe a usb device with a specific interface number
+ * @vend: the 16 bit USB Vendor ID
+ * @prod: the 16 bit USB Product ID
+ * @num: bInterfaceNumber value
+ *
+ * This macro is used to create a struct usb_device_id that matches a
+ * specific interface number of devices.
+ */
+#define USB_DEVICE_INTERFACE_NUMBER(vend, prod, num) \
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE, \
+ .idVendor = (vend), \
+ .idProduct = (prod)
+#endif /* USB_DEVICE_INTERFACE_NUMBER */
+
+#ifndef USB_DEVICE_INTERFACE_CLASS
+/**
+ * USB_DEVICE_INTERFACE_CLASS - describe a usb device with a specific interface class
+ * @vend: the 16 bit USB Vendor ID
+ * @prod: the 16 bit USB Product ID
+ * @cl: bInterfaceClass value
+ *
+ * This macro is used to create a struct usb_device_id that matches a
+ * specific interface class of devices.
+ */
+#define USB_DEVICE_INTERFACE_CLASS(vend, prod, cl) \
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \
+ USB_DEVICE_ID_MATCH_INT_CLASS, \
+ .idVendor = (vend), \
+ .idProduct = (prod), \
+ .bInterfaceClass = (cl)
+#endif /* USB_DEVICE_INTERFACE_CLASS */
+
+#ifndef USB_SUBCLASS_VENDOR_SPEC
+/* this is defined in usb/ch9.h, but we only need it through here */
+#define USB_SUBCLASS_VENDOR_SPEC 0xff
+#endif
+
+#if LINUX_VERSION_IS_LESS(3,2,0)
+#define usb_translate_errors LINUX_BACKPORT(usb_translate_errors)
+static inline int usb_translate_errors(int error_code)
+{
+ switch (error_code) {
+ case 0:
+ case -ENOMEM:
+ case -ENODEV:
+ case -EOPNOTSUPP:
+ return error_code;
+ default:
+ return -EIO;
+ }
+}
+#endif /* LINUX_VERSION_IS_LESS(3,2,0) */
+
+#endif /* __BACKPORT_USB_H */
diff --git a/backport-include/linux/usb/cdc.h b/backport-include/linux/usb/cdc.h
new file mode 100644
index 0000000..5ad31bf
--- /dev/null
+++ b/backport-include/linux/usb/cdc.h
@@ -0,0 +1,61 @@
+#ifndef __BP_USB_CDC_H
+#define __BP_USB_CDC_H
+#include <linux/version.h>
+
+#if LINUX_VERSION_IS_GEQ(4,8,0)
+#include_next <linux/usb/cdc.h>
+#else
+#include <uapi/linux/usb/cdc.h>
+
+/*
+ * inofficial magic numbers
+ */
+
+#define CDC_PHONET_MAGIC_NUMBER 0xAB
+
+#ifndef USB_CDC_MBIM_EXTENDED_TYPE
+#define USB_CDC_MBIM_EXTENDED_TYPE 0x1c
+
+/* "MBIM Extended Functional Descriptor" from CDC MBIM spec 1.0 errata-1 */
+struct usb_cdc_mbim_extended_desc {
+ __u8 bLength;
+ __u8 bDescriptorType;
+ __u8 bDescriptorSubType;
+
+ __le16 bcdMBIMExtendedVersion;
+ __u8 bMaxOutstandingCommandMessages;
+ __le16 wMTU;
+} __attribute__ ((packed));
+#endif
+
+/*
+ * parsing CDC headers
+ */
+
+struct usb_cdc_parsed_header {
+ struct usb_cdc_union_desc *usb_cdc_union_desc;
+ struct usb_cdc_header_desc *usb_cdc_header_desc;
+
+ struct usb_cdc_call_mgmt_descriptor *usb_cdc_call_mgmt_descriptor;
+ struct usb_cdc_acm_descriptor *usb_cdc_acm_descriptor;
+ struct usb_cdc_country_functional_desc *usb_cdc_country_functional_desc;
+ struct usb_cdc_network_terminal_desc *usb_cdc_network_terminal_desc;
+ struct usb_cdc_ether_desc *usb_cdc_ether_desc;
+ struct usb_cdc_dmm_desc *usb_cdc_dmm_desc;
+ struct usb_cdc_mdlm_desc *usb_cdc_mdlm_desc;
+ struct usb_cdc_mdlm_detail_desc *usb_cdc_mdlm_detail_desc;
+ struct usb_cdc_obex_desc *usb_cdc_obex_desc;
+ struct usb_cdc_ncm_desc *usb_cdc_ncm_desc;
+ struct usb_cdc_mbim_desc *usb_cdc_mbim_desc;
+ struct usb_cdc_mbim_extended_desc *usb_cdc_mbim_extended_desc;
+
+ bool phonet_magic_present;
+};
+
+#define cdc_parse_cdc_header LINUX_BACKPORT(cdc_parse_cdc_header)
+int cdc_parse_cdc_header(struct usb_cdc_parsed_header *hdr,
+ struct usb_interface *intf,
+ u8 *buffer, int buflen);
+#endif
+
+#endif /* __BP_USB_CDC_H */
diff --git a/backport-include/linux/usb/ch9.h b/backport-include/linux/usb/ch9.h
new file mode 100644
index 0000000..c2f0120
--- /dev/null
+++ b/backport-include/linux/usb/ch9.h
@@ -0,0 +1,28 @@
+#ifndef __BACKPORT__LINUX_USB_CH9_H
+#define __BACKPORT__LINUX_USB_CH9_H
+
+#include <linux/version.h>
+#include_next <linux/usb/ch9.h>
+
+#if LINUX_VERSION_IS_LESS(3,2,0)
+#include <linux/types.h> /* __u8 etc */
+#include <asm/byteorder.h> /* le16_to_cpu */
+
+/**
+ * usb_endpoint_maxp - get endpoint's max packet size
+ * @epd: endpoint to be checked
+ *
+ * Returns @epd's max packet
+ */
+#define usb_endpoint_maxp LINUX_BACKPORT(usb_endpoint_maxp)
+static inline int usb_endpoint_maxp(const struct usb_endpoint_descriptor *epd)
+{
+ return __le16_to_cpu(epd->wMaxPacketSize);
+}
+#endif /* < 3.2 */
+
+#if LINUX_VERSION_IS_LESS(4,6,0)
+#define USB_SPEED_SUPER_PLUS 6
+#endif
+
+#endif /* __BACKPORT__LINUX_USB_CH9_H */
diff --git a/backport-include/linux/version.h b/backport-include/linux/version.h
new file mode 100644
index 0000000..2cb601a
--- /dev/null
+++ b/backport-include/linux/version.h
@@ -0,0 +1,17 @@
+#ifndef _BP_LINUX_VERSION_H
+#define _BP_LINUX_VERSION_H
+#include_next <linux/version.h>
+
+#ifndef RHEL_RELEASE_VERSION
+#define RHEL_RELEASE_VERSION(a,b) (((a) << 8) + (b))
+#endif
+
+#ifndef RHEL_RELEASE_CODE
+#define RHEL_RELEASE_CODE 0
+#endif
+
+#define LINUX_VERSION_IS_LESS(x1,x2,x3) (LINUX_VERSION_CODE < KERNEL_VERSION(x1,x2,x3))
+#define LINUX_VERSION_IS_GEQ(x1,x2,x3) (LINUX_VERSION_CODE >= KERNEL_VERSION(x1,x2,x3))
+#define LINUX_VERSION_IN_RANGE(x1,x2,x3, y1,y2,y3) \
+ (LINUX_VERSION_IS_GEQ(x1,x2,x3) && LINUX_VERSION_IS_LESS(y1,y2,y3))
+#endif /* _BP_LINUX_VERSION_H */
diff --git a/backport-include/linux/wait.h b/backport-include/linux/wait.h
new file mode 100644
index 0000000..3059ff1
--- /dev/null
+++ b/backport-include/linux/wait.h
@@ -0,0 +1,79 @@
+#ifndef __BACKPORT_LINUX_WAIT_H
+#define __BACKPORT_LINUX_WAIT_H
+#include_next <linux/wait.h>
+
+#if LINUX_VERSION_IS_LESS(3,17,0)
+extern int bit_wait(void *);
+extern int bit_wait_io(void *);
+
+static inline int
+backport_wait_on_bit(void *word, int bit, unsigned mode)
+{
+ return wait_on_bit(word, bit, bit_wait, mode);
+}
+
+static inline int
+backport_wait_on_bit_io(void *word, int bit, unsigned mode)
+{
+ return wait_on_bit(word, bit, bit_wait_io, mode);
+}
+
+#define wait_on_bit LINUX_BACKPORT(wait_on_bit)
+#define wait_on_bit_io LINUX_BACKPORT(wait_on_bit_io)
+
+#endif
+
+#if LINUX_VERSION_IS_LESS(3,18,12)
+#define WQ_FLAG_WOKEN 0x02
+
+#define wait_woken LINUX_BACKPORT(wait_woken)
+long wait_woken(wait_queue_t *wait, unsigned mode, long timeout);
+#define wait_woken LINUX_BACKPORT(wait_woken)
+int woken_wake_function(wait_queue_t *wait, unsigned mode, int sync, void *key);
+#endif
+
+/**
+ * For wait_on_bit_timeout() an extra member in struct wait_bit_key is needed.
+ * This was introuced in kernel 3.17 and we are only able to backport this
+ * function on these kernel versions.
+ */
+#if LINUX_VERSION_IS_GEQ(3,17,0)
+#if LINUX_VERSION_IS_LESS(3,18,0)
+#define out_of_line_wait_on_bit_timeout LINUX_BACKPORT(out_of_line_wait_on_bit_timeout)
+int out_of_line_wait_on_bit_timeout(void *, int, wait_bit_action_f *, unsigned, unsigned long);
+
+#define bit_wait_timeout LINUX_BACKPORT(bit_wait_timeout)
+extern int bit_wait_timeout(struct wait_bit_key *);
+#endif
+
+#if LINUX_VERSION_IS_LESS(3,20,0)
+#define wait_on_bit_timeout LINUX_BACKPORT(wait_on_bit_timeout)
+/**
+ * wait_on_bit_timeout - wait for a bit to be cleared or a timeout elapses
+ * @word: the word being waited on, a kernel virtual address
+ * @bit: the bit of the word being waited on
+ * @mode: the task state to sleep in
+ * @timeout: timeout, in jiffies
+ *
+ * Use the standard hashed waitqueue table to wait for a bit
+ * to be cleared. This is similar to wait_on_bit(), except also takes a
+ * timeout parameter.
+ *
+ * Returned value will be zero if the bit was cleared before the
+ * @timeout elapsed, or non-zero if the @timeout elapsed or process
+ * received a signal and the mode permitted wakeup on that signal.
+ */
+static inline int
+wait_on_bit_timeout(void *word, int bit, unsigned mode, unsigned long timeout)
+{
+ might_sleep();
+ if (!test_bit(bit, word))
+ return 0;
+ return out_of_line_wait_on_bit_timeout(word, bit,
+ bit_wait_timeout,
+ mode, timeout);
+}
+#endif
+#endif
+
+#endif /* __BACKPORT_LINUX_WAIT_H */
diff --git a/backport-include/linux/watchdog.h b/backport-include/linux/watchdog.h
new file mode 100644
index 0000000..598c8c4
--- /dev/null
+++ b/backport-include/linux/watchdog.h
@@ -0,0 +1,11 @@
+#ifndef __BACKPORT_WATCHDOG_H
+#define __BACKPORT_WATCHDOG_H
+#include_next <linux/watchdog.h>
+
+#if LINUX_VERSION_IS_LESS(3,1,0)
+#define watchdog_device LINUX_BACKPORT(watchdog_device)
+struct watchdog_device {
+};
+#endif
+
+#endif /* __BACKPORT_WATCHDOG_H */
diff --git a/backport-include/linux/workqueue.h b/backport-include/linux/workqueue.h
new file mode 100644
index 0000000..ab68d21
--- /dev/null
+++ b/backport-include/linux/workqueue.h
@@ -0,0 +1,67 @@
+#ifndef __BACKPORT_LINUX_WORKQUEUE_H
+#define __BACKPORT_LINUX_WORKQUEUE_H
+#include_next <linux/workqueue.h>
+#include <linux/version.h>
+
+#if LINUX_VERSION_IS_LESS(3,7,0)
+#define mod_delayed_work LINUX_BACKPORT(mod_delayed_work)
+bool mod_delayed_work(struct workqueue_struct *wq, struct delayed_work *dwork,
+ unsigned long delay);
+#endif
+
+#ifndef create_freezable_workqueue
+/* note freez_a_ble -> freez_ea_able */
+#define create_freezable_workqueue create_freezeable_workqueue
+#endif
+
+#if LINUX_VERSION_IS_LESS(3,3,0)
+#define __WQ_ORDERED 0
+/*
+ * commit b196be89cdc14a88cc637cdad845a75c5886c82d
+ * Author: Tejun Heo <tj@kernel.org>
+ * Date: Tue Jan 10 15:11:35 2012 -0800
+ *
+ * workqueue: make alloc_workqueue() take printf fmt and args for name
+ */
+struct workqueue_struct *
+backport_alloc_workqueue(const char *fmt, unsigned int flags,
+ int max_active, struct lock_class_key *key,
+ const char *lock_name, ...);
+#undef alloc_workqueue
+#ifdef CONFIG_LOCKDEP
+#define alloc_workqueue(fmt, flags, max_active, args...) \
+({ \
+ static struct lock_class_key __key; \
+ const char *__lock_name; \
+ \
+ if (__builtin_constant_p(fmt)) \
+ __lock_name = (fmt); \
+ else \
+ __lock_name = #fmt; \
+ \
+ backport_alloc_workqueue((fmt), (flags), (max_active), \
+ &__key, __lock_name, ##args); \
+})
+#else
+#define alloc_workqueue(fmt, flags, max_active, args...) \
+ backport_alloc_workqueue((fmt), (flags), (max_active), \
+ NULL, NULL, ##args)
+#endif
+#undef alloc_ordered_workqueue
+#define alloc_ordered_workqueue(fmt, flags, args...) \
+ alloc_workqueue(fmt, WQ_UNBOUND | __WQ_ORDERED | (flags), 1, ##args)
+#define destroy_workqueue backport_destroy_workqueue
+void backport_destroy_workqueue(struct workqueue_struct *wq);
+#endif
+
+#if LINUX_VERSION_IS_LESS(3,11,0)
+/* power efficient workqueues were added in commit 0668106ca386. */
+#define system_power_efficient_wq system_wq
+#define system_freezable_power_efficient_wq system_freezable_wq
+#endif
+
+#if LINUX_VERSION_IS_LESS(3,1,0)
+#define drain_workqueue(wq) flush_workqueue(wq)
+#endif
+
+#endif /* __BACKPORT_LINUX_WORKQUEUE_H */
diff --git a/backport-include/net/addrconf.h b/backport-include/net/addrconf.h
new file mode 100644
index 0000000..f1e8e62
--- /dev/null
+++ b/backport-include/net/addrconf.h
@@ -0,0 +1,25 @@
+#ifndef _BACKPORT_NET_ADDRCONF_H
+#define _BACKPORT_NET_ADDRCONF_H 1
+
+#include_next <net/addrconf.h>
+
+#include <linux/version.h>
+
+#if LINUX_VERSION_IS_LESS(3,9,0)
+static inline bool ipv6_addr_is_solict_mult(const struct in6_addr *addr)
+{
+#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) && BITS_PER_LONG == 64
+ __u64 *p = (__u64 *)addr;
+ return ((p[0] ^ cpu_to_be64(0xff02000000000000UL)) |
+ ((p[1] ^ cpu_to_be64(0x00000001ff000000UL)) &
+ cpu_to_be64(0xffffffffff000000UL))) == 0UL;
+#else
+ return ((addr->s6_addr32[0] ^ htonl(0xff020000)) |
+ addr->s6_addr32[1] |
+ (addr->s6_addr32[2] ^ htonl(0x00000001)) |
+ (addr->s6_addr[12] ^ 0xff)) == 0;
+#endif
+}
+#endif /* LINUX_VERSION_IS_LESS(3,9,0) */
+
+#endif /* _BACKPORT_NET_ADDRCONF_H */
diff --git a/backport-include/net/flow_keys.h b/backport-include/net/flow_keys.h
new file mode 100644
index 0000000..093bc80
--- /dev/null
+++ b/backport-include/net/flow_keys.h
@@ -0,0 +1,21 @@
+#if LINUX_VERSION_IS_GEQ(3,3,0)
+#include_next <net/flow_keys.h>
+#else
+
+#ifndef _NET_FLOW_KEYS_H
+#define _NET_FLOW_KEYS_H
+
+struct flow_keys {
+ /* (src,dst) must be grouped, in the same way than in IP header */
+ __be32 src;
+ __be32 dst;
+ union {
+ __be32 ports;
+ __be16 port16[2];
+ };
+ u8 ip_proto;
+};
+
+extern bool skb_flow_dissect(const struct sk_buff *skb, struct flow_keys *flow);
+#endif
+#endif
diff --git a/backport-include/net/genetlink.h b/backport-include/net/genetlink.h
new file mode 100644
index 0000000..b655d24
--- /dev/null
+++ b/backport-include/net/genetlink.h
@@ -0,0 +1,205 @@
+#ifndef __BACKPORT_NET_GENETLINK_H
+#define __BACKPORT_NET_GENETLINK_H
+#include_next <net/genetlink.h>
+#include <linux/version.h>
+
+#if LINUX_VERSION_IS_LESS(4,12,0)
+#define GENL_SET_ERR_MSG(info, msg) do { } while (0)
+
+static inline int genl_err_attr(struct genl_info *info, int err,
+ struct nlattr *attr)
+{
+#if LINUX_VERSION_IS_GEQ(4,12,0)
+ info->extack->bad_attr = attr;
+#endif
+
+ return err;
+}
+#endif
+
+/* this is for patches we apply */
+static inline struct netlink_ext_ack *genl_info_extack(struct genl_info *info)
+{
+#if LINUX_VERSION_IS_GEQ(4,12,0)
+ return info->extack;
+#else
+ return NULL;
+#endif
+}
+
+/* this is for patches we apply */
+#if LINUX_VERSION_IS_LESS(3,7,0)
+#define genl_info_snd_portid(__genl_info) (__genl_info->snd_pid)
+#else
+#define genl_info_snd_portid(__genl_info) (__genl_info->snd_portid)
+#endif
+
+#ifndef GENLMSG_DEFAULT_SIZE
+#define GENLMSG_DEFAULT_SIZE (NLMSG_DEFAULT_SIZE - GENL_HDRLEN)
+#endif
+
+#if LINUX_VERSION_IS_LESS(3,1,0)
+#define genl_dump_check_consistent(cb, user_hdr, family)
+#endif
+
+#if LINUX_VERSION_IS_LESS(3,13,0) && RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(7,0)
+static inline int __real_genl_register_family(struct genl_family *family)
+{
+ return genl_register_family(family);
+}
+
+/* Needed for the mcgrps pointer */
+struct backport_genl_family {
+ struct genl_family family;
+
+ unsigned int id, hdrsize, version, maxattr;
+ char name[GENL_NAMSIZ];
+ bool netnsok;
+ bool parallel_ops;
+
+ struct nlattr **attrbuf;
+
+ int (*pre_doit)(struct genl_ops *ops, struct sk_buff *skb,
+ struct genl_info *info);
+
+ void (*post_doit)(struct genl_ops *ops, struct sk_buff *skb,
+ struct genl_info *info);
+
+ struct genl_multicast_group *mcgrps;
+ struct genl_ops *ops;
+ unsigned int n_mcgrps, n_ops;
+
+ struct module *module;
+};
+#define genl_family LINUX_BACKPORT(genl_family)
+
+int __backport_genl_register_family(struct genl_family *family);
+
+#define genl_register_family LINUX_BACKPORT(genl_register_family)
+static inline int
+genl_register_family(struct genl_family *family)
+{
+ family->module = THIS_MODULE;
+ return __backport_genl_register_family(family);
+}
+
+#define _genl_register_family_with_ops_grps \
+ _backport_genl_register_family_with_ops_grps
+static inline int
+_genl_register_family_with_ops_grps(struct genl_family *family,
+ struct genl_ops *ops, size_t n_ops,
+ struct genl_multicast_group *mcgrps,
+ size_t n_mcgrps)
+{
+ family->ops = ops;
+ family->n_ops = n_ops;
+ family->mcgrps = mcgrps;
+ family->n_mcgrps = n_mcgrps;
+ return genl_register_family(family);
+}
+
+#define genl_register_family_with_ops(family, ops) \
+ _genl_register_family_with_ops_grps((family), \
+ (ops), ARRAY_SIZE(ops), \
+ NULL, 0)
+#define genl_register_family_with_ops_groups(family, ops, grps) \
+ _genl_register_family_with_ops_grps((family), \
+ (ops), ARRAY_SIZE(ops), \
+ (grps), ARRAY_SIZE(grps))
+
+#define genl_unregister_family backport_genl_unregister_family
+int genl_unregister_family(struct genl_family *family);
+
+#if LINUX_VERSION_IS_LESS(3,3,0)
+extern void genl_notify(struct sk_buff *skb, struct net *net, u32 pid,
+ u32 group, struct nlmsghdr *nlh, gfp_t flags);
+#endif
+#define genl_notify(_fam, _skb, _info, _group, _flags) \
+ genl_notify(_skb, genl_info_net(_info), \
+ genl_info_snd_portid(_info), \
+ (_fam)->mcgrps[_group].id, _info->nlhdr, _flags)
+#define genlmsg_put(_skb, _pid, _seq, _fam, _flags, _cmd) \
+ genlmsg_put(_skb, _pid, _seq, &(_fam)->family, _flags, _cmd)
+#define genlmsg_nlhdr(_hdr, _fam) \
+ genlmsg_nlhdr(_hdr, &(_fam)->family)
+#ifndef genl_dump_check_consistent
+#define genl_dump_check_consistent(_cb, _hdr, _fam) \
+ genl_dump_check_consistent(_cb, _hdr, &(_fam)->family)
+#endif
+#ifndef genlmsg_put_reply /* might already be there from _info override above */
+#define genlmsg_put_reply(_skb, _info, _fam, _flags, _cmd) \
+ genlmsg_put_reply(_skb, _info, &(_fam)->family, _flags, _cmd)
+#endif
+#define genlmsg_multicast_netns LINUX_BACKPORT(genlmsg_multicast_netns)
+static inline int genlmsg_multicast_netns(struct genl_family *family,
+ struct net *net, struct sk_buff *skb,
+ u32 portid, unsigned int group,
+ gfp_t flags)
+{
+ if (WARN_ON_ONCE(group >= family->n_mcgrps))
+ return -EINVAL;
+ group = family->mcgrps[group].id;
+ return nlmsg_multicast(
+ net->genl_sock,
+ skb, portid, group, flags);
+}
+#define genlmsg_multicast LINUX_BACKPORT(genlmsg_multicast)
+static inline int genlmsg_multicast(struct genl_family *family,
+ struct sk_buff *skb, u32 portid,
+ unsigned int group, gfp_t flags)
+{
+ if (WARN_ON_ONCE(group >= family->n_mcgrps))
+ return -EINVAL;
+ group = family->mcgrps[group].id;
+ return nlmsg_multicast(
+ init_net.genl_sock,
+ skb, portid, group, flags);
+}
+static inline int
+backport_genlmsg_multicast_allns(struct genl_family *family,
+ struct sk_buff *skb, u32 portid,
+ unsigned int group, gfp_t flags)
+{
+ if (WARN_ON_ONCE(group >= family->n_mcgrps))
+ return -EINVAL;
+ group = family->mcgrps[group].id;
+ return genlmsg_multicast_allns(skb, portid, group, flags);
+}
+#define genlmsg_multicast_allns LINUX_BACKPORT(genlmsg_multicast_allns)
+
+#define __genl_const
+#else /* < 3.13 */
+#define __genl_const const
+#if LINUX_VERSION_IS_LESS(4,4,0)
+#define genl_notify(_fam, _skb, _info, _group, _flags) \
+ genl_notify(_fam, _skb, genl_info_net(_info), \
+ genl_info_snd_portid(_info), \
+ _group, _info->nlhdr, _flags)
+#endif /* < 4.4 */
+#endif /* < 3.13 */
+
+#if LINUX_VERSION_IS_LESS(4,10,0)
+/**
+ * genl_family_attrbuf - return family's attrbuf
+ * @family: the family
+ *
+ * Return the family's attrbuf, while validating that it's
+ * actually valid to access it.
+ *
+ * You cannot use this function with a family that has parallel_ops
+ * and you can only use it within (pre/post) doit/dumpit callbacks.
+ */
+#define genl_family_attrbuf LINUX_BACKPORT(genl_family_attrbuf)
+static inline struct nlattr **genl_family_attrbuf(struct genl_family *family)
+{
+ WARN_ON(family->parallel_ops);
+
+ return family->attrbuf;
+}
+
+#define __genl_ro_after_init
+#else
+#define __genl_ro_after_init __ro_after_init
+#endif
+
+#endif /* __BACKPORT_NET_GENETLINK_H */
diff --git a/backport-include/net/inet_frag.h b/backport-include/net/inet_frag.h
new file mode 100644
index 0000000..e95eac3
--- /dev/null
+++ b/backport-include/net/inet_frag.h
@@ -0,0 +1,76 @@
+#ifndef __BACKPORT__NET_FRAG_H__
+#define __BACKPORT__NET_FRAG_H__
+#include_next <net/inet_frag.h>
+#include <linux/version.h>
+
+#if LINUX_VERSION_IS_LESS(3,9,0)
+/* Memory Tracking Functions. */
+#define frag_mem_limit LINUX_BACKPORT(frag_mem_limit)
+static inline int frag_mem_limit(struct netns_frags *nf)
+{
+ return atomic_read(&nf->mem);
+}
+
+#define init_frag_mem_limit LINUX_BACKPORT(init_frag_mem_limit)
+static inline void init_frag_mem_limit(struct netns_frags *nf)
+{
+ atomic_set(&nf->mem, 0);
+}
+
+#define sum_frag_mem_limit LINUX_BACKPORT(sum_frag_mem_limit)
+static inline int sum_frag_mem_limit(struct netns_frags *nf)
+{
+ return atomic_read(&nf->mem);
+}
+
+#define inet_frag_maybe_warn_overflow LINUX_BACKPORT(inet_frag_maybe_warn_overflow)
+void inet_frag_maybe_warn_overflow(struct inet_frag_queue *q,
+ const char *prefix);
+#endif /* LINUX_VERSION_IS_LESS(3,9,0) */
+
+/* the type of the paramater changed with kernel 4.3 */
+#if LINUX_VERSION_IS_LESS(3,9,0)
+#define sub_frag_mem_limit LINUX_BACKPORT(sub_frag_mem_limit)
+static inline void sub_frag_mem_limit(struct netns_frags *nf, int i)
+{
+ atomic_sub(i, &nf->mem);
+}
+
+#define add_frag_mem_limit LINUX_BACKPORT(add_frag_mem_limit)
+static inline void add_frag_mem_limit(struct netns_frags *nf, int i)
+{
+ atomic_add(i, &nf->mem);
+}
+#elif LINUX_VERSION_IS_LESS(4,3,0)
+#define sub_frag_mem_limit LINUX_BACKPORT(sub_frag_mem_limit)
+static inline void sub_frag_mem_limit(struct netns_frags *nf, int i)
+{
+ __percpu_counter_add(&nf->mem, -i, frag_percpu_counter_batch);
+}
+
+#define add_frag_mem_limit LINUX_BACKPORT(add_frag_mem_limit)
+static inline void add_frag_mem_limit(struct netns_frags *nf, int i)
+{
+ __percpu_counter_add(&nf->mem, i, frag_percpu_counter_batch);
+}
+#endif /* LINUX_VERSION_IS_LESS(4,3,0) */
+
+#if LINUX_VERSION_IS_LESS(4,4,0) && \
+ LINUX_VERSION_IS_GEQ(3,9,0)
+#define inet_frags_uninit_net LINUX_BACKPORT(inet_frags_uninit_net)
+static inline void inet_frags_uninit_net(struct netns_frags *nf)
+{
+ percpu_counter_destroy(&nf->mem);
+}
+#endif /* < 4.4 && >= 3.9 */
+
+#if LINUX_VERSION_IS_LESS(4,4,0)
+static inline int backport_inet_frags_init_net(struct netns_frags *nf)
+{
+ inet_frags_init_net(nf);
+ return 0;
+}
+#define inet_frags_init_net LINUX_BACKPORT(inet_frags_init_net)
+#endif /* < 4.4 */
+
+#endif /* __BACKPORT__NET_FRAG_H__ */
diff --git a/backport-include/net/ip.h b/backport-include/net/ip.h
new file mode 100644
index 0000000..6893ba5
--- /dev/null
+++ b/backport-include/net/ip.h
@@ -0,0 +1,14 @@
+#ifndef __BACKPORT_NET_IP_H
+#define __BACKPORT_NET_IP_H
+#include_next <net/ip.h>
+#include <linux/version.h>
+
+#if LINUX_VERSION_IS_LESS(3,1,0)
+/* Backports 56f8a75c */
+static inline bool ip_is_fragment(const struct iphdr *iph)
+{
+ return (iph->frag_off & htons(IP_MF | IP_OFFSET)) != 0;
+}
+#endif
+
+#endif /* __BACKPORT_NET_IP_H */
diff --git a/backport-include/net/ip6_fib.h b/backport-include/net/ip6_fib.h
new file mode 100644
index 0000000..2f21163
--- /dev/null
+++ b/backport-include/net/ip6_fib.h
@@ -0,0 +1,26 @@
+#ifndef __BACKPORT_NET_IP6_ROUTE_H
+#define __BACKPORT_NET_IP6_ROUTE_H
+#include_next <net/ip6_fib.h>
+#include <net/ip6_route.h>
+#include <linux/route.h>
+#include <linux/version.h>
+
+/*
+ * This function is avaliable with one argument since kernel 3.10, but the
+ * secound one was added in 4.2.
+ */
+#if LINUX_VERSION_IS_LESS(4,2,0)
+#define rt6_nexthop LINUX_BACKPORT(rt6_nexthop)
+static inline struct in6_addr *rt6_nexthop(struct rt6_info *rt,
+ struct in6_addr *daddr)
+{
+ if (rt->rt6i_flags & RTF_GATEWAY)
+ return &rt->rt6i_gateway;
+ else if (rt->rt6i_flags & RTF_CACHE)
+ return &rt->rt6i_dst.addr;
+ else
+ return daddr;
+}
+#endif /* LINUX_VERSION_IS_LESS(4,2,0) */
+
+#endif /* __BACKPORT_NET_IP6_ROUTE_H */
diff --git a/backport-include/net/ipv6.h b/backport-include/net/ipv6.h
new file mode 100644
index 0000000..7416d6b
--- /dev/null
+++ b/backport-include/net/ipv6.h
@@ -0,0 +1,60 @@
+#ifndef __BACKPORT_NET_IPV6_H
+#define __BACKPORT_NET_IPV6_H
+#include_next <net/ipv6.h>
+#include <linux/version.h>
+#include <net/addrconf.h>
+#include <net/inet_frag.h>
+
+#if LINUX_VERSION_IS_LESS(3,7,0)
+/*
+ * Equivalent of ipv4 struct ip
+ */
+struct frag_queue {
+ struct inet_frag_queue q;
+
+ __be32 id; /* fragment id */
+ u32 user;
+ struct in6_addr saddr;
+ struct in6_addr daddr;
+
+ int iif;
+ unsigned int csum;
+ __u16 nhoffset;
+};
+#endif /* LINUX_VERSION_IS_LESS(3,7,0) */
+
+#if LINUX_VERSION_IS_LESS(3,6,0)
+#define ipv6_addr_hash LINUX_BACKPORT(ipv6_addr_hash)
+static inline u32 ipv6_addr_hash(const struct in6_addr *a)
+{
+#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) && BITS_PER_LONG == 64
+ const unsigned long *ul = (const unsigned long *)a;
+ unsigned long x = ul[0] ^ ul[1];
+
+ return (u32)(x ^ (x >> 32));
+#else
+ return (__force u32)(a->s6_addr32[0] ^ a->s6_addr32[1] ^
+ a->s6_addr32[2] ^ a->s6_addr32[3]);
+#endif
+}
+#endif
+
+#if LINUX_VERSION_IS_LESS(4,5,0)
+#define ipv6_addr_prefix_copy LINUX_BACKPORT(ipv6_addr_prefix_copy)
+static inline void ipv6_addr_prefix_copy(struct in6_addr *addr,
+ const struct in6_addr *pfx,
+ int plen)
+{
+ /* caller must guarantee 0 <= plen <= 128 */
+ int o = plen >> 3,
+ b = plen & 0x7;
+
+ memcpy(addr->s6_addr, pfx, o);
+ if (b != 0) {
+ addr->s6_addr[o] &= ~(0xff00 >> b);
+ addr->s6_addr[o] |= (pfx->s6_addr[o] & (0xff00 >> b));
+ }
+}
+#endif
+
+#endif /* __BACKPORT_NET_IPV6_H */
diff --git a/backport-include/net/iw_handler.h b/backport-include/net/iw_handler.h
new file mode 100644
index 0000000..84d63b3
--- /dev/null
+++ b/backport-include/net/iw_handler.h
@@ -0,0 +1,40 @@
+#ifndef __BACKPORT_IW_HANDLER_H
+#define __BACKPORT_IW_HANDLER_H
+#include_next <net/iw_handler.h>
+
+#if LINUX_VERSION_IS_LESS(4,1,0)
+static inline char *
+iwe_stream_add_event_check(struct iw_request_info *info, char *stream,
+ char *ends, struct iw_event *iwe, int event_len)
+{
+ char *res = iwe_stream_add_event(info, stream, ends, iwe, event_len);
+
+ if (res == stream)
+ return ERR_PTR(-E2BIG);
+ return res;
+}
+
+static inline char *
+iwe_stream_add_point_check(struct iw_request_info *info, char *stream,
+ char *ends, struct iw_event *iwe, char *extra)
+{
+ char *res = iwe_stream_add_point(info, stream, ends, iwe, extra);
+
+ if (res == stream)
+ return ERR_PTR(-E2BIG);
+ return res;
+}
+#endif /* LINUX_VERSION_IS_LESS(4,1,0) */
+
+/* this was added in v3.2.79, v3.18.30, v4.1.21, v4.4.6 and 4.5 */
+#if !(LINUX_VERSION_IS_GEQ(4,4,6) || \
+ (LINUX_VERSION_IS_GEQ(4,1,21) && \
+ LINUX_VERSION_IS_LESS(4,2,0)) || \
+ (LINUX_VERSION_IS_GEQ(3,18,30) && \
+ LINUX_VERSION_IS_LESS(3,19,0)) || \
+ (LINUX_VERSION_IS_GEQ(3,2,79) && \
+ LINUX_VERSION_IS_LESS(3,3,0)))
+#define wireless_nlevent_flush LINUX_BACKPORT(wireless_nlevent_flush)
+static inline void wireless_nlevent_flush(void) {}
+#endif
+#endif /* __BACKPORT_IW_HANDLER_H */
diff --git a/backport-include/net/net_namespace.h b/backport-include/net/net_namespace.h
new file mode 100644
index 0000000..1e84297
--- /dev/null
+++ b/backport-include/net/net_namespace.h
@@ -0,0 +1,44 @@
+#ifndef _COMPAT_NET_NET_NAMESPACE_H
+#define _COMPAT_NET_NET_NAMESPACE_H 1
+
+#include_next <net/net_namespace.h>
+
+#if LINUX_VERSION_IS_LESS(3,20,0)
+/*
+ * In older kernels we simply fail this function.
+ */
+#define get_net_ns_by_fd LINUX_BACKPORT(get_net_ns_by_fd)
+static inline struct net *get_net_ns_by_fd(int fd)
+{
+ return ERR_PTR(-EINVAL);
+}
+#endif
+
+#if LINUX_VERSION_IS_LESS(4,1,0)
+typedef struct {
+#ifdef CONFIG_NET_NS
+ struct net *net;
+#endif
+} possible_net_t;
+
+static inline void possible_write_pnet(possible_net_t *pnet, struct net *net)
+{
+#ifdef CONFIG_NET_NS
+ pnet->net = net;
+#endif
+}
+
+static inline struct net *possible_read_pnet(const possible_net_t *pnet)
+{
+#ifdef CONFIG_NET_NS
+ return pnet->net;
+#else
+ return &init_net;
+#endif
+}
+#else
+#define possible_write_pnet(pnet, net) write_pnet(pnet, net)
+#define possible_read_pnet(pnet) read_pnet(pnet)
+#endif /* LINUX_VERSION_IS_LESS(4,1,0) */
+
+#endif /* _COMPAT_NET_NET_NAMESPACE_H */
diff --git a/backport-include/net/netlink.h b/backport-include/net/netlink.h
new file mode 100644
index 0000000..37c899b
--- /dev/null
+++ b/backport-include/net/netlink.h
@@ -0,0 +1,360 @@
+#ifndef __BACKPORT_NET_NETLINK_H
+#define __BACKPORT_NET_NETLINK_H
+#include_next <net/netlink.h>
+#include <linux/version.h>
+#include <linux/in6.h>
+
+#if LINUX_VERSION_IS_LESS(4,12,0)
+#include <backport/magic.h>
+
+static inline int nla_validate5(const struct nlattr *head,
+ int len, int maxtype,
+ const struct nla_policy *policy,
+ struct netlink_ext_ack *extack)
+{
+ return nla_validate(head, len, maxtype, policy);
+}
+#define nla_validate4 nla_validate
+#define nla_validate(...) \
+ macro_dispatcher(nla_validate, __VA_ARGS__)(__VA_ARGS__)
+
+static inline int nla_parse6(struct nlattr **tb, int maxtype,
+ const struct nlattr *head,
+ int len, const struct nla_policy *policy,
+ struct netlink_ext_ack *extack)
+{
+ return nla_parse(tb, maxtype, head, len, policy);
+}
+#define nla_parse5(...) nla_parse(__VA_ARGS__)
+#define nla_parse(...) \
+ macro_dispatcher(nla_parse, __VA_ARGS__)(__VA_ARGS__)
+
+static inline int nlmsg_parse6(const struct nlmsghdr *nlh, int hdrlen,
+ struct nlattr *tb[], int maxtype,
+ const struct nla_policy *policy,
+ struct netlink_ext_ack *extack)
+{
+ return nlmsg_parse(nlh, hdrlen, tb, maxtype, policy);
+}
+#define nlmsg_parse5 nlmsg_parse
+#define nlmsg_parse(...) \
+ macro_dispatcher(nlmsg_parse, __VA_ARGS__)(__VA_ARGS__)
+
+static inline int nlmsg_validate5(const struct nlmsghdr *nlh,
+ int hdrlen, int maxtype,
+ const struct nla_policy *policy,
+ struct netlink_ext_ack *extack)
+{
+ return nlmsg_validate(nlh, hdrlen, maxtype, policy);
+}
+#define nlmsg_validate4 nlmsg_validate
+#define nlmsg_validate(...) \
+ macro_dispatcher(nlmsg_validate, __VA_ARGS__)(__VA_ARGS__)
+
+static inline int nla_parse_nested5(struct nlattr *tb[], int maxtype,
+ const struct nlattr *nla,
+ const struct nla_policy *policy,
+ struct netlink_ext_ack *extack)
+{
+ return nla_parse_nested(tb, maxtype, nla, policy);
+}
+#define nla_parse_nested4 nla_parse_nested
+#define nla_parse_nested(...) \
+ macro_dispatcher(nla_parse_nested, __VA_ARGS__)(__VA_ARGS__)
+
+static inline int nla_validate_nested4(const struct nlattr *start, int maxtype,
+ const struct nla_policy *policy,
+ struct netlink_ext_ack *extack)
+{
+ return nla_validate_nested(start, maxtype, policy);
+}
+#define nla_validate_nested3 nla_validate_nested
+#define nla_validate_nested(...) \
+ macro_dispatcher(nla_validate_nested, __VA_ARGS__)(__VA_ARGS__)
+#endif /* LINUX_VERSION_IS_LESS(4,12,0) */
+
+#if LINUX_VERSION_IS_LESS(3,7,0)
+/**
+ * nla_put_s8 - Add a s8 netlink attribute to a socket buffer
+ * @skb: socket buffer to add attribute to
+ * @attrtype: attribute type
+ * @value: numeric value
+ */
+#define nla_put_s8 LINUX_BACKPORT(nla_put_s8)
+static inline int nla_put_s8(struct sk_buff *skb, int attrtype, s8 value)
+{
+ return nla_put(skb, attrtype, sizeof(s8), &value);
+}
+
+/**
+ * nla_put_s16 - Add a s16 netlink attribute to a socket buffer
+ * @skb: socket buffer to add attribute to
+ * @attrtype: attribute type
+ * @value: numeric value
+ */
+#define nla_put_s16 LINUX_BACKPORT(nla_put_s16)
+static inline int nla_put_s16(struct sk_buff *skb, int attrtype, s16 value)
+{
+ return nla_put(skb, attrtype, sizeof(s16), &value);
+}
+
+/**
+ * nla_put_s32 - Add a s32 netlink attribute to a socket buffer
+ * @skb: socket buffer to add attribute to
+ * @attrtype: attribute type
+ * @value: numeric value
+ */
+#define nla_put_s32 LINUX_BACKPORT(nla_put_s32)
+static inline int nla_put_s32(struct sk_buff *skb, int attrtype, s32 value)
+{
+ return nla_put(skb, attrtype, sizeof(s32), &value);
+}
+
+/**
+ * nla_get_s32 - return payload of s32 attribute
+ * @nla: s32 netlink attribute
+ */
+#define nla_get_s32 LINUX_BACKPORT(nla_get_s32)
+static inline s32 nla_get_s32(const struct nlattr *nla)
+{
+ return *(s32 *) nla_data(nla);
+}
+
+/**
+ * nla_get_s16 - return payload of s16 attribute
+ * @nla: s16 netlink attribute
+ */
+#define nla_get_s16 LINUX_BACKPORT(nla_get_s16)
+static inline s16 nla_get_s16(const struct nlattr *nla)
+{
+ return *(s16 *) nla_data(nla);
+}
+
+/**
+ * nla_get_s8 - return payload of s8 attribute
+ * @nla: s8 netlink attribute
+ */
+#define nla_get_s8 LINUX_BACKPORT(nla_get_s8)
+static inline s8 nla_get_s8(const struct nlattr *nla)
+{
+ return *(s8 *) nla_data(nla);
+}
+
+/**
+ * nla_get_s64 - return payload of s64 attribute
+ * @nla: s64 netlink attribute
+ */
+#define nla_get_s64 LINUX_BACKPORT(nla_get_s64)
+static inline s64 nla_get_s64(const struct nlattr *nla)
+{
+ s64 tmp;
+
+ nla_memcpy(&tmp, nla, sizeof(tmp));
+
+ return tmp;
+}
+#endif /* < 3.7.0 */
+
+#if LINUX_VERSION_IS_LESS(3,5,0)
+/*
+ * This backports:
+ * commit 569a8fc38367dfafd87454f27ac646c8e6b54bca
+ * Author: David S. Miller <davem@davemloft.net>
+ * Date: Thu Mar 29 23:18:53 2012 -0400
+ *
+ * netlink: Add nla_put_be{16,32,64}() helpers.
+ */
+
+#define nla_put_be16 LINUX_BACKPORT(nla_put_be16)
+static inline int nla_put_be16(struct sk_buff *skb, int attrtype, __be16 value)
+{
+ return nla_put(skb, attrtype, sizeof(__be16), &value);
+}
+
+#define nla_put_be32 LINUX_BACKPORT(nla_put_be32)
+static inline int nla_put_be32(struct sk_buff *skb, int attrtype, __be32 value)
+{
+ return nla_put(skb, attrtype, sizeof(__be32), &value);
+}
+
+#define nla_put_be64 LINUX_BACKPORT(nla_put_be64)
+static inline int nla_put_be64(struct sk_buff *skb, int attrtype, __be64 value)
+{
+ return nla_put(skb, attrtype, sizeof(__be64), &value);
+}
+#endif /* < 3.5 */
+
+#if LINUX_VERSION_IS_LESS(3,7,0)
+#define NLA_S8 (NLA_BINARY + 1)
+#define NLA_S16 (NLA_BINARY + 2)
+#define NLA_S32 (NLA_BINARY + 3)
+#define NLA_S64 (NLA_BINARY + 4)
+#define __NLA_TYPE_MAX (NLA_BINARY + 5)
+
+#undef NLA_TYPE_MAX
+#define NLA_TYPE_MAX (__NLA_TYPE_MAX - 1)
+#endif
+
+#if LINUX_VERSION_IS_LESS(4,1,0)
+#define nla_put_in_addr LINUX_BACKPORT(nla_put_in_addr)
+static inline int nla_put_in_addr(struct sk_buff *skb, int attrtype,
+ __be32 addr)
+{
+ return nla_put_be32(skb, attrtype, addr);
+}
+
+#define nla_put_in6_addr LINUX_BACKPORT(nla_put_in6_addr)
+static inline int nla_put_in6_addr(struct sk_buff *skb, int attrtype,
+ const struct in6_addr *addr)
+{
+ return nla_put(skb, attrtype, sizeof(*addr), addr);
+}
+
+static inline __be32 nla_get_in_addr(const struct nlattr *nla)
+{
+ return *(__be32 *) nla_data(nla);
+}
+
+static inline struct in6_addr nla_get_in6_addr(const struct nlattr *nla)
+{
+ struct in6_addr tmp;
+
+ nla_memcpy(&tmp, nla, sizeof(tmp));
+ return tmp;
+}
+#endif /* < 4.1 */
+
+#if LINUX_VERSION_IS_LESS(4,4,0)
+/**
+ * nla_get_le32 - return payload of __le32 attribute
+ * @nla: __le32 netlink attribute
+ */
+#define nla_get_le32 LINUX_BACKPORT(nla_get_le32)
+static inline __le32 nla_get_le32(const struct nlattr *nla)
+{
+ return *(__le32 *) nla_data(nla);
+}
+
+/**
+ * nla_get_le64 - return payload of __le64 attribute
+ * @nla: __le64 netlink attribute
+ */
+#define nla_get_le64 LINUX_BACKPORT(nla_get_le64)
+static inline __le64 nla_get_le64(const struct nlattr *nla)
+{
+ return *(__le64 *) nla_data(nla);
+}
+#endif /* < 4.4 */
+
+#if LINUX_VERSION_IS_LESS(4,7,0)
+/**
+ * nla_need_padding_for_64bit - test 64-bit alignment of the next attribute
+ * @skb: socket buffer the message is stored in
+ *
+ * Return true if padding is needed to align the next attribute (nla_data()) to
+ * a 64-bit aligned area.
+ */
+#define nla_need_padding_for_64bit LINUX_BACKPORT(nla_need_padding_for_64bit)
+static inline bool nla_need_padding_for_64bit(struct sk_buff *skb)
+{
+#ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
+ /* The nlattr header is 4 bytes in size, that's why we test
+ * if the skb->data _is_ aligned. A NOP attribute, plus
+ * nlattr header for next attribute, will make nla_data()
+ * 8-byte aligned.
+ */
+ if (IS_ALIGNED((unsigned long)skb_tail_pointer(skb), 8))
+ return true;
+#endif
+ return false;
+}
+/**
+ * nla_align_64bit - 64-bit align the nla_data() of next attribute
+ * @skb: socket buffer the message is stored in
+ * @padattr: attribute type for the padding
+ *
+ * Conditionally emit a padding netlink attribute in order to make
+ * the next attribute we emit have a 64-bit aligned nla_data() area.
+ * This will only be done in architectures which do not have
+ * HAVE_EFFICIENT_UNALIGNED_ACCESS defined.
+ *
+ * Returns zero on success or a negative error code.
+ */
+#define nla_align_64bit LINUX_BACKPORT(nla_align_64bit)
+static inline int nla_align_64bit(struct sk_buff *skb, int padattr)
+{
+ if (nla_need_padding_for_64bit(skb) &&
+ !nla_reserve(skb, padattr, 0))
+ return -EMSGSIZE;
+ return 0;
+}
+
+/**
+ * nla_total_size_64bit - total length of attribute including padding
+ * @payload: length of payload
+ */
+#define nla_total_size_64bit LINUX_BACKPORT(nla_total_size_64bit)
+static inline int nla_total_size_64bit(int payload)
+{
+ return NLA_ALIGN(nla_attr_size(payload))
+#ifndef HAVE_EFFICIENT_UNALIGNED_ACCESS
+ + NLA_ALIGN(nla_attr_size(0))
+#endif
+ ;
+}
+#define __nla_reserve_64bit LINUX_BACKPORT(__nla_reserve_64bit)
+struct nlattr *__nla_reserve_64bit(struct sk_buff *skb, int attrtype,
+ int attrlen, int padattr);
+#define nla_reserve_64bit LINUX_BACKPORT(nla_reserve_64bit)
+struct nlattr *nla_reserve_64bit(struct sk_buff *skb, int attrtype,
+ int attrlen, int padattr);
+#define __nla_put_64bit LINUX_BACKPORT(__nla_put_64bit)
+void __nla_put_64bit(struct sk_buff *skb, int attrtype, int attrlen,
+ const void *data, int padattr);
+#define nla_put_64bit LINUX_BACKPORT(nla_put_64bit)
+int nla_put_64bit(struct sk_buff *skb, int attrtype, int attrlen,
+ const void *data, int padattr);
+/**
+ * nla_put_u64_64bit - Add a u64 netlink attribute to a skb and align it
+ * @skb: socket buffer to add attribute to
+ * @attrtype: attribute type
+ * @value: numeric value
+ * @padattr: attribute type for the padding
+ */
+#define nla_put_u64_64bit LINUX_BACKPORT(nla_put_u64_64bit)
+static inline int nla_put_u64_64bit(struct sk_buff *skb, int attrtype,
+ u64 value, int padattr)
+{
+ return nla_put_64bit(skb, attrtype, sizeof(u64), &value, padattr);
+}
+
+
+/**
+ * nla_put_s64 - Add a s64 netlink attribute to a socket buffer and align it
+ * @skb: socket buffer to add attribute to
+ * @attrtype: attribute type
+ * @value: numeric value
+ * @padattr: attribute type for the padding
+ */
+#define nla_put_s64 LINUX_BACKPORT(nla_put_s64)
+static inline int nla_put_s64(struct sk_buff *skb, int attrtype, s64 value,
+ int padattr)
+{
+ return nla_put_64bit(skb, attrtype, sizeof(s64), &value, padattr);
+}
+#endif /* < 4.7 */
+
+#if LINUX_VERSION_IS_LESS(4,10,0)
+/**
+ * nla_memdup - duplicate attribute memory (kmemdup)
+ * @src: netlink attribute to duplicate from
+ * @gfp: GFP mask
+ */
+#define nla_memdump LINUX_BACKPORT(nla_memdup)
+static inline void *nla_memdup(const struct nlattr *src, gfp_t gfp)
+{
+ return kmemdup(nla_data(src), nla_len(src), gfp);
+}
+#endif /* < 4.9 */
+
+#endif /* __BACKPORT_NET_NETLINK_H */
diff --git a/backport-include/net/sch_generic.h b/backport-include/net/sch_generic.h
new file mode 100644
index 0000000..cabc601
--- /dev/null
+++ b/backport-include/net/sch_generic.h
@@ -0,0 +1,20 @@
+#ifndef __BACKPORT_NET_SCH_GENERIC_H
+#define __BACKPORT_NET_SCH_GENERIC_H
+#include_next <net/sch_generic.h>
+
+#if LINUX_VERSION_IS_LESS(3,3,0)
+#if !((LINUX_VERSION_IS_GEQ(3,2,9) && LINUX_VERSION_IS_LESS(3,3,0)) || (LINUX_VERSION_IS_GEQ(3,0,23) && LINUX_VERSION_IS_LESS(3,1,0)))
+/* mask qdisc_cb_private_validate as RHEL6 backports this */
+#define qdisc_cb_private_validate(a,b) compat_qdisc_cb_private_validate(a,b)
+static inline void qdisc_cb_private_validate(const struct sk_buff *skb, int sz)
+{
+ BUILD_BUG_ON(sizeof(skb->cb) < sizeof(struct qdisc_skb_cb) + sz);
+}
+#endif
+#endif /* LINUX_VERSION_IS_LESS(3,3,0) */
+
+#ifndef TCQ_F_CAN_BYPASS
+#define TCQ_F_CAN_BYPASS 4
+#endif
+
+#endif /* __BACKPORT_NET_SCH_GENERIC_H */
diff --git a/backport-include/net/sock.h b/backport-include/net/sock.h
new file mode 100644
index 0000000..89191f3
--- /dev/null
+++ b/backport-include/net/sock.h
@@ -0,0 +1,66 @@
+#ifndef __BACKPORT_NET_SOCK_H
+#define __BACKPORT_NET_SOCK_H
+#include_next <net/sock.h>
+#include <linux/version.h>
+
+#if LINUX_VERSION_IS_LESS(3,9,0)
+#include <backport/magic.h>
+
+#define sk_for_each3(__sk, node, list) \
+ hlist_for_each_entry(__sk, node, list, sk_node)
+
+#define sk_for_each_safe4(__sk, node, tmp, list) \
+ hlist_for_each_entry_safe(__sk, node, tmp, list, sk_node)
+
+#define sk_for_each2(__sk, list) \
+ hlist_for_each_entry(__sk, list, sk_node)
+
+#define sk_for_each_safe3(__sk, tmp, list) \
+ hlist_for_each_entry_safe(__sk, tmp, list, sk_node)
+
+#undef sk_for_each
+#define sk_for_each(...) \
+ macro_dispatcher(sk_for_each, __VA_ARGS__)(__VA_ARGS__)
+#undef sk_for_each_safe
+#define sk_for_each_safe(...) \
+ macro_dispatcher(sk_for_each_safe, __VA_ARGS__)(__VA_ARGS__)
+
+#endif
+
+#if LINUX_VERSION_IS_LESS(3,10,0)
+/*
+ * backport SOCK_SELECT_ERR_QUEUE -- see commit
+ * "net: add option to enable error queue packets waking select"
+ *
+ * Adding 14 to SOCK_QUEUE_SHRUNK will reach a bet that can't be
+ * set on older kernels, so sock_flag() will always return false.
+ */
+#define SOCK_SELECT_ERR_QUEUE (SOCK_QUEUE_SHRUNK + 14)
+#endif
+
+#ifndef sock_skb_cb_check_size
+#define sock_skb_cb_check_size(size) \
+ BUILD_BUG_ON((size) > FIELD_SIZEOF(struct sk_buff, cb))
+#endif
+
+#if LINUX_VERSION_IS_LESS(4,2,0)
+#define sk_alloc(net, family, priority, prot, kern) sk_alloc(net, family, priority, prot)
+#endif
+
+#if LINUX_VERSION_IS_LESS(4,5,0)
+#define sk_set_bit LINUX_BACKPORT(sk_set_bit)
+static inline void sk_set_bit(int nr, struct sock *sk)
+{
+ set_bit(nr, &sk->sk_socket->flags);
+}
+#endif /* < 4.5 */
+
+#if LINUX_VERSION_IS_LESS(4,5,0)
+#define sk_clear_bit LINUX_BACKPORT(sk_clear_bit)
+static inline void sk_clear_bit(int nr, struct sock *sk)
+{
+ clear_bit(nr, &sk->sk_socket->flags);
+}
+#endif /* < 4.5 */
+
+#endif /* __BACKPORT_NET_SOCK_H */
diff --git a/backport-include/net/tso.h b/backport-include/net/tso.h
new file mode 100644
index 0000000..3a3f50b
--- /dev/null
+++ b/backport-include/net/tso.h
@@ -0,0 +1,33 @@
+#ifndef BACKPORT_TSO_H
+#define BACKPORT_TSO_H
+
+#include <net/ip.h>
+
+#if LINUX_VERSION_IS_LESS(4,4,0)
+
+#define tso_t LINUX_BACKPORT(tso_t)
+struct tso_t {
+ int next_frag_idx;
+ void *data;
+ size_t size;
+ u16 ip_id;
+ bool ipv6;
+ u32 tcp_seq;
+};
+
+#define tso_count_descs LINUX_BACKPORT(tso_count_descs)
+int tso_count_descs(struct sk_buff *skb);
+
+#define tso_build_hdr LINUX_BACKPORT(tso_build_hdr)
+void tso_build_hdr(struct sk_buff *skb, char *hdr, struct tso_t *tso,
+ int size, bool is_last);
+#define tso_build_data LINUX_BACKPORT(tso_build_data)
+void tso_build_data(struct sk_buff *skb, struct tso_t *tso, int size);
+#define tso_start LINUX_BACKPORT(tso_start)
+void tso_start(struct sk_buff *skb, struct tso_t *tso);
+
+#else
+#include_next <net/tso.h>
+#endif
+
+#endif /* BACKPORT_TSO_H */
diff --git a/backport-include/pcmcia/device_id.h b/backport-include/pcmcia/device_id.h
new file mode 100644
index 0000000..908af50
--- /dev/null
+++ b/backport-include/pcmcia/device_id.h
@@ -0,0 +1,23 @@
+#ifndef __BACKPORT_PCMCIA_DEVICE_ID_H
+#define __BACKPORT_PCMCIA_DEVICE_ID_H
+#include_next <pcmcia/device_id.h>
+
+#ifndef PCMCIA_DEVICE_MANF_CARD_PROD_ID3
+#define PCMCIA_DEVICE_MANF_CARD_PROD_ID3(manf, card, v3, vh3) { \
+ .match_flags = PCMCIA_DEV_ID_MATCH_MANF_ID| \
+ PCMCIA_DEV_ID_MATCH_CARD_ID| \
+ PCMCIA_DEV_ID_MATCH_PROD_ID3, \
+ .manf_id = (manf), \
+ .card_id = (card), \
+ .prod_id = { NULL, NULL, (v3), NULL }, \
+ .prod_id_hash = { 0, 0, (vh3), 0 }, }
+#endif
+
+#ifndef PCMCIA_DEVICE_PROD_ID3
+#define PCMCIA_DEVICE_PROD_ID3(v3, vh3) { \
+ .match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID3, \
+ .prod_id = { NULL, NULL, (v3), NULL }, \
+ .prod_id_hash = { 0, 0, (vh3), 0 }, }
+#endif
+
+#endif /* __BACKPORT_PCMCIA_DEVICE_ID_H */
diff --git a/backport-include/pcmcia/ds.h b/backport-include/pcmcia/ds.h
new file mode 100644
index 0000000..45ab33a
--- /dev/null
+++ b/backport-include/pcmcia/ds.h
@@ -0,0 +1,29 @@
+#ifndef __BACKPORT_PCMCIA_DS_H
+#define __BACKPORT_PCMCIA_DS_H
+#include_next <pcmcia/ds.h>
+
+#ifndef module_pcmcia_driver
+/**
+ * backport of:
+ *
+ * commit 6ed7ffddcf61f668114edb676417e5fb33773b59
+ * Author: H Hartley Sweeten <hsweeten@visionengravers.com>
+ * Date: Wed Mar 6 11:24:44 2013 -0700
+ *
+ * pcmcia/ds.h: introduce helper for pcmcia_driver module boilerplate
+ */
+
+/**
+ * module_pcmcia_driver() - Helper macro for registering a pcmcia driver
+ * @__pcmcia_driver: pcmcia_driver struct
+ *
+ * Helper macro for pcmcia drivers which do not do anything special in module
+ * init/exit. This eliminates a lot of boilerplate. Each module may only use
+ * this macro once, and calling it replaces module_init() and module_exit().
+ */
+#define module_pcmcia_driver(__pcmcia_driver) \
+ module_driver(__pcmcia_driver, pcmcia_register_driver, \
+ pcmcia_unregister_driver)
+#endif
+
+#endif /* __BACKPORT_PCMCIA_DS_H */
diff --git a/backport-include/sound/core.h b/backport-include/sound/core.h
new file mode 100644
index 0000000..79f8b31
--- /dev/null
+++ b/backport-include/sound/core.h
@@ -0,0 +1,20 @@
+#ifndef _BACKPORT_SOUND_CORE_H
+#define _BACKPORT_SOUND_CORE_H
+#include_next <sound/core.h>
+
+#if LINUX_VERSION_IS_LESS(3,15,0)
+#define snd_card_new LINUX_BACKPORT(snd_card_new)
+static inline
+int snd_card_new(struct device *parent, int idx, const char *xid,
+ struct module *module, int extra_size,
+ struct snd_card **card_ret)
+{
+ int ret;
+
+ ret = snd_card_create(idx, xid, module, extra_size, card_ret);
+ snd_card_set_dev(*card_ret, parent);
+ return ret;
+}
+#endif
+
+#endif /* _BACKPORT_SOUND_CORE_H */
diff --git a/backport-include/sound/pcm.h b/backport-include/sound/pcm.h
new file mode 100644
index 0000000..d5794ef
--- /dev/null
+++ b/backport-include/sound/pcm.h
@@ -0,0 +1,29 @@
+#ifndef __BACKPORT_SOUND_PCM_H
+#define __BACKPORT_SOUND_PCM_H
+#include_next <sound/pcm.h>
+#include <linux/version.h>
+
+#if LINUX_VERSION_IS_LESS(3,19,0)
+/**
+ * snd_pcm_stop_xrun - stop the running streams as XRUN
+ * @substream: the PCM substream instance
+ *
+ * This stops the given running substream (and all linked substreams) as XRUN.
+ * Unlike snd_pcm_stop(), this function takes the substream lock by itself.
+ *
+ * Return: Zero if successful, or a negative error code.
+ */
+static inline int snd_pcm_stop_xrun(struct snd_pcm_substream *substream)
+{
+ unsigned long flags;
+ int ret = 0;
+
+ snd_pcm_stream_lock_irqsave(substream, flags);
+ if (snd_pcm_running(substream))
+ ret = snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
+ snd_pcm_stream_unlock_irqrestore(substream, flags);
+ return ret;
+}
+#endif /* LINUX_VERSION_IS_LESS(3,19,0) */
+
+#endif /* __BACKPORT_SOUND_PCM_H */
diff --git a/backport-include/trace/ftrace.h b/backport-include/trace/ftrace.h
new file mode 100644
index 0000000..2daedd5
--- /dev/null
+++ b/backport-include/trace/ftrace.h
@@ -0,0 +1,13 @@
+#undef __print_array
+#define __print_array(array, count, el_size) \
+ ({ \
+ BUILD_BUG_ON(el_size != 1 && el_size != 2 && \
+ el_size != 4 && el_size != 8); \
+ ftrace_print_array_seq(p, array, count, el_size); \
+ })
+
+#undef __get_dynamic_array_len
+#define __get_dynamic_array_len(field) \
+ ((__entry->__data_loc_##field >> 16) & 0xffff)
+
+#include_next <trace/ftrace.h>
diff --git a/backport-include/uapi/linux/genetlink.h b/backport-include/uapi/linux/genetlink.h
new file mode 100644
index 0000000..620626a
--- /dev/null
+++ b/backport-include/uapi/linux/genetlink.h
@@ -0,0 +1,14 @@
+#ifndef __BACKPORT_UAPI_LINUX_GENETLINK_H
+#define __BACKPORT_UAPI_LINUX_GENETLINK_H
+#include <linux/version.h>
+#if LINUX_VERSION_IS_GEQ(3,7,0)
+#include_next <uapi/linux/genetlink.h>
+#else
+#include_next <linux/genetlink.h>
+#endif
+
+#ifndef GENL_UNS_ADMIN_PERM
+#define GENL_UNS_ADMIN_PERM GENL_ADMIN_PERM
+#endif
+
+#endif /* __BACKPORT_UAPI_LINUX_GENETLINK_H */
diff --git a/backport-include/uapi/linux/sockios.h b/backport-include/uapi/linux/sockios.h
new file mode 100644
index 0000000..12a7dd6
--- /dev/null
+++ b/backport-include/uapi/linux/sockios.h
@@ -0,0 +1,31 @@
+#ifndef __BACKPORT_LINUX_SOCKIOS_H
+#define __BACKPORT_LINUX_SOCKIOS_H
+#include_next <linux/sockios.h>
+#include <linux/version.h>
+
+/*
+ * Kernel backports UAPI note:
+ *
+ * We carry UAPI headers for backports to enable compilation
+ * of kernel / driver code to compile without any changes. If
+ * it so happens that a feature is backported it can be added
+ * here but notice that if full subsystems are backported you
+ * should just include the respective full header onto the
+ * copy-list file so that its copied intact. This strategy
+ * is used to either backport a specific feature or to just
+ * avoid having to do ifdef changes to compile.
+ *
+ * Userspace is not expected to copy over backports headers
+ * to compile userspace programs, userspace programs can
+ * and should consider carrying over a respective copy-list
+ * of the latest UAPI kernel headers they need in their
+ * upstream sources, the kernel the user uses, whether with
+ * backports or not should be able to return -EOPNOTSUPP if
+ * the feature is not available and let it through if its
+ * supported and meats the expected form.
+ */
+
+#if LINUX_VERSION_IS_LESS(3,14,0)
+#define SIOCGHWTSTAMP 0x89b1 /* get config */
+#endif /* LINUX_VERSION_IS_LESS(3,14,0) */
+#endif /* __BACKPORT_LINUX_SOCKIOS_H */
diff --git a/compat/Kconfig b/compat/Kconfig
new file mode 100644
index 0000000..c9ef42e
--- /dev/null
+++ b/compat/Kconfig
@@ -0,0 +1,175 @@
+#
+# backport Kconfig
+#
+# Some options are user-selectable ("BPAUTO_USERSEL_*")
+#
+# Most options, however, follow a few different schemes:
+#
+# A) An option that is selected by drivers ("select FOO") will be
+# changed to "select BPAUTO_FOO" (if the option BPAUTO_FOO
+# exists). The option BPAUTO_FOO then controls setting of the
+# BPAUTO_BUILD_FOO option, which is a module, like this:
+#
+# config BPAUTO_BUILD_FOO
+# tristate
+# # or bool
+#
+# # not possible on kernel < X.Y, build will fail if any
+# # drivers are allowed to build on kernels < X.Y
+# depends on KERNEL_X_Y
+#
+# # don't build the backport code if FOO is in the kernel
+# # already, but only if the kernel version is also >= X.Z;
+# # this is an example of backporting where the version of
+# # the FOO subsystem that we need is only available from
+# # kernel version X.Z
+# depends on !FOO || KERNEL_X_Z
+#
+# # build if driver needs it (it selects BPAUTO_FOO)
+# default m if BPAUTO_FOO
+#
+# # or for build-testing (BPAUTO_USERSEL_BUILD_ALL is enabled)
+# default m if BPAUTO_USERSEL_BUILD_ALL
+#
+# config BPAUTO_FOO
+# bool
+#
+# This only works as-is if the kernel code is usable on any version,
+# otherwise the "&& !FOO" part needs to be different.
+#
+#
+# B) An option for code always present on some kernels (e.g. KFIFO).
+# This simply depends on/sets the default based on the version:
+#
+# config BPAUTO_BUILD_KFIFO
+# def_bool y
+# depends on KERNEL_2_6_36
+#
+#
+# C) similarly, a kconfig symbol for an option, e.g.
+# BPAUTO_OPTION_SOME_FIX (no examples provided) check git log
+#
+#
+# Variations are obviously possible.
+#
+
+config BP_MODULES
+ option modules
+ bool
+ default MODULES
+
+ help
+ This symbol is necessary for the newer kconf tool, it looks
+ for the "option modules" to control the 'm' state.
+
+config BPAUTO_BUILD_CORDIC
+ tristate
+ depends on m
+ depends on !CORDIC
+ depends on KERNEL_3_1
+ default m if BPAUTO_CORDIC
+ default m if BPAUTO_USERSEL_BUILD_ALL
+ #module-name cordic
+ #c-file lib/cordic.c
+
+config BPAUTO_CORDIC
+ bool
+
+config BPAUTO_MII
+ bool
+
+config BPAUTO_BUILD_LEDS
+ bool
+ depends on !NEW_LEDS || LEDS_CLASS=n || !LEDS_TRIGGERS
+ default y if BPAUTO_NEW_LEDS
+ default y if BPAUTO_LEDS_CLASS
+ default y if BPAUTO_LEDS_TRIGGERS
+
+config BPAUTO_NEW_LEDS
+ bool
+
+config BPAUTO_LEDS_CLASS
+ bool
+
+config BPAUTO_LEDS_TRIGGERS
+ bool
+
+config BPAUTO_USERSEL_BUILD_ALL
+ bool "Build all compat code"
+ help
+ This option selects all the compat code options
+ that would otherwise only be selected by drivers.
+
+ It's only really useful for compat testing, so
+ you probably shouldn't enable it.
+
+config BPAUTO_CRYPTO_CCM
+ depends on CRYPTO_AEAD
+ depends on CRYPTO_CTR
+ bool
+
+config BPAUTO_BUILD_CRYPTO_CCM
+ depends on n
+ bool
+ default n if CRYPTO_CCM
+ default y if BPAUTO_CRYPTO_CCM
+ #c-file crypto/ccm.c
+
+config BPAUTO_CRYPTO_SKCIPHER
+ tristate
+ depends on m
+ depends on KERNEL_4_3
+ default y if BACKPORTED_MAC802154
+ default y if BACKPORTED_LIB80211_CRYPT_WEP
+ default y if BACKPORTED_LIB80211_CRYPT_TKIP
+ default y if BACKPORTED_BT
+ #c-file crypto/skcipher.c
+ #module-name skcipher
+
+config BPAUTO_WANT_DEV_COREDUMP
+ bool
+
+config BPAUTO_BUILD_WANT_DEV_COREDUMP
+ bool
+ default n if DEV_COREDUMP
+ default n if DISABLE_DEV_COREDUMP
+ default y if BPAUTO_WANT_DEV_COREDUMP
+ #h-file linux/devcoredump.h
+ #c-file drivers/base/devcoredump.c
+
+config BPAUTO_RHASHTABLE
+ bool
+ # current API of rhashtable was introduced in version 4.9
+ # (the one including rhltable)
+ depends on KERNEL_4_9
+ # not very nice - but better than always having it
+ default y if BACKPORTED_MAC80211
+ #h-file linux/rhashtable.h
+ #c-file lib/rhashtable.c
+
+config BPAUTO_BUILD_HDMI
+ depends on n
+ bool
+ # the hdmi driver got some new apis like hdmi_infoframe_unpack() in
+ # kernel 4.0 which are used by some drivers
+ depends on KERNEL_4_0
+ #h-file linux/hdmi.h
+ #c-file drivers/video/hdmi.c
+
+config BPAUTO_HDMI
+ bool
+ select BPAUTO_BUILD_HDMI if KERNEL_4_0
+ # these drivers are using the new features of the hdmi driver.
+ default y if BACKPORTED_VIDEO_ADV7511
+ default y if BACKPORTED_VIDEO_ADV7604
+ default y if BACKPORTED_VIDEO_ADV7842
+
+config BPAUTO_FRAME_VECTOR
+ bool
+
+config BPAUTO_BUILD_FRAME_VECTOR
+ depends on n
+ bool
+ default n if FRAME_VECTOR
+ default y if BPAUTO_FRAME_VECTOR
+ #c-file mm/frame_vector.c
diff --git a/compat/Makefile b/compat/Makefile
new file mode 100644
index 0000000..39e22fc
--- /dev/null
+++ b/compat/Makefile
@@ -0,0 +1,46 @@
+ccflags-y += -I$(src)
+ifeq ($(CONFIG_BACKPORT_INTEGRATE),)
+obj-m += compat.o
+else
+obj-y += compat.o
+endif
+compat-y += main.o
+
+# Kernel backport compatibility code
+compat-$(CPTCFG_KERNEL_3_0) += compat-3.0.o
+compat-$(CPTCFG_KERNEL_3_1) += compat-3.1.o
+compat-$(CPTCFG_KERNEL_3_2) += backport-3.2.o
+compat-$(CPTCFG_KERNEL_3_3) += compat-3.3.o
+compat-$(CPTCFG_KERNEL_3_4) += compat-3.4.o
+compat-$(CPTCFG_KERNEL_3_5) += compat-3.5.o user_namespace.o
+compat-$(CPTCFG_KERNEL_3_6) += compat-3.6.o
+compat-$(CPTCFG_KERNEL_3_7) += compat-3.7.o
+compat-$(CPTCFG_KERNEL_3_8) += compat-3.8.o
+compat-$(CPTCFG_KERNEL_3_9) += compat-3.9.o
+compat-$(CPTCFG_KERNEL_3_10) += backport-3.10.o
+compat-$(CPTCFG_KERNEL_3_11) += backport-3.11.o
+compat-$(CPTCFG_KERNEL_3_12) += backport-3.12.o
+compat-$(CPTCFG_KERNEL_3_13) += backport-3.13.o
+compat-$(CPTCFG_KERNEL_3_14) += backport-3.14.o
+compat-$(CPTCFG_KERNEL_3_15) += backport-3.15.o
+compat-$(CPTCFG_KERNEL_3_17) += backport-3.17.o
+compat-$(CPTCFG_KERNEL_3_18) += backport-3.18.o
+compat-$(CPTCFG_KERNEL_3_19) += backport-3.19.o
+compat-$(CPTCFG_KERNEL_4_0) += backport-4.0.o
+compat-$(CPTCFG_KERNEL_4_1) += backport-4.1.o
+compat-$(CPTCFG_KERNEL_4_2) += backport-4.2.o
+compat-$(CPTCFG_KERNEL_4_3) += backport-4.3.o
+compat-$(CPTCFG_KERNEL_4_4) += backport-4.4.o
+compat-$(CPTCFG_KERNEL_4_5) += backport-4.5.o
+compat-$(CPTCFG_KERNEL_4_6) += backport-4.6.o
+compat-$(CPTCFG_KERNEL_4_7) += backport-4.7.o
+compat-$(CPTCFG_KERNEL_4_8) += backport-4.8.o
+
+compat-$(CPTCFG_BPAUTO_BUILD_CRYPTO_CCM) += crypto-ccm.o
+compat-$(CPTCFG_BPAUTO_CRYPTO_SKCIPHER) += crypto-skcipher.o
+skcipher-objs += crypto-skcipher.o
+obj-$(CPTCFG_BPAUTO_CRYPTO_SKCIPHER) += skcipher.o
+compat-$(CPTCFG_BPAUTO_BUILD_WANT_DEV_COREDUMP) += drivers-base-devcoredump.o
+compat-$(CPTCFG_BPAUTO_RHASHTABLE) += lib-rhashtable.o
+cordic-objs += lib-cordic.o
+obj-$(CPTCFG_BPAUTO_BUILD_CORDIC) += cordic.o
diff --git a/compat/backport-3.10.c b/compat/backport-3.10.c
new file mode 100644
index 0000000..5cab709
--- /dev/null
+++ b/compat/backport-3.10.c
@@ -0,0 +1,199 @@
+/*
+ * Copyright (c) 2013 Luis R. Rodriguez <mcgrof@do-not-panic.com>
+ *
+ * Linux backport symbols for kernels 3.10.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/proc_fs.h>
+#include <linux/random.h>
+#include <linux/tty.h>
+#include <linux/pci.h>
+#include <linux/pci_regs.h>
+#include <linux/of.h>
+#include <linux/mm.h>
+
+void proc_set_size(struct proc_dir_entry *de, loff_t size)
+{
+ de->size = size;
+}
+EXPORT_SYMBOL_GPL(proc_set_size);
+
+void proc_set_user(struct proc_dir_entry *de, kuid_t uid, kgid_t gid)
+{
+ de->uid = uid;
+ de->gid = gid;
+}
+EXPORT_SYMBOL_GPL(proc_set_user);
+
+/* get_random_int() was not exported for module use until 3.10-rc.
+ Implement it here in terms of the more expensive get_random_bytes()
+ */
+unsigned int get_random_int(void)
+{
+ unsigned int r;
+ get_random_bytes(&r, sizeof(r));
+
+ return r;
+}
+EXPORT_SYMBOL_GPL(get_random_int);
+
+#ifdef CONFIG_TTY
+/**
+ * tty_port_tty_wakeup - helper to wake up a tty
+ *
+ * @port: tty port
+ */
+void tty_port_tty_wakeup(struct tty_port *port)
+{
+ struct tty_struct *tty = tty_port_tty_get(port);
+
+ if (tty) {
+ tty_wakeup(tty);
+ tty_kref_put(tty);
+ }
+}
+EXPORT_SYMBOL_GPL(tty_port_tty_wakeup);
+
+/**
+ * tty_port_tty_hangup - helper to hang up a tty
+ *
+ * @port: tty port
+ * @check_clocal: hang only ttys with CLOCAL unset?
+ */
+void tty_port_tty_hangup(struct tty_port *port, bool check_clocal)
+{
+ struct tty_struct *tty = tty_port_tty_get(port);
+
+ if (tty && (!check_clocal || !C_CLOCAL(tty)))
+ tty_hangup(tty);
+ tty_kref_put(tty);
+}
+EXPORT_SYMBOL_GPL(tty_port_tty_hangup);
+#endif /* CONFIG_TTY */
+
+#ifdef CONFIG_PCI_IOV
+/*
+ * pci_vfs_assigned - returns number of VFs are assigned to a guest
+ * @dev: the PCI device
+ *
+ * Returns number of VFs belonging to this device that are assigned to a guest.
+ * If device is not a physical function returns -ENODEV.
+ */
+int pci_vfs_assigned(struct pci_dev *dev)
+{
+ struct pci_dev *vfdev;
+ unsigned int vfs_assigned = 0;
+ unsigned short dev_id;
+
+ /* only search if we are a PF */
+ if (!dev->is_physfn)
+ return 0;
+
+ /*
+ * determine the device ID for the VFs, the vendor ID will be the
+ * same as the PF so there is no need to check for that one
+ */
+ pci_read_config_word(dev, dev->sriov->pos + PCI_SRIOV_VF_DID, &dev_id);
+
+ /* loop through all the VFs to see if we own any that are assigned */
+ vfdev = pci_get_device(dev->vendor, dev_id, NULL);
+ while (vfdev) {
+ /*
+ * It is considered assigned if it is a virtual function with
+ * our dev as the physical function and the assigned bit is set
+ */
+ if (vfdev->is_virtfn && (vfdev->physfn == dev) &&
+ (vfdev->dev_flags & PCI_DEV_FLAGS_ASSIGNED))
+ vfs_assigned++;
+
+ vfdev = pci_get_device(dev->vendor, dev_id, vfdev);
+ }
+
+ return vfs_assigned;
+}
+EXPORT_SYMBOL_GPL(pci_vfs_assigned);
+#endif /* CONFIG_PCI_IOV */
+
+#ifdef CONFIG_OF
+/**
+ * of_property_read_u32_index - Find and read a u32 from a multi-value property.
+ *
+ * @np: device node from which the property value is to be read.
+ * @propname: name of the property to be searched.
+ * @index: index of the u32 in the list of values
+ * @out_value: pointer to return value, modified only if no error.
+ *
+ * Search for a property in a device node and read nth 32-bit value from
+ * it. Returns 0 on success, -EINVAL if the property does not exist,
+ * -ENODATA if property does not have a value, and -EOVERFLOW if the
+ * property data isn't large enough.
+ *
+ * The out_value is modified only if a valid u32 value can be decoded.
+ */
+int of_property_read_u32_index(const struct device_node *np,
+ const char *propname,
+ u32 index, u32 *out_value)
+{
+ const u32 *val = of_find_property_value_of_size(np, propname,
+ ((index + 1) * sizeof(*out_value)));
+
+ if (IS_ERR(val))
+ return PTR_ERR(val);
+
+ *out_value = be32_to_cpup(((__be32 *)val) + index);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(of_property_read_u32_index);
+#endif /* CONFIG_OF */
+
+static inline void set_page_count(struct page *page, int v)
+{
+ atomic_set(&page->_count, v);
+}
+
+/*
+ * Turn a non-refcounted page (->_count == 0) into refcounted with
+ * a count of one.
+ */
+static inline void set_page_refcounted(struct page *page)
+{
+ VM_BUG_ON(PageTail(page));
+ VM_BUG_ON(atomic_read(&page->_count));
+ set_page_count(page, 1);
+}
+
+/*
+ * split_page takes a non-compound higher-order page, and splits it into
+ * n (1<<order) sub-pages: page[0..n]
+ * Each sub-page must be freed individually.
+ *
+ * Note: this is probably too low level an operation for use in drivers.
+ * Please consult with lkml before using this in your driver.
+ */
+void split_page(struct page *page, unsigned int order)
+{
+ int i;
+
+ VM_BUG_ON(PageCompound(page));
+ VM_BUG_ON(!page_count(page));
+
+#ifdef CONFIG_KMEMCHECK
+ /*
+ * Split shadow pages too, because free(page[0]) would
+ * otherwise free the whole shadow.
+ */
+ if (kmemcheck_page_is_tracked(page))
+ split_page(virt_to_page(page[0].shadow), order);
+#endif
+
+ for (i = 1; i < (1 << order); i++)
+ set_page_refcounted(page + i);
+}
+EXPORT_SYMBOL_GPL(split_page);
diff --git a/compat/backport-3.11.c b/compat/backport-3.11.c
new file mode 100644
index 0000000..7f9ff34
--- /dev/null
+++ b/compat/backport-3.11.c
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2016 Intel Deutschland GmbH
+ *
+ * Backport functionality introduced in Linux 3.11.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/scatterlist.h>
+
+static bool sg_miter_get_next_page(struct sg_mapping_iter *miter)
+{
+ if (!miter->__remaining) {
+ struct scatterlist *sg;
+ unsigned long pgoffset;
+
+ if (!__sg_page_iter_next(&miter->piter))
+ return false;
+
+ sg = miter->piter.sg;
+ pgoffset = miter->piter.sg_pgoffset;
+
+ miter->__offset = pgoffset ? 0 : sg->offset;
+ miter->__remaining = sg->offset + sg->length -
+ (pgoffset << PAGE_SHIFT) - miter->__offset;
+ miter->__remaining = min_t(unsigned long, miter->__remaining,
+ PAGE_SIZE - miter->__offset);
+ }
+
+ return true;
+}
+
+/**
+ * sg_miter_skip - reposition mapping iterator
+ * @miter: sg mapping iter to be skipped
+ * @offset: number of bytes to plus the current location
+ *
+ * Description:
+ * Sets the offset of @miter to its current location plus @offset bytes.
+ * If mapping iterator @miter has been proceeded by sg_miter_next(), this
+ * stops @miter.
+ *
+ * Context:
+ * Don't care if @miter is stopped, or not proceeded yet.
+ * Otherwise, preemption disabled if the SG_MITER_ATOMIC is set.
+ *
+ * Returns:
+ * true if @miter contains the valid mapping. false if end of sg
+ * list is reached.
+ */
+static bool sg_miter_skip(struct sg_mapping_iter *miter, off_t offset)
+{
+ sg_miter_stop(miter);
+
+ while (offset) {
+ off_t consumed;
+
+ if (!sg_miter_get_next_page(miter))
+ return false;
+
+ consumed = min_t(off_t, offset, miter->__remaining);
+ miter->__offset += consumed;
+ miter->__remaining -= consumed;
+ offset -= consumed;
+ }
+
+ return true;
+}
+
+/**
+ * sg_copy_buffer - Copy data between a linear buffer and an SG list
+ * @sgl: The SG list
+ * @nents: Number of SG entries
+ * @buf: Where to copy from
+ * @buflen: The number of bytes to copy
+ * @skip: Number of bytes to skip before copying
+ * @to_buffer: transfer direction (true == from an sg list to a
+ * buffer, false == from a buffer to an sg list
+ *
+ * Returns the number of copied bytes.
+ *
+ **/
+size_t sg_copy_buffer(struct scatterlist *sgl, unsigned int nents, void *buf,
+ size_t buflen, off_t skip, bool to_buffer)
+{
+ unsigned int offset = 0;
+ struct sg_mapping_iter miter;
+ unsigned long flags;
+ unsigned int sg_flags = SG_MITER_ATOMIC;
+
+ if (to_buffer)
+ sg_flags |= SG_MITER_FROM_SG;
+ else
+ sg_flags |= SG_MITER_TO_SG;
+
+ sg_miter_start(&miter, sgl, nents, sg_flags);
+
+ if (!sg_miter_skip(&miter, skip))
+ return false;
+
+ local_irq_save(flags);
+
+ while (sg_miter_next(&miter) && offset < buflen) {
+ unsigned int len;
+
+ len = min(miter.length, buflen - offset);
+
+ if (to_buffer)
+ memcpy(buf + offset, miter.addr, len);
+ else
+ memcpy(miter.addr, buf + offset, len);
+
+ offset += len;
+ }
+
+ sg_miter_stop(&miter);
+
+ local_irq_restore(flags);
+ return offset;
+}
+EXPORT_SYMBOL_GPL(sg_copy_buffer);
diff --git a/compat/backport-3.12.c b/compat/backport-3.12.c
new file mode 100644
index 0000000..c9b21e8
--- /dev/null
+++ b/compat/backport-3.12.c
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2013 Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * Backport functionality introduced in Linux 3.12.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/export.h>
+#include <linux/hid.h>
+#include <linux/bug.h>
+#include <linux/math64.h>
+
+/*
+ * Allocator for buffer that is going to be passed to hid_output_report()
+ */
+u8 *hid_alloc_report_buf(struct hid_report *report, gfp_t flags)
+{
+ /*
+ * 7 extra bytes are necessary to achieve proper functionality
+ * of implement() working on 8 byte chunks
+ */
+
+ int len = ((report->size - 1) >> 3) + 1 + (report->id > 0) + 7;
+
+ return kmalloc(len, flags);
+}
+EXPORT_SYMBOL_GPL(hid_alloc_report_buf);
+
+#if BITS_PER_LONG == 32
+/**
+ * div64_u64_rem - unsigned 64bit divide with 64bit divisor and remainder
+ * @dividend: 64bit dividend
+ * @divisor: 64bit divisor
+ * @remainder: 64bit remainder
+ *
+ * This implementation is a comparable to algorithm used by div64_u64.
+ * But this operation, which includes math for calculating the remainder,
+ * is kept distinct to avoid slowing down the div64_u64 operation on 32bit
+ * systems.
+ */
+#ifndef backports_div64_u64_rem_add
+u64 div64_u64_rem(u64 dividend, u64 divisor, u64 *remainder)
+{
+ u32 high = divisor >> 32;
+ u64 quot;
+
+ if (high == 0) {
+ u32 rem32;
+ quot = div_u64_rem(dividend, divisor, &rem32);
+ *remainder = rem32;
+ } else {
+ int n = 1 + fls(high);
+ quot = div_u64(dividend >> n, divisor >> n);
+
+ if (quot != 0)
+ quot--;
+
+ *remainder = dividend - quot * divisor;
+ if (*remainder >= divisor) {
+ quot++;
+ *remainder -= divisor;
+ }
+ }
+
+ return quot;
+}
+EXPORT_SYMBOL_GPL(div64_u64_rem);
+#endif /* backports_div64_u64_rem_add */
+#endif /* BITS_PER_LONG */
diff --git a/compat/backport-3.13.c b/compat/backport-3.13.c
new file mode 100644
index 0000000..496bee6
--- /dev/null
+++ b/compat/backport-3.13.c
@@ -0,0 +1,309 @@
+/*
+ * Copyright (c) 2013 Hauke Mehrtens <hauke@hauke-m.de>
+ * Copyright (c) 2013 Hannes Frederic Sowa <hannes@stressinduktion.org>
+ * Copyright (c) 2014 Luis R. Rodriguez <mcgrof@do-not-panic.com>
+ *
+ * Backport functionality introduced in Linux 3.13.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <net/genetlink.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/device.h>
+#include <linux/hwmon.h>
+
+#if LINUX_VERSION_IS_GEQ(3,5,0)
+#ifdef CONFIG_REGULATOR
+#include <linux/module.h>
+#include <linux/regulator/driver.h>
+#include <linux/device.h>
+#include <linux/static_key.h>
+
+static void devm_rdev_release(struct device *dev, void *res)
+{
+ regulator_unregister(*(struct regulator_dev **)res);
+}
+
+/**
+ * devm_regulator_register - Resource managed regulator_register()
+ * @regulator_desc: regulator to register
+ * @config: runtime configuration for regulator
+ *
+ * Called by regulator drivers to register a regulator. Returns a
+ * valid pointer to struct regulator_dev on success or an ERR_PTR() on
+ * error. The regulator will automatically be released when the device
+ * is unbound.
+ */
+struct regulator_dev *devm_regulator_register(struct device *dev,
+ const struct regulator_desc *regulator_desc,
+ const struct regulator_config *config)
+{
+ struct regulator_dev **ptr, *rdev;
+
+ ptr = devres_alloc(devm_rdev_release, sizeof(*ptr),
+ GFP_KERNEL);
+ if (!ptr)
+ return ERR_PTR(-ENOMEM);
+
+ rdev = regulator_register(regulator_desc, config);
+ if (!IS_ERR(rdev)) {
+ *ptr = rdev;
+ devres_add(dev, ptr);
+ } else {
+ devres_free(ptr);
+ }
+
+ return rdev;
+}
+EXPORT_SYMBOL_GPL(devm_regulator_register);
+
+static int devm_rdev_match(struct device *dev, void *res, void *data)
+{
+ struct regulator_dev **r = res;
+ if (!r || !*r) {
+ WARN_ON(!r || !*r);
+ return 0;
+ }
+ return *r == data;
+}
+
+/**
+ * devm_regulator_unregister - Resource managed regulator_unregister()
+ * @regulator: regulator to free
+ *
+ * Unregister a regulator registered with devm_regulator_register().
+ * Normally this function will not need to be called and the resource
+ * management code will ensure that the resource is freed.
+ */
+void devm_regulator_unregister(struct device *dev, struct regulator_dev *rdev)
+{
+ int rc;
+
+ rc = devres_release(dev, devm_rdev_release, devm_rdev_match, rdev);
+ if (rc != 0)
+ WARN_ON(rc);
+}
+EXPORT_SYMBOL_GPL(devm_regulator_unregister);
+#endif /* CONFIG_REGULATOR */
+#endif /* LINUX_VERSION_IS_GEQ(3,5,0) */
+
+/************* generic netlink backport *****************/
+#if RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(7,0)
+
+#undef genl_register_family
+#undef genl_unregister_family
+
+int __backport_genl_register_family(struct genl_family *family)
+{
+ int i, ret;
+
+#define __copy(_field) family->family._field = family->_field
+ __copy(id);
+ __copy(hdrsize);
+ __copy(version);
+ __copy(maxattr);
+ strncpy(family->family.name, family->name, sizeof(family->family.name));
+ __copy(netnsok);
+ __copy(pre_doit);
+ __copy(post_doit);
+#if LINUX_VERSION_IS_GEQ(3,10,0)
+ __copy(parallel_ops);
+#endif
+#if LINUX_VERSION_IS_GEQ(3,11,0)
+ __copy(module);
+#endif
+#undef __copy
+
+ ret = genl_register_family(&family->family);
+ if (ret < 0)
+ return ret;
+
+ family->attrbuf = family->family.attrbuf;
+ family->id = family->family.id;
+
+ for (i = 0; i < family->n_ops; i++) {
+ ret = genl_register_ops(&family->family, &family->ops[i]);
+ if (ret < 0)
+ goto error;
+ }
+
+ for (i = 0; i < family->n_mcgrps; i++) {
+ ret = genl_register_mc_group(&family->family,
+ &family->mcgrps[i]);
+ if (ret)
+ goto error;
+ }
+
+ return 0;
+
+ error:
+ backport_genl_unregister_family(family);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(__backport_genl_register_family);
+
+int backport_genl_unregister_family(struct genl_family *family)
+{
+ int err;
+ err = genl_unregister_family(&family->family);
+ return err;
+}
+EXPORT_SYMBOL_GPL(backport_genl_unregister_family);
+
+#endif /* RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(7,0) */
+
+#ifdef __BACKPORT_NET_GET_RANDOM_ONCE
+struct __net_random_once_work {
+ struct work_struct work;
+ struct static_key *key;
+};
+
+static void __net_random_once_deferred(struct work_struct *w)
+{
+ struct __net_random_once_work *work =
+ container_of(w, struct __net_random_once_work, work);
+ if (!static_key_enabled(work->key))
+ static_key_slow_inc(work->key);
+ kfree(work);
+}
+
+static void __net_random_once_disable_jump(struct static_key *key)
+{
+ struct __net_random_once_work *w;
+
+ w = kmalloc(sizeof(*w), GFP_ATOMIC);
+ if (!w)
+ return;
+
+ INIT_WORK(&w->work, __net_random_once_deferred);
+ w->key = key;
+ schedule_work(&w->work);
+}
+
+bool __net_get_random_once(void *buf, int nbytes, bool *done,
+ struct static_key *done_key)
+{
+ static DEFINE_SPINLOCK(lock);
+ unsigned long flags;
+
+ spin_lock_irqsave(&lock, flags);
+ if (*done) {
+ spin_unlock_irqrestore(&lock, flags);
+ return false;
+ }
+
+ get_random_bytes(buf, nbytes);
+ *done = true;
+ spin_unlock_irqrestore(&lock, flags);
+
+ __net_random_once_disable_jump(done_key);
+
+ return true;
+}
+EXPORT_SYMBOL_GPL(__net_get_random_once);
+#endif /* __BACKPORT_NET_GET_RANDOM_ONCE */
+
+#ifdef CONFIG_PCI
+#define pci_bus_read_dev_vendor_id LINUX_BACKPORT(pci_bus_read_dev_vendor_id)
+static bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *l,
+ int crs_timeout)
+{
+ int delay = 1;
+
+ if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, l))
+ return false;
+
+ /* some broken boards return 0 or ~0 if a slot is empty: */
+ if (*l == 0xffffffff || *l == 0x00000000 ||
+ *l == 0x0000ffff || *l == 0xffff0000)
+ return false;
+
+ /*
+ * Configuration Request Retry Status. Some root ports return the
+ * actual device ID instead of the synthetic ID (0xFFFF) required
+ * by the PCIe spec. Ignore the device ID and only check for
+ * (vendor id == 1).
+ */
+ while ((*l & 0xffff) == 0x0001) {
+ if (!crs_timeout)
+ return false;
+
+ msleep(delay);
+ delay *= 2;
+ if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, l))
+ return false;
+ /* Card hasn't responded in 60 seconds? Must be stuck. */
+ if (delay > crs_timeout) {
+ printk(KERN_WARNING "pci %04x:%02x:%02x.%d: not responding\n",
+ pci_domain_nr(bus), bus->number, PCI_SLOT(devfn),
+ PCI_FUNC(devfn));
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool pci_device_is_present(struct pci_dev *pdev)
+{
+ u32 v;
+
+ return pci_bus_read_dev_vendor_id(pdev->bus, pdev->devfn, &v, 0);
+}
+EXPORT_SYMBOL_GPL(pci_device_is_present);
+#endif /* CONFIG_PCI */
+
+#ifdef CONFIG_HWMON
+struct device*
+hwmon_device_register_with_groups(struct device *dev, const char *name,
+ void *drvdata,
+ const struct attribute_group **groups)
+{
+ struct device *hwdev;
+
+ hwdev = hwmon_device_register(dev);
+ hwdev->groups = groups;
+ dev_set_drvdata(hwdev, drvdata);
+ return hwdev;
+}
+
+static void devm_hwmon_release(struct device *dev, void *res)
+{
+ struct device *hwdev = *(struct device **)res;
+
+ hwmon_device_unregister(hwdev);
+}
+
+struct device *
+devm_hwmon_device_register_with_groups(struct device *dev, const char *name,
+ void *drvdata,
+ const struct attribute_group **groups)
+{
+ struct device **ptr, *hwdev;
+
+ if (!dev)
+ return ERR_PTR(-EINVAL);
+
+ ptr = devres_alloc(devm_hwmon_release, sizeof(*ptr), GFP_KERNEL);
+ if (!ptr)
+ return ERR_PTR(-ENOMEM);
+
+ hwdev = hwmon_device_register_with_groups(dev, name, drvdata, groups);
+ if (IS_ERR(hwdev))
+ goto error;
+
+ *ptr = hwdev;
+ devres_add(dev, ptr);
+ return hwdev;
+
+error:
+ devres_free(ptr);
+ return hwdev;
+}
+EXPORT_SYMBOL_GPL(devm_hwmon_device_register_with_groups);
+#endif
diff --git a/compat/backport-3.14.c b/compat/backport-3.14.c
new file mode 100644
index 0000000..aeb3004
--- /dev/null
+++ b/compat/backport-3.14.c
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2014 Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * Backport functionality introduced in Linux 3.14.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+
+#ifdef CONFIG_PCI_MSI
+/**
+ * pci_enable_msi_range - configure device's MSI capability structure
+ * @dev: device to configure
+ * @minvec: minimal number of interrupts to configure
+ * @maxvec: maximum number of interrupts to configure
+ *
+ * This function tries to allocate a maximum possible number of interrupts in a
+ * range between @minvec and @maxvec. It returns a negative errno if an error
+ * occurs. If it succeeds, it returns the actual number of interrupts allocated
+ * and updates the @dev's irq member to the lowest new interrupt number;
+ * the other interrupt numbers allocated to this device are consecutive.
+ **/
+int pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec)
+{
+ int nvec = maxvec;
+ int rc;
+
+ if (maxvec < minvec)
+ return -ERANGE;
+
+ do {
+ rc = pci_enable_msi_block(dev, nvec);
+ if (rc < 0) {
+ return rc;
+ } else if (rc > 0) {
+ if (rc < minvec)
+ return -ENOSPC;
+ nvec = rc;
+ }
+ } while (rc);
+
+ return nvec;
+}
+EXPORT_SYMBOL(pci_enable_msi_range);
+#endif
+
+#ifdef CONFIG_PCI_MSI
+/**
+ * pci_enable_msix_range - configure device's MSI-X capability structure
+ * @dev: pointer to the pci_dev data structure of MSI-X device function
+ * @entries: pointer to an array of MSI-X entries
+ * @minvec: minimum number of MSI-X irqs requested
+ * @maxvec: maximum number of MSI-X irqs requested
+ *
+ * Setup the MSI-X capability structure of device function with a maximum
+ * possible number of interrupts in the range between @minvec and @maxvec
+ * upon its software driver call to request for MSI-X mode enabled on its
+ * hardware device function. It returns a negative errno if an error occurs.
+ * If it succeeds, it returns the actual number of interrupts allocated and
+ * indicates the successful configuration of MSI-X capability structure
+ * with new allocated MSI-X interrupts.
+ **/
+int pci_enable_msix_range(struct pci_dev *dev, struct msix_entry *entries,
+ int minvec, int maxvec)
+{
+ int nvec = maxvec;
+ int rc;
+
+ if (maxvec < minvec)
+ return -ERANGE;
+
+ do {
+ rc = pci_enable_msix(dev, entries, nvec);
+ if (rc < 0) {
+ return rc;
+ } else if (rc > 0) {
+ if (rc < minvec)
+ return -ENOSPC;
+ nvec = rc;
+ }
+ } while (rc);
+
+ return nvec;
+}
+EXPORT_SYMBOL(pci_enable_msix_range);
+#endif
diff --git a/compat/backport-3.15.c b/compat/backport-3.15.c
new file mode 100644
index 0000000..c0023ac
--- /dev/null
+++ b/compat/backport-3.15.c
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2014 Hauke Mehrtens <hauke@hauke-m.de>
+ * Copyright (c) 2015 Luis R. Rodriguez <mcgrof@do-not-panic.com>
+ *
+ * Backport functionality introduced in Linux 3.15.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/of.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <net/net_namespace.h>
+
+/**
+ * devm_kstrdup - Allocate resource managed space and
+ * copy an existing string into that.
+ * @dev: Device to allocate memory for
+ * @s: the string to duplicate
+ * @gfp: the GFP mask used in the devm_kmalloc() call when
+ * allocating memory
+ * RETURNS:
+ * Pointer to allocated string on success, NULL on failure.
+ */
+char *devm_kstrdup(struct device *dev, const char *s, gfp_t gfp)
+{
+ size_t size;
+ char *buf;
+
+ if (!s)
+ return NULL;
+
+ size = strlen(s) + 1;
+ buf = devm_kmalloc(dev, size, gfp);
+ if (buf)
+ memcpy(buf, s, size);
+ return buf;
+}
+EXPORT_SYMBOL_GPL(devm_kstrdup);
+
+#ifdef CONFIG_OF
+/**
+ * of_property_count_elems_of_size - Count the number of elements in a property
+ *
+ * @np: device node from which the property value is to be read.
+ * @propname: name of the property to be searched.
+ * @elem_size: size of the individual element
+ *
+ * Search for a property in a device node and count the number of elements of
+ * size elem_size in it. Returns number of elements on sucess, -EINVAL if the
+ * property does not exist or its length does not match a multiple of elem_size
+ * and -ENODATA if the property does not have a value.
+ */
+int of_property_count_elems_of_size(const struct device_node *np,
+ const char *propname, int elem_size)
+{
+ struct property *prop = of_find_property(np, propname, NULL);
+
+ if (!prop)
+ return -EINVAL;
+ if (!prop->value)
+ return -ENODATA;
+
+ if (prop->length % elem_size != 0) {
+ pr_err("size of %s in node %s is not a multiple of %d\n",
+ propname, np->full_name, elem_size);
+ return -EINVAL;
+ }
+
+ return prop->length / elem_size;
+}
+EXPORT_SYMBOL_GPL(of_property_count_elems_of_size);
+#endif
+
+void kvfree(const void *addr)
+{
+ if (is_vmalloc_addr(addr))
+ vfree(addr);
+ else
+ kfree(addr);
+}
+EXPORT_SYMBOL_GPL(kvfree);
diff --git a/compat/backport-3.17.c b/compat/backport-3.17.c
new file mode 100644
index 0000000..bf6027c
--- /dev/null
+++ b/compat/backport-3.17.c
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2014 Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * Backport functionality introduced in Linux 3.17.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/device.h>
+#include <linux/export.h>
+#include <linux/ktime.h>
+#include <linux/jiffies.h>
+#include <linux/moduleparam.h>
+
+int bit_wait(void *word)
+{
+ schedule();
+ return 0;
+}
+EXPORT_SYMBOL_GPL(bit_wait);
+
+int bit_wait_io(void *word)
+{
+ io_schedule();
+ return 0;
+}
+EXPORT_SYMBOL_GPL(bit_wait_io);
+
+/**
+ * ktime_get_raw - Returns the raw monotonic time in ktime_t format
+ */
+ktime_t ktime_get_raw(void)
+{
+ struct timespec ts;
+
+ getrawmonotonic(&ts);
+ return timespec_to_ktime(ts);
+}
+EXPORT_SYMBOL_GPL(ktime_get_raw);
+
+
+/**
+ * nsecs_to_jiffies64 - Convert nsecs in u64 to jiffies64
+ *
+ * @n: nsecs in u64
+ *
+ * Unlike {m,u}secs_to_jiffies, type of input is not unsigned int but u64.
+ * And this doesn't return MAX_JIFFY_OFFSET since this function is designed
+ * for scheduler, not for use in device drivers to calculate timeout value.
+ *
+ * note:
+ * NSEC_PER_SEC = 10^9 = (5^9 * 2^9) = (1953125 * 512)
+ * ULLONG_MAX ns = 18446744073.709551615 secs = about 584 years
+ */
+static u64 backport_nsecs_to_jiffies64(u64 n)
+{
+#if (NSEC_PER_SEC % HZ) == 0
+ /* Common case, HZ = 100, 128, 200, 250, 256, 500, 512, 1000 etc. */
+ return div_u64(n, NSEC_PER_SEC / HZ);
+#elif (HZ % 512) == 0
+ /* overflow after 292 years if HZ = 1024 */
+ return div_u64(n * HZ / 512, NSEC_PER_SEC / 512);
+#else
+ /*
+ * Generic case - optimized for cases where HZ is a multiple of 3.
+ * overflow after 64.99 years, exact for HZ = 60, 72, 90, 120 etc.
+ */
+ return div_u64(n * 9, (9ull * NSEC_PER_SEC + HZ / 2) / HZ);
+#endif
+}
+
+/**
+ * nsecs_to_jiffies - Convert nsecs in u64 to jiffies
+ *
+ * @n: nsecs in u64
+ *
+ * Unlike {m,u}secs_to_jiffies, type of input is not unsigned int but u64.
+ * And this doesn't return MAX_JIFFY_OFFSET since this function is designed
+ * for scheduler, not for use in device drivers to calculate timeout value.
+ *
+ * note:
+ * NSEC_PER_SEC = 10^9 = (5^9 * 2^9) = (1953125 * 512)
+ * ULLONG_MAX ns = 18446744073.709551615 secs = about 584 years
+ */
+unsigned long nsecs_to_jiffies(u64 n)
+{
+ return (unsigned long)backport_nsecs_to_jiffies64(n);
+}
+EXPORT_SYMBOL_GPL(nsecs_to_jiffies);
+
+/**
+ * devm_kvasprintf - Allocate resource managed space
+ * for the formatted string.
+ * @dev: Device to allocate memory for
+ * @gfp: the GFP mask used in the devm_kmalloc() call when
+ * allocating memory
+ * @fmt: the formatted string to duplicate
+ * @ap: the list of tokens to be placed in the formatted string
+ * RETURNS:
+ * Pointer to allocated string on success, NULL on failure.
+ */
+char *devm_kvasprintf(struct device *dev, gfp_t gfp, const char *fmt,
+ va_list ap)
+{
+ unsigned int len;
+ char *p;
+ va_list aq;
+
+ va_copy(aq, ap);
+ len = vsnprintf(NULL, 0, fmt, aq);
+ va_end(aq);
+
+ p = devm_kmalloc(dev, len+1, gfp);
+ if (!p)
+ return NULL;
+
+ vsnprintf(p, len+1, fmt, ap);
+
+ return p;
+}
+EXPORT_SYMBOL_GPL(devm_kvasprintf);
+
+/**
+ * devm_kasprintf - Allocate resource managed space
+ * and copy an existing formatted string into that
+ * @dev: Device to allocate memory for
+ * @gfp: the GFP mask used in the devm_kmalloc() call when
+ * allocating memory
+ * @fmt: the string to duplicate
+ * RETURNS:
+ * Pointer to allocated string on success, NULL on failure.
+ */
+char *devm_kasprintf(struct device *dev, gfp_t gfp, const char *fmt, ...)
+{
+ va_list ap;
+ char *p;
+
+ va_start(ap, fmt);
+ p = devm_kvasprintf(dev, gfp, fmt, ap);
+ va_end(ap);
+
+ return p;
+}
+EXPORT_SYMBOL_GPL(devm_kasprintf);
+
+#define STANDARD_PARAM_DEF(name, type, format, strtolfn) \
+ int param_set_##name(const char *val, const struct kernel_param *kp) \
+ { \
+ return strtolfn(val, 0, (type *)kp->arg); \
+ } \
+ int param_get_##name(char *buffer, const struct kernel_param *kp) \
+ { \
+ return scnprintf(buffer, PAGE_SIZE, format, \
+ *((type *)kp->arg)); \
+ } \
+ struct kernel_param_ops param_ops_##name = { \
+ .set = param_set_##name, \
+ .get = param_get_##name, \
+ }; \
+ EXPORT_SYMBOL(param_set_##name); \
+ EXPORT_SYMBOL(param_get_##name); \
+ EXPORT_SYMBOL(param_ops_##name)
+STANDARD_PARAM_DEF(ullong, unsigned long long, "%llu", kstrtoull);
diff --git a/compat/backport-3.18.c b/compat/backport-3.18.c
new file mode 100644
index 0000000..dbef343
--- /dev/null
+++ b/compat/backport-3.18.c
@@ -0,0 +1,324 @@
+/*
+ * Copyright (c) 2014 Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * Backport functionality introduced in Linux 3.18.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/if_ether.h>
+#include <linux/if_vlan.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <scsi/fc/fc_fcoe.h>
+#include <linux/skbuff.h>
+#include <linux/errqueue.h>
+#include <linux/wait.h>
+#include <linux/of.h>
+#include <linux/string.h>
+
+/**
+ * eth_get_headlen - determine the the length of header for an ethernet frame
+ * @data: pointer to start of frame
+ * @len: total length of frame
+ *
+ * Make a best effort attempt to pull the length for all of the headers for
+ * a given frame in a linear buffer.
+ */
+int eth_get_headlen(unsigned char *data, unsigned int max_len)
+{
+ union {
+ unsigned char *network;
+ /* l2 headers */
+ struct ethhdr *eth;
+ struct vlan_hdr *vlan;
+ /* l3 headers */
+ struct iphdr *ipv4;
+ struct ipv6hdr *ipv6;
+ } hdr;
+ __be16 protocol;
+ u8 nexthdr = 0; /* default to not TCP */
+ u8 hlen;
+
+ /* this should never happen, but better safe than sorry */
+ if (max_len < ETH_HLEN)
+ return max_len;
+
+ /* initialize network frame pointer */
+ hdr.network = data;
+
+ /* set first protocol and move network header forward */
+ protocol = hdr.eth->h_proto;
+ hdr.network += ETH_HLEN;
+
+ /* handle any vlan tag if present */
+ if (protocol == htons(ETH_P_8021Q)) {
+ if ((hdr.network - data) > (max_len - VLAN_HLEN))
+ return max_len;
+
+ protocol = hdr.vlan->h_vlan_encapsulated_proto;
+ hdr.network += VLAN_HLEN;
+ }
+
+ /* handle L3 protocols */
+ if (protocol == htons(ETH_P_IP)) {
+ if ((hdr.network - data) > (max_len - sizeof(struct iphdr)))
+ return max_len;
+
+ /* access ihl as a u8 to avoid unaligned access on ia64 */
+ hlen = (hdr.network[0] & 0x0F) << 2;
+
+ /* verify hlen meets minimum size requirements */
+ if (hlen < sizeof(struct iphdr))
+ return hdr.network - data;
+
+ /* record next protocol if header is present */
+ if (!(hdr.ipv4->frag_off & htons(IP_OFFSET)))
+ nexthdr = hdr.ipv4->protocol;
+ } else if (protocol == htons(ETH_P_IPV6)) {
+ if ((hdr.network - data) > (max_len - sizeof(struct ipv6hdr)))
+ return max_len;
+
+ /* record next protocol */
+ nexthdr = hdr.ipv6->nexthdr;
+ hlen = sizeof(struct ipv6hdr);
+ } else if (protocol == htons(ETH_P_FCOE)) {
+ if ((hdr.network - data) > (max_len - FCOE_HEADER_LEN))
+ return max_len;
+ hlen = FCOE_HEADER_LEN;
+ } else {
+ return hdr.network - data;
+ }
+
+ /* relocate pointer to start of L4 header */
+ hdr.network += hlen;
+
+ /* finally sort out TCP/UDP */
+ if (nexthdr == IPPROTO_TCP) {
+ if ((hdr.network - data) > (max_len - sizeof(struct tcphdr)))
+ return max_len;
+
+ /* access doff as a u8 to avoid unaligned access on ia64 */
+ hlen = (hdr.network[12] & 0xF0) >> 2;
+
+ /* verify hlen meets minimum size requirements */
+ if (hlen < sizeof(struct tcphdr))
+ return hdr.network - data;
+
+ hdr.network += hlen;
+ } else if (nexthdr == IPPROTO_UDP) {
+ if ((hdr.network - data) > (max_len - sizeof(struct udphdr)))
+ return max_len;
+
+ hdr.network += sizeof(struct udphdr);
+ }
+
+ /*
+ * If everything has gone correctly hdr.network should be the
+ * data section of the packet and will be the end of the header.
+ * If not then it probably represents the end of the last recognized
+ * header.
+ */
+ if ((hdr.network - data) < max_len)
+ return hdr.network - data;
+ else
+ return max_len;
+}
+EXPORT_SYMBOL_GPL(eth_get_headlen);
+
+#define sock_efree LINUX_BACKPORT(sock_efree)
+static void sock_efree(struct sk_buff *skb)
+{
+ sock_put(skb->sk);
+}
+
+/**
+ * skb_clone_sk - create clone of skb, and take reference to socket
+ * @skb: the skb to clone
+ *
+ * This function creates a clone of a buffer that holds a reference on
+ * sk_refcnt. Buffers created via this function are meant to be
+ * returned using sock_queue_err_skb, or free via kfree_skb.
+ *
+ * When passing buffers allocated with this function to sock_queue_err_skb
+ * it is necessary to wrap the call with sock_hold/sock_put in order to
+ * prevent the socket from being released prior to being enqueued on
+ * the sk_error_queue.
+ */
+struct sk_buff *skb_clone_sk(struct sk_buff *skb)
+{
+ struct sock *sk = skb->sk;
+ struct sk_buff *clone;
+
+ if (!sk || !atomic_inc_not_zero(&sk->sk_refcnt))
+ return NULL;
+
+ clone = skb_clone(skb, GFP_ATOMIC);
+ if (!clone) {
+ sock_put(sk);
+ return NULL;
+ }
+
+ clone->sk = sk;
+ clone->destructor = sock_efree;
+
+ return clone;
+}
+EXPORT_SYMBOL_GPL(skb_clone_sk);
+
+#if LINUX_VERSION_IS_GEQ(3,3,0)
+/*
+ * skb_complete_wifi_ack() needs to get backported, because the version from
+ * 3.18 added the sock_hold() and sock_put() calles missing in older versions.
+ */
+void skb_complete_wifi_ack(struct sk_buff *skb, bool acked)
+{
+ struct sock *sk = skb->sk;
+ struct sock_exterr_skb *serr;
+ int err;
+
+#if LINUX_VERSION_IS_GEQ(3,3,0)
+ skb->wifi_acked_valid = 1;
+ skb->wifi_acked = acked;
+#endif
+
+ serr = SKB_EXT_ERR(skb);
+ memset(serr, 0, sizeof(*serr));
+ serr->ee.ee_errno = ENOMSG;
+ serr->ee.ee_origin = SO_EE_ORIGIN_TXSTATUS;
+
+ /* take a reference to prevent skb_orphan() from freeing the socket */
+ sock_hold(sk);
+
+ err = sock_queue_err_skb(sk, skb);
+ if (err)
+ kfree_skb(skb);
+
+ sock_put(sk);
+}
+EXPORT_SYMBOL_GPL(skb_complete_wifi_ack);
+#endif
+
+#if LINUX_VERSION_IS_GEQ(3,17,0)
+int __sched out_of_line_wait_on_bit_timeout(
+ void *word, int bit, wait_bit_action_f *action,
+ unsigned mode, unsigned long timeout)
+{
+ wait_queue_head_t *wq = bit_waitqueue(word, bit);
+ DEFINE_WAIT_BIT(wait, word, bit);
+
+ wait.key.private = jiffies + timeout;
+ return __wait_on_bit(wq, &wait, action, mode);
+}
+EXPORT_SYMBOL_GPL(out_of_line_wait_on_bit_timeout);
+
+__sched int bit_wait_timeout(struct wait_bit_key *word)
+{
+ unsigned long now = ACCESS_ONCE(jiffies);
+ if (signal_pending_state(current->state, current))
+ return 1;
+ if (time_after_eq(now, word->private))
+ return -EAGAIN;
+ schedule_timeout(word->private - now);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(bit_wait_timeout);
+#endif
+
+#ifdef CONFIG_OF
+/**
+ * of_find_property_value_of_size
+ *
+ * @np: device node from which the property value is to be read.
+ * @propname: name of the property to be searched.
+ * @len: requested length of property value
+ *
+ * Search for a property in a device node and valid the requested size.
+ * Returns the property value on success, -EINVAL if the property does not
+ * exist, -ENODATA if property does not have a value, and -EOVERFLOW if the
+ * property data isn't large enough.
+ *
+ */
+void *of_find_property_value_of_size(const struct device_node *np,
+ const char *propname, u32 len)
+{
+ struct property *prop = of_find_property(np, propname, NULL);
+
+ if (!prop)
+ return ERR_PTR(-EINVAL);
+ if (!prop->value)
+ return ERR_PTR(-ENODATA);
+ if (len > prop->length)
+ return ERR_PTR(-EOVERFLOW);
+
+ return prop->value;
+}
+
+/**
+ * of_property_read_u64_array - Find and read an array of 64 bit integers
+ * from a property.
+ *
+ * @np: device node from which the property value is to be read.
+ * @propname: name of the property to be searched.
+ * @out_values: pointer to return value, modified only if return value is 0.
+ * @sz: number of array elements to read
+ *
+ * Search for a property in a device node and read 64-bit value(s) from
+ * it. Returns 0 on success, -EINVAL if the property does not exist,
+ * -ENODATA if property does not have a value, and -EOVERFLOW if the
+ * property data isn't large enough.
+ *
+ * The out_values is modified only if a valid u64 value can be decoded.
+ */
+int of_property_read_u64_array(const struct device_node *np,
+ const char *propname, u64 *out_values,
+ size_t sz)
+{
+ const __be32 *val = of_find_property_value_of_size(np, propname,
+ (sz * sizeof(*out_values)));
+
+ if (IS_ERR(val))
+ return PTR_ERR(val);
+
+ while (sz--) {
+ *out_values++ = of_read_number(val, 2);
+ val += 2;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(of_property_read_u64_array);
+#endif /* CONFIG_OF */
+
+#if !(LINUX_VERSION_IS_GEQ(3,17,3) || \
+ (LINUX_VERSION_IS_GEQ(3,14,24) && \
+ LINUX_VERSION_IS_LESS(3,15,0)) || \
+ (LINUX_VERSION_IS_GEQ(3,12,33) && \
+ LINUX_VERSION_IS_LESS(3,13,0)) || \
+ (LINUX_VERSION_IS_GEQ(3,10,60) && \
+ LINUX_VERSION_IS_LESS(3,11,0)) || \
+ (LINUX_VERSION_IS_GEQ(3,4,106) && \
+ LINUX_VERSION_IS_LESS(3,5,0)) || \
+ (LINUX_VERSION_IS_GEQ(3,2,65) && \
+ LINUX_VERSION_IS_LESS(3,3,0)))
+/**
+ * memzero_explicit - Fill a region of memory (e.g. sensitive
+ * keying data) with 0s.
+ * @s: Pointer to the start of the area.
+ * @count: The size of the area.
+ *
+ * Note: usually using memset() is just fine (!), but in cases
+ * where clearing out _local_ data at the end of a scope is
+ * necessary, memzero_explicit() should be used instead in
+ * order to prevent the compiler from optimising away zeroing.
+ *
+ * memzero_explicit() doesn't need an arch-specific version as
+ * it just invokes the one of memset() implicitly.
+ */
+void memzero_explicit(void *s, size_t count)
+{
+ memset(s, 0, count);
+ barrier_data(s);
+}
+EXPORT_SYMBOL_GPL(memzero_explicit);
+#endif
diff --git a/compat/backport-3.19.c b/compat/backport-3.19.c
new file mode 100644
index 0000000..019644d
--- /dev/null
+++ b/compat/backport-3.19.c
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2014 Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * Backport functionality introduced in Linux 3.19.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/kthread.h>
+#include <linux/export.h>
+#include <linux/net.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/debugfs.h>
+
+#if LINUX_VERSION_IS_LESS(3,18,12)
+static inline bool is_kthread_should_stop(void)
+{
+ return (current->flags & PF_KTHREAD) && kthread_should_stop();
+}
+
+/*
+ * DEFINE_WAIT_FUNC(wait, woken_wake_func);
+ *
+ * add_wait_queue(&wq, &wait);
+ * for (;;) {
+ * if (condition)
+ * break;
+ *
+ * p->state = mode; condition = true;
+ * smp_mb(); // A smp_wmb(); // C
+ * if (!wait->flags & WQ_FLAG_WOKEN) wait->flags |= WQ_FLAG_WOKEN;
+ * schedule() try_to_wake_up();
+ * p->state = TASK_RUNNING; ~~~~~~~~~~~~~~~~~~
+ * wait->flags &= ~WQ_FLAG_WOKEN; condition = true;
+ * smp_mb() // B smp_wmb(); // C
+ * wait->flags |= WQ_FLAG_WOKEN;
+ * }
+ * remove_wait_queue(&wq, &wait);
+ *
+ */
+long wait_woken(wait_queue_t *wait, unsigned mode, long timeout)
+{
+ set_current_state(mode); /* A */
+ /*
+ * The above implies an smp_mb(), which matches with the smp_wmb() from
+ * woken_wake_function() such that if we observe WQ_FLAG_WOKEN we must
+ * also observe all state before the wakeup.
+ */
+ if (!(wait->flags & WQ_FLAG_WOKEN) && !is_kthread_should_stop())
+ timeout = schedule_timeout(timeout);
+ __set_current_state(TASK_RUNNING);
+
+ /*
+ * The below implies an smp_mb(), it too pairs with the smp_wmb() from
+ * woken_wake_function() such that we must either observe the wait
+ * condition being true _OR_ WQ_FLAG_WOKEN such that we will not miss
+ * an event.
+ */
+ set_mb(wait->flags, wait->flags & ~WQ_FLAG_WOKEN); /* B */
+
+ return timeout;
+}
+EXPORT_SYMBOL(wait_woken);
+
+int woken_wake_function(wait_queue_t *wait, unsigned mode, int sync, void *key)
+{
+ /*
+ * Although this function is called under waitqueue lock, LOCK
+ * doesn't imply write barrier and the users expects write
+ * barrier semantics on wakeup functions. The following
+ * smp_wmb() is equivalent to smp_wmb() in try_to_wake_up()
+ * and is paired with set_mb() in wait_woken().
+ */
+ smp_wmb(); /* C */
+ wait->flags |= WQ_FLAG_WOKEN;
+
+ return default_wake_function(wait, mode, sync, key);
+}
+EXPORT_SYMBOL(woken_wake_function);
+#endif
+
+static u8 netdev_rss_key[NETDEV_RSS_KEY_LEN];
+
+void netdev_rss_key_fill(void *buffer, size_t len)
+{
+ BUG_ON(len > sizeof(netdev_rss_key));
+#ifdef __BACKPORT_NET_GET_RANDOM_ONCE
+ net_get_random_once(netdev_rss_key, sizeof(netdev_rss_key));
+ memcpy(buffer, netdev_rss_key, len);
+#else
+ get_random_bytes(buffer, len);
+#endif
+}
+EXPORT_SYMBOL_GPL(netdev_rss_key_fill);
+
+#if defined(CONFIG_DEBUG_FS)
+struct debugfs_devm_entry {
+ int (*read)(struct seq_file *seq, void *data);
+ struct device *dev;
+};
+
+static int debugfs_devm_entry_open(struct inode *inode, struct file *f)
+{
+ struct debugfs_devm_entry *entry = inode->i_private;
+
+ return single_open(f, entry->read, entry->dev);
+}
+
+static const struct file_operations debugfs_devm_entry_ops = {
+ .owner = THIS_MODULE,
+ .open = debugfs_devm_entry_open,
+ .release = single_release,
+ .read = seq_read,
+ .llseek = seq_lseek
+};
+
+/**
+ * debugfs_create_devm_seqfile - create a debugfs file that is bound to device.
+ *
+ * @dev: device related to this debugfs file.
+ * @name: name of the debugfs file.
+ * @parent: a pointer to the parent dentry for this file. This should be a
+ * directory dentry if set. If this parameter is %NULL, then the
+ * file will be created in the root of the debugfs filesystem.
+ * @read_fn: function pointer called to print the seq_file content.
+ */
+struct dentry *debugfs_create_devm_seqfile(struct device *dev, const char *name,
+ struct dentry *parent,
+ int (*read_fn)(struct seq_file *s,
+ void *data))
+{
+ struct debugfs_devm_entry *entry;
+
+ if (IS_ERR(parent))
+ return ERR_PTR(-ENOENT);
+
+ entry = devm_kzalloc(dev, sizeof(*entry), GFP_KERNEL);
+ if (!entry)
+ return ERR_PTR(-ENOMEM);
+
+ entry->read = read_fn;
+ entry->dev = dev;
+
+ return debugfs_create_file(name, S_IRUGO, parent, entry,
+ &debugfs_devm_entry_ops);
+}
+EXPORT_SYMBOL_GPL(debugfs_create_devm_seqfile);
+
+#endif /* CONFIG_DEBUG_FS */
+
+int skb_ensure_writable(struct sk_buff *skb, int write_len)
+{
+ if (!pskb_may_pull(skb, write_len))
+ return -ENOMEM;
+
+ if (!skb_cloned(skb) || skb_clone_writable(skb, write_len))
+ return 0;
+
+ return pskb_expand_head(skb, 0, 0, GFP_ATOMIC);
+}
+EXPORT_SYMBOL_GPL(skb_ensure_writable);
diff --git a/compat/backport-3.2.c b/compat/backport-3.2.c
new file mode 100644
index 0000000..601a168
--- /dev/null
+++ b/compat/backport-3.2.c
@@ -0,0 +1,25 @@
+/*
+ * Linux backport symbols for kernels 3.2.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/export.h>
+
+int hex2bin(u8 *dst, const char *src, size_t count)
+{
+ while (count--) {
+ int hi = hex_to_bin(*src++);
+ int lo = hex_to_bin(*src++);
+
+ if ((hi < 0) || (lo < 0))
+ return -1;
+
+ *dst++ = (hi << 4) | lo;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(hex2bin);
diff --git a/compat/backport-4.0.c b/compat/backport-4.0.c
new file mode 100644
index 0000000..6a82935
--- /dev/null
+++ b/compat/backport-4.0.c
@@ -0,0 +1,377 @@
+/*
+ * Copyright (c) 2015 Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * Backport functionality introduced in Linux 4.0.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/ctype.h>
+#include <linux/printk.h>
+#include <linux/export.h>
+#include <linux/trace_seq.h>
+#include <linux/ftrace_event.h>
+#include <asm/unaligned.h>
+
+static __always_inline long __get_user_pages_locked(struct task_struct *tsk,
+ struct mm_struct *mm,
+ unsigned long start,
+ unsigned long nr_pages,
+ int write, int force,
+ struct page **pages,
+ struct vm_area_struct **vmas,
+ int *locked, bool notify_drop,
+ unsigned int flags)
+{
+ long ret, pages_done;
+ bool lock_dropped;
+
+ if (locked) {
+ /* if VM_FAULT_RETRY can be returned, vmas become invalid */
+ BUG_ON(vmas);
+ /* check caller initialized locked */
+ BUG_ON(*locked != 1);
+ }
+
+ if (pages)
+ flags |= FOLL_GET;
+ if (write)
+ flags |= FOLL_WRITE;
+ if (force)
+ flags |= FOLL_FORCE;
+
+ pages_done = 0;
+ lock_dropped = false;
+ for (;;) {
+ ret = __get_user_pages(tsk, mm, start, nr_pages, flags, pages,
+ vmas, locked);
+ if (!locked)
+ /* VM_FAULT_RETRY couldn't trigger, bypass */
+ return ret;
+
+ /* VM_FAULT_RETRY cannot return errors */
+ if (!*locked) {
+ BUG_ON(ret < 0);
+ BUG_ON(ret >= nr_pages);
+ }
+
+ if (!pages)
+ /* If it's a prefault don't insist harder */
+ return ret;
+
+ if (ret > 0) {
+ nr_pages -= ret;
+ pages_done += ret;
+ if (!nr_pages)
+ break;
+ }
+ if (*locked) {
+ /* VM_FAULT_RETRY didn't trigger */
+ if (!pages_done)
+ pages_done = ret;
+ break;
+ }
+ /* VM_FAULT_RETRY triggered, so seek to the faulting offset */
+ pages += ret;
+ start += ret << PAGE_SHIFT;
+
+ /*
+ * Repeat on the address that fired VM_FAULT_RETRY
+ * without FAULT_FLAG_ALLOW_RETRY but with
+ * FAULT_FLAG_TRIED.
+ */
+ *locked = 1;
+ lock_dropped = true;
+ down_read(&mm->mmap_sem);
+ ret = __get_user_pages(tsk, mm, start, 1, flags | FOLL_TRIED,
+ pages, NULL, NULL);
+ if (ret != 1) {
+ BUG_ON(ret > 1);
+ if (!pages_done)
+ pages_done = ret;
+ break;
+ }
+ nr_pages--;
+ pages_done++;
+ if (!nr_pages)
+ break;
+ pages++;
+ start += PAGE_SIZE;
+ }
+ if (notify_drop && lock_dropped && *locked) {
+ /*
+ * We must let the caller know we temporarily dropped the lock
+ * and so the critical section protected by it was lost.
+ */
+ up_read(&mm->mmap_sem);
+ *locked = 0;
+ }
+ return pages_done;
+}
+
+/*
+ * We can leverage the VM_FAULT_RETRY functionality in the page fault
+ * paths better by using either get_user_pages_locked() or
+ * get_user_pages_unlocked().
+ *
+ * get_user_pages_locked() is suitable to replace the form:
+ *
+ * down_read(&mm->mmap_sem);
+ * do_something()
+ * get_user_pages(tsk, mm, ..., pages, NULL);
+ * up_read(&mm->mmap_sem);
+ *
+ * to:
+ *
+ * int locked = 1;
+ * down_read(&mm->mmap_sem);
+ * do_something()
+ * get_user_pages_locked(tsk, mm, ..., pages, &locked);
+ * if (locked)
+ * up_read(&mm->mmap_sem);
+ */
+long get_user_pages_locked(unsigned long start, unsigned long nr_pages,
+ int write, int force, struct page **pages,
+ int *locked)
+{
+ return __get_user_pages_locked(current, current->mm, start, nr_pages,
+ write, force, pages, NULL, locked, true,
+ FOLL_TOUCH);
+}
+EXPORT_SYMBOL_GPL(get_user_pages_locked);
+
+/*
+ * Same as get_user_pages_unlocked(...., FOLL_TOUCH) but it allows to
+ * pass additional gup_flags as last parameter (like FOLL_HWPOISON).
+ *
+ * NOTE: here FOLL_TOUCH is not set implicitly and must be set by the
+ * caller if required (just like with __get_user_pages). "FOLL_GET",
+ * "FOLL_WRITE" and "FOLL_FORCE" are set implicitly as needed
+ * according to the parameters "pages", "write", "force"
+ * respectively.
+ */
+static __always_inline long __get_user_pages_unlocked(struct task_struct *tsk, struct mm_struct *mm,
+ unsigned long start, unsigned long nr_pages,
+ int write, int force, struct page **pages,
+ unsigned int gup_flags)
+{
+ long ret;
+ int locked = 1;
+ down_read(&mm->mmap_sem);
+ ret = __get_user_pages_locked(tsk, mm, start, nr_pages, write, force,
+ pages, NULL, &locked, false, gup_flags);
+ if (locked)
+ up_read(&mm->mmap_sem);
+ return ret;
+}
+
+/*
+ * get_user_pages_unlocked() is suitable to replace the form:
+ *
+ * down_read(&mm->mmap_sem);
+ * get_user_pages(tsk, mm, ..., pages, NULL);
+ * up_read(&mm->mmap_sem);
+ *
+ * with:
+ *
+ * get_user_pages_unlocked(tsk, mm, ..., pages);
+ *
+ * It is functionally equivalent to get_user_pages_fast so
+ * get_user_pages_fast should be used instead, if the two parameters
+ * "tsk" and "mm" are respectively equal to current and current->mm,
+ * or if "force" shall be set to 1 (get_user_pages_fast misses the
+ * "force" parameter).
+ */
+long get_user_pages_unlocked(unsigned long start, unsigned long nr_pages,
+ int write, int force, struct page **pages)
+{
+ return __get_user_pages_unlocked(current, current->mm, start, nr_pages,
+ write, force, pages, FOLL_TOUCH);
+}
+EXPORT_SYMBOL_GPL(get_user_pages_unlocked);
+
+
+/**
+ * hex_dump_to_buffer - convert a blob of data to "hex ASCII" in memory
+ * @buf: data blob to dump
+ * @len: number of bytes in the @buf
+ * @rowsize: number of bytes to print per line; must be 16 or 32
+ * @groupsize: number of bytes to print at a time (1, 2, 4, 8; default = 1)
+ * @linebuf: where to put the converted data
+ * @linebuflen: total size of @linebuf, including space for terminating NUL
+ * @ascii: include ASCII after the hex output
+ *
+ * hex_dump_to_buffer() works on one "line" of output at a time, i.e.,
+ * 16 or 32 bytes of input data converted to hex + ASCII output.
+ *
+ * Given a buffer of u8 data, hex_dump_to_buffer() converts the input data
+ * to a hex + ASCII dump at the supplied memory location.
+ * The converted output is always NUL-terminated.
+ *
+ * E.g.:
+ * hex_dump_to_buffer(frame->data, frame->len, 16, 1,
+ * linebuf, sizeof(linebuf), true);
+ *
+ * example output buffer:
+ * 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f @ABCDEFGHIJKLMNO
+ *
+ * Return:
+ * The amount of bytes placed in the buffer without terminating NUL. If the
+ * output was truncated, then the return value is the number of bytes
+ * (excluding the terminating NUL) which would have been written to the final
+ * string if enough space had been available.
+ */
+int hex_dump_to_buffer(const void *buf, size_t len, int rowsize, int groupsize,
+ char *linebuf, size_t linebuflen, bool ascii)
+{
+ const u8 *ptr = buf;
+ int ngroups;
+ u8 ch;
+ int j, lx = 0;
+ int ascii_column;
+ int ret;
+
+ if (rowsize != 16 && rowsize != 32)
+ rowsize = 16;
+
+ if (len > rowsize) /* limit to one line at a time */
+ len = rowsize;
+ if (!is_power_of_2(groupsize) || groupsize > 8)
+ groupsize = 1;
+ if ((len % groupsize) != 0) /* no mixed size output */
+ groupsize = 1;
+
+ ngroups = len / groupsize;
+ ascii_column = rowsize * 2 + rowsize / groupsize + 1;
+
+ if (!linebuflen)
+ goto overflow1;
+
+ if (!len)
+ goto nil;
+
+ if (groupsize == 8) {
+ const u64 *ptr8 = buf;
+
+ for (j = 0; j < ngroups; j++) {
+ ret = snprintf(linebuf + lx, linebuflen - lx,
+ "%s%16.16llx", j ? " " : "",
+ get_unaligned(ptr8 + j));
+ if (ret >= linebuflen - lx)
+ goto overflow1;
+ lx += ret;
+ }
+ } else if (groupsize == 4) {
+ const u32 *ptr4 = buf;
+
+ for (j = 0; j < ngroups; j++) {
+ ret = snprintf(linebuf + lx, linebuflen - lx,
+ "%s%8.8x", j ? " " : "",
+ get_unaligned(ptr4 + j));
+ if (ret >= linebuflen - lx)
+ goto overflow1;
+ lx += ret;
+ }
+ } else if (groupsize == 2) {
+ const u16 *ptr2 = buf;
+
+ for (j = 0; j < ngroups; j++) {
+ ret = snprintf(linebuf + lx, linebuflen - lx,
+ "%s%4.4x", j ? " " : "",
+ get_unaligned(ptr2 + j));
+ if (ret >= linebuflen - lx)
+ goto overflow1;
+ lx += ret;
+ }
+ } else {
+ for (j = 0; j < len; j++) {
+ if (linebuflen < lx + 3)
+ goto overflow2;
+ ch = ptr[j];
+ linebuf[lx++] = hex_asc_hi(ch);
+ linebuf[lx++] = hex_asc_lo(ch);
+ linebuf[lx++] = ' ';
+ }
+ if (j)
+ lx--;
+ }
+ if (!ascii)
+ goto nil;
+
+ while (lx < ascii_column) {
+ if (linebuflen < lx + 2)
+ goto overflow2;
+ linebuf[lx++] = ' ';
+ }
+ for (j = 0; j < len; j++) {
+ if (linebuflen < lx + 2)
+ goto overflow2;
+ ch = ptr[j];
+ linebuf[lx++] = (isascii(ch) && isprint(ch)) ? ch : '.';
+ }
+nil:
+ linebuf[lx] = '\0';
+ return lx;
+overflow2:
+ linebuf[lx++] = '\0';
+overflow1:
+ return ascii ? ascii_column + len : (groupsize * 2 + 1) * ngroups - 1;
+}
+EXPORT_SYMBOL_GPL(hex_dump_to_buffer);
+
+#if LINUX_VERSION_IS_LESS(3,17,0)
+static inline unsigned char *
+trace_seq_buffer_ptr(struct trace_seq *s)
+{
+ return s->buffer + s->len;
+}
+#endif
+
+const char *
+ftrace_print_array_seq(struct trace_seq *p, const void *buf, int buf_len,
+ size_t el_size)
+{
+ const char *ret = trace_seq_buffer_ptr(p);
+ const char *prefix = "";
+ void *ptr = (void *)buf;
+
+ trace_seq_putc(p, '{');
+
+ while (ptr < buf + buf_len) {
+ switch (el_size) {
+ case 1:
+ trace_seq_printf(p, "%s0x%x", prefix,
+ *(u8 *)ptr);
+ break;
+ case 2:
+ trace_seq_printf(p, "%s0x%x", prefix,
+ *(u16 *)ptr);
+ break;
+ case 4:
+ trace_seq_printf(p, "%s0x%x", prefix,
+ *(u32 *)ptr);
+ break;
+ case 8:
+ trace_seq_printf(p, "%s0x%llx", prefix,
+ *(u64 *)ptr);
+ break;
+ default:
+ trace_seq_printf(p, "BAD SIZE:%zu 0x%x", el_size,
+ *(u8 *)ptr);
+ el_size = 1;
+ }
+ prefix = ",";
+ ptr += el_size;
+ }
+
+ trace_seq_putc(p, '}');
+ trace_seq_putc(p, 0);
+
+ return ret;
+}
+EXPORT_SYMBOL(ftrace_print_array_seq);
diff --git a/compat/backport-4.1.c b/compat/backport-4.1.c
new file mode 100644
index 0000000..e0a3ec6
--- /dev/null
+++ b/compat/backport-4.1.c
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2015 Stefan Assmann <sassmann@kpanic.de>
+ * Copyright (c) 2015 Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * Backport functionality introduced in Linux 4.1.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/netdevice.h>
+#include <linux/tty.h>
+
+netdev_features_t passthru_features_check(struct sk_buff *skb,
+ struct net_device *dev,
+ netdev_features_t features)
+{
+ return features;
+}
+EXPORT_SYMBOL_GPL(passthru_features_check);
+
+#ifdef CONFIG_TTY
+#if LINUX_VERSION_IS_GEQ(4,0,0)
+static void unset_locked_termios(struct ktermios *termios,
+ struct ktermios *old,
+ struct ktermios *locked)
+{
+ int i;
+
+#define NOSET_MASK(x, y, z) (x = ((x) & ~(z)) | ((y) & (z)))
+
+ if (!locked) {
+ printk(KERN_WARNING "Warning?!? termios_locked is NULL.\n");
+ return;
+ }
+
+ NOSET_MASK(termios->c_iflag, old->c_iflag, locked->c_iflag);
+ NOSET_MASK(termios->c_oflag, old->c_oflag, locked->c_oflag);
+ NOSET_MASK(termios->c_cflag, old->c_cflag, locked->c_cflag);
+ NOSET_MASK(termios->c_lflag, old->c_lflag, locked->c_lflag);
+ termios->c_line = locked->c_line ? old->c_line : termios->c_line;
+ for (i = 0; i < NCCS; i++)
+ termios->c_cc[i] = locked->c_cc[i] ?
+ old->c_cc[i] : termios->c_cc[i];
+ /* FIXME: What should we do for i/ospeed */
+}
+
+int tty_set_termios(struct tty_struct *tty, struct ktermios *new_termios)
+{
+ struct ktermios old_termios;
+ struct tty_ldisc *ld;
+
+ WARN_ON(tty->driver->type == TTY_DRIVER_TYPE_PTY &&
+ tty->driver->subtype == PTY_TYPE_MASTER);
+ /*
+ * Perform the actual termios internal changes under lock.
+ */
+
+
+ /* FIXME: we need to decide on some locking/ordering semantics
+ for the set_termios notification eventually */
+ down_write(&tty->termios_rwsem);
+ old_termios = tty->termios;
+ tty->termios = *new_termios;
+ unset_locked_termios(&tty->termios, &old_termios, &tty->termios_locked);
+
+ if (tty->ops->set_termios)
+ tty->ops->set_termios(tty, &old_termios);
+ else
+ tty_termios_copy_hw(&tty->termios, &old_termios);
+
+ ld = tty_ldisc_ref(tty);
+ if (ld != NULL) {
+ if (ld->ops->set_termios)
+ ld->ops->set_termios(tty, &old_termios);
+ tty_ldisc_deref(ld);
+ }
+ up_write(&tty->termios_rwsem);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(tty_set_termios);
+#endif /* LINUX_VERSION_IS_GEQ(4,0,0) */
+#endif /* CONFIG_TTY */
diff --git a/compat/backport-4.2.c b/compat/backport-4.2.c
new file mode 100644
index 0000000..e00aa49
--- /dev/null
+++ b/compat/backport-4.2.c
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2015 Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * Backport functionality introduced in Linux 4.2.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <crypto/scatterwalk.h>
+#include <crypto/aead.h>
+
+static struct scatterlist *scatterwalk_ffwd(struct scatterlist dst[2],
+ struct scatterlist *src,
+ unsigned int len)
+{
+ for (;;) {
+ if (!len)
+ return src;
+
+ if (src->length > len)
+ break;
+
+ len -= src->length;
+ src = sg_next(src);
+ }
+
+ sg_init_table(dst, 2);
+ sg_set_page(dst, sg_page(src), src->length - len, src->offset + len);
+ scatterwalk_crypto_chain(dst, sg_next(src), 0, 2);
+
+ return dst;
+}
+
+struct aead_old_request {
+ struct scatterlist srcbuf[2];
+ struct scatterlist dstbuf[2];
+ struct aead_request subreq;
+};
+
+unsigned int crypto_aead_reqsize(struct crypto_aead *tfm)
+{
+ return crypto_aead_crt(tfm)->reqsize + sizeof(struct aead_old_request);
+}
+EXPORT_SYMBOL_GPL(crypto_aead_reqsize);
+
+struct aead_request *crypto_backport_convert(struct aead_request *req)
+{
+ struct aead_old_request *nreq = aead_request_ctx(req);
+ struct crypto_aead *aead = crypto_aead_reqtfm(req);
+ struct scatterlist *src, *dst;
+
+ src = scatterwalk_ffwd(nreq->srcbuf, req->src, req->assoclen);
+ dst = req->src == req->dst ?
+ src : scatterwalk_ffwd(nreq->dstbuf, req->dst, req->assoclen);
+
+ aead_request_set_tfm(&nreq->subreq, aead);
+ aead_request_set_callback(&nreq->subreq, aead_request_flags(req),
+ req->base.complete, req->base.data);
+ aead_request_set_crypt(&nreq->subreq, src, dst, req->cryptlen,
+ req->iv);
+ aead_request_set_assoc(&nreq->subreq, req->src, req->assoclen);
+
+ return &nreq->subreq;
+}
+EXPORT_SYMBOL_GPL(crypto_backport_convert);
diff --git a/compat/backport-4.3.c b/compat/backport-4.3.c
new file mode 100644
index 0000000..2d8a5e5
--- /dev/null
+++ b/compat/backport-4.3.c
@@ -0,0 +1,245 @@
+/*
+ * Copyright (c) 2015 Hauke Mehrtens <hauke@hauke-m.de>
+ * Copyright (c) 2015 - 2016 Intel Deutschland GmbH
+ *
+ * Backport functionality introduced in Linux 4.3.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/version.h>
+#include <linux/seq_file.h>
+#include <linux/export.h>
+#include <linux/printk.h>
+#include <linux/thermal.h>
+#include <linux/slab.h>
+
+#if LINUX_VERSION_IS_GEQ(3,8,0)
+struct backport_thermal_ops_wrapper {
+ old_thermal_zone_device_ops_t ops;
+ struct thermal_zone_device_ops *driver_ops;
+};
+
+static int backport_thermal_get_temp(struct thermal_zone_device *dev,
+ unsigned long *temp)
+{
+ struct backport_thermal_ops_wrapper *wrapper =
+ container_of(dev->ops, struct backport_thermal_ops_wrapper, ops);
+ int _temp, ret;
+
+ ret = wrapper->driver_ops->get_temp(dev, &_temp);
+ if (!ret)
+ *temp = (unsigned long)_temp;
+
+ return ret;
+}
+
+static int backport_thermal_get_trip_temp(struct thermal_zone_device *dev,
+ int i, unsigned long *temp)
+{
+ struct backport_thermal_ops_wrapper *wrapper =
+ container_of(dev->ops, struct backport_thermal_ops_wrapper, ops);
+ int _temp, ret;
+
+ ret = wrapper->driver_ops->get_trip_temp(dev, i, &_temp);
+ if (!ret)
+ *temp = (unsigned long)_temp;
+
+ return ret;
+}
+
+static int backport_thermal_set_trip_temp(struct thermal_zone_device *dev,
+ int i, unsigned long temp)
+{
+ struct backport_thermal_ops_wrapper *wrapper =
+ container_of(dev->ops, struct backport_thermal_ops_wrapper, ops);
+
+ return wrapper->driver_ops->set_trip_temp(dev, i, (int)temp);
+}
+
+static int backport_thermal_get_trip_hyst(struct thermal_zone_device *dev,
+ int i, unsigned long *temp)
+{
+ struct backport_thermal_ops_wrapper *wrapper =
+ container_of(dev->ops, struct backport_thermal_ops_wrapper, ops);
+ int _temp, ret;
+
+ ret = wrapper->driver_ops->get_trip_hyst(dev, i, &_temp);
+ if (!ret)
+ *temp = (unsigned long)_temp;
+
+ return ret;
+}
+
+static int backport_thermal_set_trip_hyst(struct thermal_zone_device *dev,
+ int i, unsigned long temp)
+{
+ struct backport_thermal_ops_wrapper *wrapper =
+ container_of(dev->ops, struct backport_thermal_ops_wrapper, ops);
+
+ return wrapper->driver_ops->set_trip_hyst(dev, i, (int)temp);
+}
+
+static int backport_thermal_get_crit_temp(struct thermal_zone_device *dev,
+ unsigned long *temp)
+{
+ struct backport_thermal_ops_wrapper *wrapper =
+ container_of(dev->ops, struct backport_thermal_ops_wrapper, ops);
+ int _temp, ret;
+
+ ret = wrapper->driver_ops->get_crit_temp(dev, &_temp);
+ if (!ret)
+ *temp = (unsigned long)_temp;
+
+ return ret;
+}
+
+#if LINUX_VERSION_IS_GEQ(3, 19, 0)
+static int backport_thermal_set_emul_temp(struct thermal_zone_device *dev,
+ unsigned long temp)
+{
+ struct backport_thermal_ops_wrapper *wrapper =
+ container_of(dev->ops, struct backport_thermal_ops_wrapper, ops);
+
+ return wrapper->driver_ops->set_emul_temp(dev, (int)temp);
+}
+#endif /* LINUX_VERSION_IS_GEQ(3, 19, 0) */
+
+struct thermal_zone_device *backport_thermal_zone_device_register(
+ const char *type, int trips, int mask, void *devdata,
+ struct thermal_zone_device_ops *ops,
+ const struct thermal_zone_params *tzp,
+ int passive_delay, int polling_delay)
+{
+ struct backport_thermal_ops_wrapper *wrapper = kzalloc(sizeof(*wrapper), GFP_KERNEL);
+ struct thermal_zone_device *ret;
+
+ if (!wrapper)
+ return NULL;
+
+ wrapper->driver_ops = ops;
+
+#define copy(_op) \
+ wrapper->ops._op = ops->_op
+
+ copy(bind);
+ copy(unbind);
+ copy(get_mode);
+ copy(set_mode);
+ copy(get_trip_type);
+ copy(get_trend);
+ copy(notify);
+
+ /* Assign the backport ops to the old struct to get the
+ * correct types. But only assign if the registrant defined
+ * the ops.
+ */
+#define assign_ops(_op) \
+ if (ops->_op) \
+ wrapper->ops._op = backport_thermal_##_op
+
+ assign_ops(get_temp);
+ assign_ops(get_trip_temp);
+ assign_ops(set_trip_temp);
+ assign_ops(get_trip_hyst);
+ assign_ops(set_trip_hyst);
+ assign_ops(get_crit_temp);
+#if LINUX_VERSION_IS_GEQ(3, 19, 0)
+ assign_ops(set_emul_temp);
+#endif /* LINUX_VERSION_IS_GEQ(3, 19, 0) */
+#undef assign_ops
+
+ ret = old_thermal_zone_device_register(type, trips, mask, devdata,
+ &wrapper->ops, tzp, passive_delay,
+ polling_delay);
+ if (!ret)
+ kfree(wrapper);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(backport_thermal_zone_device_register);
+
+void backport_thermal_zone_device_unregister(struct thermal_zone_device *dev)
+{
+ struct backport_thermal_ops_wrapper *wrapper =
+ container_of(dev->ops, struct backport_thermal_ops_wrapper, ops);
+
+ old_thermal_zone_device_unregister(dev);
+ kfree(wrapper);
+}
+EXPORT_SYMBOL_GPL(backport_thermal_zone_device_unregister);
+
+#endif /* >= 3.8.0 */
+
+static void seq_set_overflow(struct seq_file *m)
+{
+ m->count = m->size;
+}
+
+/* A complete analogue of print_hex_dump() */
+void seq_hex_dump(struct seq_file *m, const char *prefix_str, int prefix_type,
+ int rowsize, int groupsize, const void *buf, size_t len,
+ bool ascii)
+{
+ const u8 *ptr = buf;
+ int i, linelen, remaining = len;
+ int ret;
+
+ if (rowsize != 16 && rowsize != 32)
+ rowsize = 16;
+
+ for (i = 0; i < len && !seq_has_overflowed(m); i += rowsize) {
+ linelen = min(remaining, rowsize);
+ remaining -= rowsize;
+
+ switch (prefix_type) {
+ case DUMP_PREFIX_ADDRESS:
+ seq_printf(m, "%s%p: ", prefix_str, ptr + i);
+ break;
+ case DUMP_PREFIX_OFFSET:
+ seq_printf(m, "%s%.8x: ", prefix_str, i);
+ break;
+ default:
+ seq_printf(m, "%s", prefix_str);
+ break;
+ }
+
+ ret = hex_dump_to_buffer(ptr + i, linelen, rowsize, groupsize,
+ m->buf + m->count, m->size - m->count,
+ ascii);
+ if (ret >= m->size - m->count) {
+ seq_set_overflow(m);
+ } else {
+ m->count += ret;
+ seq_putc(m, '\n');
+ }
+ }
+}
+EXPORT_SYMBOL_GPL(seq_hex_dump);
+
+ssize_t strscpy(char *dest, const char *src, size_t count)
+{
+ long res = 0;
+
+ if (count == 0)
+ return -E2BIG;
+
+ while (count) {
+ char c;
+
+ c = src[res];
+ dest[res] = c;
+ if (!c)
+ return res;
+ res++;
+ count--;
+ }
+
+ /* Hit buffer length without finding a NUL; force NUL-termination. */
+ if (res)
+ dest[res-1] = '\0';
+
+ return -E2BIG;
+}
+EXPORT_SYMBOL_GPL(strscpy);
diff --git a/compat/backport-4.4.c b/compat/backport-4.4.c
new file mode 100644
index 0000000..1c11a20
--- /dev/null
+++ b/compat/backport-4.4.c
@@ -0,0 +1,154 @@
+/*
+ * Copyright(c) 2015 Intel Deutschland GmbH
+ *
+ * Backport functionality introduced in Linux 4.4.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/debugfs.h>
+#include <linux/export.h>
+#include <linux/uaccess.h>
+#include <linux/fs.h>
+#include <linux/if_vlan.h>
+#include <linux/mm.h>
+#include <linux/skbuff.h>
+#include <net/ip.h>
+#include <net/tso.h>
+#include <asm/unaligned.h>
+
+#ifdef CONFIG_DEBUG_FS
+#if LINUX_VERSION_IS_LESS(4,3,0)
+static ssize_t debugfs_read_file_bool(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ char buf[3];
+ bool *val = file->private_data;
+
+ if (*val)
+ buf[0] = 'Y';
+ else
+ buf[0] = 'N';
+ buf[1] = '\n';
+ buf[2] = 0x00;
+ return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
+}
+
+static ssize_t debugfs_write_file_bool(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ char buf[32];
+ size_t buf_size;
+ bool bv;
+ bool *val = file->private_data;
+
+ buf_size = min(count, (sizeof(buf)-1));
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+
+ buf[buf_size] = '\0';
+ if (strtobool(buf, &bv) == 0)
+ *val = bv;
+
+ return count;
+}
+#endif /* < 4.3.0 */
+
+static const struct file_operations fops_bool = {
+ .read = debugfs_read_file_bool,
+ .write = debugfs_write_file_bool,
+ .open = simple_open,
+ .llseek = default_llseek,
+};
+
+struct dentry *debugfs_create_bool(const char *name, umode_t mode,
+ struct dentry *parent, bool *value)
+{
+ return debugfs_create_file(name, mode, parent, value, &fops_bool);
+}
+EXPORT_SYMBOL_GPL(debugfs_create_bool);
+#endif /* CONFIG_DEBUG_FS */
+
+/* Calculate expected number of TX descriptors */
+int tso_count_descs(struct sk_buff *skb)
+{
+ /* The Marvell Way */
+ return skb_shinfo(skb)->gso_segs * 2 + skb_shinfo(skb)->nr_frags;
+}
+EXPORT_SYMBOL(tso_count_descs);
+
+void tso_build_hdr(struct sk_buff *skb, char *hdr, struct tso_t *tso,
+ int size, bool is_last)
+{
+ struct tcphdr *tcph;
+ int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
+ int mac_hdr_len = skb_network_offset(skb);
+
+ memcpy(hdr, skb->data, hdr_len);
+ if (!tso->ipv6) {
+ struct iphdr *iph = (void *)(hdr + mac_hdr_len);
+
+ iph->id = htons(tso->ip_id);
+ iph->tot_len = htons(size + hdr_len - mac_hdr_len);
+ tso->ip_id++;
+ } else {
+ struct ipv6hdr *iph = (void *)(hdr + mac_hdr_len);
+
+ iph->payload_len = htons(size + tcp_hdrlen(skb));
+ }
+ tcph = (struct tcphdr *)(hdr + skb_transport_offset(skb));
+ put_unaligned_be32(tso->tcp_seq, &tcph->seq);
+
+ if (!is_last) {
+ /* Clear all special flags for not last packet */
+ tcph->psh = 0;
+ tcph->fin = 0;
+ tcph->rst = 0;
+ }
+}
+EXPORT_SYMBOL(tso_build_hdr);
+
+void tso_build_data(struct sk_buff *skb, struct tso_t *tso, int size)
+{
+ tso->tcp_seq += size;
+ tso->size -= size;
+ tso->data += size;
+
+ if ((tso->size == 0) &&
+ (tso->next_frag_idx < skb_shinfo(skb)->nr_frags)) {
+ skb_frag_t *frag = &skb_shinfo(skb)->frags[tso->next_frag_idx];
+
+ /* Move to next segment */
+ tso->size = frag->size;
+ tso->data = page_address(skb_frag_page(frag)) + frag->page_offset;
+ tso->next_frag_idx++;
+ }
+}
+EXPORT_SYMBOL(tso_build_data);
+
+void tso_start(struct sk_buff *skb, struct tso_t *tso)
+{
+ int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
+
+ tso->ip_id = ntohs(ip_hdr(skb)->id);
+ tso->tcp_seq = ntohl(tcp_hdr(skb)->seq);
+ tso->next_frag_idx = 0;
+ tso->ipv6 = vlan_get_protocol(skb) == htons(ETH_P_IPV6);
+
+ /* Build first data */
+ tso->size = skb_headlen(skb) - hdr_len;
+ tso->data = skb->data + hdr_len;
+ if ((tso->size == 0) &&
+ (tso->next_frag_idx < skb_shinfo(skb)->nr_frags)) {
+ skb_frag_t *frag = &skb_shinfo(skb)->frags[tso->next_frag_idx];
+
+ /* Move to next segment */
+ tso->size = frag->size;
+ tso->data = page_address(skb_frag_page(frag)) + frag->page_offset;
+ tso->next_frag_idx++;
+ }
+}
+EXPORT_SYMBOL(tso_start);
diff --git a/compat/backport-4.5.c b/compat/backport-4.5.c
new file mode 100644
index 0000000..69751d5
--- /dev/null
+++ b/compat/backport-4.5.c
@@ -0,0 +1,153 @@
+/*
+ * Copyright(c) 2015 Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * Backport functionality introduced in Linux 4.5.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/leds.h>
+#include <linux/device.h>
+#include <linux/export.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/leds.h>
+#include <linux/phy.h>
+#include <linux/printk.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <asm/uaccess.h>
+
+#if LINUX_VERSION_IS_GEQ(3,19,0)
+int led_set_brightness_sync(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ if (led_cdev->blink_delay_on || led_cdev->blink_delay_off)
+ return -EBUSY;
+
+ led_cdev->brightness = min(value, led_cdev->max_brightness);
+
+ if (led_cdev->flags & LED_SUSPENDED)
+ return 0;
+
+ if (led_cdev->brightness_set_sync)
+ return led_cdev->brightness_set_sync(led_cdev,
+ led_cdev->brightness);
+ return -ENOTSUPP;
+}
+EXPORT_SYMBOL_GPL(led_set_brightness_sync);
+#endif /* >= 3.19 */
+
+#if LINUX_VERSION_IS_GEQ(3,2,0)
+/**
+ * no_seek_end_llseek - llseek implementation for fixed-sized devices
+ * @file: file structure to seek on
+ * @offset: file offset to seek to
+ * @whence: type of seek
+ *
+ */
+loff_t no_seek_end_llseek(struct file *file, loff_t offset, int whence)
+{
+ switch (whence) {
+ case SEEK_SET: case SEEK_CUR:
+#if LINUX_VERSION_IS_GEQ(3,6,0)
+ return generic_file_llseek_size(file, offset, whence,
+ ~0ULL, 0);
+#else
+ return generic_file_llseek_size(file, offset, whence,
+ ~0ULL);
+#endif
+ default:
+ return -EINVAL;
+ }
+}
+EXPORT_SYMBOL_GPL(no_seek_end_llseek);
+#endif /* >= 3.2 */
+
+/**
+ * memdup_user_nul - duplicate memory region from user space and NUL-terminate
+ *
+ * @src: source address in user space
+ * @len: number of bytes to copy
+ *
+ * Returns an ERR_PTR() on failure.
+ */
+void *memdup_user_nul(const void __user *src, size_t len)
+{
+ char *p;
+
+ /*
+ * Always use GFP_KERNEL, since copy_from_user() can sleep and
+ * cause pagefault, which makes it pointless to use GFP_NOFS
+ * or GFP_ATOMIC.
+ */
+ p = kmalloc(len + 1, GFP_KERNEL);
+ if (!p)
+ return ERR_PTR(-ENOMEM);
+
+ if (copy_from_user(p, src, len)) {
+ kfree(p);
+ return ERR_PTR(-EFAULT);
+ }
+ p[len] = '\0';
+
+ return p;
+}
+EXPORT_SYMBOL_GPL(memdup_user_nul);
+
+void phy_attached_info(struct phy_device *phydev)
+{
+ phy_attached_print(phydev, NULL);
+}
+EXPORT_SYMBOL_GPL(phy_attached_info);
+
+#define ATTACHED_FMT "attached PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)"
+void phy_attached_print(struct phy_device *phydev, const char *fmt, ...)
+{
+ if (!fmt) {
+ dev_info(&phydev->dev, ATTACHED_FMT "\n",
+ phydev->drv->name, phydev_name(phydev),
+ phydev->irq);
+ } else {
+ va_list ap;
+
+ dev_info(&phydev->dev, ATTACHED_FMT,
+ phydev->drv->name, phydev_name(phydev),
+ phydev->irq);
+
+ va_start(ap, fmt);
+ vprintk(fmt, ap);
+ va_end(ap);
+ }
+}
+EXPORT_SYMBOL_GPL(phy_attached_print);
+
+static void devm_led_trigger_release(struct device *dev, void *res)
+{
+ led_trigger_unregister(*(struct led_trigger **)res);
+}
+
+int devm_led_trigger_register(struct device *dev,
+ struct led_trigger *trig)
+{
+ struct led_trigger **dr;
+ int rc;
+
+ dr = devres_alloc(devm_led_trigger_release, sizeof(*dr),
+ GFP_KERNEL);
+ if (!dr)
+ return -ENOMEM;
+
+ *dr = trig;
+
+ rc = led_trigger_register(trig);
+ if (rc)
+ devres_free(dr);
+ else
+ devres_add(dev, dr);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(devm_led_trigger_register);
diff --git a/compat/backport-4.6.c b/compat/backport-4.6.c
new file mode 100644
index 0000000..54ff669
--- /dev/null
+++ b/compat/backport-4.6.c
@@ -0,0 +1,77 @@
+/*
+ * Copyright(c) 2016 Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * Backport functionality introduced in Linux 4.6.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/uaccess.h>
+#include <linux/export.h>
+
+/**
+ * kstrtobool - convert common user inputs into boolean values
+ * @s: input string
+ * @res: result
+ *
+ * This routine returns 0 iff the first character is one of 'Yy1Nn0', or
+ * [oO][NnFf] for "on" and "off". Otherwise it will return -EINVAL. Value
+ * pointed to by res is updated upon finding a match.
+ */
+int kstrtobool(const char *s, bool *res)
+{
+ if (!s)
+ return -EINVAL;
+
+ switch (s[0]) {
+ case 'y':
+ case 'Y':
+ case '1':
+ *res = true;
+ return 0;
+ case 'n':
+ case 'N':
+ case '0':
+ *res = false;
+ return 0;
+ case 'o':
+ case 'O':
+ switch (s[1]) {
+ case 'n':
+ case 'N':
+ *res = true;
+ return 0;
+ case 'f':
+ case 'F':
+ *res = false;
+ return 0;
+ default:
+ break;
+ }
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(kstrtobool);
+
+/*
+ * Since "base" would be a nonsense argument, this open-codes the
+ * _from_user helper instead of using the helper macro below.
+ */
+int kstrtobool_from_user(const char __user *s, size_t count, bool *res)
+{
+ /* Longest string needed to differentiate, newline, terminator */
+ char buf[4];
+
+ count = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, s, count))
+ return -EFAULT;
+ buf[count] = '\0';
+ return kstrtobool(buf, res);
+}
+EXPORT_SYMBOL_GPL(kstrtobool_from_user);
diff --git a/compat/backport-4.7.c b/compat/backport-4.7.c
new file mode 100644
index 0000000..b064707
--- /dev/null
+++ b/compat/backport-4.7.c
@@ -0,0 +1,182 @@
+/*
+ * Copyright(c) 2016 Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * Backport functionality introduced in Linux 4.7.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/export.h>
+#include <linux/list.h>
+#include <linux/rcupdate.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/skbuff.h>
+#include <net/netlink.h>
+
+/**
+ * __nla_reserve_64bit - reserve room for attribute on the skb and align it
+ * @skb: socket buffer to reserve room on
+ * @attrtype: attribute type
+ * @attrlen: length of attribute payload
+ *
+ * Adds a netlink attribute header to a socket buffer and reserves
+ * room for the payload but does not copy it. It also ensure that this
+ * attribute will be 64-bit aign.
+ *
+ * The caller is responsible to ensure that the skb provides enough
+ * tailroom for the attribute header and payload.
+ */
+struct nlattr *__nla_reserve_64bit(struct sk_buff *skb, int attrtype,
+ int attrlen, int padattr)
+{
+ if (nla_need_padding_for_64bit(skb))
+ nla_align_64bit(skb, padattr);
+
+ return __nla_reserve(skb, attrtype, attrlen);
+}
+EXPORT_SYMBOL_GPL(__nla_reserve_64bit);
+
+/**
+ * nla_reserve_64bit - reserve room for attribute on the skb and align it
+ * @skb: socket buffer to reserve room on
+ * @attrtype: attribute type
+ * @attrlen: length of attribute payload
+ *
+ * Adds a netlink attribute header to a socket buffer and reserves
+ * room for the payload but does not copy it. It also ensure that this
+ * attribute will be 64-bit aign.
+ *
+ * Returns NULL if the tailroom of the skb is insufficient to store
+ * the attribute header and payload.
+ */
+struct nlattr *nla_reserve_64bit(struct sk_buff *skb, int attrtype, int attrlen,
+ int padattr)
+{
+ size_t len;
+
+ if (nla_need_padding_for_64bit(skb))
+ len = nla_total_size_64bit(attrlen);
+ else
+ len = nla_total_size(attrlen);
+ if (unlikely(skb_tailroom(skb) < len))
+ return NULL;
+
+ return __nla_reserve_64bit(skb, attrtype, attrlen, padattr);
+}
+EXPORT_SYMBOL_GPL(nla_reserve_64bit);
+
+/**
+ * __nla_put_64bit - Add a netlink attribute to a socket buffer and align it
+ * @skb: socket buffer to add attribute to
+ * @attrtype: attribute type
+ * @attrlen: length of attribute payload
+ * @data: head of attribute payload
+ *
+ * The caller is responsible to ensure that the skb provides enough
+ * tailroom for the attribute header and payload.
+ */
+void __nla_put_64bit(struct sk_buff *skb, int attrtype, int attrlen,
+ const void *data, int padattr)
+{
+ struct nlattr *nla;
+
+ nla = __nla_reserve_64bit(skb, attrtype, attrlen, padattr);
+ memcpy(nla_data(nla), data, attrlen);
+}
+EXPORT_SYMBOL_GPL(__nla_put_64bit);
+
+/**
+ * nla_put_64bit - Add a netlink attribute to a socket buffer and align it
+ * @skb: socket buffer to add attribute to
+ * @attrtype: attribute type
+ * @attrtype: attribute type
+ * @attrlen: length of attribute payload
+ * @data: head of attribute payload
+ *
+ * Returns -EMSGSIZE if the tailroom of the skb is insufficient to store
+ * the attribute header and payload.
+ */
+int nla_put_64bit(struct sk_buff *skb, int attrtype, int attrlen,
+ const void *data, int padattr)
+{
+ size_t len;
+
+ if (nla_need_padding_for_64bit(skb))
+ len = nla_total_size_64bit(attrlen);
+ else
+ len = nla_total_size(attrlen);
+ if (unlikely(skb_tailroom(skb) < len))
+ return -EMSGSIZE;
+
+ __nla_put_64bit(skb, attrtype, attrlen, data, padattr);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(nla_put_64bit);
+
+/*
+ * Below 3.18 or if the kernel has devcoredump disabled, we copied the
+ * entire devcoredump, so no need to define these functions.
+ */
+#if LINUX_VERSION_IS_GEQ(3,18,0) && \
+ !defined(CPTCFG_BPAUTO_BUILD_WANT_DEV_COREDUMP)
+#include <linux/devcoredump.h>
+#include <linux/scatterlist.h>
+
+static void devcd_free_sgtable(void *data)
+{
+ struct scatterlist *table = data;
+ int i;
+ struct page *page;
+ struct scatterlist *iter;
+ struct scatterlist *delete_iter;
+
+ /* free pages */
+ iter = table;
+ for_each_sg(table, iter, sg_nents(table), i) {
+ page = sg_page(iter);
+ if (page)
+ __free_page(page);
+ }
+
+ /* then free all chained tables */
+ iter = table;
+ delete_iter = table; /* always points on a head of a table */
+ while (!sg_is_last(iter)) {
+ iter++;
+ if (sg_is_chain(iter)) {
+ iter = sg_chain_ptr(iter);
+ kfree(delete_iter);
+ delete_iter = iter;
+ }
+ }
+
+ /* free the last table */
+ kfree(delete_iter);
+}
+
+static ssize_t devcd_read_from_sgtable(char *buffer, loff_t offset,
+ size_t buf_len, void *data,
+ size_t data_len)
+{
+ struct scatterlist *table = data;
+
+ if (offset > data_len)
+ return -EINVAL;
+
+ if (offset + buf_len > data_len)
+ buf_len = data_len - offset;
+ return sg_pcopy_to_buffer(table, sg_nents(table), buffer, buf_len,
+ offset);
+}
+
+void dev_coredumpsg(struct device *dev, struct scatterlist *table,
+ size_t datalen, gfp_t gfp)
+{
+ dev_coredumpm(dev, THIS_MODULE, table, datalen, gfp,
+ devcd_read_from_sgtable, devcd_free_sgtable);
+}
+EXPORT_SYMBOL_GPL(dev_coredumpsg);
+#endif /* >= 3.18.0 */
diff --git a/compat/backport-4.8.c b/compat/backport-4.8.c
new file mode 100644
index 0000000..7d05644
--- /dev/null
+++ b/compat/backport-4.8.c
@@ -0,0 +1,146 @@
+/*
+ * Copyright(c) 2017 Intel Deutschland GmbH
+ *
+ * Backport functionality introduced in Linux 4.8.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/usb.h>
+#include <linux/usb/cdc.h>
+
+int cdc_parse_cdc_header(struct usb_cdc_parsed_header *hdr,
+ struct usb_interface *intf,
+ u8 *buffer, int buflen)
+{
+ /* duplicates are ignored */
+ struct usb_cdc_union_desc *union_header = NULL;
+
+ /* duplicates are not tolerated */
+ struct usb_cdc_header_desc *header = NULL;
+ struct usb_cdc_ether_desc *ether = NULL;
+ struct usb_cdc_mdlm_detail_desc *detail = NULL;
+ struct usb_cdc_mdlm_desc *desc = NULL;
+
+ unsigned int elength;
+ int cnt = 0;
+
+ memset(hdr, 0x00, sizeof(struct usb_cdc_parsed_header));
+ hdr->phonet_magic_present = false;
+ while (buflen > 0) {
+ elength = buffer[0];
+ if (!elength) {
+ dev_err(&intf->dev, "skipping garbage byte\n");
+ elength = 1;
+ goto next_desc;
+ }
+ if (buffer[1] != USB_DT_CS_INTERFACE) {
+ dev_err(&intf->dev, "skipping garbage\n");
+ goto next_desc;
+ }
+
+ switch (buffer[2]) {
+ case USB_CDC_UNION_TYPE: /* we've found it */
+ if (elength < sizeof(struct usb_cdc_union_desc))
+ goto next_desc;
+ if (union_header) {
+ dev_err(&intf->dev, "More than one union descriptor, skipping ...\n");
+ goto next_desc;
+ }
+ union_header = (struct usb_cdc_union_desc *)buffer;
+ break;
+ case USB_CDC_COUNTRY_TYPE:
+ if (elength < sizeof(struct usb_cdc_country_functional_desc))
+ goto next_desc;
+ hdr->usb_cdc_country_functional_desc =
+ (struct usb_cdc_country_functional_desc *)buffer;
+ break;
+ case USB_CDC_HEADER_TYPE:
+ if (elength != sizeof(struct usb_cdc_header_desc))
+ goto next_desc;
+ if (header)
+ return -EINVAL;
+ header = (struct usb_cdc_header_desc *)buffer;
+ break;
+ case USB_CDC_ACM_TYPE:
+ if (elength < sizeof(struct usb_cdc_acm_descriptor))
+ goto next_desc;
+ hdr->usb_cdc_acm_descriptor =
+ (struct usb_cdc_acm_descriptor *)buffer;
+ break;
+ case USB_CDC_ETHERNET_TYPE:
+ if (elength != sizeof(struct usb_cdc_ether_desc))
+ goto next_desc;
+ if (ether)
+ return -EINVAL;
+ ether = (struct usb_cdc_ether_desc *)buffer;
+ break;
+ case USB_CDC_CALL_MANAGEMENT_TYPE:
+ if (elength < sizeof(struct usb_cdc_call_mgmt_descriptor))
+ goto next_desc;
+ hdr->usb_cdc_call_mgmt_descriptor =
+ (struct usb_cdc_call_mgmt_descriptor *)buffer;
+ break;
+ case USB_CDC_DMM_TYPE:
+ if (elength < sizeof(struct usb_cdc_dmm_desc))
+ goto next_desc;
+ hdr->usb_cdc_dmm_desc =
+ (struct usb_cdc_dmm_desc *)buffer;
+ break;
+ case USB_CDC_MDLM_TYPE:
+ if (elength < sizeof(struct usb_cdc_mdlm_desc *))
+ goto next_desc;
+ if (desc)
+ return -EINVAL;
+ desc = (struct usb_cdc_mdlm_desc *)buffer;
+ break;
+ case USB_CDC_MDLM_DETAIL_TYPE:
+ if (elength < sizeof(struct usb_cdc_mdlm_detail_desc *))
+ goto next_desc;
+ if (detail)
+ return -EINVAL;
+ detail = (struct usb_cdc_mdlm_detail_desc *)buffer;
+ break;
+ case USB_CDC_NCM_TYPE:
+ if (elength < sizeof(struct usb_cdc_ncm_desc))
+ goto next_desc;
+ hdr->usb_cdc_ncm_desc = (struct usb_cdc_ncm_desc *)buffer;
+ break;
+ case USB_CDC_MBIM_TYPE:
+ if (elength < sizeof(struct usb_cdc_mbim_desc))
+ goto next_desc;
+
+ hdr->usb_cdc_mbim_desc = (struct usb_cdc_mbim_desc *)buffer;
+ break;
+ case USB_CDC_MBIM_EXTENDED_TYPE:
+ if (elength < sizeof(struct usb_cdc_mbim_extended_desc))
+ break;
+ hdr->usb_cdc_mbim_extended_desc =
+ (struct usb_cdc_mbim_extended_desc *)buffer;
+ break;
+ case CDC_PHONET_MAGIC_NUMBER:
+ hdr->phonet_magic_present = true;
+ break;
+ default:
+ /*
+ * there are LOTS more CDC descriptors that
+ * could legitimately be found here.
+ */
+ dev_dbg(&intf->dev, "Ignoring descriptor: type %02x, length %ud\n",
+ buffer[2], elength);
+ goto next_desc;
+ }
+ cnt++;
+next_desc:
+ buflen -= elength;
+ buffer += elength;
+ }
+ hdr->usb_cdc_union_desc = union_header;
+ hdr->usb_cdc_header_desc = header;
+ hdr->usb_cdc_mdlm_detail_desc = detail;
+ hdr->usb_cdc_mdlm_desc = desc;
+ hdr->usb_cdc_ether_desc = ether;
+ return cnt;
+}
+EXPORT_SYMBOL_GPL(cdc_parse_cdc_header);
diff --git a/compat/backports.h b/compat/backports.h
new file mode 100644
index 0000000..ccc8972
--- /dev/null
+++ b/compat/backports.h
@@ -0,0 +1,26 @@
+#ifndef LINUX_BACKPORTS_PRIVATE_H
+#define LINUX_BACKPORTS_PRIVATE_H
+
+#include <linux/version.h>
+
+#ifdef CPTCFG_BPAUTO_BUILD_CRYPTO_CCM
+int crypto_ccm_module_init(void);
+void crypto_ccm_module_exit(void);
+#else
+static inline int crypto_ccm_module_init(void)
+{ return 0; }
+static inline void crypto_ccm_module_exit(void)
+{}
+#endif
+
+#ifdef CPTCFG_BPAUTO_BUILD_WANT_DEV_COREDUMP
+int devcoredump_init(void);
+void devcoredump_exit(void);
+#else
+static inline int devcoredump_init(void)
+{ return 0; }
+static inline void devcoredump_exit(void)
+{}
+#endif
+
+#endif /* LINUX_BACKPORTS_PRIVATE_H */
diff --git a/compat/compat-3.0.c b/compat/compat-3.0.c
new file mode 100644
index 0000000..1bed6a6
--- /dev/null
+++ b/compat/compat-3.0.c
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2011 Hauke Mehrtens <hauke@hauke-m.de>
+ * Copyright 2011 Alexey Dobriyan <adobriyan@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Backport functionality introduced in Linux 3.0.
+ */
+
+#include <linux/compat.h>
+#include <linux/if_ether.h>
+
+int mac_pton(const char *s, u8 *mac)
+{
+ int i;
+
+ /* XX:XX:XX:XX:XX:XX */
+ if (strlen(s) < 3 * ETH_ALEN - 1)
+ return 0;
+
+ /* Don't dirty result unless string is valid MAC. */
+ for (i = 0; i < ETH_ALEN; i++) {
+ if (!strchr("0123456789abcdefABCDEF", s[i * 3]))
+ return 0;
+ if (!strchr("0123456789abcdefABCDEF", s[i * 3 + 1]))
+ return 0;
+ if (i != ETH_ALEN - 1 && s[i * 3 + 2] != ':')
+ return 0;
+ }
+ for (i = 0; i < ETH_ALEN; i++) {
+ mac[i] = (hex_to_bin(s[i * 3]) << 4) | hex_to_bin(s[i * 3 + 1]);
+ }
+ return 1;
+}
+EXPORT_SYMBOL_GPL(mac_pton);
+
+#define kstrto_from_user(f, g, type) \
+int f(const char __user *s, size_t count, unsigned int base, type *res) \
+{ \
+ /* sign, base 2 representation, newline, terminator */ \
+ char buf[1 + sizeof(type) * 8 + 1 + 1]; \
+ \
+ count = min(count, sizeof(buf) - 1); \
+ if (copy_from_user(buf, s, count)) \
+ return -EFAULT; \
+ buf[count] = '\0'; \
+ return g(buf, base, res); \
+} \
+EXPORT_SYMBOL_GPL(f)
+
+kstrto_from_user(kstrtoull_from_user, kstrtoull, unsigned long long);
+kstrto_from_user(kstrtoll_from_user, kstrtoll, long long);
+kstrto_from_user(kstrtoul_from_user, kstrtoul, unsigned long);
+kstrto_from_user(kstrtol_from_user, kstrtol, long);
+kstrto_from_user(kstrtouint_from_user, kstrtouint, unsigned int);
+kstrto_from_user(kstrtoint_from_user, kstrtoint, int);
+kstrto_from_user(kstrtou16_from_user, kstrtou16, u16);
+kstrto_from_user(kstrtos16_from_user, kstrtos16, s16);
+kstrto_from_user(kstrtou8_from_user, kstrtou8, u8);
+kstrto_from_user(kstrtos8_from_user, kstrtos8, s8);
+
+/**
+ * strtobool - convert common user inputs into boolean values
+ * @s: input string
+ * @res: result
+ *
+ * This routine returns 0 iff the first character is one of 'Yy1Nn0'.
+ * Otherwise it will return -EINVAL. Value pointed to by res is
+ * updated upon finding a match.
+ */
+int strtobool(const char *s, bool *res)
+{
+ switch (s[0]) {
+ case 'y':
+ case 'Y':
+ case '1':
+ *res = true;
+ break;
+ case 'n':
+ case 'N':
+ case '0':
+ *res = false;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(strtobool);
diff --git a/compat/compat-3.1.c b/compat/compat-3.1.c
new file mode 100644
index 0000000..2618780
--- /dev/null
+++ b/compat/compat-3.1.c
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2012 Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Backport functionality introduced in Linux 3.1.
+ */
+
+#include <linux/idr.h>
+#include <linux/cpufreq.h>
+#include <linux/of.h>
+
+static DEFINE_SPINLOCK(compat_simple_ida_lock);
+
+/**
+ * ida_simple_get - get a new id.
+ * @ida: the (initialized) ida.
+ * @start: the minimum id (inclusive, < 0x8000000)
+ * @end: the maximum id (exclusive, < 0x8000000 or 0)
+ * @gfp_mask: memory allocation flags
+ *
+ * Allocates an id in the range start <= id < end, or returns -ENOSPC.
+ * On memory allocation failure, returns -ENOMEM.
+ *
+ * Use ida_simple_remove() to get rid of an id.
+ */
+int ida_simple_get(struct ida *ida, unsigned int start, unsigned int end,
+ gfp_t gfp_mask)
+{
+ int ret, id;
+ unsigned int max;
+ unsigned long flags;
+
+ BUG_ON((int)start < 0);
+ BUG_ON((int)end < 0);
+
+ if (end == 0)
+ max = 0x80000000;
+ else {
+ BUG_ON(end < start);
+ max = end - 1;
+ }
+
+again:
+ if (!ida_pre_get(ida, gfp_mask))
+ return -ENOMEM;
+
+ spin_lock_irqsave(&compat_simple_ida_lock, flags);
+ ret = ida_get_new_above(ida, start, &id);
+ if (!ret) {
+ if (id > max) {
+ ida_remove(ida, id);
+ ret = -ENOSPC;
+ } else {
+ ret = id;
+ }
+ }
+ spin_unlock_irqrestore(&compat_simple_ida_lock, flags);
+
+ if (unlikely(ret == -EAGAIN))
+ goto again;
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(ida_simple_get);
+
+/**
+ * ida_simple_remove - remove an allocated id.
+ * @ida: the (initialized) ida.
+ * @id: the id returned by ida_simple_get.
+ */
+void ida_simple_remove(struct ida *ida, unsigned int id)
+{
+ unsigned long flags;
+
+ BUG_ON((int)id < 0);
+ spin_lock_irqsave(&compat_simple_ida_lock, flags);
+ ida_remove(ida, id);
+ spin_unlock_irqrestore(&compat_simple_ida_lock, flags);
+}
+EXPORT_SYMBOL_GPL(ida_simple_remove);
+/* source lib/idr.c */
+
+#ifdef CONFIG_OF
+/**
+ * of_property_read_u32_array - Find and read an array of 32 bit integers
+ * from a property.
+ *
+ * @np: device node from which the property value is to be read.
+ * @propname: name of the property to be searched.
+ * @out_values: pointer to return value, modified only if return value is 0.
+ * @sz: number of array elements to read
+ *
+ * Search for a property in a device node and read 32-bit value(s) from
+ * it. Returns 0 on success, -EINVAL if the property does not exist,
+ * -ENODATA if property does not have a value, and -EOVERFLOW if the
+ * property data isn't large enough.
+ *
+ * The out_values is modified only if a valid u32 value can be decoded.
+ */
+int of_property_read_u32_array(const struct device_node *np,
+ const char *propname, u32 *out_values,
+ size_t sz)
+{
+ const __be32 *val = of_find_property_value_of_size(np, propname,
+ (sz * sizeof(*out_values)));
+
+ if (IS_ERR(val))
+ return PTR_ERR(val);
+
+ while (sz--)
+ *out_values++ = be32_to_cpup(val++);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(of_property_read_u32_array);
+#endif
diff --git a/compat/compat-3.3.c b/compat/compat-3.3.c
new file mode 100644
index 0000000..c064f09
--- /dev/null
+++ b/compat/compat-3.3.c
@@ -0,0 +1,239 @@
+/*
+ * Copyright 2012 Luis R. Rodriguez <mcgrof@frijolero.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Backport functionality introduced in Linux 3.3.
+ */
+
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/skbuff.h>
+#include <linux/module.h>
+#include <linux/workqueue.h>
+#include <net/dst.h>
+#include <net/xfrm.h>
+
+static void __copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
+{
+ new->tstamp = old->tstamp;
+ new->dev = old->dev;
+ new->transport_header = old->transport_header;
+ new->network_header = old->network_header;
+ new->mac_header = old->mac_header;
+ skb_dst_copy(new, old);
+ new->rxhash = old->rxhash;
+#if LINUX_VERSION_IS_GEQ(3,1,0)
+ new->ooo_okay = old->ooo_okay;
+#endif
+#if LINUX_VERSION_IS_GEQ(3,2,0)
+ new->l4_rxhash = old->l4_rxhash;
+#endif
+#ifdef CONFIG_XFRM
+ new->sp = secpath_get(old->sp);
+#endif
+ memcpy(new->cb, old->cb, sizeof(old->cb));
+ new->csum = old->csum;
+ new->local_df = old->local_df;
+ new->pkt_type = old->pkt_type;
+ new->ip_summed = old->ip_summed;
+ skb_copy_queue_mapping(new, old);
+ new->priority = old->priority;
+#if IS_ENABLED(CONFIG_IP_VS)
+ new->ipvs_property = old->ipvs_property;
+#endif
+ new->protocol = old->protocol;
+ new->mark = old->mark;
+ new->skb_iif = old->skb_iif;
+ __nf_copy(new, old);
+#if IS_ENABLED(CONFIG_NETFILTER_XT_TARGET_TRACE)
+ new->nf_trace = old->nf_trace;
+#endif
+#ifdef CONFIG_NET_SCHED
+ new->tc_index = old->tc_index;
+#ifdef CONFIG_NET_CLS_ACT
+ new->tc_verd = old->tc_verd;
+#endif
+#endif
+ new->vlan_tci = old->vlan_tci;
+
+ skb_copy_secmark(new, old);
+}
+
+static void copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
+{
+#ifndef NET_SKBUFF_DATA_USES_OFFSET
+ /*
+ * Shift between the two data areas in bytes
+ */
+ unsigned long offset = new->data - old->data;
+#endif
+
+ __copy_skb_header(new, old);
+
+#ifndef NET_SKBUFF_DATA_USES_OFFSET
+ /* {transport,network,mac}_header are relative to skb->head */
+ new->transport_header += offset;
+ new->network_header += offset;
+ if (skb_mac_header_was_set(new))
+ new->mac_header += offset;
+#endif
+ skb_shinfo(new)->gso_size = skb_shinfo(old)->gso_size;
+ skb_shinfo(new)->gso_segs = skb_shinfo(old)->gso_segs;
+ skb_shinfo(new)->gso_type = skb_shinfo(old)->gso_type;
+}
+
+static void skb_clone_fraglist(struct sk_buff *skb)
+{
+ struct sk_buff *list;
+
+ skb_walk_frags(skb, list)
+ skb_get(list);
+}
+
+
+/**
+ * __pskb_copy - create copy of an sk_buff with private head.
+ * @skb: buffer to copy
+ * @headroom: headroom of new skb
+ * @gfp_mask: allocation priority
+ *
+ * Make a copy of both an &sk_buff and part of its data, located
+ * in header. Fragmented data remain shared. This is used when
+ * the caller wishes to modify only header of &sk_buff and needs
+ * private copy of the header to alter. Returns %NULL on failure
+ * or the pointer to the buffer on success.
+ * The returned buffer has a reference count of 1.
+ */
+
+struct sk_buff *__pskb_copy(struct sk_buff *skb, int headroom, gfp_t gfp_mask)
+{
+ unsigned int size = skb_headlen(skb) + headroom;
+ struct sk_buff *n = alloc_skb(size, gfp_mask);
+
+ if (!n)
+ goto out;
+
+ /* Set the data pointer */
+ skb_reserve(n, headroom);
+ /* Set the tail pointer and length */
+ skb_put(n, skb_headlen(skb));
+ /* Copy the bytes */
+ skb_copy_from_linear_data(skb, n->data, n->len);
+
+ n->truesize += skb->data_len;
+ n->data_len = skb->data_len;
+ n->len = skb->len;
+
+ if (skb_shinfo(skb)->nr_frags) {
+ int i;
+
+/*
+ * SKBTX_DEV_ZEROCOPY was added on 3.1 as well but requires ubuf
+ * stuff added to the skb which we do not have
+ */
+#if 0
+ if (skb_shinfo(skb)->tx_flags & SKBTX_DEV_ZEROCOPY) {
+ if (skb_copy_ubufs(skb, gfp_mask)) {
+ kfree_skb(n);
+ n = NULL;
+ goto out;
+ }
+ }
+#endif
+ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+ skb_shinfo(n)->frags[i] = skb_shinfo(skb)->frags[i];
+#if LINUX_VERSION_IS_GEQ(3,2,0)
+ skb_frag_ref(skb, i);
+#else
+ get_page(skb_shinfo(skb)->frags[i].page);
+#endif
+ }
+ skb_shinfo(n)->nr_frags = i;
+ }
+
+ if (skb_has_frag_list(skb)) {
+ skb_shinfo(n)->frag_list = skb_shinfo(skb)->frag_list;
+ skb_clone_fraglist(n);
+ }
+
+ copy_skb_header(n, skb);
+out:
+ return n;
+}
+EXPORT_SYMBOL_GPL(__pskb_copy);
+
+static DEFINE_SPINLOCK(wq_name_lock);
+static LIST_HEAD(wq_name_list);
+
+struct wq_name {
+ struct list_head list;
+ struct workqueue_struct *wq;
+ char name[24];
+};
+
+struct workqueue_struct *
+backport_alloc_workqueue(const char *fmt, unsigned int flags,
+ int max_active, struct lock_class_key *key,
+ const char *lock_name, ...)
+{
+ struct workqueue_struct *wq;
+ struct wq_name *n = kzalloc(sizeof(*n), GFP_KERNEL);
+ va_list args;
+
+ if (!n)
+ return NULL;
+
+ va_start(args, lock_name);
+ vsnprintf(n->name, sizeof(n->name), fmt, args);
+ va_end(args);
+
+ wq = __alloc_workqueue_key(n->name, flags, max_active, key, lock_name);
+ if (!wq) {
+ kfree(n);
+ return NULL;
+ }
+
+ n->wq = wq;
+ spin_lock(&wq_name_lock);
+ list_add(&n->list, &wq_name_list);
+ spin_unlock(&wq_name_lock);
+
+ return wq;
+}
+EXPORT_SYMBOL_GPL(backport_alloc_workqueue);
+
+void backport_destroy_workqueue(struct workqueue_struct *wq)
+{
+ struct wq_name *n, *tmp;
+
+ /* call original */
+#undef destroy_workqueue
+ destroy_workqueue(wq);
+
+ spin_lock(&wq_name_lock);
+ list_for_each_entry_safe(n, tmp, &wq_name_list, list) {
+ if (n->wq == wq) {
+ list_del(&n->list);
+ kfree(n);
+ break;
+ }
+ }
+ spin_unlock(&wq_name_lock);
+}
+EXPORT_SYMBOL_GPL(backport_destroy_workqueue);
+
+void genl_notify(struct sk_buff *skb, struct net *net, u32 pid, u32 group,
+ struct nlmsghdr *nlh, gfp_t flags)
+{
+ struct sock *sk = net->genl_sock;
+ int report = 0;
+
+ if (nlh)
+ report = nlmsg_report(nlh);
+
+ nlmsg_notify(sk, skb, pid, group, report, flags);
+}
+EXPORT_SYMBOL_GPL(genl_notify);
diff --git a/compat/compat-3.4.c b/compat/compat-3.4.c
new file mode 100644
index 0000000..5275372
--- /dev/null
+++ b/compat/compat-3.4.c
@@ -0,0 +1,201 @@
+/*
+ * Copyright 2012 Luis R. Rodriguez <mcgrof@frijolero.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Backport functionality introduced in Linux 3.4.
+ */
+
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/wait.h>
+#include <linux/compat.h>
+#include <asm/uaccess.h>
+
+#if LINUX_VERSION_IS_GEQ(3,2,0)
+#include <linux/regmap.h>
+#include <linux/i2c.h>
+#include <linux/spi/spi.h>
+#endif /* LINUX_VERSION_IS_GEQ(3,2,0) */
+
+#if LINUX_VERSION_IS_GEQ(3,2,0)
+
+#if defined(CONFIG_REGMAP)
+static void devm_regmap_release(struct device *dev, void *res)
+{
+ regmap_exit(*(struct regmap **)res);
+}
+
+#if defined(CONFIG_REGMAP_I2C)
+static int regmap_i2c_write(
+ struct device *dev,
+ const void *data,
+ size_t count)
+{
+ struct i2c_client *i2c = to_i2c_client(dev);
+ int ret;
+
+ ret = i2c_master_send(i2c, data, count);
+ if (ret == count)
+ return 0;
+ else if (ret < 0)
+ return ret;
+ else
+ return -EIO;
+}
+
+static int regmap_i2c_gather_write(
+ struct device *dev,
+ const void *reg, size_t reg_size,
+ const void *val, size_t val_size)
+{
+ struct i2c_client *i2c = to_i2c_client(dev);
+ struct i2c_msg xfer[2];
+ int ret;
+
+ /* If the I2C controller can't do a gather tell the core, it
+ * will substitute in a linear write for us.
+ */
+ if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_NOSTART))
+ return -ENOTSUPP;
+
+ xfer[0].addr = i2c->addr;
+ xfer[0].flags = 0;
+ xfer[0].len = reg_size;
+ xfer[0].buf = (void *)reg;
+
+ xfer[1].addr = i2c->addr;
+ xfer[1].flags = I2C_M_NOSTART;
+ xfer[1].len = val_size;
+ xfer[1].buf = (void *)val;
+
+ ret = i2c_transfer(i2c->adapter, xfer, 2);
+ if (ret == 2)
+ return 0;
+ if (ret < 0)
+ return ret;
+ else
+ return -EIO;
+}
+
+static int regmap_i2c_read(
+ struct device *dev,
+ const void *reg, size_t reg_size,
+ void *val, size_t val_size)
+{
+ struct i2c_client *i2c = to_i2c_client(dev);
+ struct i2c_msg xfer[2];
+ int ret;
+
+ xfer[0].addr = i2c->addr;
+ xfer[0].flags = 0;
+ xfer[0].len = reg_size;
+ xfer[0].buf = (void *)reg;
+
+ xfer[1].addr = i2c->addr;
+ xfer[1].flags = I2C_M_RD;
+ xfer[1].len = val_size;
+ xfer[1].buf = val;
+
+ ret = i2c_transfer(i2c->adapter, xfer, 2);
+ if (ret == 2)
+ return 0;
+ else if (ret < 0)
+ return ret;
+ else
+ return -EIO;
+}
+
+static struct regmap_bus regmap_i2c = {
+ .write = regmap_i2c_write,
+ .gather_write = regmap_i2c_gather_write,
+ .read = regmap_i2c_read,
+};
+#endif /* defined(CONFIG_REGMAP_I2C) */
+
+/**
+ * devm_regmap_init(): Initialise managed register map
+ *
+ * @dev: Device that will be interacted with
+ * @bus: Bus-specific callbacks to use with device
+ * @bus_context: Data passed to bus-specific callbacks
+ * @config: Configuration for register map
+ *
+ * The return value will be an ERR_PTR() on error or a valid pointer
+ * to a struct regmap. This function should generally not be called
+ * directly, it should be called by bus-specific init functions. The
+ * map will be automatically freed by the device management code.
+ */
+struct regmap *devm_regmap_init(struct device *dev,
+ const struct regmap_bus *bus,
+ const struct regmap_config *config)
+{
+ struct regmap **ptr, *regmap;
+
+ ptr = devres_alloc(devm_regmap_release, sizeof(*ptr), GFP_KERNEL);
+ if (!ptr)
+ return ERR_PTR(-ENOMEM);
+
+ regmap = regmap_init(dev,
+ bus,
+ config);
+ if (!IS_ERR(regmap)) {
+ *ptr = regmap;
+ devres_add(dev, ptr);
+ } else {
+ devres_free(ptr);
+ }
+
+ return regmap;
+}
+EXPORT_SYMBOL_GPL(devm_regmap_init);
+
+#if defined(CONFIG_REGMAP_I2C)
+/**
+ * devm_regmap_init_i2c(): Initialise managed register map
+ *
+ * @i2c: Device that will be interacted with
+ * @config: Configuration for register map
+ *
+ * The return value will be an ERR_PTR() on error or a valid pointer
+ * to a struct regmap. The regmap will be automatically freed by the
+ * device management code.
+ */
+struct regmap *devm_regmap_init_i2c(struct i2c_client *i2c,
+ const struct regmap_config *config)
+{
+ return devm_regmap_init(&i2c->dev, ®map_i2c, config);
+}
+EXPORT_SYMBOL_GPL(devm_regmap_init_i2c);
+#endif /* defined(CONFIG_REGMAP_I2C) */
+
+#endif /* defined(CONFIG_REGMAP) */
+#endif /* LINUX_VERSION_IS_GEQ(3,2,0) */
+
+int simple_open(struct inode *inode, struct file *file)
+{
+ if (inode->i_private)
+ file->private_data = inode->i_private;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(simple_open);
+
+#ifdef CONFIG_COMPAT
+static int __compat_put_timespec(const struct timespec *ts, struct compat_timespec __user *cts)
+{
+ return (!access_ok(VERIFY_WRITE, cts, sizeof(*cts)) ||
+ __put_user(ts->tv_sec, &cts->tv_sec) ||
+ __put_user(ts->tv_nsec, &cts->tv_nsec)) ? -EFAULT : 0;
+}
+
+int compat_put_timespec(const struct timespec *ts, void __user *uts)
+{
+ if (COMPAT_USE_64BIT_TIME)
+ return copy_to_user(uts, ts, sizeof *ts) ? -EFAULT : 0;
+ else
+ return __compat_put_timespec(ts, uts);
+}
+EXPORT_SYMBOL_GPL(compat_put_timespec);
+#endif
diff --git a/compat/compat-3.5.c b/compat/compat-3.5.c
new file mode 100644
index 0000000..de31228
--- /dev/null
+++ b/compat/compat-3.5.c
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2012-2013 Luis R. Rodriguez <mcgrof@do-not-panic.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Backport functionality introduced in Linux 3.5.
+ */
+
+#include <linux/module.h>
+#include <linux/highuid.h>
+#include <linux/ktime.h>
+#include <linux/hrtimer.h>
+#include <linux/gpio.h>
+#include <linux/ptp_clock_kernel.h>
+
+#if LINUX_VERSION_IS_GEQ(3,2,0)
+#include <linux/device.h>
+
+/**
+ * devres_release - Find a device resource and destroy it, calling release
+ * @dev: Device to find resource from
+ * @release: Look for resources associated with this release function
+ * @match: Match function (optional)
+ * @match_data: Data for the match function
+ *
+ * Find the latest devres of @dev associated with @release and for
+ * which @match returns 1. If @match is NULL, it's considered to
+ * match all. If found, the resource is removed atomically, the
+ * release function called and the resource freed.
+ *
+ * RETURNS:
+ * 0 if devres is found and freed, -ENOENT if not found.
+ */
+int devres_release(struct device *dev, dr_release_t release,
+ dr_match_t match, void *match_data)
+{
+ void *res;
+
+ res = devres_remove(dev, release, match, match_data);
+ if (unlikely(!res))
+ return -ENOENT;
+
+ (*release)(dev, res);
+ devres_free(res);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(devres_release);
+#endif /* LINUX_VERSION_IS_GEQ(3,2,0) */
+
+/*
+ * Commit 7a4e7408c5cadb240e068a662251754a562355e3
+ * exported overflowuid and overflowgid for all
+ * kernel configurations, prior to that we only
+ * had it exported when CONFIG_UID16 was enabled.
+ * We are technically redefining it here but
+ * nothing seems to be changing it, except
+ * kernel/ code does epose it via sysctl and
+ * proc... if required later we can add that here.
+ */
+#ifndef CONFIG_UID16
+int overflowuid = DEFAULT_OVERFLOWUID;
+int overflowgid = DEFAULT_OVERFLOWGID;
+
+EXPORT_SYMBOL_GPL(overflowuid);
+EXPORT_SYMBOL_GPL(overflowgid);
+#endif
+
+#if IS_ENABLED(CONFIG_PTP_1588_CLOCK)
+int ptp_clock_index(struct ptp_clock *ptp)
+{
+ return ptp->index;
+}
+EXPORT_SYMBOL(ptp_clock_index);
+#endif /* CONFIG_PTP_1588_CLOCK */
+
+#ifdef CONFIG_GPIOLIB
+static void devm_gpio_release(struct device *dev, void *res)
+{
+ unsigned *gpio = res;
+
+ gpio_free(*gpio);
+}
+
+/**
+ * devm_gpio_request - request a GPIO for a managed device
+ * @dev: device to request the GPIO for
+ * @gpio: GPIO to allocate
+ * @label: the name of the requested GPIO
+ *
+ * Except for the extra @dev argument, this function takes the
+ * same arguments and performs the same function as
+ * gpio_request(). GPIOs requested with this function will be
+ * automatically freed on driver detach.
+ *
+ * If an GPIO allocated with this function needs to be freed
+ * separately, devm_gpio_free() must be used.
+ */
+
+int devm_gpio_request(struct device *dev, unsigned gpio, const char *label)
+{
+ unsigned *dr;
+ int rc;
+
+ dr = devres_alloc(devm_gpio_release, sizeof(unsigned), GFP_KERNEL);
+ if (!dr)
+ return -ENOMEM;
+
+ rc = gpio_request(gpio, label);
+ if (rc) {
+ devres_free(dr);
+ return rc;
+ }
+
+ *dr = gpio;
+ devres_add(dev, dr);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(devm_gpio_request);
+
+/**
+ * devm_gpio_request_one - request a single GPIO with initial setup
+ * @dev: device to request for
+ * @gpio: the GPIO number
+ * @flags: GPIO configuration as specified by GPIOF_*
+ * @label: a literal description string of this GPIO
+ */
+int devm_gpio_request_one(struct device *dev, unsigned gpio,
+ unsigned long flags, const char *label)
+{
+ unsigned *dr;
+ int rc;
+
+ dr = devres_alloc(devm_gpio_release, sizeof(unsigned), GFP_KERNEL);
+ if (!dr)
+ return -ENOMEM;
+
+ rc = gpio_request_one(gpio, flags, label);
+ if (rc) {
+ devres_free(dr);
+ return rc;
+ }
+
+ *dr = gpio;
+ devres_add(dev, dr);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(devm_gpio_request_one);
+
+static int devm_gpio_match(struct device *dev, void *res, void *data)
+{
+ unsigned *this = res, *gpio = data;
+
+ return *this == *gpio;
+}
+
+void devm_gpio_free(struct device *dev, unsigned int gpio)
+{
+ WARN_ON(devres_destroy(dev, devm_gpio_release, devm_gpio_match,
+ &gpio));
+ gpio_free(gpio);
+}
+EXPORT_SYMBOL_GPL(devm_gpio_free);
+#endif /* CONFIG_GPIOLIB */
diff --git a/compat/compat-3.6.c b/compat/compat-3.6.c
new file mode 100644
index 0000000..ef36485
--- /dev/null
+++ b/compat/compat-3.6.c
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2013 Luis R. Rodriguez <mcgrof@do-not-panic.com>
+ *
+ * Backport compatibility file for Linux for kernels 3.6.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/export.h>
+#include <linux/clk.h>
+
+/* whoopsie ! */
+#ifndef CONFIG_COMMON_CLK
+int clk_enable(struct clk *clk)
+{
+ return 0;
+}
+EXPORT_SYMBOL_GPL(clk_enable);
+
+void clk_disable(struct clk *clk)
+{
+}
+EXPORT_SYMBOL_GPL(clk_disable);
+#endif
diff --git a/compat/compat-3.7.c b/compat/compat-3.7.c
new file mode 100644
index 0000000..a70709c
--- /dev/null
+++ b/compat/compat-3.7.c
@@ -0,0 +1,290 @@
+/*
+ * Copyright 2012 Luis R. Rodriguez <mcgrof@do-not-panic.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Backport functionality introduced in Linux 3.7.
+ */
+
+#include <linux/workqueue.h>
+#include <linux/export.h>
+#include <linux/pci.h>
+#include <linux/pci_regs.h>
+#include <linux/of.h>
+#include <linux/scatterlist.h>
+
+bool mod_delayed_work(struct workqueue_struct *wq, struct delayed_work *dwork,
+ unsigned long delay)
+{
+ cancel_delayed_work(dwork);
+ queue_delayed_work(wq, dwork, delay);
+ return false;
+}
+EXPORT_SYMBOL_GPL(mod_delayed_work);
+
+#ifdef CONFIG_PCI
+/*
+ * Kernels >= 3.7 get their PCI-E Capabilities Register cached
+ * via the pci_dev->pcie_flags_reg so for older kernels we have
+ * no other option but to read this every single time we need
+ * it accessed. If we really cared to improve the efficiency
+ * of this we could try to find an unused u16 varible on the
+ * pci_dev but if we found it we likely would remove it from
+ * the kernel anyway right? Bite me.
+ */
+static inline u16 pcie_flags_reg(struct pci_dev *dev)
+{
+ int pos;
+ u16 reg16;
+
+ pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
+ if (!pos)
+ return 0;
+
+ pci_read_config_word(dev, pos + PCI_EXP_FLAGS, ®16);
+
+ return reg16;
+}
+
+#define pci_pcie_type LINUX_BACKPORT(pci_pcie_type)
+static inline int pci_pcie_type(struct pci_dev *dev)
+{
+ return (pcie_flags_reg(dev) & PCI_EXP_FLAGS_TYPE) >> 4;
+}
+
+#define pcie_cap_version LINUX_BACKPORT(pcie_cap_version)
+static inline int pcie_cap_version(struct pci_dev *dev)
+{
+ return pcie_flags_reg(dev) & PCI_EXP_FLAGS_VERS;
+}
+
+static inline bool pcie_cap_has_lnkctl(struct pci_dev *dev)
+{
+ int type = pci_pcie_type(dev);
+
+ return pcie_cap_version(dev) > 1 ||
+ type == PCI_EXP_TYPE_ROOT_PORT ||
+ type == PCI_EXP_TYPE_ENDPOINT ||
+ type == PCI_EXP_TYPE_LEG_END;
+}
+
+static inline bool pcie_cap_has_sltctl(struct pci_dev *dev)
+{
+ int type = pci_pcie_type(dev);
+
+ return pcie_cap_version(dev) > 1 ||
+ type == PCI_EXP_TYPE_ROOT_PORT ||
+ (type == PCI_EXP_TYPE_DOWNSTREAM &&
+ pcie_flags_reg(dev) & PCI_EXP_FLAGS_SLOT);
+}
+
+static inline bool pcie_cap_has_rtctl(struct pci_dev *dev)
+{
+ int type = pci_pcie_type(dev);
+
+ return pcie_cap_version(dev) > 1 ||
+ type == PCI_EXP_TYPE_ROOT_PORT ||
+ type == PCI_EXP_TYPE_RC_EC;
+}
+
+static bool pcie_capability_reg_implemented(struct pci_dev *dev, int pos)
+{
+ if (!pci_is_pcie(dev))
+ return false;
+
+ switch (pos) {
+ case PCI_EXP_FLAGS_TYPE:
+ return true;
+ case PCI_EXP_DEVCAP:
+ case PCI_EXP_DEVCTL:
+ case PCI_EXP_DEVSTA:
+ return true;
+ case PCI_EXP_LNKCAP:
+ case PCI_EXP_LNKCTL:
+ case PCI_EXP_LNKSTA:
+ return pcie_cap_has_lnkctl(dev);
+ case PCI_EXP_SLTCAP:
+ case PCI_EXP_SLTCTL:
+ case PCI_EXP_SLTSTA:
+ return pcie_cap_has_sltctl(dev);
+ case PCI_EXP_RTCTL:
+ case PCI_EXP_RTCAP:
+ case PCI_EXP_RTSTA:
+ return pcie_cap_has_rtctl(dev);
+ case PCI_EXP_DEVCAP2:
+ case PCI_EXP_DEVCTL2:
+ case PCI_EXP_LNKCAP2:
+ case PCI_EXP_LNKCTL2:
+ case PCI_EXP_LNKSTA2:
+ return pcie_cap_version(dev) > 1;
+ default:
+ return false;
+ }
+}
+
+/*
+ * Note that these accessor functions are only for the "PCI Express
+ * Capability" (see PCIe spec r3.0, sec 7.8). They do not apply to the
+ * other "PCI Express Extended Capabilities" (AER, VC, ACS, MFVC, etc.)
+ */
+int pcie_capability_read_word(struct pci_dev *dev, int pos, u16 *val)
+{
+ int ret;
+
+ *val = 0;
+ if (pos & 1)
+ return -EINVAL;
+
+ if (pcie_capability_reg_implemented(dev, pos)) {
+ ret = pci_read_config_word(dev, pci_pcie_cap(dev) + pos, val);
+ /*
+ * Reset *val to 0 if pci_read_config_word() fails, it may
+ * have been written as 0xFFFF if hardware error happens
+ * during pci_read_config_word().
+ */
+ if (ret)
+ *val = 0;
+ return ret;
+ }
+
+ /*
+ * For Functions that do not implement the Slot Capabilities,
+ * Slot Status, and Slot Control registers, these spaces must
+ * be hardwired to 0b, with the exception of the Presence Detect
+ * State bit in the Slot Status register of Downstream Ports,
+ * which must be hardwired to 1b. (PCIe Base Spec 3.0, sec 7.8)
+ */
+ if (pci_is_pcie(dev) && pos == PCI_EXP_SLTSTA &&
+ pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM) {
+ *val = PCI_EXP_SLTSTA_PDS;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pcie_capability_read_word);
+
+int pcie_capability_read_dword(struct pci_dev *dev, int pos, u32 *val)
+{
+ int ret;
+
+ *val = 0;
+ if (pos & 3)
+ return -EINVAL;
+
+ if (pcie_capability_reg_implemented(dev, pos)) {
+ ret = pci_read_config_dword(dev, pci_pcie_cap(dev) + pos, val);
+ /*
+ * Reset *val to 0 if pci_read_config_dword() fails, it may
+ * have been written as 0xFFFFFFFF if hardware error happens
+ * during pci_read_config_dword().
+ */
+ if (ret)
+ *val = 0;
+ return ret;
+ }
+
+ if (pci_is_pcie(dev) && pos == PCI_EXP_SLTCTL &&
+ pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM) {
+ *val = PCI_EXP_SLTSTA_PDS;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pcie_capability_read_dword);
+
+int pcie_capability_write_word(struct pci_dev *dev, int pos, u16 val)
+{
+ if (pos & 1)
+ return -EINVAL;
+
+ if (!pcie_capability_reg_implemented(dev, pos))
+ return 0;
+
+ return pci_write_config_word(dev, pci_pcie_cap(dev) + pos, val);
+}
+EXPORT_SYMBOL_GPL(pcie_capability_write_word);
+
+int pcie_capability_write_dword(struct pci_dev *dev, int pos, u32 val)
+{
+ if (pos & 3)
+ return -EINVAL;
+
+ if (!pcie_capability_reg_implemented(dev, pos))
+ return 0;
+
+ return pci_write_config_dword(dev, pci_pcie_cap(dev) + pos, val);
+}
+EXPORT_SYMBOL_GPL(pcie_capability_write_dword);
+
+int pcie_capability_clear_and_set_word(struct pci_dev *dev, int pos,
+ u16 clear, u16 set)
+{
+ int ret;
+ u16 val;
+
+ ret = pcie_capability_read_word(dev, pos, &val);
+ if (!ret) {
+ val &= ~clear;
+ val |= set;
+ ret = pcie_capability_write_word(dev, pos, val);
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(pcie_capability_clear_and_set_word);
+
+int pcie_capability_clear_and_set_dword(struct pci_dev *dev, int pos,
+ u32 clear, u32 set)
+{
+ int ret;
+ u32 val;
+
+ ret = pcie_capability_read_dword(dev, pos, &val);
+ if (!ret) {
+ val &= ~clear;
+ val |= set;
+ ret = pcie_capability_write_dword(dev, pos, val);
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(pcie_capability_clear_and_set_dword);
+#endif
+
+#ifdef CONFIG_OF
+#if LINUX_VERSION_IS_LESS(3,7,0)
+/**
+ * of_get_child_by_name - Find the child node by name for a given parent
+ * @node: parent node
+ * @name: child name to look for.
+ *
+ * This function looks for child node for given matching name
+ *
+ * Returns a node pointer if found, with refcount incremented, use
+ * of_node_put() on it when done.
+ * Returns NULL if node is not found.
+ */
+struct device_node *of_get_child_by_name(const struct device_node *node,
+ const char *name)
+{
+ struct device_node *child;
+
+ for_each_child_of_node(node, child)
+ if (child->name && (of_node_cmp(child->name, name) == 0))
+ break;
+ return child;
+}
+EXPORT_SYMBOL_GPL(of_get_child_by_name);
+#endif /* LINUX_VERSION_IS_LESS(3,7,0) */
+#endif /* CONFIG_OF */
+
+int sg_nents(struct scatterlist *sg)
+{
+ int nents;
+ for (nents = 0; sg; sg = sg_next(sg))
+ nents++;
+ return nents;
+}
+EXPORT_SYMBOL_GPL(sg_nents);
diff --git a/compat/compat-3.8.c b/compat/compat-3.8.c
new file mode 100644
index 0000000..ff9cd49
--- /dev/null
+++ b/compat/compat-3.8.c
@@ -0,0 +1,455 @@
+/*
+ * Copyright (c) 1999 Andreas Gal
+ * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ * Copyright (c) 2006-2012 Jiri Kosina
+ * Copyright (c) 2012 Luis R. Rodriguez <mcgrof@do-not-panic.com>
+ *
+ * Backport functionality introduced in Linux 3.8.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/hid.h>
+#include <linux/module.h>
+#include "hid-ids.h"
+#include <linux/netdevice.h>
+#include <linux/random.h>
+#include <linux/of.h>
+#include <linux/mm.h>
+#include <linux/pci.h>
+#include <linux/pci_regs.h>
+
+#if LINUX_VERSION_IS_LESS(3,7,8)
+void netdev_set_default_ethtool_ops(struct net_device *dev,
+ const struct ethtool_ops *ops)
+{
+ if (!dev->ethtool_ops)
+ dev->ethtool_ops = ops;
+}
+EXPORT_SYMBOL_GPL(netdev_set_default_ethtool_ops);
+#endif
+
+/* a list of devices that shouldn't be handled by HID core at all */
+static const struct hid_device_id hid_ignore_list[] = {
+ { HID_USB_DEVICE(USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_FLAIR) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_302) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ADS_TECH, USB_DEVICE_ID_ADS_TECH_RADIO_SI470X) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_01) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_10) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_20) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_21) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_22) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_23) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_24) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_AIRCABLE, USB_DEVICE_ID_AIRCABLE1) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ALCOR, USB_DEVICE_ID_ALCOR_USBRS232) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_LCM)},
+ { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_LCM2)},
+ { HID_USB_DEVICE(USB_VENDOR_ID_AVERMEDIA, USB_DEVICE_ID_AVER_FM_MR800) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_AXENTIA, USB_DEVICE_ID_AXENTIA_FM_RADIO) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_BERKSHIRE, USB_DEVICE_ID_BERKSHIRE_PCWD) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_CIDC, 0x0103) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_CYGNAL, USB_DEVICE_ID_CYGNAL_RADIO_SI470X) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_CMEDIA, USB_DEVICE_ID_CM109) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_HIDCOM) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_ULTRAMOUSE) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_DEALEXTREAME, USB_DEVICE_ID_DEALEXTREAME_RADIO_SI4701) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EARTHMATE) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EM_LT20) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_DREAM_CHEEKY, 0x0004) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_DREAM_CHEEKY, 0x000a) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ESSENTIAL_REALITY, USB_DEVICE_ID_ESSENTIAL_REALITY_P5) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ETT, USB_DEVICE_ID_TC5UH) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ETT, USB_DEVICE_ID_TC4UM) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0001) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0002) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0004) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_4_PHIDGETSERVO_30) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_1_PHIDGETSERVO_30) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_0_4_IF_KIT) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_16_16_IF_KIT) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_8_8_8_IF_KIT) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_7_IF_KIT) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_8_IF_KIT) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_PHIDGET_MOTORCONTROL) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GOTOP, USB_DEVICE_ID_SUPER_Q2) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GOTOP, USB_DEVICE_ID_GOGOPEN) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GOTOP, USB_DEVICE_ID_PENPOWER) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GRETAGMACBETH, USB_DEVICE_ID_GRETAGMACBETH_HUEY) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_POWERMATE) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_SOUNDKNOB) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_RADIOSHARK) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_90) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_100) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_101) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_103) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_104) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_105) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_106) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_107) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_108) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_200) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_201) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_202) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_203) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_204) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_205) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_206) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_207) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_300) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_301) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_302) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_303) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_304) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_305) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_306) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_307) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_308) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_309) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_400) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_401) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_402) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_403) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_404) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_405) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_500) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_501) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_502) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_503) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_504) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1000) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1001) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1002) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1003) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1004) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1005) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1006) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1007) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_IMATION, USB_DEVICE_ID_DISC_STAKKA) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_KBGEAR, USB_DEVICE_ID_KBGEAR_JAMSTUDIO) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_KWORLD, USB_DEVICE_ID_KWORLD_RADIO_FM700) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_GPEN_560) },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_KYE, 0x0058) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_CASSY) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_CASSY2) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POCKETCASSY) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POCKETCASSY2) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MOBILECASSY) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MOBILECASSY2) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MICROCASSYVOLTAGE) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MICROCASSYCURRENT) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MICROCASSYTIME) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MICROCASSYTEMPERATURE) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MICROCASSYPH) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_JWM) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_DMMP) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_UMIP) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_UMIC) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_UMIB) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_XRAY) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_XRAY2) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_VIDEOCOM) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MOTOR) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_COM3LAB) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_TELEPORT) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_NETWORKANALYSER) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POWERCONTROL) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MACHINETEST) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MOSTANALYSER) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MOSTANALYSER2) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_ABSESP) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_AUTODATABUS) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MCT) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_HYBRID) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_HEATCONTROL) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_MADCATZ, USB_DEVICE_ID_MADCATZ_BEATPAD) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_MCC, USB_DEVICE_ID_MCC_PMD1024LS) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_MCC, USB_DEVICE_ID_MCC_PMD1208LS) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICKIT1) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICKIT2) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_NATIONAL_SEMICONDUCTOR, USB_DEVICE_ID_N_S_HARMONY) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 20) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 30) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 100) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 108) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 118) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 200) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 300) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 400) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 500) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0001) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0002) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0003) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0004) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_PHILIPS, USB_DEVICE_ID_PHILIPS_IEEE802154_DONGLE) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_POWERCOM, USB_DEVICE_ID_POWERCOM_UPS) },
+#if defined(CONFIG_MOUSE_SYNAPTICS_USB) || defined(CONFIG_MOUSE_SYNAPTICS_USB_MODULE)
+ { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_TP) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_INT_TP) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_CPAD) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_STICK) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_WP) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_COMP_TP) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_WTP) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_DPAD) },
+#endif
+ { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LABPRO) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_GOTEMP) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_SKIP) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_CYCLOPS) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LCSPEC) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_WACOM, HID_ANY_ID) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_4_PHIDGETSERVO_20) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_1_PHIDGETSERVO_20) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_8_8_4_IF_KIT) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_YEALINK, USB_DEVICE_ID_YEALINK_P1K_P4K_B2K) },
+ { }
+};
+
+/**
+ * hid_mouse_ignore_list - mouse devices which should not be handled by the hid layer
+ *
+ * There are composite devices for which we want to ignore only a certain
+ * interface. This is a list of devices for which only the mouse interface will
+ * be ignored. This allows a dedicated driver to take care of the interface.
+ */
+static const struct hid_device_id hid_mouse_ignore_list[] = {
+ /* appletouch driver */
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ISO) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ANSI) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ISO) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_JIS) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ANSI) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ISO) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_JIS) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ANSI) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ISO) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_JIS) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_ANSI) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_ISO) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_JIS) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ANSI) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ISO) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_JIS) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_ISO) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_JIS) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING3_ANSI) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING3_ISO) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING3_JIS) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING4_ISO) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING4_JIS) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING4A_ANSI) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING4A_ISO) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5_ISO) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5_JIS) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5A_ANSI) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5A_ISO) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5A_JIS) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6_ANSI) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6_ISO) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6_JIS) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6A_ANSI) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6A_ISO) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6A_JIS) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7_ANSI) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7_ISO) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7_JIS) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7A_ANSI) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7A_ISO) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7A_JIS) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY) },
+ { }
+};
+
+static bool hid_match_one_id(struct hid_device *hdev,
+ const struct hid_device_id *id)
+{
+ return (id->bus == HID_BUS_ANY || id->bus == hdev->bus) &&
+#if LINUX_VERSION_IS_GEQ(3,8,0)
+ (id->group == HID_GROUP_ANY || id->group == hdev->group) &&
+#endif
+ (id->vendor == HID_ANY_ID || id->vendor == hdev->vendor) &&
+ (id->product == HID_ANY_ID || id->product == hdev->product);
+}
+
+#define hid_match_id LINUX_BACKPORT(hid_match_id)
+static const struct hid_device_id *
+hid_match_id(struct hid_device *hdev, const struct hid_device_id *id)
+{
+ for (; id->bus; id++)
+ if (hid_match_one_id(hdev, id))
+ return id;
+
+ return NULL;
+}
+
+bool hid_ignore(struct hid_device *hdev)
+{
+ if (hdev->quirks & HID_QUIRK_NO_IGNORE)
+ return false;
+ if (hdev->quirks & HID_QUIRK_IGNORE)
+ return true;
+
+ switch (hdev->vendor) {
+ case USB_VENDOR_ID_CODEMERCS:
+ /* ignore all Code Mercenaries IOWarrior devices */
+ if (hdev->product >= USB_DEVICE_ID_CODEMERCS_IOW_FIRST &&
+ hdev->product <= USB_DEVICE_ID_CODEMERCS_IOW_LAST)
+ return true;
+ break;
+ case USB_VENDOR_ID_LOGITECH:
+ if (hdev->product >= USB_DEVICE_ID_LOGITECH_HARMONY_FIRST &&
+ hdev->product <= USB_DEVICE_ID_LOGITECH_HARMONY_LAST)
+ return true;
+ /*
+ * The Keene FM transmitter USB device has the same USB ID as
+ * the Logitech AudioHub Speaker, but it should ignore the hid.
+ * Check if the name is that of the Keene device.
+ * For reference: the name of the AudioHub is
+ * "HOLTEK AudioHub Speaker".
+ */
+ if (hdev->product == USB_DEVICE_ID_LOGITECH_AUDIOHUB &&
+ !strcmp(hdev->name, "HOLTEK B-LINK USB Audio "))
+ return true;
+ break;
+ case USB_VENDOR_ID_SOUNDGRAPH:
+ if (hdev->product >= USB_DEVICE_ID_SOUNDGRAPH_IMON_FIRST &&
+ hdev->product <= USB_DEVICE_ID_SOUNDGRAPH_IMON_LAST)
+ return true;
+ break;
+ case USB_VENDOR_ID_HANWANG:
+ if (hdev->product >= USB_DEVICE_ID_HANWANG_TABLET_FIRST &&
+ hdev->product <= USB_DEVICE_ID_HANWANG_TABLET_LAST)
+ return true;
+ break;
+ case USB_VENDOR_ID_JESS:
+ if (hdev->product == USB_DEVICE_ID_JESS_YUREX &&
+ hdev->type == HID_TYPE_USBNONE)
+ return true;
+ break;
+ case USB_VENDOR_ID_DWAV:
+ /* These are handled by usbtouchscreen. hdev->type is probably
+ * HID_TYPE_USBNONE, but we say !HID_TYPE_USBMOUSE to match
+ * usbtouchscreen. */
+ if ((hdev->product == USB_DEVICE_ID_EGALAX_TOUCHCONTROLLER ||
+ hdev->product == USB_DEVICE_ID_DWAV_TOUCHCONTROLLER) &&
+ hdev->type != HID_TYPE_USBMOUSE)
+ return true;
+ break;
+ }
+
+ if (hdev->type == HID_TYPE_USBMOUSE &&
+ hid_match_id(hdev, hid_mouse_ignore_list))
+ return true;
+
+ return !!hid_match_id(hdev, hid_ignore_list);
+}
+EXPORT_SYMBOL_GPL(hid_ignore);
+
+/**
+ * prandom_bytes - get the requested number of pseudo-random bytes
+ * @buf: where to copy the pseudo-random bytes to
+ * @bytes: the requested number of bytes
+ */
+void prandom_bytes(void *buf, int bytes)
+{
+ unsigned char *p = buf;
+ int i;
+
+ for (i = 0; i < round_down(bytes, sizeof(u32)); i += sizeof(u32)) {
+ u32 random = random32();
+ int j;
+
+ for (j = 0; j < sizeof(u32); j++) {
+ p[i + j] = random;
+ random >>= BITS_PER_BYTE;
+ }
+ }
+
+ if (i < bytes) {
+ u32 random = random32();
+
+ for (; i < bytes; i++) {
+ p[i] = random;
+ random >>= BITS_PER_BYTE;
+ }
+ }
+}
+EXPORT_SYMBOL_GPL(prandom_bytes);
+
+#ifdef CONFIG_OF
+/**
+ * of_property_read_u8_array - Find and read an array of u8 from a property.
+ *
+ * @np: device node from which the property value is to be read.
+ * @propname: name of the property to be searched.
+ * @out_values: pointer to return value, modified only if return value is 0.
+ * @sz: number of array elements to read
+ *
+ * Search for a property in a device node and read 8-bit value(s) from
+ * it. Returns 0 on success, -EINVAL if the property does not exist,
+ * -ENODATA if property does not have a value, and -EOVERFLOW if the
+ * property data isn't large enough.
+ *
+ * dts entry of array should be like:
+ * property = /bits/ 8 <0x50 0x60 0x70>;
+ *
+ * The out_values is modified only if a valid u8 value can be decoded.
+ */
+int of_property_read_u8_array(const struct device_node *np,
+ const char *propname, u8 *out_values, size_t sz)
+{
+ const u8 *val = of_find_property_value_of_size(np, propname,
+ (sz * sizeof(*out_values)));
+
+ if (IS_ERR(val))
+ return PTR_ERR(val);
+
+ while (sz--)
+ *out_values++ = *val++;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(of_property_read_u8_array);
+#endif /* CONFIG_OF */
+
+#ifdef CONFIG_PCI_IOV
+/**
+ * pci_sriov_set_totalvfs -- reduce the TotalVFs available
+ * @dev: the PCI PF device
+ * @numvfs: number that should be used for TotalVFs supported
+ *
+ * Should be called from PF driver's probe routine with
+ * device's mutex held.
+ *
+ * Returns 0 if PF is an SRIOV-capable device and
+ * value of numvfs valid. If not a PF return -ENOSYS;
+ * if numvfs is invalid return -EINVAL;
+ * if VFs already enabled, return -EBUSY.
+ */
+int pci_sriov_set_totalvfs(struct pci_dev *dev, u16 numvfs)
+{
+ if (!dev->is_physfn)
+ return -ENOSYS;
+ if (numvfs > dev->sriov->total_VFs)
+ return -EINVAL;
+
+ /* Shouldn't change if VFs already enabled */
+ if (dev->sriov->ctrl & PCI_SRIOV_CTRL_VFE)
+ return -EBUSY;
+ else
+ dev->sriov->driver_max_VFs = numvfs;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pci_sriov_set_totalvfs);
+#endif /* CONFIG_PCI_IOV */
diff --git a/compat/compat-3.9.c b/compat/compat-3.9.c
new file mode 100644
index 0000000..93889b5
--- /dev/null
+++ b/compat/compat-3.9.c
@@ -0,0 +1,238 @@
+/*
+ * Copyright (c) 2013 Luis R. Rodriguez <mcgrof@do-not-panic.com>
+ *
+ * Backport functionality introduced in Linux 3.9.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/netdevice.h>
+#include <linux/if.h>
+#include <linux/if_ether.h>
+#include <linux/etherdevice.h>
+#include <linux/scatterlist.h>
+#include <linux/highmem.h>
+#include <net/inet_frag.h>
+#include <net/sock.h>
+
+void __iomem *devm_ioremap_resource(struct device *dev, struct resource *res)
+{
+ void __iomem *dest_ptr;
+
+ dest_ptr = devm_ioremap_resource(dev, res);
+ if (!dest_ptr)
+ return (void __iomem *)ERR_PTR(-ENOMEM);
+ return dest_ptr;
+}
+EXPORT_SYMBOL_GPL(devm_ioremap_resource);
+
+/**
+ * eth_prepare_mac_addr_change - prepare for mac change
+ * @dev: network device
+ * @p: socket address
+ */
+int eth_prepare_mac_addr_change(struct net_device *dev, void *p)
+{
+ struct sockaddr *addr = p;
+
+ if (!(dev->priv_flags & IFF_LIVE_ADDR_CHANGE) && netif_running(dev))
+ return -EBUSY;
+ if (!is_valid_ether_addr(addr->sa_data))
+ return -EADDRNOTAVAIL;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(eth_prepare_mac_addr_change);
+
+/**
+ * eth_commit_mac_addr_change - commit mac change
+ * @dev: network device
+ * @p: socket address
+ */
+void eth_commit_mac_addr_change(struct net_device *dev, void *p)
+{
+ struct sockaddr *addr = p;
+
+ memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN);
+}
+EXPORT_SYMBOL_GPL(eth_commit_mac_addr_change);
+
+void inet_frag_maybe_warn_overflow(struct inet_frag_queue *q,
+ const char *prefix)
+{
+ static const char msg[] = "inet_frag_find: Fragment hash bucket"
+ " list length grew over limit " __stringify(INETFRAGS_MAXDEPTH)
+ ". Dropping fragment.\n";
+
+ if (PTR_ERR(q) == -ENOBUFS)
+ LIMIT_NETDEBUG(KERN_WARNING "%s%s", prefix, msg);
+}
+EXPORT_SYMBOL_GPL(inet_frag_maybe_warn_overflow);
+
+void __sg_page_iter_start(struct sg_page_iter *piter,
+ struct scatterlist *sglist, unsigned int nents,
+ unsigned long pgoffset)
+{
+ piter->__pg_advance = 0;
+ piter->__nents = nents;
+
+ piter->page = NULL;
+ piter->sg = sglist;
+ piter->sg_pgoffset = pgoffset;
+}
+EXPORT_SYMBOL_GPL(__sg_page_iter_start);
+
+static int sg_page_count(struct scatterlist *sg)
+{
+ return PAGE_ALIGN(sg->offset + sg->length) >> PAGE_SHIFT;
+}
+
+bool __sg_page_iter_next(struct sg_page_iter *piter)
+{
+ if (!piter->__nents || !piter->sg)
+ return false;
+
+ piter->sg_pgoffset += piter->__pg_advance;
+ piter->__pg_advance = 1;
+
+ while (piter->sg_pgoffset >= sg_page_count(piter->sg)) {
+ piter->sg_pgoffset -= sg_page_count(piter->sg);
+ piter->sg = sg_next(piter->sg);
+ if (!--piter->__nents || !piter->sg)
+ return false;
+ }
+ piter->page = nth_page(sg_page(piter->sg), piter->sg_pgoffset);
+
+ return true;
+}
+EXPORT_SYMBOL_GPL(__sg_page_iter_next);
+
+static bool sg_miter_get_next_page(struct sg_mapping_iter *miter)
+{
+ if (!miter->__remaining) {
+ struct scatterlist *sg;
+ unsigned long pgoffset;
+
+ if (!__sg_page_iter_next(&miter->piter))
+ return false;
+
+ sg = miter->piter.sg;
+ pgoffset = miter->piter.sg_pgoffset;
+
+ miter->__offset = pgoffset ? 0 : sg->offset;
+ miter->__remaining = sg->offset + sg->length -
+ (pgoffset << PAGE_SHIFT) - miter->__offset;
+ miter->__remaining = min_t(unsigned long, miter->__remaining,
+ PAGE_SIZE - miter->__offset);
+ }
+
+ return true;
+}
+
+/**
+ * sg_miter_start - start mapping iteration over a sg list
+ * @miter: sg mapping iter to be started
+ * @sgl: sg list to iterate over
+ * @nents: number of sg entries
+ *
+ * Description:
+ * Starts mapping iterator @miter.
+ *
+ * Context:
+ * Don't care.
+ */
+void backport_sg_miter_start(struct sg_mapping_iter *miter, struct scatterlist *sgl,
+ unsigned int nents, unsigned int flags)
+{
+ memset(miter, 0, sizeof(struct sg_mapping_iter));
+
+ __sg_page_iter_start(&miter->piter, sgl, nents, 0);
+ WARN_ON(!(flags & (SG_MITER_TO_SG | SG_MITER_FROM_SG)));
+ miter->__flags = flags;
+}
+EXPORT_SYMBOL_GPL(backport_sg_miter_start);
+
+/**
+ * sg_miter_next - proceed mapping iterator to the next mapping
+ * @miter: sg mapping iter to proceed
+ *
+ * Description:
+ * Proceeds @miter to the next mapping. @miter should have been started
+ * using sg_miter_start(). On successful return, @miter->page,
+ * @miter->addr and @miter->length point to the current mapping.
+ *
+ * Context:
+ * Preemption disabled if SG_MITER_ATOMIC. Preemption must stay disabled
+ * till @miter is stopped. May sleep if !SG_MITER_ATOMIC.
+ *
+ * Returns:
+ * true if @miter contains the next mapping. false if end of sg
+ * list is reached.
+ */
+bool backport_sg_miter_next(struct sg_mapping_iter *miter)
+{
+ sg_miter_stop(miter);
+
+ /*
+ * Get to the next page if necessary.
+ * __remaining, __offset is adjusted by sg_miter_stop
+ */
+ if (!sg_miter_get_next_page(miter))
+ return false;
+
+ miter->page = sg_page_iter_page(&miter->piter);
+ miter->consumed = miter->length = miter->__remaining;
+
+ if (miter->__flags & SG_MITER_ATOMIC)
+ miter->addr = kmap_atomic(miter->page) + miter->__offset;
+ else
+ miter->addr = kmap(miter->page) + miter->__offset;
+
+ return true;
+}
+EXPORT_SYMBOL_GPL(backport_sg_miter_next);
+
+/**
+ * sg_miter_stop - stop mapping iteration
+ * @miter: sg mapping iter to be stopped
+ *
+ * Description:
+ * Stops mapping iterator @miter. @miter should have been started
+ * using sg_miter_start(). A stopped iteration can be resumed by
+ * calling sg_miter_next() on it. This is useful when resources (kmap)
+ * need to be released during iteration.
+ *
+ * Context:
+ * Preemption disabled if the SG_MITER_ATOMIC is set. Don't care
+ * otherwise.
+ */
+void backport_sg_miter_stop(struct sg_mapping_iter *miter)
+{
+ WARN_ON(miter->consumed > miter->length);
+
+ /* drop resources from the last iteration */
+ if (miter->addr) {
+ miter->__offset += miter->consumed;
+ miter->__remaining -= miter->consumed;
+
+ if ((miter->__flags & SG_MITER_TO_SG) &&
+ !PageSlab(miter->page))
+ flush_kernel_dcache_page(miter->page);
+
+ if (miter->__flags & SG_MITER_ATOMIC) {
+ WARN_ON_ONCE(preemptible());
+ kunmap_atomic(miter->addr);
+ } else
+ kunmap(miter->page);
+
+ miter->page = NULL;
+ miter->addr = NULL;
+ miter->length = 0;
+ miter->consumed = 0;
+ }
+}
+EXPORT_SYMBOL_GPL(backport_sg_miter_stop);
diff --git a/compat/crypto-skcipher.c b/compat/crypto-skcipher.c
new file mode 100644
index 0000000..9ccfb5e
--- /dev/null
+++ b/compat/crypto-skcipher.c
@@ -0,0 +1,1035 @@
+/*
+ * Symmetric key cipher operations.
+ *
+ * Generic encrypt/decrypt wrapper for ciphers, handles operations across
+ * multiple page boundaries by using temporary blocks. In user context,
+ * the kernel is given a chance to schedule us once per page.
+ *
+ * Copyright (c) 2015 Herbert Xu <herbert@gondor.apana.org.au>
+ *
+ * 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.
+ *
+ */
+
+#include <crypto/internal/aead.h>
+#include <crypto/internal/skcipher.h>
+#include <crypto/scatterwalk.h>
+#include <linux/bug.h>
+#include <linux/cryptouser.h>
+#include <linux/compiler.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/rtnetlink.h>
+#include <linux/seq_file.h>
+#include <net/netlink.h>
+
+struct crypto_tfm *__crypto_alloc_tfm(struct crypto_alg *alg, u32 type,
+ u32 mask);
+
+void *crypto_alloc_tfm(const char *alg_name,
+ const struct crypto_type *frontend, u32 type, u32 mask);
+
+struct crypto_alg *crypto_mod_get(struct crypto_alg *alg);
+
+static inline void *crypto_skcipher_ctx(struct crypto_skcipher *tfm)
+{
+ return crypto_tfm_ctx(&tfm->base);
+}
+
+static inline void *skcipher_request_ctx(struct skcipher_request *req)
+{
+ return req->__ctx;
+}
+
+static inline u32 skcipher_request_flags(struct skcipher_request *req)
+{
+ return req->base.flags;
+}
+
+enum {
+ SKCIPHER_WALK_PHYS = 1 << 0,
+ SKCIPHER_WALK_SLOW = 1 << 1,
+ SKCIPHER_WALK_COPY = 1 << 2,
+ SKCIPHER_WALK_DIFF = 1 << 3,
+ SKCIPHER_WALK_SLEEP = 1 << 4,
+};
+
+struct skcipher_walk_buffer {
+ struct list_head entry;
+ struct scatter_walk dst;
+ unsigned int len;
+ u8 *data;
+ u8 buffer[];
+};
+
+static int skcipher_walk_next(struct skcipher_walk *walk);
+
+static inline void skcipher_unmap(struct scatter_walk *walk, void *vaddr)
+{
+ if (PageHighMem(scatterwalk_page(walk)))
+ kunmap_atomic(vaddr);
+}
+
+static inline void *skcipher_map(struct scatter_walk *walk)
+{
+ struct page *page = scatterwalk_page(walk);
+
+ return (PageHighMem(page) ? kmap_atomic(page) : page_address(page)) +
+ offset_in_page(walk->offset);
+}
+
+static inline void skcipher_map_src(struct skcipher_walk *walk)
+{
+ walk->src.virt.addr = skcipher_map(&walk->in);
+}
+
+static inline void skcipher_map_dst(struct skcipher_walk *walk)
+{
+ walk->dst.virt.addr = skcipher_map(&walk->out);
+}
+
+static inline void skcipher_unmap_src(struct skcipher_walk *walk)
+{
+ skcipher_unmap(&walk->in, walk->src.virt.addr);
+}
+
+static inline void skcipher_unmap_dst(struct skcipher_walk *walk)
+{
+ skcipher_unmap(&walk->out, walk->dst.virt.addr);
+}
+
+static inline gfp_t skcipher_walk_gfp(struct skcipher_walk *walk)
+{
+ return walk->flags & SKCIPHER_WALK_SLEEP ? GFP_KERNEL : GFP_ATOMIC;
+}
+
+/* Get a spot of the specified length that does not straddle a page.
+ * The caller needs to ensure that there is enough space for this operation.
+ */
+static inline u8 *skcipher_get_spot(u8 *start, unsigned int len)
+{
+ u8 *end_page = (u8 *)(((unsigned long)(start + len - 1)) & PAGE_MASK);
+
+ return max(start, end_page);
+}
+
+static int skcipher_done_slow(struct skcipher_walk *walk, unsigned int bsize)
+{
+ u8 *addr;
+
+ addr = (u8 *)ALIGN((unsigned long)walk->buffer, walk->alignmask + 1);
+ addr = skcipher_get_spot(addr, bsize);
+ scatterwalk_copychunks(addr, &walk->out, bsize,
+ (walk->flags & SKCIPHER_WALK_PHYS) ? 2 : 1);
+ return 0;
+}
+
+int skcipher_walk_done(struct skcipher_walk *walk, int err)
+{
+ unsigned int n = walk->nbytes - err;
+ unsigned int nbytes;
+
+ nbytes = walk->total - n;
+
+ if (unlikely(err < 0)) {
+ nbytes = 0;
+ n = 0;
+ } else if (likely(!(walk->flags & (SKCIPHER_WALK_PHYS |
+ SKCIPHER_WALK_SLOW |
+ SKCIPHER_WALK_COPY |
+ SKCIPHER_WALK_DIFF)))) {
+unmap_src:
+ skcipher_unmap_src(walk);
+ } else if (walk->flags & SKCIPHER_WALK_DIFF) {
+ skcipher_unmap_dst(walk);
+ goto unmap_src;
+ } else if (walk->flags & SKCIPHER_WALK_COPY) {
+ skcipher_map_dst(walk);
+ memcpy(walk->dst.virt.addr, walk->page, n);
+ skcipher_unmap_dst(walk);
+ } else if (unlikely(walk->flags & SKCIPHER_WALK_SLOW)) {
+ if (WARN_ON(err)) {
+ err = -EINVAL;
+ nbytes = 0;
+ } else
+ n = skcipher_done_slow(walk, n);
+ }
+
+ if (err > 0)
+ err = 0;
+
+ walk->total = nbytes;
+ walk->nbytes = nbytes;
+
+ scatterwalk_advance(&walk->in, n);
+ scatterwalk_advance(&walk->out, n);
+ scatterwalk_done(&walk->in, 0, nbytes);
+ scatterwalk_done(&walk->out, 1, nbytes);
+
+ if (nbytes) {
+ crypto_yield(walk->flags & SKCIPHER_WALK_SLEEP ?
+ CRYPTO_TFM_REQ_MAY_SLEEP : 0);
+ return skcipher_walk_next(walk);
+ }
+
+ /* Short-circuit for the common/fast path. */
+ if (!((unsigned long)walk->buffer | (unsigned long)walk->page))
+ goto out;
+
+ if (walk->flags & SKCIPHER_WALK_PHYS)
+ goto out;
+
+ if (walk->iv != walk->oiv)
+ memcpy(walk->oiv, walk->iv, walk->ivsize);
+ if (walk->buffer != walk->page)
+ kfree(walk->buffer);
+ if (walk->page)
+ free_page((unsigned long)walk->page);
+
+out:
+ return err;
+}
+EXPORT_SYMBOL_GPL(skcipher_walk_done);
+
+void skcipher_walk_complete(struct skcipher_walk *walk, int err)
+{
+ struct skcipher_walk_buffer *p, *tmp;
+
+ list_for_each_entry_safe(p, tmp, &walk->buffers, entry) {
+ u8 *data;
+
+ if (err)
+ goto done;
+
+ data = p->data;
+ if (!data) {
+ data = PTR_ALIGN(&p->buffer[0], walk->alignmask + 1);
+ data = skcipher_get_spot(data, walk->stride);
+ }
+
+ scatterwalk_copychunks(data, &p->dst, p->len, 1);
+
+ if (offset_in_page(p->data) + p->len + walk->stride >
+ PAGE_SIZE)
+ free_page((unsigned long)p->data);
+
+done:
+ list_del(&p->entry);
+ kfree(p);
+ }
+
+ if (!err && walk->iv != walk->oiv)
+ memcpy(walk->oiv, walk->iv, walk->ivsize);
+ if (walk->buffer != walk->page)
+ kfree(walk->buffer);
+ if (walk->page)
+ free_page((unsigned long)walk->page);
+}
+EXPORT_SYMBOL_GPL(skcipher_walk_complete);
+
+static void skcipher_queue_write(struct skcipher_walk *walk,
+ struct skcipher_walk_buffer *p)
+{
+ p->dst = walk->out;
+ list_add_tail(&p->entry, &walk->buffers);
+}
+
+static int skcipher_next_slow(struct skcipher_walk *walk, unsigned int bsize)
+{
+ bool phys = walk->flags & SKCIPHER_WALK_PHYS;
+ unsigned alignmask = walk->alignmask;
+ struct skcipher_walk_buffer *p;
+ unsigned a;
+ unsigned n;
+ u8 *buffer;
+ void *v;
+
+ if (!phys) {
+ if (!walk->buffer)
+ walk->buffer = walk->page;
+ buffer = walk->buffer;
+ if (buffer)
+ goto ok;
+ }
+
+ /* Start with the minimum alignment of kmalloc. */
+ a = crypto_tfm_ctx_alignment() - 1;
+ n = bsize;
+
+ if (phys) {
+ /* Calculate the minimum alignment of p->buffer. */
+ a &= (sizeof(*p) ^ (sizeof(*p) - 1)) >> 1;
+ n += sizeof(*p);
+ }
+
+ /* Minimum size to align p->buffer by alignmask. */
+ n += alignmask & ~a;
+
+ /* Minimum size to ensure p->buffer does not straddle a page. */
+ n += (bsize - 1) & ~(alignmask | a);
+
+ v = kzalloc(n, skcipher_walk_gfp(walk));
+ if (!v)
+ return skcipher_walk_done(walk, -ENOMEM);
+
+ if (phys) {
+ p = v;
+ p->len = bsize;
+ skcipher_queue_write(walk, p);
+ buffer = p->buffer;
+ } else {
+ walk->buffer = v;
+ buffer = v;
+ }
+
+ok:
+ walk->dst.virt.addr = PTR_ALIGN(buffer, alignmask + 1);
+ walk->dst.virt.addr = skcipher_get_spot(walk->dst.virt.addr, bsize);
+ walk->src.virt.addr = walk->dst.virt.addr;
+
+ scatterwalk_copychunks(walk->src.virt.addr, &walk->in, bsize, 0);
+
+ walk->nbytes = bsize;
+ walk->flags |= SKCIPHER_WALK_SLOW;
+
+ return 0;
+}
+
+static int skcipher_next_copy(struct skcipher_walk *walk)
+{
+ struct skcipher_walk_buffer *p;
+ u8 *tmp = walk->page;
+
+ skcipher_map_src(walk);
+ memcpy(tmp, walk->src.virt.addr, walk->nbytes);
+ skcipher_unmap_src(walk);
+
+ walk->src.virt.addr = tmp;
+ walk->dst.virt.addr = tmp;
+
+ if (!(walk->flags & SKCIPHER_WALK_PHYS))
+ return 0;
+
+ p = kmalloc(sizeof(*p), skcipher_walk_gfp(walk));
+ if (!p)
+ return -ENOMEM;
+
+ p->data = walk->page;
+ p->len = walk->nbytes;
+ skcipher_queue_write(walk, p);
+
+ if (offset_in_page(walk->page) + walk->nbytes + walk->stride >
+ PAGE_SIZE)
+ walk->page = NULL;
+ else
+ walk->page += walk->nbytes;
+
+ return 0;
+}
+
+static int skcipher_next_fast(struct skcipher_walk *walk)
+{
+ unsigned long diff;
+
+ walk->src.phys.page = scatterwalk_page(&walk->in);
+ walk->src.phys.offset = offset_in_page(walk->in.offset);
+ walk->dst.phys.page = scatterwalk_page(&walk->out);
+ walk->dst.phys.offset = offset_in_page(walk->out.offset);
+
+ if (walk->flags & SKCIPHER_WALK_PHYS)
+ return 0;
+
+ diff = walk->src.phys.offset - walk->dst.phys.offset;
+ diff |= walk->src.virt.page - walk->dst.virt.page;
+
+ skcipher_map_src(walk);
+ walk->dst.virt.addr = walk->src.virt.addr;
+
+ if (diff) {
+ walk->flags |= SKCIPHER_WALK_DIFF;
+ skcipher_map_dst(walk);
+ }
+
+ return 0;
+}
+
+static int skcipher_walk_next(struct skcipher_walk *walk)
+{
+ unsigned int bsize;
+ unsigned int n;
+ int err;
+
+ walk->flags &= ~(SKCIPHER_WALK_SLOW | SKCIPHER_WALK_COPY |
+ SKCIPHER_WALK_DIFF);
+
+ n = walk->total;
+ bsize = min(walk->stride, max(n, walk->blocksize));
+ n = scatterwalk_clamp(&walk->in, n);
+ n = scatterwalk_clamp(&walk->out, n);
+
+ if (unlikely(n < bsize)) {
+ if (unlikely(walk->total < walk->blocksize))
+ return skcipher_walk_done(walk, -EINVAL);
+
+slow_path:
+ err = skcipher_next_slow(walk, bsize);
+ goto set_phys_lowmem;
+ }
+
+ if (unlikely((walk->in.offset | walk->out.offset) & walk->alignmask)) {
+ if (!walk->page) {
+ gfp_t gfp = skcipher_walk_gfp(walk);
+
+ walk->page = (void *)__get_free_page(gfp);
+ if (!walk->page)
+ goto slow_path;
+ }
+
+ walk->nbytes = min_t(unsigned, n,
+ PAGE_SIZE - offset_in_page(walk->page));
+ walk->flags |= SKCIPHER_WALK_COPY;
+ err = skcipher_next_copy(walk);
+ goto set_phys_lowmem;
+ }
+
+ walk->nbytes = n;
+
+ return skcipher_next_fast(walk);
+
+set_phys_lowmem:
+ if (!err && (walk->flags & SKCIPHER_WALK_PHYS)) {
+ walk->src.phys.page = virt_to_page(walk->src.virt.addr);
+ walk->dst.phys.page = virt_to_page(walk->dst.virt.addr);
+ walk->src.phys.offset &= PAGE_SIZE - 1;
+ walk->dst.phys.offset &= PAGE_SIZE - 1;
+ }
+ return err;
+}
+EXPORT_SYMBOL_GPL(skcipher_walk_next);
+
+static int skcipher_copy_iv(struct skcipher_walk *walk)
+{
+ unsigned a = crypto_tfm_ctx_alignment() - 1;
+ unsigned alignmask = walk->alignmask;
+ unsigned ivsize = walk->ivsize;
+ unsigned bs = walk->stride;
+ unsigned aligned_bs;
+ unsigned size;
+ u8 *iv;
+
+ aligned_bs = ALIGN(bs, alignmask);
+
+ /* Minimum size to align buffer by alignmask. */
+ size = alignmask & ~a;
+
+ if (walk->flags & SKCIPHER_WALK_PHYS)
+ size += ivsize;
+ else {
+ size += aligned_bs + ivsize;
+
+ /* Minimum size to ensure buffer does not straddle a page. */
+ size += (bs - 1) & ~(alignmask | a);
+ }
+
+ walk->buffer = kmalloc(size, skcipher_walk_gfp(walk));
+ if (!walk->buffer)
+ return -ENOMEM;
+
+ iv = PTR_ALIGN(walk->buffer, alignmask + 1);
+ iv = skcipher_get_spot(iv, bs) + aligned_bs;
+
+ walk->iv = memcpy(iv, walk->iv, walk->ivsize);
+ return 0;
+}
+
+static int skcipher_walk_first(struct skcipher_walk *walk)
+{
+ walk->nbytes = 0;
+
+ if (WARN_ON_ONCE(in_irq()))
+ return -EDEADLK;
+
+ if (unlikely(!walk->total))
+ return 0;
+
+ walk->buffer = NULL;
+ if (unlikely(((unsigned long)walk->iv & walk->alignmask))) {
+ int err = skcipher_copy_iv(walk);
+ if (err)
+ return err;
+ }
+
+ walk->page = NULL;
+ walk->nbytes = walk->total;
+
+ return skcipher_walk_next(walk);
+}
+
+static int skcipher_walk_skcipher(struct skcipher_walk *walk,
+ struct skcipher_request *req)
+{
+ struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
+
+ scatterwalk_start(&walk->in, req->src);
+ scatterwalk_start(&walk->out, req->dst);
+
+ walk->total = req->cryptlen;
+ walk->iv = req->iv;
+ walk->oiv = req->iv;
+
+ walk->flags &= ~SKCIPHER_WALK_SLEEP;
+ walk->flags |= req->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP ?
+ SKCIPHER_WALK_SLEEP : 0;
+
+ walk->blocksize = crypto_skcipher_blocksize(tfm);
+ walk->stride = crypto_skcipher_walksize(tfm);
+ walk->ivsize = crypto_skcipher_ivsize(tfm);
+ walk->alignmask = crypto_skcipher_alignmask(tfm);
+
+ return skcipher_walk_first(walk);
+}
+
+int skcipher_walk_virt(struct skcipher_walk *walk,
+ struct skcipher_request *req, bool atomic)
+{
+ int err;
+
+ walk->flags &= ~SKCIPHER_WALK_PHYS;
+
+ err = skcipher_walk_skcipher(walk, req);
+
+ walk->flags &= atomic ? ~SKCIPHER_WALK_SLEEP : ~0;
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(skcipher_walk_virt);
+
+void skcipher_walk_atomise(struct skcipher_walk *walk)
+{
+ walk->flags &= ~SKCIPHER_WALK_SLEEP;
+}
+EXPORT_SYMBOL_GPL(skcipher_walk_atomise);
+
+int skcipher_walk_async(struct skcipher_walk *walk,
+ struct skcipher_request *req)
+{
+ walk->flags |= SKCIPHER_WALK_PHYS;
+
+ INIT_LIST_HEAD(&walk->buffers);
+
+ return skcipher_walk_skcipher(walk, req);
+}
+EXPORT_SYMBOL_GPL(skcipher_walk_async);
+
+static int skcipher_walk_aead_common(struct skcipher_walk *walk,
+ struct aead_request *req, bool atomic)
+{
+ struct crypto_aead *tfm = crypto_aead_reqtfm(req);
+ int err;
+
+ walk->flags &= ~SKCIPHER_WALK_PHYS;
+
+ scatterwalk_start(&walk->in, req->src);
+ scatterwalk_start(&walk->out, req->dst);
+
+ scatterwalk_copychunks(NULL, &walk->in, req->assoclen, 2);
+ scatterwalk_copychunks(NULL, &walk->out, req->assoclen, 2);
+
+ walk->iv = req->iv;
+ walk->oiv = req->iv;
+
+ if (req->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP)
+ walk->flags |= SKCIPHER_WALK_SLEEP;
+ else
+ walk->flags &= ~SKCIPHER_WALK_SLEEP;
+
+ walk->blocksize = crypto_aead_blocksize(tfm);
+ walk->stride = crypto_aead_chunksize(tfm);
+ walk->ivsize = crypto_aead_ivsize(tfm);
+ walk->alignmask = crypto_aead_alignmask(tfm);
+
+ err = skcipher_walk_first(walk);
+
+ if (atomic)
+ walk->flags &= ~SKCIPHER_WALK_SLEEP;
+
+ return err;
+}
+
+int skcipher_walk_aead(struct skcipher_walk *walk, struct aead_request *req,
+ bool atomic)
+{
+ walk->total = req->cryptlen;
+
+ return skcipher_walk_aead_common(walk, req, atomic);
+}
+EXPORT_SYMBOL_GPL(skcipher_walk_aead);
+
+int skcipher_walk_aead_encrypt(struct skcipher_walk *walk,
+ struct aead_request *req, bool atomic)
+{
+ walk->total = req->cryptlen;
+
+ return skcipher_walk_aead_common(walk, req, atomic);
+}
+EXPORT_SYMBOL_GPL(skcipher_walk_aead_encrypt);
+
+int skcipher_walk_aead_decrypt(struct skcipher_walk *walk,
+ struct aead_request *req, bool atomic)
+{
+ struct crypto_aead *tfm = crypto_aead_reqtfm(req);
+
+ walk->total = req->cryptlen - crypto_aead_authsize(tfm);
+
+ return skcipher_walk_aead_common(walk, req, atomic);
+}
+EXPORT_SYMBOL_GPL(skcipher_walk_aead_decrypt);
+
+static unsigned int crypto_skcipher_extsize(struct crypto_alg *alg)
+{
+ if (alg->cra_type == &crypto_blkcipher_type)
+ return sizeof(struct crypto_blkcipher *);
+
+ if (alg->cra_type == &crypto_ablkcipher_type ||
+ alg->cra_type == &crypto_givcipher_type)
+ return sizeof(struct crypto_ablkcipher *);
+
+ return crypto_alg_extsize(alg);
+}
+
+static int skcipher_setkey_blkcipher(struct crypto_skcipher *tfm,
+ const u8 *key, unsigned int keylen)
+{
+ struct crypto_blkcipher **ctx = crypto_skcipher_ctx(tfm);
+ struct crypto_blkcipher *blkcipher = *ctx;
+ int err;
+
+ crypto_blkcipher_clear_flags(blkcipher, ~0);
+ crypto_blkcipher_set_flags(blkcipher, crypto_skcipher_get_flags(tfm) &
+ CRYPTO_TFM_REQ_MASK);
+ err = crypto_blkcipher_setkey(blkcipher, key, keylen);
+ crypto_skcipher_set_flags(tfm, crypto_blkcipher_get_flags(blkcipher) &
+ CRYPTO_TFM_RES_MASK);
+
+ return err;
+}
+
+static int skcipher_crypt_blkcipher(struct skcipher_request *req,
+ int (*crypt)(struct blkcipher_desc *,
+ struct scatterlist *,
+ struct scatterlist *,
+ unsigned int))
+{
+ struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
+ struct crypto_blkcipher **ctx = crypto_skcipher_ctx(tfm);
+ struct blkcipher_desc desc = {
+ .tfm = *ctx,
+ .info = req->iv,
+ .flags = req->base.flags,
+ };
+
+
+ return crypt(&desc, req->dst, req->src, req->cryptlen);
+}
+
+static int skcipher_encrypt_blkcipher(struct skcipher_request *req)
+{
+ struct crypto_skcipher *skcipher = crypto_skcipher_reqtfm(req);
+ struct crypto_tfm *tfm = crypto_skcipher_tfm(skcipher);
+ struct blkcipher_alg *alg = &tfm->__crt_alg->cra_blkcipher;
+
+ return skcipher_crypt_blkcipher(req, alg->encrypt);
+}
+
+static int skcipher_decrypt_blkcipher(struct skcipher_request *req)
+{
+ struct crypto_skcipher *skcipher = crypto_skcipher_reqtfm(req);
+ struct crypto_tfm *tfm = crypto_skcipher_tfm(skcipher);
+ struct blkcipher_alg *alg = &tfm->__crt_alg->cra_blkcipher;
+
+ return skcipher_crypt_blkcipher(req, alg->decrypt);
+}
+
+static void crypto_exit_skcipher_ops_blkcipher(struct crypto_tfm *tfm)
+{
+ struct crypto_blkcipher **ctx = crypto_tfm_ctx(tfm);
+
+ crypto_free_blkcipher(*ctx);
+}
+
+static int crypto_init_skcipher_ops_blkcipher(struct crypto_tfm *tfm)
+{
+ struct crypto_alg *calg = tfm->__crt_alg;
+ struct crypto_skcipher *skcipher = __crypto_skcipher_cast(tfm);
+ struct crypto_blkcipher **ctx = crypto_tfm_ctx(tfm);
+ struct crypto_blkcipher *blkcipher;
+ struct crypto_tfm *btfm;
+
+ if (!crypto_mod_get(calg))
+ return -EAGAIN;
+
+ btfm = __crypto_alloc_tfm(calg, CRYPTO_ALG_TYPE_BLKCIPHER,
+ CRYPTO_ALG_TYPE_MASK);
+ if (IS_ERR(btfm)) {
+ crypto_mod_put(calg);
+ return PTR_ERR(btfm);
+ }
+
+ blkcipher = __crypto_blkcipher_cast(btfm);
+ *ctx = blkcipher;
+ tfm->exit = crypto_exit_skcipher_ops_blkcipher;
+
+ skcipher->setkey = skcipher_setkey_blkcipher;
+ skcipher->encrypt = skcipher_encrypt_blkcipher;
+ skcipher->decrypt = skcipher_decrypt_blkcipher;
+
+ skcipher->ivsize = crypto_blkcipher_ivsize(blkcipher);
+ skcipher->keysize = calg->cra_blkcipher.max_keysize;
+
+ return 0;
+}
+
+static int skcipher_setkey_ablkcipher(struct crypto_skcipher *tfm,
+ const u8 *key, unsigned int keylen)
+{
+ struct crypto_ablkcipher **ctx = crypto_skcipher_ctx(tfm);
+ struct crypto_ablkcipher *ablkcipher = *ctx;
+ int err;
+
+ crypto_ablkcipher_clear_flags(ablkcipher, ~0);
+ crypto_ablkcipher_set_flags(ablkcipher,
+ crypto_skcipher_get_flags(tfm) &
+ CRYPTO_TFM_REQ_MASK);
+ err = crypto_ablkcipher_setkey(ablkcipher, key, keylen);
+ crypto_skcipher_set_flags(tfm,
+ crypto_ablkcipher_get_flags(ablkcipher) &
+ CRYPTO_TFM_RES_MASK);
+
+ return err;
+}
+
+static int skcipher_crypt_ablkcipher(struct skcipher_request *req,
+ int (*crypt)(struct ablkcipher_request *))
+{
+ struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
+ struct crypto_ablkcipher **ctx = crypto_skcipher_ctx(tfm);
+ struct ablkcipher_request *subreq = skcipher_request_ctx(req);
+
+ ablkcipher_request_set_tfm(subreq, *ctx);
+ ablkcipher_request_set_callback(subreq, skcipher_request_flags(req),
+ req->base.complete, req->base.data);
+ ablkcipher_request_set_crypt(subreq, req->src, req->dst, req->cryptlen,
+ req->iv);
+
+ return crypt(subreq);
+}
+
+static int skcipher_encrypt_ablkcipher(struct skcipher_request *req)
+{
+ struct crypto_skcipher *skcipher = crypto_skcipher_reqtfm(req);
+ struct crypto_tfm *tfm = crypto_skcipher_tfm(skcipher);
+ struct ablkcipher_alg *alg = &tfm->__crt_alg->cra_ablkcipher;
+
+ return skcipher_crypt_ablkcipher(req, alg->encrypt);
+}
+
+static int skcipher_decrypt_ablkcipher(struct skcipher_request *req)
+{
+ struct crypto_skcipher *skcipher = crypto_skcipher_reqtfm(req);
+ struct crypto_tfm *tfm = crypto_skcipher_tfm(skcipher);
+ struct ablkcipher_alg *alg = &tfm->__crt_alg->cra_ablkcipher;
+
+ return skcipher_crypt_ablkcipher(req, alg->decrypt);
+}
+
+static void crypto_exit_skcipher_ops_ablkcipher(struct crypto_tfm *tfm)
+{
+ struct crypto_ablkcipher **ctx = crypto_tfm_ctx(tfm);
+
+ crypto_free_ablkcipher(*ctx);
+}
+
+static int crypto_init_skcipher_ops_ablkcipher(struct crypto_tfm *tfm)
+{
+ struct crypto_alg *calg = tfm->__crt_alg;
+ struct crypto_skcipher *skcipher = __crypto_skcipher_cast(tfm);
+ struct crypto_ablkcipher **ctx = crypto_tfm_ctx(tfm);
+ struct crypto_ablkcipher *ablkcipher;
+ struct crypto_tfm *abtfm;
+
+ if (!crypto_mod_get(calg))
+ return -EAGAIN;
+
+ abtfm = __crypto_alloc_tfm(calg, 0, 0);
+ if (IS_ERR(abtfm)) {
+ crypto_mod_put(calg);
+ return PTR_ERR(abtfm);
+ }
+
+ ablkcipher = __crypto_ablkcipher_cast(abtfm);
+ *ctx = ablkcipher;
+ tfm->exit = crypto_exit_skcipher_ops_ablkcipher;
+
+ skcipher->setkey = skcipher_setkey_ablkcipher;
+ skcipher->encrypt = skcipher_encrypt_ablkcipher;
+ skcipher->decrypt = skcipher_decrypt_ablkcipher;
+
+ skcipher->ivsize = crypto_ablkcipher_ivsize(ablkcipher);
+ skcipher->reqsize = crypto_ablkcipher_reqsize(ablkcipher) +
+ sizeof(struct ablkcipher_request);
+ skcipher->keysize = calg->cra_ablkcipher.max_keysize;
+
+ return 0;
+}
+
+static int skcipher_setkey_unaligned(struct crypto_skcipher *tfm,
+ const u8 *key, unsigned int keylen)
+{
+ unsigned long alignmask = crypto_skcipher_alignmask(tfm);
+ struct skcipher_alg *cipher = crypto_skcipher_alg(tfm);
+ u8 *buffer, *alignbuffer;
+ unsigned long absize;
+ int ret;
+
+ absize = keylen + alignmask;
+ buffer = kmalloc(absize, GFP_ATOMIC);
+ if (!buffer)
+ return -ENOMEM;
+
+ alignbuffer = (u8 *)ALIGN((unsigned long)buffer, alignmask + 1);
+ memcpy(alignbuffer, key, keylen);
+ ret = cipher->setkey(tfm, alignbuffer, keylen);
+ kzfree(buffer);
+ return ret;
+}
+
+static int skcipher_setkey(struct crypto_skcipher *tfm, const u8 *key,
+ unsigned int keylen)
+{
+ struct skcipher_alg *cipher = crypto_skcipher_alg(tfm);
+ unsigned long alignmask = crypto_skcipher_alignmask(tfm);
+
+ if (keylen < cipher->min_keysize || keylen > cipher->max_keysize) {
+ crypto_skcipher_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN);
+ return -EINVAL;
+ }
+
+ if ((unsigned long)key & alignmask)
+ return skcipher_setkey_unaligned(tfm, key, keylen);
+
+ return cipher->setkey(tfm, key, keylen);
+}
+
+static void crypto_skcipher_exit_tfm(struct crypto_tfm *tfm)
+{
+ struct crypto_skcipher *skcipher = __crypto_skcipher_cast(tfm);
+ struct skcipher_alg *alg = crypto_skcipher_alg(skcipher);
+
+ alg->exit(skcipher);
+}
+
+static int crypto_skcipher_init_tfm(struct crypto_tfm *tfm)
+{
+ struct crypto_skcipher *skcipher = __crypto_skcipher_cast(tfm);
+ struct skcipher_alg *alg = crypto_skcipher_alg(skcipher);
+
+ if (tfm->__crt_alg->cra_type == &crypto_blkcipher_type)
+ return crypto_init_skcipher_ops_blkcipher(tfm);
+
+ if (tfm->__crt_alg->cra_type == &crypto_ablkcipher_type ||
+ tfm->__crt_alg->cra_type == &crypto_givcipher_type)
+ return crypto_init_skcipher_ops_ablkcipher(tfm);
+
+ skcipher->setkey = skcipher_setkey;
+ skcipher->encrypt = alg->encrypt;
+ skcipher->decrypt = alg->decrypt;
+ skcipher->ivsize = alg->ivsize;
+ skcipher->keysize = alg->max_keysize;
+
+ if (alg->exit)
+ skcipher->base.exit = crypto_skcipher_exit_tfm;
+
+ if (alg->init)
+ return alg->init(skcipher);
+
+ return 0;
+}
+
+static void crypto_skcipher_free_instance(struct crypto_instance *inst)
+{
+ struct skcipher_instance *skcipher =
+ container_of(inst, struct skcipher_instance, s.base);
+
+ skcipher->free(skcipher);
+}
+
+static void crypto_skcipher_show(struct seq_file *m, struct crypto_alg *alg)
+ __maybe_unused;
+static void crypto_skcipher_show(struct seq_file *m, struct crypto_alg *alg)
+{
+ struct skcipher_alg *skcipher = container_of(alg, struct skcipher_alg,
+ base);
+
+ seq_printf(m, "type : skcipher\n");
+ seq_printf(m, "async : %s\n",
+ alg->cra_flags & CRYPTO_ALG_ASYNC ? "yes" : "no");
+ seq_printf(m, "blocksize : %u\n", alg->cra_blocksize);
+ seq_printf(m, "min keysize : %u\n", skcipher->min_keysize);
+ seq_printf(m, "max keysize : %u\n", skcipher->max_keysize);
+ seq_printf(m, "ivsize : %u\n", skcipher->ivsize);
+ seq_printf(m, "chunksize : %u\n", skcipher->chunksize);
+ seq_printf(m, "walksize : %u\n", skcipher->walksize);
+}
+
+#ifdef CONFIG_NET
+static int crypto_skcipher_report(struct sk_buff *skb, struct crypto_alg *alg)
+{
+ struct crypto_report_blkcipher rblkcipher;
+ struct skcipher_alg *skcipher = container_of(alg, struct skcipher_alg,
+ base);
+
+ strncpy(rblkcipher.type, "skcipher", sizeof(rblkcipher.type));
+ strncpy(rblkcipher.geniv, "<none>", sizeof(rblkcipher.geniv));
+
+ rblkcipher.blocksize = alg->cra_blocksize;
+ rblkcipher.min_keysize = skcipher->min_keysize;
+ rblkcipher.max_keysize = skcipher->max_keysize;
+ rblkcipher.ivsize = skcipher->ivsize;
+
+ if (nla_put(skb, CRYPTOCFGA_REPORT_BLKCIPHER,
+ sizeof(struct crypto_report_blkcipher), &rblkcipher))
+ goto nla_put_failure;
+ return 0;
+
+nla_put_failure:
+ return -EMSGSIZE;
+}
+#else
+static int crypto_skcipher_report(struct sk_buff *skb, struct crypto_alg *alg)
+{
+ return -ENOSYS;
+}
+#endif
+
+static const struct crypto_type crypto_skcipher_type2 = {
+ .extsize = crypto_skcipher_extsize,
+ .init_tfm = crypto_skcipher_init_tfm,
+ .free = crypto_skcipher_free_instance,
+#ifdef CONFIG_PROC_FS
+ .show = crypto_skcipher_show,
+#endif
+ .report = crypto_skcipher_report,
+ .maskclear = ~CRYPTO_ALG_TYPE_MASK,
+ .maskset = CRYPTO_ALG_TYPE_BLKCIPHER_MASK,
+ .type = CRYPTO_ALG_TYPE_SKCIPHER,
+ .tfmsize = offsetof(struct crypto_skcipher, base),
+};
+
+int crypto_grab_skcipher(struct crypto_skcipher_spawn *spawn,
+ const char *name, u32 type, u32 mask)
+{
+ spawn->base.frontend = &crypto_skcipher_type2;
+ return crypto_grab_spawn(&spawn->base, name, type, mask);
+}
+EXPORT_SYMBOL_GPL(crypto_grab_skcipher);
+
+struct crypto_skcipher *crypto_alloc_skcipher(const char *alg_name,
+ u32 type, u32 mask)
+{
+ return crypto_alloc_tfm(alg_name, &crypto_skcipher_type2, type, mask);
+}
+EXPORT_SYMBOL_GPL(crypto_alloc_skcipher);
+
+int crypto_has_skcipher2(const char *alg_name, u32 type, u32 mask)
+{
+ return crypto_type_has_alg(alg_name, &crypto_skcipher_type2,
+ type, mask);
+}
+EXPORT_SYMBOL_GPL(crypto_has_skcipher2);
+
+static int skcipher_prepare_alg(struct skcipher_alg *alg)
+{
+ struct crypto_alg *base = &alg->base;
+
+ if (alg->ivsize > PAGE_SIZE / 8 || alg->chunksize > PAGE_SIZE / 8 ||
+ alg->walksize > PAGE_SIZE / 8)
+ return -EINVAL;
+
+ if (!alg->chunksize)
+ alg->chunksize = base->cra_blocksize;
+ if (!alg->walksize)
+ alg->walksize = alg->chunksize;
+
+ base->cra_type = &crypto_skcipher_type2;
+ base->cra_flags &= ~CRYPTO_ALG_TYPE_MASK;
+ base->cra_flags |= CRYPTO_ALG_TYPE_SKCIPHER;
+
+ return 0;
+}
+
+int crypto_register_skcipher(struct skcipher_alg *alg)
+{
+ struct crypto_alg *base = &alg->base;
+ int err;
+
+ err = skcipher_prepare_alg(alg);
+ if (err)
+ return err;
+
+ return crypto_register_alg(base);
+}
+EXPORT_SYMBOL_GPL(crypto_register_skcipher);
+
+void crypto_unregister_skcipher(struct skcipher_alg *alg)
+{
+ crypto_unregister_alg(&alg->base);
+}
+EXPORT_SYMBOL_GPL(crypto_unregister_skcipher);
+
+int crypto_register_skciphers(struct skcipher_alg *algs, int count)
+{
+ int i, ret;
+
+ for (i = 0; i < count; i++) {
+ ret = crypto_register_skcipher(&algs[i]);
+ if (ret)
+ goto err;
+ }
+
+ return 0;
+
+err:
+ for (--i; i >= 0; --i)
+ crypto_unregister_skcipher(&algs[i]);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(crypto_register_skciphers);
+
+void crypto_unregister_skciphers(struct skcipher_alg *algs, int count)
+{
+ int i;
+
+ for (i = count - 1; i >= 0; --i)
+ crypto_unregister_skcipher(&algs[i]);
+}
+EXPORT_SYMBOL_GPL(crypto_unregister_skciphers);
+
+int skcipher_register_instance(struct crypto_template *tmpl,
+ struct skcipher_instance *inst)
+{
+ int err;
+
+ err = skcipher_prepare_alg(&inst->alg);
+ if (err)
+ return err;
+
+ return crypto_register_instance(tmpl, skcipher_crypto_instance(inst));
+}
+EXPORT_SYMBOL_GPL(skcipher_register_instance);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Symmetric key cipher type");
diff --git a/compat/drivers-base-devcoredump.c b/compat/drivers-base-devcoredump.c
new file mode 100644
index 0000000..ffdba48
--- /dev/null
+++ b/compat/drivers-base-devcoredump.c
@@ -0,0 +1,390 @@
+/*
+ * This file is provided under the GPLv2 license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2014 Intel Mobile Communications GmbH
+ * Copyright(c) 2015 Intel Deutschland GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * Author: Johannes Berg <johannes@sipsolutions.net>
+ */
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/devcoredump.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/workqueue.h>
+#include "backports.h"
+
+static struct class devcd_class;
+
+/* global disable flag, for security purposes */
+static bool devcd_disabled;
+
+/* if data isn't read by userspace after 5 minutes then delete it */
+#define DEVCD_TIMEOUT (HZ * 60 * 5)
+
+#if LINUX_VERSION_IS_LESS(3,11,0)
+static struct bin_attribute devcd_attr_data;
+#endif
+
+struct devcd_entry {
+ struct device devcd_dev;
+ void *data;
+ size_t datalen;
+ struct module *owner;
+ ssize_t (*read)(char *buffer, loff_t offset, size_t count,
+ void *data, size_t datalen);
+ void (*free)(void *data);
+ struct delayed_work del_wk;
+ struct device *failing_dev;
+};
+
+static struct devcd_entry *dev_to_devcd(struct device *dev)
+{
+ return container_of(dev, struct devcd_entry, devcd_dev);
+}
+
+static void devcd_dev_release(struct device *dev)
+{
+ struct devcd_entry *devcd = dev_to_devcd(dev);
+
+ devcd->free(devcd->data);
+ module_put(devcd->owner);
+
+ /*
+ * this seems racy, but I don't see a notifier or such on
+ * a struct device to know when it goes away?
+ */
+ if (devcd->failing_dev->kobj.sd)
+ sysfs_remove_link(&devcd->failing_dev->kobj, "devcoredump");
+
+ put_device(devcd->failing_dev);
+ kfree(devcd);
+}
+
+static void devcd_del(struct work_struct *wk)
+{
+ struct devcd_entry *devcd;
+
+ devcd = container_of(wk, struct devcd_entry, del_wk.work);
+
+#if LINUX_VERSION_IS_LESS(3,11,0)
+ device_remove_bin_file(&devcd->devcd_dev, &devcd_attr_data);
+#endif
+ device_del(&devcd->devcd_dev);
+ put_device(&devcd->devcd_dev);
+}
+
+static ssize_t devcd_data_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buffer, loff_t offset, size_t count)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct devcd_entry *devcd = dev_to_devcd(dev);
+
+ return devcd->read(buffer, offset, count, devcd->data, devcd->datalen);
+}
+
+static ssize_t devcd_data_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buffer, loff_t offset, size_t count)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct devcd_entry *devcd = dev_to_devcd(dev);
+
+ mod_delayed_work(system_wq, &devcd->del_wk, 0);
+
+ return count;
+}
+
+static struct bin_attribute devcd_attr_data = {
+ .attr = { .name = "data", .mode = S_IRUSR | S_IWUSR, },
+ .size = 0,
+ .read = devcd_data_read,
+ .write = devcd_data_write,
+};
+
+#if LINUX_VERSION_IS_GEQ(3,11,0)
+static struct bin_attribute *devcd_dev_bin_attrs[] = {
+ &devcd_attr_data, NULL,
+};
+
+static const struct attribute_group devcd_dev_group = {
+ .bin_attrs = devcd_dev_bin_attrs,
+};
+
+static const struct attribute_group *devcd_dev_groups[] = {
+ &devcd_dev_group, NULL,
+};
+#endif /* LINUX_VERSION_IS_GEQ(3,11,0) */
+
+static int devcd_free(struct device *dev, void *data)
+{
+ struct devcd_entry *devcd = dev_to_devcd(dev);
+
+ flush_delayed_work(&devcd->del_wk);
+ return 0;
+}
+
+#if LINUX_VERSION_IS_GEQ(3,11,0)
+static ssize_t disabled_show(struct class *class, struct class_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%d\n", devcd_disabled);
+}
+
+static ssize_t disabled_store(struct class *class, struct class_attribute *attr,
+ const char *buf, size_t count)
+{
+ long tmp = simple_strtol(buf, NULL, 10);
+
+ /*
+ * This essentially makes the attribute write-once, since you can't
+ * go back to not having it disabled. This is intentional, it serves
+ * as a system lockdown feature.
+ */
+ if (tmp != 1)
+ return -EINVAL;
+
+ devcd_disabled = true;
+
+ class_for_each_device(&devcd_class, NULL, NULL, devcd_free);
+
+ return count;
+}
+static CLASS_ATTR_RW(disabled);
+
+static struct attribute *devcd_class_attrs[] = {
+ &class_attr_disabled.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(devcd_class);
+#endif
+
+static struct class devcd_class = {
+ .name = "devcoredump",
+ .owner = THIS_MODULE,
+ .dev_release = devcd_dev_release,
+#if LINUX_VERSION_IS_GEQ(3,11,0)
+ .dev_groups = devcd_dev_groups,
+#endif
+#if LINUX_VERSION_IS_GEQ(4,10,0)
+ .class_groups = devcd_class_groups,
+#endif
+};
+
+static ssize_t devcd_readv(char *buffer, loff_t offset, size_t count,
+ void *data, size_t datalen)
+{
+ if (offset > datalen)
+ return -EINVAL;
+
+ if (offset + count > datalen)
+ count = datalen - offset;
+
+ if (count)
+ memcpy(buffer, ((u8 *)data) + offset, count);
+
+ return count;
+}
+
+static void devcd_freev(void *data)
+{
+ vfree(data);
+}
+
+/**
+ * dev_coredumpv - create device coredump with vmalloc data
+ * @dev: the struct device for the crashed device
+ * @data: vmalloc data containing the device coredump
+ * @datalen: length of the data
+ * @gfp: allocation flags
+ *
+ * This function takes ownership of the vmalloc'ed data and will free
+ * it when it is no longer used. See dev_coredumpm() for more information.
+ */
+void dev_coredumpv(struct device *dev, void *data, size_t datalen,
+ gfp_t gfp)
+{
+ dev_coredumpm(dev, NULL, data, datalen, gfp, devcd_readv, devcd_freev);
+}
+EXPORT_SYMBOL_GPL(dev_coredumpv);
+
+static int devcd_match_failing(struct device *dev, const void *failing)
+{
+ struct devcd_entry *devcd = dev_to_devcd(dev);
+
+ return devcd->failing_dev == failing;
+}
+
+/**
+ * devcd_free_sgtable - free all the memory of the given scatterlist table
+ * (i.e. both pages and scatterlist instances)
+ * NOTE: if two tables allocated with devcd_alloc_sgtable and then chained
+ * using the sg_chain function then that function should be called only once
+ * on the chained table
+ * @table: pointer to sg_table to free
+ */
+static void devcd_free_sgtable(void *data)
+{
+ _devcd_free_sgtable(data);
+}
+
+/**
+ * devcd_read_from_table - copy data from sg_table to a given buffer
+ * and return the number of bytes read
+ * @buffer: the buffer to copy the data to it
+ * @buf_len: the length of the buffer
+ * @data: the scatterlist table to copy from
+ * @offset: start copy from @offset@ bytes from the head of the data
+ * in the given scatterlist
+ * @data_len: the length of the data in the sg_table
+ */
+static ssize_t devcd_read_from_sgtable(char *buffer, loff_t offset,
+ size_t buf_len, void *data,
+ size_t data_len)
+{
+ struct scatterlist *table = data;
+
+ if (offset > data_len)
+ return -EINVAL;
+
+ if (offset + buf_len > data_len)
+ buf_len = data_len - offset;
+ return sg_pcopy_to_buffer(table, sg_nents(table), buffer, buf_len,
+ offset);
+}
+
+/**
+ * dev_coredumpm - create device coredump with read/free methods
+ * @dev: the struct device for the crashed device
+ * @owner: the module that contains the read/free functions, use %THIS_MODULE
+ * @data: data cookie for the @read/@free functions
+ * @datalen: length of the data
+ * @gfp: allocation flags
+ * @read: function to read from the given buffer
+ * @free: function to free the given buffer
+ *
+ * Creates a new device coredump for the given device. If a previous one hasn't
+ * been read yet, the new coredump is discarded. The data lifetime is determined
+ * by the device coredump framework and when it is no longer needed the @free
+ * function will be called to free the data.
+ */
+void dev_coredumpm(struct device *dev, struct module *owner,
+ void *data, size_t datalen, gfp_t gfp,
+ ssize_t (*read)(char *buffer, loff_t offset, size_t count,
+ void *data, size_t datalen),
+ void (*free)(void *data))
+{
+ static atomic_t devcd_count = ATOMIC_INIT(0);
+ struct devcd_entry *devcd;
+ struct device *existing;
+
+ if (devcd_disabled)
+ goto free;
+
+ existing = class_find_device(&devcd_class, NULL, dev,
+ devcd_match_failing);
+ if (existing) {
+ put_device(existing);
+ goto free;
+ }
+
+ if (!try_module_get(owner))
+ goto free;
+
+ devcd = kzalloc(sizeof(*devcd), gfp);
+ if (!devcd)
+ goto put_module;
+
+ devcd->owner = owner;
+ devcd->data = data;
+ devcd->datalen = datalen;
+ devcd->read = read;
+ devcd->free = free;
+ devcd->failing_dev = get_device(dev);
+
+ device_initialize(&devcd->devcd_dev);
+
+ dev_set_name(&devcd->devcd_dev, "devcd%d",
+ atomic_inc_return(&devcd_count));
+ devcd->devcd_dev.class = &devcd_class;
+
+ if (device_add(&devcd->devcd_dev))
+ goto put_device;
+
+#if LINUX_VERSION_IS_LESS(3,11,0)
+ if (device_create_bin_file(&devcd->devcd_dev, &devcd_attr_data))
+ goto put_device;
+#endif
+
+ if (sysfs_create_link(&devcd->devcd_dev.kobj, &dev->kobj,
+ "failing_device"))
+ /* nothing - symlink will be missing */;
+
+ if (sysfs_create_link(&dev->kobj, &devcd->devcd_dev.kobj,
+ "devcoredump"))
+ /* nothing - symlink will be missing */;
+
+ INIT_DELAYED_WORK(&devcd->del_wk, devcd_del);
+ schedule_delayed_work(&devcd->del_wk, DEVCD_TIMEOUT);
+
+ return;
+ put_device:
+ put_device(&devcd->devcd_dev);
+ put_module:
+ module_put(owner);
+ free:
+ free(data);
+}
+EXPORT_SYMBOL_GPL(dev_coredumpm);
+
+/**
+ * dev_coredumpmsg - create device coredump that uses scatterlist as data
+ * parameter
+ * @dev: the struct device for the crashed device
+ * @table: the dump data
+ * @datalen: length of the data
+ * @gfp: allocation flags
+ *
+ * Creates a new device coredump for the given device. If a previous one hasn't
+ * been read yet, the new coredump is discarded. The data lifetime is determined
+ * by the device coredump framework and when it is no longer needed
+ * it will free the data.
+ */
+void dev_coredumpsg(struct device *dev, struct scatterlist *table,
+ size_t datalen, gfp_t gfp)
+{
+ dev_coredumpm(dev, NULL, table, datalen, gfp, devcd_read_from_sgtable,
+ devcd_free_sgtable);
+}
+EXPORT_SYMBOL_GPL(dev_coredumpsg);
+
+int __init devcoredump_init(void)
+{
+ return class_register(&devcd_class);
+}
+
+void __exit devcoredump_exit(void)
+{
+ class_for_each_device(&devcd_class, NULL, NULL, devcd_free);
+ class_unregister(&devcd_class);
+}
diff --git a/compat/hid-ids.h b/compat/hid-ids.h
new file mode 100644
index 0000000..c147dc0
--- /dev/null
+++ b/compat/hid-ids.h
@@ -0,0 +1,866 @@
+/*
+ * USB HID quirks support for Linux
+ *
+ * Copyright (c) 1999 Andreas Gal
+ * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ * Copyright (c) 2006-2007 Jiri Kosina
+ */
+
+/*
+ * 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.
+ */
+
+#ifndef HID_IDS_H_FILE
+#define HID_IDS_H_FILE
+
+#define USB_VENDOR_ID_3M 0x0596
+#define USB_DEVICE_ID_3M1968 0x0500
+#define USB_DEVICE_ID_3M2256 0x0502
+#define USB_DEVICE_ID_3M3266 0x0506
+
+#define USB_VENDOR_ID_A4TECH 0x09da
+#define USB_DEVICE_ID_A4TECH_WCP32PU 0x0006
+#define USB_DEVICE_ID_A4TECH_X5_005D 0x000a
+#define USB_DEVICE_ID_A4TECH_RP_649 0x001a
+
+#define USB_VENDOR_ID_AASHIMA 0x06d6
+#define USB_DEVICE_ID_AASHIMA_GAMEPAD 0x0025
+#define USB_DEVICE_ID_AASHIMA_PREDATOR 0x0026
+
+#define USB_VENDOR_ID_ACECAD 0x0460
+#define USB_DEVICE_ID_ACECAD_FLAIR 0x0004
+#define USB_DEVICE_ID_ACECAD_302 0x0008
+
+#define USB_VENDOR_ID_ACRUX 0x1a34
+
+#define USB_VENDOR_ID_ACTIONSTAR 0x2101
+#define USB_DEVICE_ID_ACTIONSTAR_1011 0x1011
+
+#define USB_VENDOR_ID_ADS_TECH 0x06e1
+#define USB_DEVICE_ID_ADS_TECH_RADIO_SI470X 0xa155
+
+#define USB_VENDOR_ID_AFATECH 0x15a4
+#define USB_DEVICE_ID_AFATECH_AF9016 0x9016
+
+#define USB_VENDOR_ID_AIPTEK 0x08ca
+#define USB_DEVICE_ID_AIPTEK_01 0x0001
+#define USB_DEVICE_ID_AIPTEK_10 0x0010
+#define USB_DEVICE_ID_AIPTEK_20 0x0020
+#define USB_DEVICE_ID_AIPTEK_21 0x0021
+#define USB_DEVICE_ID_AIPTEK_22 0x0022
+#define USB_DEVICE_ID_AIPTEK_23 0x0023
+#define USB_DEVICE_ID_AIPTEK_24 0x0024
+
+#define USB_VENDOR_ID_AIRCABLE 0x16CA
+#define USB_DEVICE_ID_AIRCABLE1 0x1502
+
+#define USB_VENDOR_ID_AIREN 0x1a2c
+#define USB_DEVICE_ID_AIREN_SLIMPLUS 0x0002
+
+#define USB_VENDOR_ID_ALCOR 0x058f
+#define USB_DEVICE_ID_ALCOR_USBRS232 0x9720
+
+#define USB_VENDOR_ID_ALPS 0x0433
+#define USB_DEVICE_ID_IBM_GAMEPAD 0x1101
+
+#define USB_VENDOR_ID_APPLE 0x05ac
+#define USB_DEVICE_ID_APPLE_MIGHTYMOUSE 0x0304
+#define USB_DEVICE_ID_APPLE_MAGICMOUSE 0x030d
+#define USB_DEVICE_ID_APPLE_MAGICTRACKPAD 0x030e
+#define USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI 0x020e
+#define USB_DEVICE_ID_APPLE_FOUNTAIN_ISO 0x020f
+#define USB_DEVICE_ID_APPLE_GEYSER_ANSI 0x0214
+#define USB_DEVICE_ID_APPLE_GEYSER_ISO 0x0215
+#define USB_DEVICE_ID_APPLE_GEYSER_JIS 0x0216
+#define USB_DEVICE_ID_APPLE_GEYSER3_ANSI 0x0217
+#define USB_DEVICE_ID_APPLE_GEYSER3_ISO 0x0218
+#define USB_DEVICE_ID_APPLE_GEYSER3_JIS 0x0219
+#define USB_DEVICE_ID_APPLE_GEYSER4_ANSI 0x021a
+#define USB_DEVICE_ID_APPLE_GEYSER4_ISO 0x021b
+#define USB_DEVICE_ID_APPLE_GEYSER4_JIS 0x021c
+#define USB_DEVICE_ID_APPLE_ALU_MINI_ANSI 0x021d
+#define USB_DEVICE_ID_APPLE_ALU_MINI_ISO 0x021e
+#define USB_DEVICE_ID_APPLE_ALU_MINI_JIS 0x021f
+#define USB_DEVICE_ID_APPLE_ALU_ANSI 0x0220
+#define USB_DEVICE_ID_APPLE_ALU_ISO 0x0221
+#define USB_DEVICE_ID_APPLE_ALU_JIS 0x0222
+#define USB_DEVICE_ID_APPLE_WELLSPRING_ANSI 0x0223
+#define USB_DEVICE_ID_APPLE_WELLSPRING_ISO 0x0224
+#define USB_DEVICE_ID_APPLE_WELLSPRING_JIS 0x0225
+#define USB_DEVICE_ID_APPLE_GEYSER4_HF_ANSI 0x0229
+#define USB_DEVICE_ID_APPLE_GEYSER4_HF_ISO 0x022a
+#define USB_DEVICE_ID_APPLE_GEYSER4_HF_JIS 0x022b
+#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI 0x022c
+#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_ISO 0x022d
+#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS 0x022e
+#define USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI 0x0230
+#define USB_DEVICE_ID_APPLE_WELLSPRING2_ISO 0x0231
+#define USB_DEVICE_ID_APPLE_WELLSPRING2_JIS 0x0232
+#define USB_DEVICE_ID_APPLE_WELLSPRING3_ANSI 0x0236
+#define USB_DEVICE_ID_APPLE_WELLSPRING3_ISO 0x0237
+#define USB_DEVICE_ID_APPLE_WELLSPRING3_JIS 0x0238
+#define USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI 0x023f
+#define USB_DEVICE_ID_APPLE_WELLSPRING4_ISO 0x0240
+#define USB_DEVICE_ID_APPLE_WELLSPRING4_JIS 0x0241
+#define USB_DEVICE_ID_APPLE_WELLSPRING4A_ANSI 0x0242
+#define USB_DEVICE_ID_APPLE_WELLSPRING4A_ISO 0x0243
+#define USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS 0x0244
+#define USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI 0x0245
+#define USB_DEVICE_ID_APPLE_WELLSPRING5_ISO 0x0246
+#define USB_DEVICE_ID_APPLE_WELLSPRING5_JIS 0x0247
+#define USB_DEVICE_ID_APPLE_ALU_REVB_ANSI 0x024f
+#define USB_DEVICE_ID_APPLE_ALU_REVB_ISO 0x0250
+#define USB_DEVICE_ID_APPLE_ALU_REVB_JIS 0x0251
+#define USB_DEVICE_ID_APPLE_WELLSPRING5A_ANSI 0x0252
+#define USB_DEVICE_ID_APPLE_WELLSPRING5A_ISO 0x0253
+#define USB_DEVICE_ID_APPLE_WELLSPRING5A_JIS 0x0254
+#define USB_DEVICE_ID_APPLE_WELLSPRING7A_ANSI 0x0259
+#define USB_DEVICE_ID_APPLE_WELLSPRING7A_ISO 0x025a
+#define USB_DEVICE_ID_APPLE_WELLSPRING7A_JIS 0x025b
+#define USB_DEVICE_ID_APPLE_WELLSPRING6A_ANSI 0x0249
+#define USB_DEVICE_ID_APPLE_WELLSPRING6A_ISO 0x024a
+#define USB_DEVICE_ID_APPLE_WELLSPRING6A_JIS 0x024b
+#define USB_DEVICE_ID_APPLE_WELLSPRING6_ANSI 0x024c
+#define USB_DEVICE_ID_APPLE_WELLSPRING6_ISO 0x024d
+#define USB_DEVICE_ID_APPLE_WELLSPRING6_JIS 0x024e
+#define USB_DEVICE_ID_APPLE_WELLSPRING7_ANSI 0x0262
+#define USB_DEVICE_ID_APPLE_WELLSPRING7_ISO 0x0263
+#define USB_DEVICE_ID_APPLE_WELLSPRING7_JIS 0x0264
+#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ANSI 0x0239
+#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO 0x023a
+#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_JIS 0x023b
+#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ANSI 0x0255
+#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ISO 0x0256
+#define USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY 0x030a
+#define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY 0x030b
+#define USB_DEVICE_ID_APPLE_ATV_IRCONTROL 0x8241
+#define USB_DEVICE_ID_APPLE_IRCONTROL4 0x8242
+
+#define USB_VENDOR_ID_ASUS 0x0486
+#define USB_DEVICE_ID_ASUS_T91MT 0x0185
+#define USB_DEVICE_ID_ASUSTEK_MULTITOUCH_YFO 0x0186
+
+#define USB_VENDOR_ID_ASUSTEK 0x0b05
+#define USB_DEVICE_ID_ASUSTEK_LCM 0x1726
+#define USB_DEVICE_ID_ASUSTEK_LCM2 0x175b
+
+#define USB_VENDOR_ID_ATEN 0x0557
+#define USB_DEVICE_ID_ATEN_UC100KM 0x2004
+#define USB_DEVICE_ID_ATEN_CS124U 0x2202
+#define USB_DEVICE_ID_ATEN_2PORTKVM 0x2204
+#define USB_DEVICE_ID_ATEN_4PORTKVM 0x2205
+#define USB_DEVICE_ID_ATEN_4PORTKVMC 0x2208
+
+#define USB_VENDOR_ID_ATMEL 0x03eb
+#define USB_DEVICE_ID_ATMEL_MULTITOUCH 0x211c
+#define USB_DEVICE_ID_ATMEL_MXT_DIGITIZER 0x2118
+
+#define USB_VENDOR_ID_AUREAL 0x0755
+#define USB_DEVICE_ID_AUREAL_W01RN 0x2626
+
+#define USB_VENDOR_ID_AVERMEDIA 0x07ca
+#define USB_DEVICE_ID_AVER_FM_MR800 0xb800
+
+#define USB_VENDOR_ID_AXENTIA 0x12cf
+#define USB_DEVICE_ID_AXENTIA_FM_RADIO 0x7111
+
+#define USB_VENDOR_ID_BAANTO 0x2453
+#define USB_DEVICE_ID_BAANTO_MT_190W2 0x0100
+
+#define USB_VENDOR_ID_BELKIN 0x050d
+#define USB_DEVICE_ID_FLIP_KVM 0x3201
+
+#define USB_VENDOR_ID_BERKSHIRE 0x0c98
+#define USB_DEVICE_ID_BERKSHIRE_PCWD 0x1140
+
+#define USB_VENDOR_ID_BTC 0x046e
+#define USB_DEVICE_ID_BTC_EMPREX_REMOTE 0x5578
+#define USB_DEVICE_ID_BTC_EMPREX_REMOTE_2 0x5577
+
+#define USB_VENDOR_ID_CANDO 0x2087
+#define USB_DEVICE_ID_CANDO_PIXCIR_MULTI_TOUCH 0x0703
+#define USB_DEVICE_ID_CANDO_MULTI_TOUCH 0x0a01
+#define USB_DEVICE_ID_CANDO_MULTI_TOUCH_10_1 0x0a02
+#define USB_DEVICE_ID_CANDO_MULTI_TOUCH_11_6 0x0b03
+#define USB_DEVICE_ID_CANDO_MULTI_TOUCH_15_6 0x0f01
+
+#define USB_VENDOR_ID_CH 0x068e
+#define USB_DEVICE_ID_CH_PRO_THROTTLE 0x00f1
+#define USB_DEVICE_ID_CH_PRO_PEDALS 0x00f2
+#define USB_DEVICE_ID_CH_FIGHTERSTICK 0x00f3
+#define USB_DEVICE_ID_CH_COMBATSTICK 0x00f4
+#define USB_DEVICE_ID_CH_FLIGHT_SIM_ECLIPSE_YOKE 0x0051
+#define USB_DEVICE_ID_CH_FLIGHT_SIM_YOKE 0x00ff
+#define USB_DEVICE_ID_CH_3AXIS_5BUTTON_STICK 0x00d3
+#define USB_DEVICE_ID_CH_AXIS_295 0x001c
+
+#define USB_VENDOR_ID_CHERRY 0x046a
+#define USB_DEVICE_ID_CHERRY_CYMOTION 0x0023
+#define USB_DEVICE_ID_CHERRY_CYMOTION_SOLAR 0x0027
+
+#define USB_VENDOR_ID_CHIC 0x05fe
+#define USB_DEVICE_ID_CHIC_GAMEPAD 0x0014
+
+#define USB_VENDOR_ID_CHICONY 0x04f2
+#define USB_DEVICE_ID_CHICONY_TACTICAL_PAD 0x0418
+#define USB_DEVICE_ID_CHICONY_MULTI_TOUCH 0xb19d
+#define USB_DEVICE_ID_CHICONY_WIRELESS 0x0618
+#define USB_DEVICE_ID_CHICONY_WIRELESS2 0x1123
+#define USB_DEVICE_ID_CHICONY_AK1D 0x1125
+
+#define USB_VENDOR_ID_CHUNGHWAT 0x2247
+#define USB_DEVICE_ID_CHUNGHWAT_MULTITOUCH 0x0001
+
+#define USB_VENDOR_ID_CIDC 0x1677
+
+#define USB_VENDOR_ID_CMEDIA 0x0d8c
+#define USB_DEVICE_ID_CM109 0x000e
+
+#define USB_VENDOR_ID_CODEMERCS 0x07c0
+#define USB_DEVICE_ID_CODEMERCS_IOW_FIRST 0x1500
+#define USB_DEVICE_ID_CODEMERCS_IOW_LAST 0x15ff
+
+#define USB_VENDOR_ID_CREATIVELABS 0x041e
+#define USB_DEVICE_ID_PRODIKEYS_PCMIDI 0x2801
+
+#define USB_VENDOR_ID_CVTOUCH 0x1ff7
+#define USB_DEVICE_ID_CVTOUCH_SCREEN 0x0013
+
+#define USB_VENDOR_ID_CYGNAL 0x10c4
+#define USB_DEVICE_ID_CYGNAL_RADIO_SI470X 0x818a
+
+#define USB_VENDOR_ID_CYPRESS 0x04b4
+#define USB_DEVICE_ID_CYPRESS_MOUSE 0x0001
+#define USB_DEVICE_ID_CYPRESS_HIDCOM 0x5500
+#define USB_DEVICE_ID_CYPRESS_ULTRAMOUSE 0x7417
+#define USB_DEVICE_ID_CYPRESS_BARCODE_1 0xde61
+#define USB_DEVICE_ID_CYPRESS_BARCODE_2 0xde64
+#define USB_DEVICE_ID_CYPRESS_BARCODE_3 0xbca1
+#define USB_DEVICE_ID_CYPRESS_BARCODE_4 0xed81
+#define USB_DEVICE_ID_CYPRESS_TRUETOUCH 0xc001
+
+#define USB_VENDOR_ID_DEALEXTREAME 0x10c5
+#define USB_DEVICE_ID_DEALEXTREAME_RADIO_SI4701 0x819a
+
+#define USB_VENDOR_ID_DELORME 0x1163
+#define USB_DEVICE_ID_DELORME_EARTHMATE 0x0100
+#define USB_DEVICE_ID_DELORME_EM_LT20 0x0200
+
+#define USB_VENDOR_ID_DMI 0x0c0b
+#define USB_DEVICE_ID_DMI_ENC 0x5fab
+
+#define USB_VENDOR_ID_DRAGONRISE 0x0079
+
+#define USB_VENDOR_ID_DWAV 0x0eef
+#define USB_DEVICE_ID_EGALAX_TOUCHCONTROLLER 0x0001
+#define USB_DEVICE_ID_DWAV_TOUCHCONTROLLER 0x0002
+#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_480D 0x480d
+#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_480E 0x480e
+#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7207 0x7207
+#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_720C 0x720c
+#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7224 0x7224
+#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_722A 0x722A
+#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_725E 0x725e
+#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7262 0x7262
+#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_726B 0x726b
+#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72AA 0x72aa
+#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72A1 0x72a1
+#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72FA 0x72fa
+#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7302 0x7302
+#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7349 0x7349
+#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_73F7 0x73f7
+#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001 0xa001
+#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7224 0x7224
+#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72D0 0x72d0
+#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72C4 0x72c4
+
+#define USB_VENDOR_ID_ELECOM 0x056e
+#define USB_DEVICE_ID_ELECOM_BM084 0x0061
+
+#define USB_VENDOR_ID_DREAM_CHEEKY 0x1d34
+
+#define USB_VENDOR_ID_ELO 0x04E7
+#define USB_DEVICE_ID_ELO_TS2515 0x0022
+#define USB_DEVICE_ID_ELO_TS2700 0x0020
+
+#define USB_VENDOR_ID_EMS 0x2006
+#define USB_DEVICE_ID_EMS_TRIO_LINKER_PLUS_II 0x0118
+
+#define USB_VENDOR_ID_FLATFROG 0x25b5
+#define USB_DEVICE_ID_MULTITOUCH_3200 0x0002
+
+#define USB_VENDOR_ID_ESSENTIAL_REALITY 0x0d7f
+#define USB_DEVICE_ID_ESSENTIAL_REALITY_P5 0x0100
+
+#define USB_VENDOR_ID_ETT 0x0664
+#define USB_DEVICE_ID_TC5UH 0x0309
+#define USB_DEVICE_ID_TC4UM 0x0306
+
+#define USB_VENDOR_ID_ETURBOTOUCH 0x22b9
+#define USB_DEVICE_ID_ETURBOTOUCH 0x0006
+
+#define USB_VENDOR_ID_EZKEY 0x0518
+#define USB_DEVICE_ID_BTC_8193 0x0002
+
+#define USB_VENDOR_ID_FREESCALE 0x15A2
+#define USB_DEVICE_ID_FREESCALE_MX28 0x004F
+
+#define USB_VENDOR_ID_FRUCTEL 0x25B6
+#define USB_DEVICE_ID_GAMETEL_MT_MODE 0x0002
+
+#define USB_VENDOR_ID_GAMERON 0x0810
+#define USB_DEVICE_ID_GAMERON_DUAL_PSX_ADAPTOR 0x0001
+#define USB_DEVICE_ID_GAMERON_DUAL_PCS_ADAPTOR 0x0002
+
+#define USB_VENDOR_ID_GENERAL_TOUCH 0x0dfc
+#define USB_DEVICE_ID_GENERAL_TOUCH_WIN7_TWOFINGERS 0x0003
+#define USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PWT_TENFINGERS 0x0100
+
+#define USB_VENDOR_ID_GLAB 0x06c2
+#define USB_DEVICE_ID_4_PHIDGETSERVO_30 0x0038
+#define USB_DEVICE_ID_1_PHIDGETSERVO_30 0x0039
+#define USB_DEVICE_ID_0_0_4_IF_KIT 0x0040
+#define USB_DEVICE_ID_0_16_16_IF_KIT 0x0044
+#define USB_DEVICE_ID_8_8_8_IF_KIT 0x0045
+#define USB_DEVICE_ID_0_8_7_IF_KIT 0x0051
+#define USB_DEVICE_ID_0_8_8_IF_KIT 0x0053
+#define USB_DEVICE_ID_PHIDGET_MOTORCONTROL 0x0058
+
+#define USB_VENDOR_ID_GOODTOUCH 0x1aad
+#define USB_DEVICE_ID_GOODTOUCH_000f 0x000f
+
+#define USB_VENDOR_ID_GOTOP 0x08f2
+#define USB_DEVICE_ID_SUPER_Q2 0x007f
+#define USB_DEVICE_ID_GOGOPEN 0x00ce
+#define USB_DEVICE_ID_PENPOWER 0x00f4
+
+#define USB_VENDOR_ID_GREENASIA 0x0e8f
+#define USB_DEVICE_ID_GREENASIA_DUAL_USB_JOYPAD 0x3013
+
+#define USB_VENDOR_ID_GRETAGMACBETH 0x0971
+#define USB_DEVICE_ID_GRETAGMACBETH_HUEY 0x2005
+
+#define USB_VENDOR_ID_GRIFFIN 0x077d
+#define USB_DEVICE_ID_POWERMATE 0x0410
+#define USB_DEVICE_ID_SOUNDKNOB 0x04AA
+#define USB_DEVICE_ID_RADIOSHARK 0x627a
+
+#define USB_VENDOR_ID_GTCO 0x078c
+#define USB_DEVICE_ID_GTCO_90 0x0090
+#define USB_DEVICE_ID_GTCO_100 0x0100
+#define USB_DEVICE_ID_GTCO_101 0x0101
+#define USB_DEVICE_ID_GTCO_103 0x0103
+#define USB_DEVICE_ID_GTCO_104 0x0104
+#define USB_DEVICE_ID_GTCO_105 0x0105
+#define USB_DEVICE_ID_GTCO_106 0x0106
+#define USB_DEVICE_ID_GTCO_107 0x0107
+#define USB_DEVICE_ID_GTCO_108 0x0108
+#define USB_DEVICE_ID_GTCO_200 0x0200
+#define USB_DEVICE_ID_GTCO_201 0x0201
+#define USB_DEVICE_ID_GTCO_202 0x0202
+#define USB_DEVICE_ID_GTCO_203 0x0203
+#define USB_DEVICE_ID_GTCO_204 0x0204
+#define USB_DEVICE_ID_GTCO_205 0x0205
+#define USB_DEVICE_ID_GTCO_206 0x0206
+#define USB_DEVICE_ID_GTCO_207 0x0207
+#define USB_DEVICE_ID_GTCO_300 0x0300
+#define USB_DEVICE_ID_GTCO_301 0x0301
+#define USB_DEVICE_ID_GTCO_302 0x0302
+#define USB_DEVICE_ID_GTCO_303 0x0303
+#define USB_DEVICE_ID_GTCO_304 0x0304
+#define USB_DEVICE_ID_GTCO_305 0x0305
+#define USB_DEVICE_ID_GTCO_306 0x0306
+#define USB_DEVICE_ID_GTCO_307 0x0307
+#define USB_DEVICE_ID_GTCO_308 0x0308
+#define USB_DEVICE_ID_GTCO_309 0x0309
+#define USB_DEVICE_ID_GTCO_400 0x0400
+#define USB_DEVICE_ID_GTCO_401 0x0401
+#define USB_DEVICE_ID_GTCO_402 0x0402
+#define USB_DEVICE_ID_GTCO_403 0x0403
+#define USB_DEVICE_ID_GTCO_404 0x0404
+#define USB_DEVICE_ID_GTCO_405 0x0405
+#define USB_DEVICE_ID_GTCO_500 0x0500
+#define USB_DEVICE_ID_GTCO_501 0x0501
+#define USB_DEVICE_ID_GTCO_502 0x0502
+#define USB_DEVICE_ID_GTCO_503 0x0503
+#define USB_DEVICE_ID_GTCO_504 0x0504
+#define USB_DEVICE_ID_GTCO_1000 0x1000
+#define USB_DEVICE_ID_GTCO_1001 0x1001
+#define USB_DEVICE_ID_GTCO_1002 0x1002
+#define USB_DEVICE_ID_GTCO_1003 0x1003
+#define USB_DEVICE_ID_GTCO_1004 0x1004
+#define USB_DEVICE_ID_GTCO_1005 0x1005
+#define USB_DEVICE_ID_GTCO_1006 0x1006
+#define USB_DEVICE_ID_GTCO_1007 0x1007
+
+#define USB_VENDOR_ID_GYRATION 0x0c16
+#define USB_DEVICE_ID_GYRATION_REMOTE 0x0002
+#define USB_DEVICE_ID_GYRATION_REMOTE_2 0x0003
+#define USB_DEVICE_ID_GYRATION_REMOTE_3 0x0008
+
+#define USB_VENDOR_ID_HANWANG 0x0b57
+#define USB_DEVICE_ID_HANWANG_TABLET_FIRST 0x5000
+#define USB_DEVICE_ID_HANWANG_TABLET_LAST 0x8fff
+
+#define USB_VENDOR_ID_HANVON 0x20b3
+#define USB_DEVICE_ID_HANVON_MULTITOUCH 0x0a18
+
+#define USB_VENDOR_ID_HANVON_ALT 0x22ed
+#define USB_DEVICE_ID_HANVON_ALT_MULTITOUCH 0x1010
+
+#define USB_VENDOR_ID_HAPP 0x078b
+#define USB_DEVICE_ID_UGCI_DRIVING 0x0010
+#define USB_DEVICE_ID_UGCI_FLYING 0x0020
+#define USB_DEVICE_ID_UGCI_FIGHTING 0x0030
+
+#define USB_VENDOR_ID_IDEACOM 0x1cb6
+#define USB_DEVICE_ID_IDEACOM_IDC6650 0x6650
+#define USB_DEVICE_ID_IDEACOM_IDC6651 0x6651
+
+#define USB_VENDOR_ID_ILITEK 0x222a
+#define USB_DEVICE_ID_ILITEK_MULTITOUCH 0x0001
+
+#define USB_VENDOR_ID_ION 0x15e4
+#define USB_DEVICE_ID_ICADE 0x0132
+
+#define USB_VENDOR_ID_HOLTEK 0x1241
+#define USB_DEVICE_ID_HOLTEK_ON_LINE_GRIP 0x5015
+
+#define USB_VENDOR_ID_HOLTEK_ALT 0x04d9
+#define USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD 0xa055
+
+#define USB_VENDOR_ID_IMATION 0x0718
+#define USB_DEVICE_ID_DISC_STAKKA 0xd000
+
+#define USB_VENDOR_ID_INTEL_8086 0x8086
+#define USB_VENDOR_ID_INTEL_8087 0x8087
+#define USB_DEVICE_ID_SENSOR_HUB_1020 0x1020
+#define USB_DEVICE_ID_SENSOR_HUB_09FA 0x09FA
+
+#define USB_VENDOR_ID_IRTOUCHSYSTEMS 0x6615
+#define USB_DEVICE_ID_IRTOUCH_INFRARED_USB 0x0070
+
+#define USB_VENDOR_ID_JESS 0x0c45
+#define USB_DEVICE_ID_JESS_YUREX 0x1010
+
+#define USB_VENDOR_ID_KBGEAR 0x084e
+#define USB_DEVICE_ID_KBGEAR_JAMSTUDIO 0x1001
+
+#define USB_VENDOR_ID_KENSINGTON 0x047d
+#define USB_DEVICE_ID_KS_SLIMBLADE 0x2041
+
+#define USB_VENDOR_ID_KWORLD 0x1b80
+#define USB_DEVICE_ID_KWORLD_RADIO_FM700 0xd700
+
+#define USB_VENDOR_ID_KEYTOUCH 0x0926
+#define USB_DEVICE_ID_KEYTOUCH_IEC 0x3333
+
+#define USB_VENDOR_ID_KYE 0x0458
+#define USB_DEVICE_ID_KYE_ERGO_525V 0x0087
+#define USB_DEVICE_ID_KYE_GPEN_560 0x5003
+#define USB_DEVICE_ID_KYE_EASYPEN_I405X 0x5010
+#define USB_DEVICE_ID_KYE_MOUSEPEN_I608X 0x5011
+#define USB_DEVICE_ID_KYE_EASYPEN_M610X 0x5013
+
+#define USB_VENDOR_ID_LABTEC 0x1020
+#define USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD 0x0006
+
+#define USB_VENDOR_ID_LCPOWER 0x1241
+#define USB_DEVICE_ID_LCPOWER_LC1000 0xf767
+
+#define USB_VENDOR_ID_LD 0x0f11
+#define USB_DEVICE_ID_LD_CASSY 0x1000
+#define USB_DEVICE_ID_LD_CASSY2 0x1001
+#define USB_DEVICE_ID_LD_POCKETCASSY 0x1010
+#define USB_DEVICE_ID_LD_POCKETCASSY2 0x1011
+#define USB_DEVICE_ID_LD_MOBILECASSY 0x1020
+#define USB_DEVICE_ID_LD_MOBILECASSY2 0x1021
+#define USB_DEVICE_ID_LD_MICROCASSYVOLTAGE 0x1031
+#define USB_DEVICE_ID_LD_MICROCASSYCURRENT 0x1032
+#define USB_DEVICE_ID_LD_MICROCASSYTIME 0x1033
+#define USB_DEVICE_ID_LD_MICROCASSYTEMPERATURE 0x1035
+#define USB_DEVICE_ID_LD_MICROCASSYPH 0x1038
+#define USB_DEVICE_ID_LD_JWM 0x1080
+#define USB_DEVICE_ID_LD_DMMP 0x1081
+#define USB_DEVICE_ID_LD_UMIP 0x1090
+#define USB_DEVICE_ID_LD_UMIC 0x10A0
+#define USB_DEVICE_ID_LD_UMIB 0x10B0
+#define USB_DEVICE_ID_LD_XRAY 0x1100
+#define USB_DEVICE_ID_LD_XRAY2 0x1101
+#define USB_DEVICE_ID_LD_XRAYCT 0x1110
+#define USB_DEVICE_ID_LD_VIDEOCOM 0x1200
+#define USB_DEVICE_ID_LD_MOTOR 0x1210
+#define USB_DEVICE_ID_LD_COM3LAB 0x2000
+#define USB_DEVICE_ID_LD_TELEPORT 0x2010
+#define USB_DEVICE_ID_LD_NETWORKANALYSER 0x2020
+#define USB_DEVICE_ID_LD_POWERCONTROL 0x2030
+#define USB_DEVICE_ID_LD_MACHINETEST 0x2040
+#define USB_DEVICE_ID_LD_MOSTANALYSER 0x2050
+#define USB_DEVICE_ID_LD_MOSTANALYSER2 0x2051
+#define USB_DEVICE_ID_LD_ABSESP 0x2060
+#define USB_DEVICE_ID_LD_AUTODATABUS 0x2070
+#define USB_DEVICE_ID_LD_MCT 0x2080
+#define USB_DEVICE_ID_LD_HYBRID 0x2090
+#define USB_DEVICE_ID_LD_HEATCONTROL 0x20A0
+
+#define USB_VENDOR_ID_LENOVO 0x17ef
+#define USB_DEVICE_ID_LENOVO_TPKBD 0x6009
+
+#define USB_VENDOR_ID_LG 0x1fd2
+#define USB_DEVICE_ID_LG_MULTITOUCH 0x0064
+
+#define USB_VENDOR_ID_LOGITECH 0x046d
+#define USB_DEVICE_ID_LOGITECH_AUDIOHUB 0x0a0e
+#define USB_DEVICE_ID_LOGITECH_RECEIVER 0xc101
+#define USB_DEVICE_ID_LOGITECH_HARMONY_FIRST 0xc110
+#define USB_DEVICE_ID_LOGITECH_HARMONY_LAST 0xc14f
+#define USB_DEVICE_ID_LOGITECH_HARMONY_PS3 0x0306
+#define USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD 0xc20a
+#define USB_DEVICE_ID_LOGITECH_RUMBLEPAD 0xc211
+#define USB_DEVICE_ID_LOGITECH_EXTREME_3D 0xc215
+#define USB_DEVICE_ID_LOGITECH_RUMBLEPAD2 0xc218
+#define USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2 0xc219
+#define USB_DEVICE_ID_LOGITECH_WINGMAN_F3D 0xc283
+#define USB_DEVICE_ID_LOGITECH_FORCE3D_PRO 0xc286
+#define USB_DEVICE_ID_LOGITECH_FLIGHT_SYSTEM_G940 0xc287
+#define USB_DEVICE_ID_LOGITECH_WHEEL 0xc294
+#define USB_DEVICE_ID_LOGITECH_WINGMAN_FFG 0xc293
+#define USB_DEVICE_ID_LOGITECH_MOMO_WHEEL 0xc295
+#define USB_DEVICE_ID_LOGITECH_DFP_WHEEL 0xc298
+#define USB_DEVICE_ID_LOGITECH_G25_WHEEL 0xc299
+#define USB_DEVICE_ID_LOGITECH_DFGT_WHEEL 0xc29a
+#define USB_DEVICE_ID_LOGITECH_G27_WHEEL 0xc29b
+#define USB_DEVICE_ID_LOGITECH_WII_WHEEL 0xc29c
+#define USB_DEVICE_ID_LOGITECH_ELITE_KBD 0xc30a
+#define USB_DEVICE_ID_S510_RECEIVER 0xc50c
+#define USB_DEVICE_ID_S510_RECEIVER_2 0xc517
+#define USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500 0xc512
+#define USB_DEVICE_ID_MX3000_RECEIVER 0xc513
+#define USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER 0xc52b
+#define USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER_2 0xc532
+#define USB_DEVICE_ID_SPACETRAVELLER 0xc623
+#define USB_DEVICE_ID_SPACENAVIGATOR 0xc626
+#define USB_DEVICE_ID_DINOVO_DESKTOP 0xc704
+#define USB_DEVICE_ID_DINOVO_EDGE 0xc714
+#define USB_DEVICE_ID_DINOVO_MINI 0xc71f
+#define USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2 0xca03
+
+#define USB_VENDOR_ID_LUMIO 0x202e
+#define USB_DEVICE_ID_CRYSTALTOUCH 0x0006
+#define USB_DEVICE_ID_CRYSTALTOUCH_DUAL 0x0007
+
+#define USB_VENDOR_ID_MADCATZ 0x0738
+#define USB_DEVICE_ID_MADCATZ_BEATPAD 0x4540
+
+#define USB_VENDOR_ID_MCC 0x09db
+#define USB_DEVICE_ID_MCC_PMD1024LS 0x0076
+#define USB_DEVICE_ID_MCC_PMD1208LS 0x007a
+
+#define USB_VENDOR_ID_MGE 0x0463
+#define USB_DEVICE_ID_MGE_UPS 0xffff
+#define USB_DEVICE_ID_MGE_UPS1 0x0001
+
+#define USB_VENDOR_ID_MICROCHIP 0x04d8
+#define USB_DEVICE_ID_PICKIT1 0x0032
+#define USB_DEVICE_ID_PICKIT2 0x0033
+#define USB_DEVICE_ID_PICOLCD 0xc002
+#define USB_DEVICE_ID_PICOLCD_BOOTLOADER 0xf002
+
+#define USB_VENDOR_ID_MICROSOFT 0x045e
+#define USB_DEVICE_ID_SIDEWINDER_GV 0x003b
+#define USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0 0x009d
+#define USB_DEVICE_ID_MS_NE4K 0x00db
+#define USB_DEVICE_ID_MS_LK6K 0x00f9
+#define USB_DEVICE_ID_MS_PRESENTER_8K_BT 0x0701
+#define USB_DEVICE_ID_MS_PRESENTER_8K_USB 0x0713
+#define USB_DEVICE_ID_MS_DIGITAL_MEDIA_3K 0x0730
+#define USB_DEVICE_ID_MS_COMFORT_MOUSE_4500 0x076c
+
+#define USB_VENDOR_ID_MOJO 0x8282
+#define USB_DEVICE_ID_RETRO_ADAPTER 0x3201
+
+#define USB_VENDOR_ID_MONTEREY 0x0566
+#define USB_DEVICE_ID_GENIUS_KB29E 0x3004
+
+#define USB_VENDOR_ID_NATIONAL_SEMICONDUCTOR 0x0400
+#define USB_DEVICE_ID_N_S_HARMONY 0xc359
+
+#define USB_VENDOR_ID_NATSU 0x08b7
+#define USB_DEVICE_ID_NATSU_GAMEPAD 0x0001
+
+#define USB_VENDOR_ID_NCR 0x0404
+#define USB_DEVICE_ID_NCR_FIRST 0x0300
+#define USB_DEVICE_ID_NCR_LAST 0x03ff
+
+#define USB_VENDOR_ID_NEC 0x073e
+#define USB_DEVICE_ID_NEC_USB_GAME_PAD 0x0301
+
+#define USB_VENDOR_ID_NEXTWINDOW 0x1926
+#define USB_DEVICE_ID_NEXTWINDOW_TOUCHSCREEN 0x0003
+
+#define USB_VENDOR_ID_NINTENDO 0x057e
+#define USB_DEVICE_ID_NINTENDO_WIIMOTE 0x0306
+
+#define USB_VENDOR_ID_NOVATEK 0x0603
+#define USB_DEVICE_ID_NOVATEK_PCT 0x0600
+
+#define USB_VENDOR_ID_NTRIG 0x1b96
+#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN 0x0001
+#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_1 0x0003
+#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_2 0x0004
+#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_3 0x0005
+#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_4 0x0006
+#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_5 0x0007
+#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_6 0x0008
+#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_7 0x0009
+#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_8 0x000A
+#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_9 0x000B
+#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_10 0x000C
+#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_11 0x000D
+#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_12 0x000E
+#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_13 0x000F
+#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_14 0x0010
+#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_15 0x0011
+#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_16 0x0012
+#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_17 0x0013
+#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_18 0x0014
+
+#define USB_VENDOR_ID_ONTRAK 0x0a07
+#define USB_DEVICE_ID_ONTRAK_ADU100 0x0064
+
+#define USB_VENDOR_ID_ORTEK 0x05a4
+#define USB_DEVICE_ID_ORTEK_PKB1700 0x1700
+#define USB_DEVICE_ID_ORTEK_WKB2000 0x2000
+
+#define USB_VENDOR_ID_PANASONIC 0x04da
+#define USB_DEVICE_ID_PANABOARD_UBT780 0x1044
+#define USB_DEVICE_ID_PANABOARD_UBT880 0x104d
+
+#define USB_VENDOR_ID_PANJIT 0x134c
+
+#define USB_VENDOR_ID_PANTHERLORD 0x0810
+#define USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK 0x0001
+
+#define USB_VENDOR_ID_PENMOUNT 0x14e1
+#define USB_DEVICE_ID_PENMOUNT_PCI 0x3500
+
+#define USB_VENDOR_ID_PETALYNX 0x18b1
+#define USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE 0x0037
+
+#define USB_VENDOR_ID_PHILIPS 0x0471
+#define USB_DEVICE_ID_PHILIPS_IEEE802154_DONGLE 0x0617
+
+#define USB_VENDOR_ID_PI_ENGINEERING 0x05f3
+#define USB_DEVICE_ID_PI_ENGINEERING_VEC_USB_FOOTPEDAL 0xff
+
+#define USB_VENDOR_ID_PIXART 0x093a
+#define USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN 0x8001
+#define USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN1 0x8002
+#define USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN2 0x8003
+
+#define USB_VENDOR_ID_PLAYDOTCOM 0x0b43
+#define USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII 0x0003
+
+#define USB_VENDOR_ID_POWERCOM 0x0d9f
+#define USB_DEVICE_ID_POWERCOM_UPS 0x0002
+
+#define USB_VENDOR_ID_PRODIGE 0x05af
+#define USB_DEVICE_ID_PRODIGE_CORDLESS 0x3062
+
+#define USB_VENDOR_ID_QUANTA 0x0408
+#define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH 0x3000
+#define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3001 0x3001
+#define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3008 0x3008
+
+#define USB_VENDOR_ID_ROCCAT 0x1e7d
+#define USB_DEVICE_ID_ROCCAT_ARVO 0x30d4
+#define USB_DEVICE_ID_ROCCAT_ISKU 0x319c
+#define USB_DEVICE_ID_ROCCAT_KONE 0x2ced
+#define USB_DEVICE_ID_ROCCAT_KONEPLUS 0x2d51
+#define USB_DEVICE_ID_ROCCAT_KONEXTD 0x2e22
+#define USB_DEVICE_ID_ROCCAT_KOVAPLUS 0x2d50
+#define USB_DEVICE_ID_ROCCAT_LUA 0x2c2e
+#define USB_DEVICE_ID_ROCCAT_PYRA_WIRED 0x2c24
+#define USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS 0x2cf6
+#define USB_DEVICE_ID_ROCCAT_SAVU 0x2d5a
+
+#define USB_VENDOR_ID_SAITEK 0x06a3
+#define USB_DEVICE_ID_SAITEK_RUMBLEPAD 0xff17
+#define USB_DEVICE_ID_SAITEK_PS1000 0x0621
+
+#define USB_VENDOR_ID_SAMSUNG 0x0419
+#define USB_DEVICE_ID_SAMSUNG_IR_REMOTE 0x0001
+#define USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE 0x0600
+
+#define USB_VENDOR_ID_SENNHEISER 0x1395
+#define USB_DEVICE_ID_SENNHEISER_BTD500USB 0x002c
+
+#define USB_VENDOR_ID_SIGMA_MICRO 0x1c4f
+#define USB_DEVICE_ID_SIGMA_MICRO_KEYBOARD 0x0002
+
+#define USB_VENDOR_ID_SIGMATEL 0x066F
+#define USB_DEVICE_ID_SIGMATEL_STMP3780 0x3780
+
+#define USB_VENDOR_ID_SKYCABLE 0x1223
+#define USB_DEVICE_ID_SKYCABLE_WIRELESS_PRESENTER 0x3F07
+
+#define USB_VENDOR_ID_SONY 0x054c
+#define USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE 0x024b
+#define USB_DEVICE_ID_SONY_PS3_BDREMOTE 0x0306
+#define USB_DEVICE_ID_SONY_PS3_CONTROLLER 0x0268
+#define USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER 0x042f
+
+#define USB_VENDOR_ID_SOUNDGRAPH 0x15c2
+#define USB_DEVICE_ID_SOUNDGRAPH_IMON_FIRST 0x0034
+#define USB_DEVICE_ID_SOUNDGRAPH_IMON_LAST 0x0046
+
+#define USB_VENDOR_ID_STANTUM 0x1f87
+#define USB_DEVICE_ID_MTP 0x0002
+
+#define USB_VENDOR_ID_STANTUM_STM 0x0483
+#define USB_DEVICE_ID_MTP_STM 0x3261
+#define USB_DEVICE_ID_SENSOR_HUB_7014 0x7014
+
+#define USB_VENDOR_ID_STANTUM_SITRONIX 0x1403
+#define USB_DEVICE_ID_MTP_SITRONIX 0x5001
+
+#define USB_VENDOR_ID_SUN 0x0430
+#define USB_DEVICE_ID_RARITAN_KVM_DONGLE 0xcdab
+
+#define USB_VENDOR_ID_SUNPLUS 0x04fc
+#define USB_DEVICE_ID_SUNPLUS_WDESKTOP 0x05d8
+
+#define USB_VENDOR_ID_SYMBOL 0x05e0
+#define USB_DEVICE_ID_SYMBOL_SCANNER_1 0x0800
+#define USB_DEVICE_ID_SYMBOL_SCANNER_2 0x1300
+
+#define USB_VENDOR_ID_SYNAPTICS 0x06cb
+#define USB_DEVICE_ID_SYNAPTICS_TP 0x0001
+#define USB_DEVICE_ID_SYNAPTICS_INT_TP 0x0002
+#define USB_DEVICE_ID_SYNAPTICS_CPAD 0x0003
+#define USB_DEVICE_ID_SYNAPTICS_TS 0x0006
+#define USB_DEVICE_ID_SYNAPTICS_STICK 0x0007
+#define USB_DEVICE_ID_SYNAPTICS_WP 0x0008
+#define USB_DEVICE_ID_SYNAPTICS_COMP_TP 0x0009
+#define USB_DEVICE_ID_SYNAPTICS_WTP 0x0010
+#define USB_DEVICE_ID_SYNAPTICS_DPAD 0x0013
+
+#define USB_VENDOR_ID_THRUSTMASTER 0x044f
+
+#define USB_VENDOR_ID_TIVO 0x150a
+#define USB_DEVICE_ID_TIVO_SLIDE_BT 0x1200
+#define USB_DEVICE_ID_TIVO_SLIDE 0x1201
+
+#define USB_VENDOR_ID_TOPSEED 0x0766
+#define USB_DEVICE_ID_TOPSEED_CYBERLINK 0x0204
+
+#define USB_VENDOR_ID_TOPSEED2 0x1784
+#define USB_DEVICE_ID_TOPSEED2_RF_COMBO 0x0004
+#define USB_DEVICE_ID_TOPSEED2_PERIPAD_701 0x0016
+
+#define USB_VENDOR_ID_TOPMAX 0x0663
+#define USB_DEVICE_ID_TOPMAX_COBRAPAD 0x0103
+
+#define USB_VENDOR_ID_TOUCH_INTL 0x1e5e
+#define USB_DEVICE_ID_TOUCH_INTL_MULTI_TOUCH 0x0313
+
+#define USB_VENDOR_ID_TOUCHPACK 0x1bfd
+#define USB_DEVICE_ID_TOUCHPACK_RTS 0x1688
+
+#define USB_VENDOR_ID_TPV 0x25aa
+#define USB_DEVICE_ID_TPV_OPTICAL_TOUCHSCREEN 0x8883
+
+#define USB_VENDOR_ID_TURBOX 0x062a
+#define USB_DEVICE_ID_TURBOX_KEYBOARD 0x0201
+#define USB_DEVICE_ID_TURBOX_TOUCHSCREEN_MOSART 0x7100
+
+#define USB_VENDOR_ID_TWINHAN 0x6253
+#define USB_DEVICE_ID_TWINHAN_IR_REMOTE 0x0100
+
+#define USB_VENDOR_ID_UCLOGIC 0x5543
+#define USB_DEVICE_ID_UCLOGIC_TABLET_PF1209 0x0042
+#define USB_DEVICE_ID_UCLOGIC_TABLET_KNA5 0x6001
+#define USB_DEVICE_ID_UCLOGIC_TABLET_TWA60 0x0064
+#define USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U 0x0003
+#define USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U 0x0004
+#define USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U 0x0005
+#define USB_DEVICE_ID_UCLOGIC_TABLET_WP1062 0x0064
+#define USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850 0x0522
+#define USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60 0x0781
+
+#define USB_VENDOR_ID_UNITEC 0x227d
+#define USB_DEVICE_ID_UNITEC_USB_TOUCH_0709 0x0709
+#define USB_DEVICE_ID_UNITEC_USB_TOUCH_0A19 0x0a19
+
+#define USB_VENDOR_ID_VERNIER 0x08f7
+#define USB_DEVICE_ID_VERNIER_LABPRO 0x0001
+#define USB_DEVICE_ID_VERNIER_GOTEMP 0x0002
+#define USB_DEVICE_ID_VERNIER_SKIP 0x0003
+#define USB_DEVICE_ID_VERNIER_CYCLOPS 0x0004
+#define USB_DEVICE_ID_VERNIER_LCSPEC 0x0006
+
+#define USB_VENDOR_ID_WACOM 0x056a
+#define USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH 0x81
+#define USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH 0x00BD
+
+#define USB_VENDOR_ID_WALTOP 0x172f
+#define USB_DEVICE_ID_WALTOP_SLIM_TABLET_5_8_INCH 0x0032
+#define USB_DEVICE_ID_WALTOP_SLIM_TABLET_12_1_INCH 0x0034
+#define USB_DEVICE_ID_WALTOP_Q_PAD 0x0037
+#define USB_DEVICE_ID_WALTOP_PID_0038 0x0038
+#define USB_DEVICE_ID_WALTOP_MEDIA_TABLET_10_6_INCH 0x0501
+#define USB_DEVICE_ID_WALTOP_MEDIA_TABLET_14_1_INCH 0x0500
+#define USB_DEVICE_ID_WALTOP_SIRIUS_BATTERY_FREE_TABLET 0x0502
+
+#define USB_VENDOR_ID_WISEGROUP 0x0925
+#define USB_DEVICE_ID_SMARTJOY_PLUS 0x0005
+#define USB_DEVICE_ID_1_PHIDGETSERVO_20 0x8101
+#define USB_DEVICE_ID_4_PHIDGETSERVO_20 0x8104
+#define USB_DEVICE_ID_8_8_4_IF_KIT 0x8201
+#define USB_DEVICE_ID_SUPER_JOY_BOX_3 0x8888
+#define USB_DEVICE_ID_QUAD_USB_JOYPAD 0x8800
+#define USB_DEVICE_ID_DUAL_USB_JOYPAD 0x8866
+
+#define USB_VENDOR_ID_WISEGROUP_LTD 0x6666
+#define USB_VENDOR_ID_WISEGROUP_LTD2 0x6677
+#define USB_DEVICE_ID_SMARTJOY_DUAL_PLUS 0x8802
+#define USB_DEVICE_ID_SUPER_JOY_BOX_3_PRO 0x8801
+#define USB_DEVICE_ID_SUPER_DUAL_BOX_PRO 0x8802
+#define USB_DEVICE_ID_SUPER_JOY_BOX_5_PRO 0x8804
+
+#define USB_VENDOR_ID_X_TENSIONS 0x1ae7
+#define USB_DEVICE_ID_SPEEDLINK_VAD_CEZANNE 0x9001
+
+#define USB_VENDOR_ID_XAT 0x2505
+#define USB_DEVICE_ID_XAT_CSR 0x0220
+
+#define USB_VENDOR_ID_XIROKU 0x1477
+#define USB_DEVICE_ID_XIROKU_SPX 0x1006
+#define USB_DEVICE_ID_XIROKU_MPX 0x1007
+#define USB_DEVICE_ID_XIROKU_CSR 0x100e
+#define USB_DEVICE_ID_XIROKU_SPX1 0x1021
+#define USB_DEVICE_ID_XIROKU_CSR1 0x1022
+#define USB_DEVICE_ID_XIROKU_MPX1 0x1023
+#define USB_DEVICE_ID_XIROKU_SPX2 0x1024
+#define USB_DEVICE_ID_XIROKU_CSR2 0x1025
+#define USB_DEVICE_ID_XIROKU_MPX2 0x1026
+
+#define USB_VENDOR_ID_YEALINK 0x6993
+#define USB_DEVICE_ID_YEALINK_P1K_P4K_B2K 0xb001
+
+#define USB_VENDOR_ID_ZEROPLUS 0x0c12
+
+#define USB_VENDOR_ID_ZYDACRON 0x13EC
+#define USB_DEVICE_ID_ZYDACRON_REMOTE_CONTROL 0x0006
+
+#define USB_VENDOR_ID_ZYTRONIC 0x14c8
+#define USB_DEVICE_ID_ZYTRONIC_ZXY100 0x0005
+
+#define USB_VENDOR_ID_PRIMAX 0x0461
+#define USB_DEVICE_ID_PRIMAX_KEYBOARD 0x4e05
+
+#endif
diff --git a/compat/lib-cordic.c b/compat/lib-cordic.c
new file mode 100644
index 0000000..6cf4778
--- /dev/null
+++ b/compat/lib-cordic.c
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2011 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <linux/module.h>
+#include <linux/cordic.h>
+
+#define CORDIC_ANGLE_GEN 39797
+#define CORDIC_PRECISION_SHIFT 16
+#define CORDIC_NUM_ITER (CORDIC_PRECISION_SHIFT + 2)
+
+#define FIXED(X) ((s32)((X) << CORDIC_PRECISION_SHIFT))
+#define FLOAT(X) (((X) >= 0) \
+ ? ((((X) >> (CORDIC_PRECISION_SHIFT - 1)) + 1) >> 1) \
+ : -((((-(X)) >> (CORDIC_PRECISION_SHIFT - 1)) + 1) >> 1))
+
+static const s32 arctan_table[] = {
+ 2949120,
+ 1740967,
+ 919879,
+ 466945,
+ 234379,
+ 117304,
+ 58666,
+ 29335,
+ 14668,
+ 7334,
+ 3667,
+ 1833,
+ 917,
+ 458,
+ 229,
+ 115,
+ 57,
+ 29
+};
+
+/*
+ * cordic_calc_iq() - calculates the i/q coordinate for given angle
+ *
+ * theta: angle in degrees for which i/q coordinate is to be calculated
+ * coord: function output parameter holding the i/q coordinate
+ */
+struct cordic_iq cordic_calc_iq(s32 theta)
+{
+ struct cordic_iq coord;
+ s32 angle, valtmp;
+ unsigned iter;
+ int signx = 1;
+ int signtheta;
+
+ coord.i = CORDIC_ANGLE_GEN;
+ coord.q = 0;
+ angle = 0;
+
+ theta = FIXED(theta);
+ signtheta = (theta < 0) ? -1 : 1;
+ theta = ((theta + FIXED(180) * signtheta) % FIXED(360)) -
+ FIXED(180) * signtheta;
+
+ if (FLOAT(theta) > 90) {
+ theta -= FIXED(180);
+ signx = -1;
+ } else if (FLOAT(theta) < -90) {
+ theta += FIXED(180);
+ signx = -1;
+ }
+
+ for (iter = 0; iter < CORDIC_NUM_ITER; iter++) {
+ if (theta > angle) {
+ valtmp = coord.i - (coord.q >> iter);
+ coord.q += (coord.i >> iter);
+ angle += arctan_table[iter];
+ } else {
+ valtmp = coord.i + (coord.q >> iter);
+ coord.q -= (coord.i >> iter);
+ angle -= arctan_table[iter];
+ }
+ coord.i = valtmp;
+ }
+
+ coord.i *= signx;
+ coord.q *= signx;
+ return coord;
+}
+EXPORT_SYMBOL(cordic_calc_iq);
+
+MODULE_DESCRIPTION("CORDIC algorithm");
+MODULE_AUTHOR("Broadcom Corporation");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/compat/lib-rhashtable.c b/compat/lib-rhashtable.c
new file mode 100644
index 0000000..2a29dc7
--- /dev/null
+++ b/compat/lib-rhashtable.c
@@ -0,0 +1,1172 @@
+/*
+ * Resizable, Scalable, Concurrent Hash Table
+ *
+ * Copyright (c) 2015 Herbert Xu <herbert@gondor.apana.org.au>
+ * Copyright (c) 2014-2015 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2008-2014 Patrick McHardy <kaber@trash.net>
+ *
+ * Code partially derived from nft_hash
+ * Rewritten with rehash code from br_multicast plus single list
+ * pointer as suggested by Josh Triplett
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/atomic.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/log2.h>
+#include <linux/sched.h>
+#include <linux/rculist.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/mm.h>
+#include <linux/jhash.h>
+#include <linux/random.h>
+#include <linux/rhashtable.h>
+#include <linux/err.h>
+#include <linux/export.h>
+
+#define HASH_DEFAULT_SIZE 64UL
+#define HASH_MIN_SIZE 4U
+#define BUCKET_LOCKS_PER_CPU 32UL
+
+union nested_table {
+ union nested_table __rcu *table;
+ struct rhash_head __rcu *bucket;
+};
+
+static u32 head_hashfn(struct rhashtable *ht,
+ const struct bucket_table *tbl,
+ const struct rhash_head *he)
+{
+ return rht_head_hashfn(ht, tbl, he, ht->p);
+}
+
+#ifdef CONFIG_PROVE_LOCKING
+#define ASSERT_RHT_MUTEX(HT) BUG_ON(!lockdep_rht_mutex_is_held(HT))
+
+int lockdep_rht_mutex_is_held(struct rhashtable *ht)
+{
+ return (debug_locks) ? lockdep_is_held(&ht->mutex) : 1;
+}
+EXPORT_SYMBOL_GPL(lockdep_rht_mutex_is_held);
+
+int lockdep_rht_bucket_is_held(const struct bucket_table *tbl, u32 hash)
+{
+ spinlock_t *lock = rht_bucket_lock(tbl, hash);
+
+ return (debug_locks) ? lockdep_is_held(lock) : 1;
+}
+EXPORT_SYMBOL_GPL(lockdep_rht_bucket_is_held);
+#else
+#define ASSERT_RHT_MUTEX(HT)
+#endif
+
+
+static int alloc_bucket_locks(struct rhashtable *ht, struct bucket_table *tbl,
+ gfp_t gfp)
+{
+ unsigned int i, size;
+#if defined(CONFIG_PROVE_LOCKING)
+ unsigned int nr_pcpus = 2;
+#else
+ unsigned int nr_pcpus = num_possible_cpus();
+#endif
+
+ nr_pcpus = min_t(unsigned int, nr_pcpus, 64UL);
+ size = roundup_pow_of_two(nr_pcpus * ht->p.locks_mul);
+
+ /* Never allocate more than 0.5 locks per bucket */
+ size = min_t(unsigned int, size, tbl->size >> 1);
+
+ if (tbl->nest)
+ size = min(size, 1U << tbl->nest);
+
+ if (sizeof(spinlock_t) != 0) {
+#if LINUX_VERSION_IS_LESS(4,12,0)
+ tbl->locks = NULL;
+#ifdef CONFIG_NUMA
+ if (size * sizeof(spinlock_t) > PAGE_SIZE &&
+ gfp == GFP_KERNEL)
+ tbl->locks = vmalloc(size * sizeof(spinlock_t));
+#endif
+ if (gfp != GFP_KERNEL)
+ gfp |= __GFP_NOWARN | __GFP_NORETRY;
+
+ if (!tbl->locks)
+ tbl->locks = kmalloc_array(size, sizeof(spinlock_t),
+ gfp);
+#else
+ if (gfpflags_allow_blocking(gfp))
+ tbl->locks = kvmalloc(size * sizeof(spinlock_t), gfp);
+ else
+ tbl->locks = kmalloc_array(size, sizeof(spinlock_t),
+ gfp);
+#endif
+ if (!tbl->locks)
+ return -ENOMEM;
+ for (i = 0; i < size; i++)
+ spin_lock_init(&tbl->locks[i]);
+ }
+ tbl->locks_mask = size - 1;
+
+ return 0;
+}
+
+static void nested_table_free(union nested_table *ntbl, unsigned int size)
+{
+ const unsigned int shift = PAGE_SHIFT - ilog2(sizeof(void *));
+ const unsigned int len = 1 << shift;
+ unsigned int i;
+
+ ntbl = rcu_dereference_raw(ntbl->table);
+ if (!ntbl)
+ return;
+
+ if (size > len) {
+ size >>= shift;
+ for (i = 0; i < len; i++)
+ nested_table_free(ntbl + i, size);
+ }
+
+ kfree(ntbl);
+}
+
+static void nested_bucket_table_free(const struct bucket_table *tbl)
+{
+ unsigned int size = tbl->size >> tbl->nest;
+ unsigned int len = 1 << tbl->nest;
+ union nested_table *ntbl;
+ unsigned int i;
+
+ ntbl = (union nested_table *)rcu_dereference_raw(tbl->buckets[0]);
+
+ for (i = 0; i < len; i++)
+ nested_table_free(ntbl + i, size);
+
+ kfree(ntbl);
+}
+
+static void bucket_table_free(const struct bucket_table *tbl)
+{
+ if (tbl->nest)
+ nested_bucket_table_free(tbl);
+
+ kvfree(tbl->locks);
+ kvfree(tbl);
+}
+
+static void bucket_table_free_rcu(struct rcu_head *head)
+{
+ bucket_table_free(container_of(head, struct bucket_table, rcu));
+}
+
+static union nested_table *nested_table_alloc(struct rhashtable *ht,
+ union nested_table __rcu **prev,
+ unsigned int shifted,
+ unsigned int nhash)
+{
+ union nested_table *ntbl;
+ int i;
+
+ ntbl = rcu_dereference(*prev);
+ if (ntbl)
+ return ntbl;
+
+ ntbl = kzalloc(PAGE_SIZE, GFP_ATOMIC);
+
+ if (ntbl && shifted) {
+ for (i = 0; i < PAGE_SIZE / sizeof(ntbl[0].bucket); i++)
+ INIT_RHT_NULLS_HEAD(ntbl[i].bucket, ht,
+ (i << shifted) | nhash);
+ }
+
+ rcu_assign_pointer(*prev, ntbl);
+
+ return ntbl;
+}
+
+static struct bucket_table *nested_bucket_table_alloc(struct rhashtable *ht,
+ size_t nbuckets,
+ gfp_t gfp)
+{
+ const unsigned int shift = PAGE_SHIFT - ilog2(sizeof(void *));
+ struct bucket_table *tbl;
+ size_t size;
+
+ if (nbuckets < (1 << (shift + 1)))
+ return NULL;
+
+ size = sizeof(*tbl) + sizeof(tbl->buckets[0]);
+
+ tbl = kzalloc(size, gfp);
+ if (!tbl)
+ return NULL;
+
+ if (!nested_table_alloc(ht, (union nested_table __rcu **)tbl->buckets,
+ 0, 0)) {
+ kfree(tbl);
+ return NULL;
+ }
+
+ tbl->nest = (ilog2(nbuckets) - 1) % shift + 1;
+
+ return tbl;
+}
+
+static struct bucket_table *bucket_table_alloc(struct rhashtable *ht,
+ size_t nbuckets,
+ gfp_t gfp)
+{
+ struct bucket_table *tbl = NULL;
+ size_t size;
+ int i;
+
+ size = sizeof(*tbl) + nbuckets * sizeof(tbl->buckets[0]);
+ if (size <= (PAGE_SIZE << PAGE_ALLOC_COSTLY_ORDER) ||
+ gfp != GFP_KERNEL)
+ tbl = kzalloc(size, gfp | __GFP_NOWARN | __GFP_NORETRY);
+ if (tbl == NULL && gfp == GFP_KERNEL)
+ tbl = vzalloc(size);
+
+ size = nbuckets;
+
+ if (tbl == NULL && gfp != GFP_KERNEL) {
+ tbl = nested_bucket_table_alloc(ht, nbuckets, gfp);
+ nbuckets = 0;
+ }
+ if (tbl == NULL)
+ return NULL;
+
+ tbl->size = size;
+
+ if (alloc_bucket_locks(ht, tbl, gfp) < 0) {
+ bucket_table_free(tbl);
+ return NULL;
+ }
+
+ INIT_LIST_HEAD(&tbl->walkers);
+
+ get_random_bytes(&tbl->hash_rnd, sizeof(tbl->hash_rnd));
+
+ for (i = 0; i < nbuckets; i++)
+ INIT_RHT_NULLS_HEAD(tbl->buckets[i], ht, i);
+
+ return tbl;
+}
+
+static struct bucket_table *rhashtable_last_table(struct rhashtable *ht,
+ struct bucket_table *tbl)
+{
+ struct bucket_table *new_tbl;
+
+ do {
+ new_tbl = tbl;
+ tbl = rht_dereference_rcu(tbl->future_tbl, ht);
+ } while (tbl);
+
+ return new_tbl;
+}
+
+static int rhashtable_rehash_one(struct rhashtable *ht, unsigned int old_hash)
+{
+ struct bucket_table *old_tbl = rht_dereference(ht->tbl, ht);
+ struct bucket_table *new_tbl = rhashtable_last_table(ht,
+ rht_dereference_rcu(old_tbl->future_tbl, ht));
+ struct rhash_head __rcu **pprev = rht_bucket_var(old_tbl, old_hash);
+ int err = -EAGAIN;
+ struct rhash_head *head, *next, *entry;
+ spinlock_t *new_bucket_lock;
+ unsigned int new_hash;
+
+ if (new_tbl->nest)
+ goto out;
+
+ err = -ENOENT;
+
+ rht_for_each(entry, old_tbl, old_hash) {
+ err = 0;
+ next = rht_dereference_bucket(entry->next, old_tbl, old_hash);
+
+ if (rht_is_a_nulls(next))
+ break;
+
+ pprev = &entry->next;
+ }
+
+ if (err)
+ goto out;
+
+ new_hash = head_hashfn(ht, new_tbl, entry);
+
+ new_bucket_lock = rht_bucket_lock(new_tbl, new_hash);
+
+ spin_lock_nested(new_bucket_lock, SINGLE_DEPTH_NESTING);
+ head = rht_dereference_bucket(new_tbl->buckets[new_hash],
+ new_tbl, new_hash);
+
+ RCU_INIT_POINTER(entry->next, head);
+
+ rcu_assign_pointer(new_tbl->buckets[new_hash], entry);
+ spin_unlock(new_bucket_lock);
+
+ rcu_assign_pointer(*pprev, next);
+
+out:
+ return err;
+}
+
+static int rhashtable_rehash_chain(struct rhashtable *ht,
+ unsigned int old_hash)
+{
+ struct bucket_table *old_tbl = rht_dereference(ht->tbl, ht);
+ spinlock_t *old_bucket_lock;
+ int err;
+
+ old_bucket_lock = rht_bucket_lock(old_tbl, old_hash);
+
+ spin_lock_bh(old_bucket_lock);
+ while (!(err = rhashtable_rehash_one(ht, old_hash)))
+ ;
+
+ if (err == -ENOENT) {
+ old_tbl->rehash++;
+ err = 0;
+ }
+ spin_unlock_bh(old_bucket_lock);
+
+ return err;
+}
+
+static int rhashtable_rehash_attach(struct rhashtable *ht,
+ struct bucket_table *old_tbl,
+ struct bucket_table *new_tbl)
+{
+ /* Protect future_tbl using the first bucket lock. */
+ spin_lock_bh(old_tbl->locks);
+
+ /* Did somebody beat us to it? */
+ if (rcu_access_pointer(old_tbl->future_tbl)) {
+ spin_unlock_bh(old_tbl->locks);
+ return -EEXIST;
+ }
+
+ /* Make insertions go into the new, empty table right away. Deletions
+ * and lookups will be attempted in both tables until we synchronize.
+ */
+ rcu_assign_pointer(old_tbl->future_tbl, new_tbl);
+
+ spin_unlock_bh(old_tbl->locks);
+
+ return 0;
+}
+
+static int rhashtable_rehash_table(struct rhashtable *ht)
+{
+ struct bucket_table *old_tbl = rht_dereference(ht->tbl, ht);
+ struct bucket_table *new_tbl;
+ struct rhashtable_walker *walker;
+ unsigned int old_hash;
+ int err;
+
+ new_tbl = rht_dereference(old_tbl->future_tbl, ht);
+ if (!new_tbl)
+ return 0;
+
+ for (old_hash = 0; old_hash < old_tbl->size; old_hash++) {
+ err = rhashtable_rehash_chain(ht, old_hash);
+ if (err)
+ return err;
+ }
+
+ /* Publish the new table pointer. */
+ rcu_assign_pointer(ht->tbl, new_tbl);
+
+ spin_lock(&ht->lock);
+ list_for_each_entry(walker, &old_tbl->walkers, list)
+ walker->tbl = NULL;
+ spin_unlock(&ht->lock);
+
+ /* Wait for readers. All new readers will see the new
+ * table, and thus no references to the old table will
+ * remain.
+ */
+ call_rcu(&old_tbl->rcu, bucket_table_free_rcu);
+
+ return rht_dereference(new_tbl->future_tbl, ht) ? -EAGAIN : 0;
+}
+
+static int rhashtable_rehash_alloc(struct rhashtable *ht,
+ struct bucket_table *old_tbl,
+ unsigned int size)
+{
+ struct bucket_table *new_tbl;
+ int err;
+
+ ASSERT_RHT_MUTEX(ht);
+
+ new_tbl = bucket_table_alloc(ht, size, GFP_KERNEL);
+ if (new_tbl == NULL)
+ return -ENOMEM;
+
+ err = rhashtable_rehash_attach(ht, old_tbl, new_tbl);
+ if (err)
+ bucket_table_free(new_tbl);
+
+ return err;
+}
+
+/**
+ * rhashtable_shrink - Shrink hash table while allowing concurrent lookups
+ * @ht: the hash table to shrink
+ *
+ * This function shrinks the hash table to fit, i.e., the smallest
+ * size would not cause it to expand right away automatically.
+ *
+ * The caller must ensure that no concurrent resizing occurs by holding
+ * ht->mutex.
+ *
+ * The caller must ensure that no concurrent table mutations take place.
+ * It is however valid to have concurrent lookups if they are RCU protected.
+ *
+ * It is valid to have concurrent insertions and deletions protected by per
+ * bucket locks or concurrent RCU protected lookups and traversals.
+ */
+static int rhashtable_shrink(struct rhashtable *ht)
+{
+ struct bucket_table *old_tbl = rht_dereference(ht->tbl, ht);
+ unsigned int nelems = atomic_read(&ht->nelems);
+ unsigned int size = 0;
+
+ if (nelems)
+ size = roundup_pow_of_two(nelems * 3 / 2);
+ if (size < ht->p.min_size)
+ size = ht->p.min_size;
+
+ if (old_tbl->size <= size)
+ return 0;
+
+ if (rht_dereference(old_tbl->future_tbl, ht))
+ return -EEXIST;
+
+ return rhashtable_rehash_alloc(ht, old_tbl, size);
+}
+
+static void rht_deferred_worker(struct work_struct *work)
+{
+ struct rhashtable *ht;
+ struct bucket_table *tbl;
+ int err = 0;
+
+ ht = container_of(work, struct rhashtable, run_work);
+ mutex_lock(&ht->mutex);
+
+ tbl = rht_dereference(ht->tbl, ht);
+ tbl = rhashtable_last_table(ht, tbl);
+
+ if (rht_grow_above_75(ht, tbl))
+ err = rhashtable_rehash_alloc(ht, tbl, tbl->size * 2);
+ else if (ht->p.automatic_shrinking && rht_shrink_below_30(ht, tbl))
+ err = rhashtable_shrink(ht);
+ else if (tbl->nest)
+ err = rhashtable_rehash_alloc(ht, tbl, tbl->size);
+
+ if (!err)
+ err = rhashtable_rehash_table(ht);
+
+ mutex_unlock(&ht->mutex);
+
+ if (err)
+ schedule_work(&ht->run_work);
+}
+
+static int rhashtable_insert_rehash(struct rhashtable *ht,
+ struct bucket_table *tbl)
+{
+ struct bucket_table *old_tbl;
+ struct bucket_table *new_tbl;
+ unsigned int size;
+ int err;
+
+ old_tbl = rht_dereference_rcu(ht->tbl, ht);
+
+ size = tbl->size;
+
+ err = -EBUSY;
+
+ if (rht_grow_above_75(ht, tbl))
+ size *= 2;
+ /* Do not schedule more than one rehash */
+ else if (old_tbl != tbl)
+ goto fail;
+
+ err = -ENOMEM;
+
+ new_tbl = bucket_table_alloc(ht, size, GFP_ATOMIC);
+ if (new_tbl == NULL)
+ goto fail;
+
+ err = rhashtable_rehash_attach(ht, tbl, new_tbl);
+ if (err) {
+ bucket_table_free(new_tbl);
+ if (err == -EEXIST)
+ err = 0;
+ } else
+ schedule_work(&ht->run_work);
+
+ return err;
+
+fail:
+ /* Do not fail the insert if someone else did a rehash. */
+ if (likely(rcu_dereference_raw(tbl->future_tbl)))
+ return 0;
+
+ /* Schedule async rehash to retry allocation in process context. */
+ if (err == -ENOMEM)
+ schedule_work(&ht->run_work);
+
+ return err;
+}
+
+static void *rhashtable_lookup_one(struct rhashtable *ht,
+ struct bucket_table *tbl, unsigned int hash,
+ const void *key, struct rhash_head *obj)
+{
+ struct rhashtable_compare_arg arg = {
+ .ht = ht,
+ .key = key,
+ };
+ struct rhash_head __rcu **pprev;
+ struct rhash_head *head;
+ int elasticity;
+
+ elasticity = RHT_ELASTICITY;
+ pprev = rht_bucket_var(tbl, hash);
+ rht_for_each_continue(head, *pprev, tbl, hash) {
+ struct rhlist_head *list;
+ struct rhlist_head *plist;
+
+ elasticity--;
+ if (!key ||
+ (ht->p.obj_cmpfn ?
+ ht->p.obj_cmpfn(&arg, rht_obj(ht, head)) :
+ rhashtable_compare(&arg, rht_obj(ht, head))))
+ continue;
+
+ if (!ht->rhlist)
+ return rht_obj(ht, head);
+
+ list = container_of(obj, struct rhlist_head, rhead);
+ plist = container_of(head, struct rhlist_head, rhead);
+
+ RCU_INIT_POINTER(list->next, plist);
+ head = rht_dereference_bucket(head->next, tbl, hash);
+ RCU_INIT_POINTER(list->rhead.next, head);
+ rcu_assign_pointer(*pprev, obj);
+
+ return NULL;
+ }
+
+ if (elasticity <= 0)
+ return ERR_PTR(-EAGAIN);
+
+ return ERR_PTR(-ENOENT);
+}
+
+static struct bucket_table *rhashtable_insert_one(struct rhashtable *ht,
+ struct bucket_table *tbl,
+ unsigned int hash,
+ struct rhash_head *obj,
+ void *data)
+{
+ struct rhash_head __rcu **pprev;
+ struct bucket_table *new_tbl;
+ struct rhash_head *head;
+
+ if (!IS_ERR_OR_NULL(data))
+ return ERR_PTR(-EEXIST);
+
+ if (PTR_ERR(data) != -EAGAIN && PTR_ERR(data) != -ENOENT)
+ return ERR_CAST(data);
+
+ new_tbl = rcu_dereference(tbl->future_tbl);
+ if (new_tbl)
+ return new_tbl;
+
+ if (PTR_ERR(data) != -ENOENT)
+ return ERR_CAST(data);
+
+ if (unlikely(rht_grow_above_max(ht, tbl)))
+ return ERR_PTR(-E2BIG);
+
+ if (unlikely(rht_grow_above_100(ht, tbl)))
+ return ERR_PTR(-EAGAIN);
+
+ pprev = rht_bucket_insert(ht, tbl, hash);
+ if (!pprev)
+ return ERR_PTR(-ENOMEM);
+
+ head = rht_dereference_bucket(*pprev, tbl, hash);
+
+ RCU_INIT_POINTER(obj->next, head);
+ if (ht->rhlist) {
+ struct rhlist_head *list;
+
+ list = container_of(obj, struct rhlist_head, rhead);
+ RCU_INIT_POINTER(list->next, NULL);
+ }
+
+ rcu_assign_pointer(*pprev, obj);
+
+ atomic_inc(&ht->nelems);
+ if (rht_grow_above_75(ht, tbl))
+ schedule_work(&ht->run_work);
+
+ return NULL;
+}
+
+static void *rhashtable_try_insert(struct rhashtable *ht, const void *key,
+ struct rhash_head *obj)
+{
+ struct bucket_table *new_tbl;
+ struct bucket_table *tbl;
+ unsigned int hash;
+ spinlock_t *lock;
+ void *data;
+
+ tbl = rcu_dereference(ht->tbl);
+
+ /* All insertions must grab the oldest table containing
+ * the hashed bucket that is yet to be rehashed.
+ */
+ for (;;) {
+ hash = rht_head_hashfn(ht, tbl, obj, ht->p);
+ lock = rht_bucket_lock(tbl, hash);
+ spin_lock_bh(lock);
+
+ if (tbl->rehash <= hash)
+ break;
+
+ spin_unlock_bh(lock);
+ tbl = rcu_dereference(tbl->future_tbl);
+ }
+
+ data = rhashtable_lookup_one(ht, tbl, hash, key, obj);
+ new_tbl = rhashtable_insert_one(ht, tbl, hash, obj, data);
+ if (PTR_ERR(new_tbl) != -EEXIST)
+ data = ERR_CAST(new_tbl);
+
+ while (!IS_ERR_OR_NULL(new_tbl)) {
+ tbl = new_tbl;
+ hash = rht_head_hashfn(ht, tbl, obj, ht->p);
+ spin_lock_nested(rht_bucket_lock(tbl, hash),
+ SINGLE_DEPTH_NESTING);
+
+ data = rhashtable_lookup_one(ht, tbl, hash, key, obj);
+ new_tbl = rhashtable_insert_one(ht, tbl, hash, obj, data);
+ if (PTR_ERR(new_tbl) != -EEXIST)
+ data = ERR_CAST(new_tbl);
+
+ spin_unlock(rht_bucket_lock(tbl, hash));
+ }
+
+ spin_unlock_bh(lock);
+
+ if (PTR_ERR(data) == -EAGAIN)
+ data = ERR_PTR(rhashtable_insert_rehash(ht, tbl) ?:
+ -EAGAIN);
+
+ return data;
+}
+
+void *rhashtable_insert_slow(struct rhashtable *ht, const void *key,
+ struct rhash_head *obj)
+{
+ void *data;
+
+ do {
+ rcu_read_lock();
+ data = rhashtable_try_insert(ht, key, obj);
+ rcu_read_unlock();
+ } while (PTR_ERR(data) == -EAGAIN);
+
+ return data;
+}
+EXPORT_SYMBOL_GPL(rhashtable_insert_slow);
+
+/**
+ * rhashtable_walk_enter - Initialise an iterator
+ * @ht: Table to walk over
+ * @iter: Hash table Iterator
+ *
+ * This function prepares a hash table walk.
+ *
+ * Note that if you restart a walk after rhashtable_walk_stop you
+ * may see the same object twice. Also, you may miss objects if
+ * there are removals in between rhashtable_walk_stop and the next
+ * call to rhashtable_walk_start.
+ *
+ * For a completely stable walk you should construct your own data
+ * structure outside the hash table.
+ *
+ * This function may sleep so you must not call it from interrupt
+ * context or with spin locks held.
+ *
+ * You must call rhashtable_walk_exit after this function returns.
+ */
+void rhashtable_walk_enter(struct rhashtable *ht, struct rhashtable_iter *iter)
+{
+ iter->ht = ht;
+ iter->p = NULL;
+ iter->slot = 0;
+ iter->skip = 0;
+
+ spin_lock(&ht->lock);
+ iter->walker.tbl =
+ rcu_dereference_protected(ht->tbl, lockdep_is_held(&ht->lock));
+ list_add(&iter->walker.list, &iter->walker.tbl->walkers);
+ spin_unlock(&ht->lock);
+}
+EXPORT_SYMBOL_GPL(rhashtable_walk_enter);
+
+/**
+ * rhashtable_walk_exit - Free an iterator
+ * @iter: Hash table Iterator
+ *
+ * This function frees resources allocated by rhashtable_walk_init.
+ */
+void rhashtable_walk_exit(struct rhashtable_iter *iter)
+{
+ spin_lock(&iter->ht->lock);
+ if (iter->walker.tbl)
+ list_del(&iter->walker.list);
+ spin_unlock(&iter->ht->lock);
+}
+EXPORT_SYMBOL_GPL(rhashtable_walk_exit);
+
+/**
+ * rhashtable_walk_start - Start a hash table walk
+ * @iter: Hash table iterator
+ *
+ * Start a hash table walk. Note that we take the RCU lock in all
+ * cases including when we return an error. So you must always call
+ * rhashtable_walk_stop to clean up.
+ *
+ * Returns zero if successful.
+ *
+ * Returns -EAGAIN if resize event occured. Note that the iterator
+ * will rewind back to the beginning and you may use it immediately
+ * by calling rhashtable_walk_next.
+ */
+int rhashtable_walk_start(struct rhashtable_iter *iter)
+ __acquires(RCU)
+{
+ struct rhashtable *ht = iter->ht;
+
+ rcu_read_lock();
+
+ spin_lock(&ht->lock);
+ if (iter->walker.tbl)
+ list_del(&iter->walker.list);
+ spin_unlock(&ht->lock);
+
+ if (!iter->walker.tbl) {
+ iter->walker.tbl = rht_dereference_rcu(ht->tbl, ht);
+ return -EAGAIN;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rhashtable_walk_start);
+
+/**
+ * rhashtable_walk_next - Return the next object and advance the iterator
+ * @iter: Hash table iterator
+ *
+ * Note that you must call rhashtable_walk_stop when you are finished
+ * with the walk.
+ *
+ * Returns the next object or NULL when the end of the table is reached.
+ *
+ * Returns -EAGAIN if resize event occured. Note that the iterator
+ * will rewind back to the beginning and you may continue to use it.
+ */
+void *rhashtable_walk_next(struct rhashtable_iter *iter)
+{
+ struct bucket_table *tbl = iter->walker.tbl;
+ struct rhlist_head *list = iter->list;
+ struct rhashtable *ht = iter->ht;
+ struct rhash_head *p = iter->p;
+ bool rhlist = ht->rhlist;
+
+ if (p) {
+ if (!rhlist || !(list = rcu_dereference(list->next))) {
+ p = rcu_dereference(p->next);
+ list = container_of(p, struct rhlist_head, rhead);
+ }
+ goto next;
+ }
+
+ for (; iter->slot < tbl->size; iter->slot++) {
+ int skip = iter->skip;
+
+ rht_for_each_rcu(p, tbl, iter->slot) {
+ if (rhlist) {
+ list = container_of(p, struct rhlist_head,
+ rhead);
+ do {
+ if (!skip)
+ goto next;
+ skip--;
+ list = rcu_dereference(list->next);
+ } while (list);
+
+ continue;
+ }
+ if (!skip)
+ break;
+ skip--;
+ }
+
+next:
+ if (!rht_is_a_nulls(p)) {
+ iter->skip++;
+ iter->p = p;
+ iter->list = list;
+ return rht_obj(ht, rhlist ? &list->rhead : p);
+ }
+
+ iter->skip = 0;
+ }
+
+ iter->p = NULL;
+
+ /* Ensure we see any new tables. */
+ smp_rmb();
+
+ iter->walker.tbl = rht_dereference_rcu(tbl->future_tbl, ht);
+ if (iter->walker.tbl) {
+ iter->slot = 0;
+ iter->skip = 0;
+ return ERR_PTR(-EAGAIN);
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(rhashtable_walk_next);
+
+/**
+ * rhashtable_walk_stop - Finish a hash table walk
+ * @iter: Hash table iterator
+ *
+ * Finish a hash table walk.
+ */
+void rhashtable_walk_stop(struct rhashtable_iter *iter)
+ __releases(RCU)
+{
+ struct rhashtable *ht;
+ struct bucket_table *tbl = iter->walker.tbl;
+
+ if (!tbl)
+ goto out;
+
+ ht = iter->ht;
+
+ spin_lock(&ht->lock);
+ if (tbl->rehash < tbl->size)
+ list_add(&iter->walker.list, &tbl->walkers);
+ else
+ iter->walker.tbl = NULL;
+ spin_unlock(&ht->lock);
+
+ iter->p = NULL;
+
+out:
+ rcu_read_unlock();
+}
+EXPORT_SYMBOL_GPL(rhashtable_walk_stop);
+
+static size_t rounded_hashtable_size(const struct rhashtable_params *params)
+{
+ return max(roundup_pow_of_two(params->nelem_hint * 4 / 3),
+ (unsigned long)params->min_size);
+}
+
+static u32 rhashtable_jhash2(const void *key, u32 length, u32 seed)
+{
+ return jhash2(key, length, seed);
+}
+
+/**
+ * rhashtable_init - initialize a new hash table
+ * @ht: hash table to be initialized
+ * @params: configuration parameters
+ *
+ * Initializes a new hash table based on the provided configuration
+ * parameters. A table can be configured either with a variable or
+ * fixed length key:
+ *
+ * Configuration Example 1: Fixed length keys
+ * struct test_obj {
+ * int key;
+ * void * my_member;
+ * struct rhash_head node;
+ * };
+ *
+ * struct rhashtable_params params = {
+ * .head_offset = offsetof(struct test_obj, node),
+ * .key_offset = offsetof(struct test_obj, key),
+ * .key_len = sizeof(int),
+ * .hashfn = jhash,
+ * .nulls_base = (1U << RHT_BASE_SHIFT),
+ * };
+ *
+ * Configuration Example 2: Variable length keys
+ * struct test_obj {
+ * [...]
+ * struct rhash_head node;
+ * };
+ *
+ * u32 my_hash_fn(const void *data, u32 len, u32 seed)
+ * {
+ * struct test_obj *obj = data;
+ *
+ * return [... hash ...];
+ * }
+ *
+ * struct rhashtable_params params = {
+ * .head_offset = offsetof(struct test_obj, node),
+ * .hashfn = jhash,
+ * .obj_hashfn = my_hash_fn,
+ * };
+ */
+int rhashtable_init(struct rhashtable *ht,
+ const struct rhashtable_params *params)
+{
+ struct bucket_table *tbl;
+ size_t size;
+
+ size = HASH_DEFAULT_SIZE;
+
+ if ((!params->key_len && !params->obj_hashfn) ||
+ (params->obj_hashfn && !params->obj_cmpfn))
+ return -EINVAL;
+
+ if (params->nulls_base && params->nulls_base < (1U << RHT_BASE_SHIFT))
+ return -EINVAL;
+
+ memset(ht, 0, sizeof(*ht));
+ mutex_init(&ht->mutex);
+ spin_lock_init(&ht->lock);
+ memcpy(&ht->p, params, sizeof(*params));
+
+ if (params->min_size)
+ ht->p.min_size = roundup_pow_of_two(params->min_size);
+
+ /* Cap total entries at 2^31 to avoid nelems overflow. */
+ ht->max_elems = 1u << 31;
+
+ if (params->max_size) {
+ ht->p.max_size = rounddown_pow_of_two(params->max_size);
+ if (ht->p.max_size < ht->max_elems / 2)
+ ht->max_elems = ht->p.max_size * 2;
+ }
+
+ ht->p.min_size = max_t(u16, ht->p.min_size, HASH_MIN_SIZE);
+
+ if (params->nelem_hint)
+ size = rounded_hashtable_size(&ht->p);
+
+ if (params->locks_mul)
+ ht->p.locks_mul = roundup_pow_of_two(params->locks_mul);
+ else
+ ht->p.locks_mul = BUCKET_LOCKS_PER_CPU;
+
+ ht->key_len = ht->p.key_len;
+ if (!params->hashfn) {
+ ht->p.hashfn = jhash;
+
+ if (!(ht->key_len & (sizeof(u32) - 1))) {
+ ht->key_len /= sizeof(u32);
+ ht->p.hashfn = rhashtable_jhash2;
+ }
+ }
+
+ tbl = bucket_table_alloc(ht, size, GFP_KERNEL);
+ if (tbl == NULL)
+ return -ENOMEM;
+
+ atomic_set(&ht->nelems, 0);
+
+ RCU_INIT_POINTER(ht->tbl, tbl);
+
+ INIT_WORK(&ht->run_work, rht_deferred_worker);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rhashtable_init);
+
+/**
+ * rhltable_init - initialize a new hash list table
+ * @hlt: hash list table to be initialized
+ * @params: configuration parameters
+ *
+ * Initializes a new hash list table.
+ *
+ * See documentation for rhashtable_init.
+ */
+int rhltable_init(struct rhltable *hlt, const struct rhashtable_params *params)
+{
+ int err;
+
+ /* No rhlist NULLs marking for now. */
+ if (params->nulls_base)
+ return -EINVAL;
+
+ err = rhashtable_init(&hlt->ht, params);
+ hlt->ht.rhlist = true;
+ return err;
+}
+EXPORT_SYMBOL_GPL(rhltable_init);
+
+static void rhashtable_free_one(struct rhashtable *ht, struct rhash_head *obj,
+ void (*free_fn)(void *ptr, void *arg),
+ void *arg)
+{
+ struct rhlist_head *list;
+
+ if (!ht->rhlist) {
+ free_fn(rht_obj(ht, obj), arg);
+ return;
+ }
+
+ list = container_of(obj, struct rhlist_head, rhead);
+ do {
+ obj = &list->rhead;
+ list = rht_dereference(list->next, ht);
+ free_fn(rht_obj(ht, obj), arg);
+ } while (list);
+}
+
+/**
+ * rhashtable_free_and_destroy - free elements and destroy hash table
+ * @ht: the hash table to destroy
+ * @free_fn: callback to release resources of element
+ * @arg: pointer passed to free_fn
+ *
+ * Stops an eventual async resize. If defined, invokes free_fn for each
+ * element to releasal resources. Please note that RCU protected
+ * readers may still be accessing the elements. Releasing of resources
+ * must occur in a compatible manner. Then frees the bucket array.
+ *
+ * This function will eventually sleep to wait for an async resize
+ * to complete. The caller is responsible that no further write operations
+ * occurs in parallel.
+ */
+void rhashtable_free_and_destroy(struct rhashtable *ht,
+ void (*free_fn)(void *ptr, void *arg),
+ void *arg)
+{
+ struct bucket_table *tbl;
+ unsigned int i;
+
+ cancel_work_sync(&ht->run_work);
+
+ mutex_lock(&ht->mutex);
+ tbl = rht_dereference(ht->tbl, ht);
+ if (free_fn) {
+ for (i = 0; i < tbl->size; i++) {
+ struct rhash_head *pos, *next;
+
+ for (pos = rht_dereference(*rht_bucket(tbl, i), ht),
+ next = !rht_is_a_nulls(pos) ?
+ rht_dereference(pos->next, ht) : NULL;
+ !rht_is_a_nulls(pos);
+ pos = next,
+ next = !rht_is_a_nulls(pos) ?
+ rht_dereference(pos->next, ht) : NULL)
+ rhashtable_free_one(ht, pos, free_fn, arg);
+ }
+ }
+
+ bucket_table_free(tbl);
+ mutex_unlock(&ht->mutex);
+}
+EXPORT_SYMBOL_GPL(rhashtable_free_and_destroy);
+
+void rhashtable_destroy(struct rhashtable *ht)
+{
+ return rhashtable_free_and_destroy(ht, NULL, NULL);
+}
+EXPORT_SYMBOL_GPL(rhashtable_destroy);
+
+struct rhash_head __rcu **rht_bucket_nested(const struct bucket_table *tbl,
+ unsigned int hash)
+{
+ const unsigned int shift = PAGE_SHIFT - ilog2(sizeof(void *));
+ static struct rhash_head __rcu *rhnull =
+ (struct rhash_head __rcu *)NULLS_MARKER(0);
+ unsigned int index = hash & ((1 << tbl->nest) - 1);
+ unsigned int size = tbl->size >> tbl->nest;
+ unsigned int subhash = hash;
+ union nested_table *ntbl;
+
+ ntbl = (union nested_table *)rcu_dereference_raw(tbl->buckets[0]);
+ ntbl = rht_dereference_bucket_rcu(ntbl[index].table, tbl, hash);
+ subhash >>= tbl->nest;
+
+ while (ntbl && size > (1 << shift)) {
+ index = subhash & ((1 << shift) - 1);
+ ntbl = rht_dereference_bucket_rcu(ntbl[index].table,
+ tbl, hash);
+ size >>= shift;
+ subhash >>= shift;
+ }
+
+ if (!ntbl)
+ return &rhnull;
+
+ return &ntbl[subhash].bucket;
+
+}
+EXPORT_SYMBOL_GPL(rht_bucket_nested);
+
+struct rhash_head __rcu **rht_bucket_nested_insert(struct rhashtable *ht,
+ struct bucket_table *tbl,
+ unsigned int hash)
+{
+ const unsigned int shift = PAGE_SHIFT - ilog2(sizeof(void *));
+ unsigned int index = hash & ((1 << tbl->nest) - 1);
+ unsigned int size = tbl->size >> tbl->nest;
+ union nested_table *ntbl;
+ unsigned int shifted;
+ unsigned int nhash;
+
+ ntbl = (union nested_table *)rcu_dereference_raw(tbl->buckets[0]);
+ hash >>= tbl->nest;
+ nhash = index;
+ shifted = tbl->nest;
+ ntbl = nested_table_alloc(ht, &ntbl[index].table,
+ size <= (1 << shift) ? shifted : 0, nhash);
+
+ while (ntbl && size > (1 << shift)) {
+ index = hash & ((1 << shift) - 1);
+ size >>= shift;
+ hash >>= shift;
+ nhash |= index << shifted;
+ shifted += shift;
+ ntbl = nested_table_alloc(ht, &ntbl[index].table,
+ size <= (1 << shift) ? shifted : 0,
+ nhash);
+ }
+
+ if (!ntbl)
+ return NULL;
+
+ return &ntbl[hash].bucket;
+
+}
+EXPORT_SYMBOL_GPL(rht_bucket_nested_insert);
diff --git a/compat/main.c b/compat/main.c
new file mode 100644
index 0000000..5d45e3d
--- /dev/null
+++ b/compat/main.c
@@ -0,0 +1,92 @@
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pm_qos.h>
+#include <linux/workqueue.h>
+#include "backports.h"
+
+MODULE_AUTHOR("Luis R. Rodriguez");
+MODULE_DESCRIPTION("Kernel backport module");
+MODULE_LICENSE("GPL");
+
+#ifndef CPTCFG_KERNEL_NAME
+#error "You need a CPTCFG_KERNEL_NAME"
+#endif
+
+#ifndef CPTCFG_KERNEL_VERSION
+#error "You need a CPTCFG_KERNEL_VERSION"
+#endif
+
+#ifndef CPTCFG_VERSION
+#error "You need a CPTCFG_VERSION"
+#endif
+
+static char *backported_kernel_name = CPTCFG_KERNEL_NAME;
+
+module_param(backported_kernel_name, charp, 0400);
+MODULE_PARM_DESC(backported_kernel_name,
+ "The kernel tree name that was used for this backport (" CPTCFG_KERNEL_NAME ")");
+
+#ifdef BACKPORTS_GIT_TRACKED
+static char *backports_tracker_id = BACKPORTS_GIT_TRACKED;
+module_param(backports_tracker_id, charp, 0400);
+MODULE_PARM_DESC(backports_tracker_id,
+ "The version of the tree containing this backport (" BACKPORTS_GIT_TRACKED ")");
+#else
+static char *backported_kernel_version = CPTCFG_KERNEL_VERSION;
+static char *backports_version = CPTCFG_VERSION;
+
+module_param(backported_kernel_version, charp, 0400);
+MODULE_PARM_DESC(backported_kernel_version,
+ "The kernel version that was used for this backport (" CPTCFG_KERNEL_VERSION ")");
+
+module_param(backports_version, charp, 0400);
+MODULE_PARM_DESC(backports_version,
+ "The git version of the backports tree used to generate this backport (" CPTCFG_VERSION ")");
+
+#endif
+
+void backport_dependency_symbol(void)
+{
+}
+EXPORT_SYMBOL_GPL(backport_dependency_symbol);
+
+
+static int __init backport_init(void)
+{
+ int ret = crypto_ccm_module_init();
+ if (ret)
+ return ret;
+
+ ret = devcoredump_init();
+ if (ret) {
+ crypto_ccm_module_exit();
+ return ret;
+ }
+
+ printk(KERN_INFO "Loading modules backported from " CPTCFG_KERNEL_NAME
+#ifndef BACKPORTS_GIT_TRACKED
+ " version " CPTCFG_KERNEL_VERSION
+#endif
+ "\n");
+#ifdef BACKPORTS_GIT_TRACKED
+ printk(KERN_INFO BACKPORTS_GIT_TRACKED "\n");
+#else
+
+#ifdef CONFIG_BACKPORT_INTEGRATE
+ printk(KERN_INFO "Backport integrated by backports.git " CPTCFG_VERSION "\n");
+#else
+ printk(KERN_INFO "Backport generated by backports.git " CPTCFG_VERSION "\n");
+#endif /* CONFIG_BACKPORT_INTEGRATE */
+
+#endif /* BACKPORTS_GIT_TRACKED */
+
+ return 0;
+}
+subsys_initcall(backport_init);
+
+static void __exit backport_exit(void)
+{
+ crypto_ccm_module_exit();
+ devcoredump_exit();
+}
+module_exit(backport_exit);
diff --git a/compat/user_namespace.c b/compat/user_namespace.c
new file mode 100644
index 0000000..6d01404
--- /dev/null
+++ b/compat/user_namespace.c
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2012 Luis R. Rodriguez <mcgrof@do-not-panic.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Backport functionality introduced in Linux user_namespace.c
+ */
+
+#include <linux/module.h>
+#include <linux/highuid.h>
+#include <linux/uidgid.h>
+#include <linux/user_namespace.h>
+
+#ifdef CONFIG_USER_NS
+
+kuid_t make_kuid(struct user_namespace *ns, uid_t uid)
+{
+ /* Map the uid to a global kernel uid */
+ return KUIDT_INIT(uid);
+}
+EXPORT_SYMBOL_GPL(make_kuid);
+
+uid_t from_kuid(struct user_namespace *targ, kuid_t kuid)
+{
+ /* Map the uid from a global kernel uid */
+ return __kuid_val(kuid);
+}
+EXPORT_SYMBOL_GPL(from_kuid);
+
+uid_t from_kuid_munged(struct user_namespace *targ, kuid_t kuid)
+{
+ uid_t uid;
+ uid = from_kuid(targ, kuid);
+
+ if (uid == (uid_t) -1)
+ uid = overflowuid;
+ return uid;
+}
+EXPORT_SYMBOL_GPL(from_kuid_munged);
+
+kgid_t make_kgid(struct user_namespace *ns, gid_t gid)
+{
+ /* Map the gid to a global kernel gid */
+ return KGIDT_INIT(gid);
+}
+EXPORT_SYMBOL_GPL(make_kgid);
+
+gid_t from_kgid(struct user_namespace *targ, kgid_t kgid)
+{
+ /* Map the gid from a global kernel gid */
+ return __kgid_val(kgid);
+}
+EXPORT_SYMBOL_GPL(from_kgid);
+
+gid_t from_kgid_munged(struct user_namespace *targ, kgid_t kgid)
+{
+ gid_t gid;
+ gid = from_kgid(targ, kgid);
+
+ if (gid == (gid_t) -1)
+ gid = overflowgid;
+ return gid;
+}
+EXPORT_SYMBOL_GPL(from_kgid_munged);
+
+#endif /* CONFIG_USER_NS */
diff --git a/defconfigs/brcmfmac b/defconfigs/brcmfmac
new file mode 100644
index 0000000..4f882f5
--- /dev/null
+++ b/defconfigs/brcmfmac
@@ -0,0 +1,10 @@
+CPTCFG_CFG80211=m
+CPTCFG_CFG80211_WEXT=y
+CPTCFG_CFG80211_INTERNAL_REGDB=y
+CPTCFG_WLAN=y
+CPTCFG_WLAN_VENDOR_BROADCOM=y
+CPTCFG_BRCMFMAC=m
+CPTCFG_BRCMFMAC_SDIO=y
+CPTCFG_BRCMFMAC_USB=y
+CPTCFG_BRCMFMAC_PCIE=y
+CPTCFG_BRCMDBG=y
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
new file mode 100644
index 0000000..abdda1f
--- /dev/null
+++ b/drivers/net/wireless/Kconfig
@@ -0,0 +1,124 @@
+#
+# Wireless LAN device configuration
+#
+
+menuconfig WLAN
+ bool "Wireless LAN"
+ depends on !S390
+ depends on NET
+ select WIRELESS
+ default y
+ ---help---
+ This section contains all the pre 802.11 and 802.11 wireless
+ device drivers. For a complete list of drivers and documentation
+ on them refer to the wireless wiki:
+
+ http://wireless.kernel.org/en/users/Drivers
+
+if WLAN
+
+config WIRELESS_WDS
+ bool "mac80211-based legacy WDS support" if EXPERT
+ help
+ This option enables the deprecated WDS support, the newer
+ mac80211-based 4-addr AP/client support supersedes it with
+ a much better feature set (HT, VHT, ...)
+
+ We plan to remove this option and code, so if you find
+ that you have to enable it, please let us know on the
+ linux-wireless@vger.kernel.org mailing list, so we can
+ help you migrate to 4-addr AP/client (or, if it's really
+ necessary, give up on our plan of removing it).
+
+#source "drivers/net/wireless/admtek/Kconfig"
+#source "drivers/net/wireless/ath/Kconfig"
+#source "drivers/net/wireless/atmel/Kconfig"
+source "drivers/net/wireless/broadcom/Kconfig"
+#source "drivers/net/wireless/cisco/Kconfig"
+#source "drivers/net/wireless/intel/Kconfig"
+#source "drivers/net/wireless/intersil/Kconfig"
+#source "drivers/net/wireless/marvell/Kconfig"
+#source "drivers/net/wireless/mediatek/Kconfig"
+#source "drivers/net/wireless/ralink/Kconfig"
+#source "drivers/net/wireless/realtek/Kconfig"
+#source "drivers/net/wireless/rsi/Kconfig"
+#source "drivers/net/wireless/st/Kconfig"
+#source "drivers/net/wireless/ti/Kconfig"
+#source "drivers/net/wireless/zydas/Kconfig"
+
+config PCMCIA_RAYCS
+ depends on n
+ tristate "Aviator/Raytheon 2.4GHz wireless support"
+ depends on m
+ depends on PCMCIA
+ depends on WIRELESS_EXT
+ depends on WEXT_SPY
+ depends on WEXT_PRIV
+ ---help---
+ Say Y here if you intend to attach an Aviator/Raytheon PCMCIA
+ (PC-card) wireless Ethernet networking card to your computer.
+ Please read the file <file:Documentation/networking/ray_cs.txt> for
+ details.
+
+ To compile this driver as a module, choose M here: the module will be
+ called ray_cs. If unsure, say N.
+
+config PCMCIA_WL3501
+ depends on n
+ tristate "Planet WL3501 PCMCIA cards"
+ depends on m
+ depends on CFG80211 && PCMCIA
+ depends on WIRELESS_EXT
+ depends on WEXT_SPY
+ help
+ A driver for WL3501 PCMCIA 802.11 wireless cards made by Planet.
+ It has basic support for Linux wireless extensions and initial
+ micro support for ethtool.
+
+config MAC80211_HWSIM
+ depends on n
+ tristate "Simulated radio testing tool for mac80211"
+ depends on m
+ depends on MAC80211
+ ---help---
+ This driver is a developer testing tool that can be used to test
+ IEEE 802.11 networking stack (mac80211) functionality. This is not
+ needed for normal wireless LAN usage and is only for testing. See
+ Documentation/networking/mac80211_hwsim for more information on how
+ to use this tool.
+
+ To compile this driver as a module, choose M here: the module will be
+ called mac80211_hwsim. If unsure, say N.
+
+config USB_NET_RNDIS_WLAN
+ depends on n
+ tristate "Wireless RNDIS USB support"
+ depends on m
+ depends on USB
+ depends on CFG80211
+ depends on USB_NET_DRIVERS
+ depends on USB_USBNET
+ depends on USB_NET_CDCETHER
+ depends on USB_NET_RNDIS_HOST
+ ---help---
+ This is a driver for wireless RNDIS devices.
+ These are USB based adapters found in devices such as:
+
+ Buffalo WLI-U2-KG125S
+ U.S. Robotics USR5421
+ Belkin F5D7051
+ Linksys WUSB54GSv2
+ Linksys WUSB54GSC
+ Asus WL169gE
+ Eminent EM4045
+ BT Voyager 1055
+ Linksys WUSB54GSv1
+ U.S. Robotics USR5420
+ BUFFALO WLI-USB-G54
+
+ All of these devices are based on Broadcom 4320 chip which is the
+ only wireless RNDIS chip known to date.
+
+ If you choose to build a module, it'll be called rndis_wlan.
+
+endif # WLAN
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
new file mode 100644
index 0000000..47c497a
--- /dev/null
+++ b/drivers/net/wireless/Makefile
@@ -0,0 +1,27 @@
+#
+# Makefile for the Linux Wireless network device drivers.
+#
+#
+#obj-$(CONFIG_WLAN_VENDOR_ADMTEK) += admtek/
+#obj-$(CONFIG_WLAN_VENDOR_ATH) += ath/
+#obj-$(CONFIG_WLAN_VENDOR_ATMEL) += atmel/
+obj-$(CPTCFG_WLAN_VENDOR_BROADCOM) += broadcom/
+#obj-$(CONFIG_WLAN_VENDOR_CISCO) += cisco/
+#obj-$(CONFIG_WLAN_VENDOR_INTEL) += intel/
+#obj-$(CONFIG_WLAN_VENDOR_INTERSIL) += intersil/
+#obj-$(CONFIG_WLAN_VENDOR_MARVELL) += marvell/
+#obj-$(CONFIG_WLAN_VENDOR_MEDIATEK) += mediatek/
+#obj-$(CONFIG_WLAN_VENDOR_RALINK) += ralink/
+#obj-$(CONFIG_WLAN_VENDOR_REALTEK) += realtek/
+#obj-$(CONFIG_WLAN_VENDOR_RSI) += rsi/
+#obj-$(CONFIG_WLAN_VENDOR_ST) += st/
+#obj-$(CONFIG_WLAN_VENDOR_TI) += ti/
+#obj-$(CONFIG_WLAN_VENDOR_ZYDAS) += zydas/
+
+# 16-bit wireless PCMCIA client drivers
+#obj-$(CPTCFG_PCMCIA_RAYCS) += ray_cs.o
+#obj-$(CPTCFG_PCMCIA_WL3501) += wl3501_cs.o
+#
+#obj-$(CPTCFG_USB_NET_RNDIS_WLAN) += rndis_wlan.o
+#
+#obj-$(CPTCFG_MAC80211_HWSIM) += mac80211_hwsim.o
diff --git a/drivers/net/wireless/broadcom/Kconfig b/drivers/net/wireless/broadcom/Kconfig
new file mode 100644
index 0000000..5d08edf
--- /dev/null
+++ b/drivers/net/wireless/broadcom/Kconfig
@@ -0,0 +1,18 @@
+config WLAN_VENDOR_BROADCOM
+ bool "Broadcom devices"
+ default y
+ ---help---
+ If you have a wireless card belonging to this class, say Y.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about cards. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if WLAN_VENDOR_BROADCOM
+
+#source "drivers/net/wireless/broadcom/b43/Kconfig"
+#source "drivers/net/wireless/broadcom/b43legacy/Kconfig"
+source "drivers/net/wireless/broadcom/brcm80211/Kconfig"
+
+endif # WLAN_VENDOR_BROADCOM
diff --git a/drivers/net/wireless/broadcom/Makefile b/drivers/net/wireless/broadcom/Makefile
new file mode 100644
index 0000000..f9f7c9a
--- /dev/null
+++ b/drivers/net/wireless/broadcom/Makefile
@@ -0,0 +1,5 @@
+#obj-$(CONFIG_B43) += b43/
+#obj-$(CONFIG_B43LEGACY) += b43legacy/
+
+obj-$(CPTCFG_BRCMFMAC) += brcm80211/
+#obj-$(CPTCFG_BRCMSMAC) += brcm80211/
diff --git a/drivers/net/wireless/broadcom/brcm80211/Kconfig b/drivers/net/wireless/broadcom/brcm80211/Kconfig
new file mode 100644
index 0000000..84ee498
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/Kconfig
@@ -0,0 +1,91 @@
+config BRCMUTIL
+ tristate
+ depends on m
+
+config BRCMSMAC
+ depends on n
+ tristate "Broadcom IEEE802.11n PCIe SoftMAC WLAN driver"
+ depends on m
+ depends on MAC80211
+ depends on BCMA_POSSIBLE
+ depends on BCMA
+ select NEW_LEDS if BCMA_DRIVER_GPIO
+ select LEDS_CLASS if BCMA_DRIVER_GPIO
+ select BRCMUTIL
+ depends on FW_LOADER
+ select BPAUTO_CORDIC
+ ---help---
+ This module adds support for PCIe wireless adapters based on Broadcom
+ IEEE802.11n SoftMAC chipsets. It also has WLAN led support, which will
+ be available if you select BCMA_DRIVER_GPIO. If you choose to build a
+ module, the driver will be called brcmsmac.ko.
+
+config BRCMFMAC
+ tristate "Broadcom FullMAC WLAN driver"
+ depends on m
+ depends on CFG80211
+ select BRCMUTIL
+ ---help---
+ This module adds support for wireless adapters based on Broadcom
+ FullMAC chipsets. It has to work with at least one of the bus
+ interface support. If you choose to build a module, it'll be called
+ brcmfmac.ko.
+
+config BRCMFMAC_PROTO_BCDC
+ bool
+
+config BRCMFMAC_PROTO_MSGBUF
+ bool
+
+config BRCMFMAC_SDIO
+ bool "SDIO bus interface support for FullMAC driver"
+ depends on (MMC = y || MMC = BRCMFMAC)
+ depends on BRCMFMAC
+ select BRCMFMAC_PROTO_BCDC
+ depends on FW_LOADER
+ default y
+ ---help---
+ This option enables the SDIO bus interface support for Broadcom
+ IEEE802.11n embedded FullMAC WLAN driver. Say Y if you want to
+ use the driver for a SDIO wireless card.
+
+config BRCMFMAC_USB
+ bool "USB bus interface support for FullMAC driver"
+ depends on (USB = y || USB = BRCMFMAC)
+ depends on BRCMFMAC
+ select BRCMFMAC_PROTO_BCDC
+ depends on FW_LOADER
+ ---help---
+ This option enables the USB bus interface support for Broadcom
+ IEEE802.11n embedded FullMAC WLAN driver. Say Y if you want to
+ use the driver for an USB wireless card.
+
+config BRCMFMAC_PCIE
+ bool "PCIE bus interface support for FullMAC driver"
+ depends on BRCMFMAC
+ depends on PCI
+ depends on HAS_DMA
+ select BRCMFMAC_PROTO_MSGBUF
+ depends on FW_LOADER
+ ---help---
+ This option enables the PCIE bus interface support for Broadcom
+ IEEE802.11ac embedded FullMAC WLAN driver. Say Y if you want to
+ use the driver for an PCIE wireless card.
+
+config BRCM_TRACING
+ bool "Broadcom device tracing"
+ depends on BRCMSMAC || BRCMFMAC
+ ---help---
+ If you say Y here, the Broadcom wireless drivers will register
+ with ftrace to dump event information into the trace ringbuffer.
+ Tracing can be enabled at runtime to aid in debugging wireless
+ issues. This option adds a small amount of overhead when tracing
+ is disabled. If unsure, say Y to allow developers to better help
+ you when wireless problems occur.
+
+config BRCMDBG
+ bool "Broadcom driver debug functions"
+ depends on BRCMSMAC || BRCMFMAC
+ select BPAUTO_WANT_DEV_COREDUMP
+ ---help---
+ Selecting this enables additional code for debug purposes.
diff --git a/drivers/net/wireless/broadcom/brcm80211/Makefile b/drivers/net/wireless/broadcom/brcm80211/Makefile
new file mode 100644
index 0000000..f2f7bfa
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/Makefile
@@ -0,0 +1,23 @@
+#
+# Makefile fragment for Broadcom 802.11n Networking Device Driver
+#
+# Copyright (c) 2010 Broadcom Corporation
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+# SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+# OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+# common flags
+subdir-ccflags-$(CPTCFG_BRCMDBG) += -DDEBUG
+
+obj-$(CPTCFG_BRCMUTIL) += brcmutil/
+obj-$(CPTCFG_BRCMFMAC) += brcmfmac/
+#obj-$(CPTCFG_BRCMSMAC) += brcmsmac/
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile
new file mode 100644
index 0000000..fca7746
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile
@@ -0,0 +1,56 @@
+#
+# Makefile fragment for Broadcom 802.11n Networking Device Driver
+#
+# Copyright (c) 2010 Broadcom Corporation
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+# SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+# OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+ccflags-y += \
+ -I$(backport_srctree)/drivers/net/wireless/broadcom/brcm80211/brcmfmac \
+ -I$(backport_srctree)/drivers/net/wireless/broadcom/brcm80211/include
+
+obj-$(CPTCFG_BRCMFMAC) += brcmfmac.o
+brcmfmac-objs += \
+ cfg80211.o \
+ chip.o \
+ fwil.o \
+ fweh.o \
+ p2p.o \
+ proto.o \
+ common.o \
+ core.o \
+ firmware.o \
+ feature.o \
+ btcoex.o \
+ vendor.o \
+ pno.o
+brcmfmac-$(CPTCFG_BRCMFMAC_PROTO_BCDC) += \
+ bcdc.o \
+ fwsignal.o
+brcmfmac-$(CPTCFG_BRCMFMAC_PROTO_MSGBUF) += \
+ commonring.o \
+ flowring.o \
+ msgbuf.o
+brcmfmac-$(CPTCFG_BRCMFMAC_SDIO) += \
+ sdio.o \
+ bcmsdh.o
+brcmfmac-$(CPTCFG_BRCMFMAC_USB) += \
+ usb.o
+brcmfmac-$(CPTCFG_BRCMFMAC_PCIE) += \
+ pcie.o
+brcmfmac-$(CPTCFG_BRCMDBG) += \
+ debug.o
+brcmfmac-$(CPTCFG_BRCM_TRACING) += \
+ tracepoint.o
+brcmfmac-$(CONFIG_OF) += \
+ of.o
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c
new file mode 100644
index 0000000..9f2d0b0
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c
@@ -0,0 +1,489 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*******************************************************************************
+ * Communicates with the dongle by using dcmd codes.
+ * For certain dcmd codes, the dongle interprets string data from the host.
+ ******************************************************************************/
+
+#include <linux/types.h>
+#include <linux/netdevice.h>
+
+#include <brcmu_utils.h>
+#include <brcmu_wifi.h>
+
+#include "core.h"
+#include "bus.h"
+#include "fwsignal.h"
+#include "debug.h"
+#include "tracepoint.h"
+#include "proto.h"
+#include "bcdc.h"
+
+struct brcmf_proto_bcdc_dcmd {
+ __le32 cmd; /* dongle command value */
+ __le32 len; /* lower 16: output buflen;
+ * upper 16: input buflen (excludes header) */
+ __le32 flags; /* flag defns given below */
+ __le32 status; /* status code returned from the device */
+};
+
+/* BCDC flag definitions */
+#define BCDC_DCMD_ERROR 0x01 /* 1=cmd failed */
+#define BCDC_DCMD_SET 0x02 /* 0=get, 1=set cmd */
+#define BCDC_DCMD_IF_MASK 0xF000 /* I/F index */
+#define BCDC_DCMD_IF_SHIFT 12
+#define BCDC_DCMD_ID_MASK 0xFFFF0000 /* id an cmd pairing */
+#define BCDC_DCMD_ID_SHIFT 16 /* ID Mask shift bits */
+#define BCDC_DCMD_ID(flags) \
+ (((flags) & BCDC_DCMD_ID_MASK) >> BCDC_DCMD_ID_SHIFT)
+
+/*
+ * BCDC header - Broadcom specific extension of CDC.
+ * Used on data packets to convey priority across USB.
+ */
+#define BCDC_HEADER_LEN 4
+#define BCDC_PROTO_VER 2 /* Protocol version */
+#define BCDC_FLAG_VER_MASK 0xf0 /* Protocol version mask */
+#define BCDC_FLAG_VER_SHIFT 4 /* Protocol version shift */
+#define BCDC_FLAG_SUM_GOOD 0x04 /* Good RX checksums */
+#define BCDC_FLAG_SUM_NEEDED 0x08 /* Dongle needs to do TX checksums */
+#define BCDC_PRIORITY_MASK 0x7
+#define BCDC_FLAG2_IF_MASK 0x0f /* packet rx interface in APSTA */
+#define BCDC_FLAG2_IF_SHIFT 0
+
+#define BCDC_GET_IF_IDX(hdr) \
+ ((int)((((hdr)->flags2) & BCDC_FLAG2_IF_MASK) >> BCDC_FLAG2_IF_SHIFT))
+#define BCDC_SET_IF_IDX(hdr, idx) \
+ ((hdr)->flags2 = (((hdr)->flags2 & ~BCDC_FLAG2_IF_MASK) | \
+ ((idx) << BCDC_FLAG2_IF_SHIFT)))
+
+/**
+ * struct brcmf_proto_bcdc_header - BCDC header format
+ *
+ * @flags: flags contain protocol and checksum info.
+ * @priority: 802.1d priority and USB flow control info (bit 4:7).
+ * @flags2: additional flags containing dongle interface index.
+ * @data_offset: start of packet data. header is following by firmware signals.
+ */
+struct brcmf_proto_bcdc_header {
+ u8 flags;
+ u8 priority;
+ u8 flags2;
+ u8 data_offset;
+};
+
+/*
+ * maximum length of firmware signal data between
+ * the BCDC header and packet data in the tx path.
+ */
+#define BRCMF_PROT_FW_SIGNAL_MAX_TXBYTES 12
+
+#define RETRIES 2 /* # of retries to retrieve matching dcmd response */
+#define BUS_HEADER_LEN (16+64) /* Must be atleast SDPCM_RESERVE
+ * (amount of header tha might be added)
+ * plus any space that might be needed
+ * for bus alignment padding.
+ */
+struct brcmf_bcdc {
+ u16 reqid;
+ u8 bus_header[BUS_HEADER_LEN];
+ struct brcmf_proto_bcdc_dcmd msg;
+ unsigned char buf[BRCMF_DCMD_MAXLEN];
+ struct brcmf_fws_info *fws;
+};
+
+
+struct brcmf_fws_info *drvr_to_fws(struct brcmf_pub *drvr)
+{
+ struct brcmf_bcdc *bcdc = drvr->proto->pd;
+
+ return bcdc->fws;
+}
+
+static int
+brcmf_proto_bcdc_msg(struct brcmf_pub *drvr, int ifidx, uint cmd, void *buf,
+ uint len, bool set)
+{
+ struct brcmf_bcdc *bcdc = (struct brcmf_bcdc *)drvr->proto->pd;
+ struct brcmf_proto_bcdc_dcmd *msg = &bcdc->msg;
+ u32 flags;
+
+ brcmf_dbg(BCDC, "Enter\n");
+
+ memset(msg, 0, sizeof(struct brcmf_proto_bcdc_dcmd));
+
+ msg->cmd = cpu_to_le32(cmd);
+ msg->len = cpu_to_le32(len);
+ flags = (++bcdc->reqid << BCDC_DCMD_ID_SHIFT);
+ if (set)
+ flags |= BCDC_DCMD_SET;
+ flags = (flags & ~BCDC_DCMD_IF_MASK) |
+ (ifidx << BCDC_DCMD_IF_SHIFT);
+ msg->flags = cpu_to_le32(flags);
+
+ if (buf)
+ memcpy(bcdc->buf, buf, len);
+
+ len += sizeof(*msg);
+ if (len > BRCMF_TX_IOCTL_MAX_MSG_SIZE)
+ len = BRCMF_TX_IOCTL_MAX_MSG_SIZE;
+
+ /* Send request */
+ return brcmf_bus_txctl(drvr->bus_if, (unsigned char *)&bcdc->msg, len);
+}
+
+static int brcmf_proto_bcdc_cmplt(struct brcmf_pub *drvr, u32 id, u32 len)
+{
+ int ret;
+ struct brcmf_bcdc *bcdc = (struct brcmf_bcdc *)drvr->proto->pd;
+
+ brcmf_dbg(BCDC, "Enter\n");
+ len += sizeof(struct brcmf_proto_bcdc_dcmd);
+ do {
+ ret = brcmf_bus_rxctl(drvr->bus_if, (unsigned char *)&bcdc->msg,
+ len);
+ if (ret < 0)
+ break;
+ } while (BCDC_DCMD_ID(le32_to_cpu(bcdc->msg.flags)) != id);
+
+ return ret;
+}
+
+static int
+brcmf_proto_bcdc_query_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd,
+ void *buf, uint len)
+{
+ struct brcmf_bcdc *bcdc = (struct brcmf_bcdc *)drvr->proto->pd;
+ struct brcmf_proto_bcdc_dcmd *msg = &bcdc->msg;
+ void *info;
+ int ret = 0, retries = 0;
+ u32 id, flags;
+
+ brcmf_dbg(BCDC, "Enter, cmd %d len %d\n", cmd, len);
+
+ ret = brcmf_proto_bcdc_msg(drvr, ifidx, cmd, buf, len, false);
+ if (ret < 0) {
+ brcmf_err("brcmf_proto_bcdc_msg failed w/status %d\n",
+ ret);
+ goto done;
+ }
+
+retry:
+ /* wait for interrupt and get first fragment */
+ ret = brcmf_proto_bcdc_cmplt(drvr, bcdc->reqid, len);
+ if (ret < 0)
+ goto done;
+
+ flags = le32_to_cpu(msg->flags);
+ id = (flags & BCDC_DCMD_ID_MASK) >> BCDC_DCMD_ID_SHIFT;
+
+ if ((id < bcdc->reqid) && (++retries < RETRIES))
+ goto retry;
+ if (id != bcdc->reqid) {
+ brcmf_err("%s: unexpected request id %d (expected %d)\n",
+ brcmf_ifname(brcmf_get_ifp(drvr, ifidx)), id,
+ bcdc->reqid);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ /* Check info buffer */
+ info = (void *)&bcdc->buf[0];
+
+ /* Copy info buffer */
+ if (buf) {
+ if (ret < (int)len)
+ len = ret;
+ memcpy(buf, info, len);
+ }
+
+ /* Check the ERROR flag */
+ if (flags & BCDC_DCMD_ERROR)
+ ret = le32_to_cpu(msg->status);
+
+done:
+ return ret;
+}
+
+static int
+brcmf_proto_bcdc_set_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd,
+ void *buf, uint len)
+{
+ struct brcmf_bcdc *bcdc = (struct brcmf_bcdc *)drvr->proto->pd;
+ struct brcmf_proto_bcdc_dcmd *msg = &bcdc->msg;
+ int ret = 0;
+ u32 flags, id;
+
+ brcmf_dbg(BCDC, "Enter, cmd %d len %d\n", cmd, len);
+
+ ret = brcmf_proto_bcdc_msg(drvr, ifidx, cmd, buf, len, true);
+ if (ret < 0)
+ goto done;
+
+ ret = brcmf_proto_bcdc_cmplt(drvr, bcdc->reqid, len);
+ if (ret < 0)
+ goto done;
+
+ flags = le32_to_cpu(msg->flags);
+ id = (flags & BCDC_DCMD_ID_MASK) >> BCDC_DCMD_ID_SHIFT;
+
+ if (id != bcdc->reqid) {
+ brcmf_err("%s: unexpected request id %d (expected %d)\n",
+ brcmf_ifname(brcmf_get_ifp(drvr, ifidx)), id,
+ bcdc->reqid);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ /* Check the ERROR flag */
+ if (flags & BCDC_DCMD_ERROR)
+ ret = le32_to_cpu(msg->status);
+
+done:
+ return ret;
+}
+
+static void
+brcmf_proto_bcdc_hdrpush(struct brcmf_pub *drvr, int ifidx, u8 offset,
+ struct sk_buff *pktbuf)
+{
+ struct brcmf_proto_bcdc_header *h;
+
+ brcmf_dbg(BCDC, "Enter\n");
+
+ /* Push BDC header used to convey priority for buses that don't */
+ skb_push(pktbuf, BCDC_HEADER_LEN);
+
+ h = (struct brcmf_proto_bcdc_header *)(pktbuf->data);
+
+ h->flags = (BCDC_PROTO_VER << BCDC_FLAG_VER_SHIFT);
+ if (pktbuf->ip_summed == CHECKSUM_PARTIAL)
+ h->flags |= BCDC_FLAG_SUM_NEEDED;
+
+ h->priority = (pktbuf->priority & BCDC_PRIORITY_MASK);
+ h->flags2 = 0;
+ h->data_offset = offset;
+ BCDC_SET_IF_IDX(h, ifidx);
+ trace_brcmf_bcdchdr(pktbuf->data);
+}
+
+static int
+brcmf_proto_bcdc_hdrpull(struct brcmf_pub *drvr, bool do_fws,
+ struct sk_buff *pktbuf, struct brcmf_if **ifp)
+{
+ struct brcmf_proto_bcdc_header *h;
+ struct brcmf_if *tmp_if;
+
+ brcmf_dbg(BCDC, "Enter\n");
+
+ /* Pop BCDC header used to convey priority for buses that don't */
+ if (pktbuf->len <= BCDC_HEADER_LEN) {
+ brcmf_dbg(INFO, "rx data too short (%d <= %d)\n",
+ pktbuf->len, BCDC_HEADER_LEN);
+ return -EBADE;
+ }
+
+ trace_brcmf_bcdchdr(pktbuf->data);
+ h = (struct brcmf_proto_bcdc_header *)(pktbuf->data);
+
+ tmp_if = brcmf_get_ifp(drvr, BCDC_GET_IF_IDX(h));
+ if (!tmp_if) {
+ brcmf_dbg(INFO, "no matching ifp found\n");
+ return -EBADE;
+ }
+ if (((h->flags & BCDC_FLAG_VER_MASK) >> BCDC_FLAG_VER_SHIFT) !=
+ BCDC_PROTO_VER) {
+ brcmf_err("%s: non-BCDC packet received, flags 0x%x\n",
+ brcmf_ifname(tmp_if), h->flags);
+ return -EBADE;
+ }
+
+ if (h->flags & BCDC_FLAG_SUM_GOOD) {
+ brcmf_dbg(BCDC, "%s: BDC rcv, good checksum, flags 0x%x\n",
+ brcmf_ifname(tmp_if), h->flags);
+ pktbuf->ip_summed = CHECKSUM_UNNECESSARY;
+ }
+
+ pktbuf->priority = h->priority & BCDC_PRIORITY_MASK;
+
+ skb_pull(pktbuf, BCDC_HEADER_LEN);
+ if (do_fws)
+ brcmf_fws_hdrpull(tmp_if, h->data_offset << 2, pktbuf);
+ else
+ skb_pull(pktbuf, h->data_offset << 2);
+
+ if (pktbuf->len == 0)
+ return -ENODATA;
+
+ if (ifp != NULL)
+ *ifp = tmp_if;
+ return 0;
+}
+
+static int brcmf_proto_bcdc_tx_queue_data(struct brcmf_pub *drvr, int ifidx,
+ struct sk_buff *skb)
+{
+ struct brcmf_if *ifp = brcmf_get_ifp(drvr, ifidx);
+ struct brcmf_bcdc *bcdc = drvr->proto->pd;
+
+ if (!brcmf_fws_queue_skbs(bcdc->fws))
+ return brcmf_proto_txdata(drvr, ifidx, 0, skb);
+
+ return brcmf_fws_process_skb(ifp, skb);
+}
+
+static int
+brcmf_proto_bcdc_txdata(struct brcmf_pub *drvr, int ifidx, u8 offset,
+ struct sk_buff *pktbuf)
+{
+ brcmf_proto_bcdc_hdrpush(drvr, ifidx, offset, pktbuf);
+ return brcmf_bus_txdata(drvr->bus_if, pktbuf);
+}
+
+void brcmf_proto_bcdc_txflowblock(struct device *dev, bool state)
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+ struct brcmf_pub *drvr = bus_if->drvr;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ brcmf_fws_bus_blocked(drvr, state);
+}
+
+void
+brcmf_proto_bcdc_txcomplete(struct device *dev, struct sk_buff *txp,
+ bool success)
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+ struct brcmf_bcdc *bcdc = bus_if->drvr->proto->pd;
+ struct brcmf_if *ifp;
+
+ /* await txstatus signal for firmware if active */
+ if (brcmf_fws_fc_active(bcdc->fws)) {
+ if (!success)
+ brcmf_fws_bustxfail(bcdc->fws, txp);
+ } else {
+ if (brcmf_proto_bcdc_hdrpull(bus_if->drvr, false, txp, &ifp))
+ brcmu_pkt_buf_free_skb(txp);
+ else
+ brcmf_txfinalize(ifp, txp, success);
+ }
+}
+
+static void
+brcmf_proto_bcdc_configure_addr_mode(struct brcmf_pub *drvr, int ifidx,
+ enum proto_addr_mode addr_mode)
+{
+}
+
+static void
+brcmf_proto_bcdc_delete_peer(struct brcmf_pub *drvr, int ifidx,
+ u8 peer[ETH_ALEN])
+{
+}
+
+static void
+brcmf_proto_bcdc_add_tdls_peer(struct brcmf_pub *drvr, int ifidx,
+ u8 peer[ETH_ALEN])
+{
+}
+
+static void brcmf_proto_bcdc_rxreorder(struct brcmf_if *ifp,
+ struct sk_buff *skb)
+{
+ brcmf_fws_rxreorder(ifp, skb);
+}
+
+static void
+brcmf_proto_bcdc_add_if(struct brcmf_if *ifp)
+{
+ brcmf_fws_add_interface(ifp);
+}
+
+static void
+brcmf_proto_bcdc_del_if(struct brcmf_if *ifp)
+{
+ brcmf_fws_del_interface(ifp);
+}
+
+static void
+brcmf_proto_bcdc_reset_if(struct brcmf_if *ifp)
+{
+ brcmf_fws_reset_interface(ifp);
+}
+
+static int
+brcmf_proto_bcdc_init_done(struct brcmf_pub *drvr)
+{
+ struct brcmf_bcdc *bcdc = drvr->proto->pd;
+ struct brcmf_fws_info *fws;
+
+ fws = brcmf_fws_attach(drvr);
+ if (IS_ERR(fws))
+ return PTR_ERR(fws);
+
+ bcdc->fws = fws;
+ return 0;
+}
+
+int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr)
+{
+ struct brcmf_bcdc *bcdc;
+
+ bcdc = kzalloc(sizeof(*bcdc), GFP_ATOMIC);
+ if (!bcdc)
+ goto fail;
+
+ /* ensure that the msg buf directly follows the cdc msg struct */
+ if ((unsigned long)(&bcdc->msg + 1) != (unsigned long)bcdc->buf) {
+ brcmf_err("struct brcmf_proto_bcdc is not correctly defined\n");
+ goto fail;
+ }
+
+ drvr->proto->hdrpull = brcmf_proto_bcdc_hdrpull;
+ drvr->proto->query_dcmd = brcmf_proto_bcdc_query_dcmd;
+ drvr->proto->set_dcmd = brcmf_proto_bcdc_set_dcmd;
+ drvr->proto->tx_queue_data = brcmf_proto_bcdc_tx_queue_data;
+ drvr->proto->txdata = brcmf_proto_bcdc_txdata;
+ drvr->proto->configure_addr_mode = brcmf_proto_bcdc_configure_addr_mode;
+ drvr->proto->delete_peer = brcmf_proto_bcdc_delete_peer;
+ drvr->proto->add_tdls_peer = brcmf_proto_bcdc_add_tdls_peer;
+ drvr->proto->rxreorder = brcmf_proto_bcdc_rxreorder;
+ drvr->proto->add_if = brcmf_proto_bcdc_add_if;
+ drvr->proto->del_if = brcmf_proto_bcdc_del_if;
+ drvr->proto->reset_if = brcmf_proto_bcdc_reset_if;
+ drvr->proto->init_done = brcmf_proto_bcdc_init_done;
+ drvr->proto->pd = bcdc;
+
+ drvr->hdrlen += BCDC_HEADER_LEN + BRCMF_PROT_FW_SIGNAL_MAX_TXBYTES;
+ drvr->bus_if->maxctl = BRCMF_DCMD_MAXLEN +
+ sizeof(struct brcmf_proto_bcdc_dcmd);
+ return 0;
+
+fail:
+ kfree(bcdc);
+ return -ENOMEM;
+}
+
+void brcmf_proto_bcdc_detach(struct brcmf_pub *drvr)
+{
+ struct brcmf_bcdc *bcdc = drvr->proto->pd;
+
+ drvr->proto->pd = NULL;
+ brcmf_fws_detach(bcdc->fws);
+ kfree(bcdc);
+}
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.h
new file mode 100644
index 0000000..1c1b4c1
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2013 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#ifndef BRCMFMAC_BCDC_H
+#define BRCMFMAC_BCDC_H
+
+#ifdef CPTCFG_BRCMFMAC_PROTO_BCDC
+int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr);
+void brcmf_proto_bcdc_detach(struct brcmf_pub *drvr);
+void brcmf_proto_bcdc_txflowblock(struct device *dev, bool state);
+void brcmf_proto_bcdc_txcomplete(struct device *dev, struct sk_buff *txp,
+ bool success);
+struct brcmf_fws_info *drvr_to_fws(struct brcmf_pub *drvr);
+#else
+static inline int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr) { return 0; }
+static inline void brcmf_proto_bcdc_detach(struct brcmf_pub *drvr) {}
+#endif
+
+#endif /* BRCMFMAC_BCDC_H */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c
new file mode 100644
index 0000000..b13f1c4
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c
@@ -0,0 +1,1350 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/* ****************** SDIO CARD Interface Functions **************************/
+
+#include <linux/types.h>
+#include <linux/netdevice.h>
+#include <linux/pci.h>
+#include <linux/pci_ids.h>
+#include <linux/sched.h>
+#include <linux/completion.h>
+#include <linux/interrupt.h>
+#include <linux/scatterlist.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/core.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/host.h>
+#include <linux/pm_runtime.h>
+#include <linux/suspend.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/acpi.h>
+#include <net/cfg80211.h>
+
+#include <defs.h>
+#include <brcm_hw_ids.h>
+#include <brcmu_utils.h>
+#include <brcmu_wifi.h>
+#include <chipcommon.h>
+#include <soc.h>
+#include "chip.h"
+#include "bus.h"
+#include "debug.h"
+#include "sdio.h"
+#include "core.h"
+#include "common.h"
+
+#define SDIOH_API_ACCESS_RETRY_LIMIT 2
+
+#define DMA_ALIGN_MASK 0x03
+
+#define SDIO_FUNC1_BLOCKSIZE 64
+#define SDIO_FUNC2_BLOCKSIZE 512
+#define SDIO_4373_FUNC2_BLOCKSIZE 256
+/* Maximum milliseconds to wait for F2 to come up */
+#define SDIO_WAIT_F2RDY 3000
+
+#define BRCMF_DEFAULT_RXGLOM_SIZE 32 /* max rx frames in glom chain */
+
+struct brcmf_sdiod_freezer {
+ atomic_t freezing;
+ atomic_t thread_count;
+ u32 frozen_count;
+ wait_queue_head_t thread_freeze;
+ struct completion resumed;
+};
+
+static irqreturn_t brcmf_sdiod_oob_irqhandler(int irq, void *dev_id)
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(dev_id);
+ struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
+
+ brcmf_dbg(INTR, "OOB intr triggered\n");
+
+ /* out-of-band interrupt is level-triggered which won't
+ * be cleared until dpc
+ */
+ if (sdiodev->irq_en) {
+ disable_irq_nosync(irq);
+ sdiodev->irq_en = false;
+ }
+
+ brcmf_sdio_isr(sdiodev->bus);
+
+ return IRQ_HANDLED;
+}
+
+static void brcmf_sdiod_ib_irqhandler(struct sdio_func *func)
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(&func->dev);
+ struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
+
+ brcmf_dbg(INTR, "IB intr triggered\n");
+
+ brcmf_sdio_isr(sdiodev->bus);
+}
+
+/* dummy handler for SDIO function 2 interrupt */
+static void brcmf_sdiod_dummy_irqhandler(struct sdio_func *func)
+{
+}
+
+int brcmf_sdiod_intr_register(struct brcmf_sdio_dev *sdiodev)
+{
+ struct brcmfmac_sdio_pd *pdata;
+ int ret = 0;
+ u8 data;
+ u32 addr, gpiocontrol;
+ unsigned long flags;
+
+ pdata = &sdiodev->settings->bus.sdio;
+ if (pdata->oob_irq_supported) {
+ brcmf_dbg(SDIO, "Enter, register OOB IRQ %d\n",
+ pdata->oob_irq_nr);
+ ret = request_irq(pdata->oob_irq_nr, brcmf_sdiod_oob_irqhandler,
+ pdata->oob_irq_flags, "brcmf_oob_intr",
+ &sdiodev->func[1]->dev);
+ if (ret != 0) {
+ brcmf_err("request_irq failed %d\n", ret);
+ return ret;
+ }
+ sdiodev->oob_irq_requested = true;
+ spin_lock_init(&sdiodev->irq_en_lock);
+ spin_lock_irqsave(&sdiodev->irq_en_lock, flags);
+ sdiodev->irq_en = true;
+ spin_unlock_irqrestore(&sdiodev->irq_en_lock, flags);
+
+ ret = enable_irq_wake(pdata->oob_irq_nr);
+ if (ret != 0) {
+ brcmf_err("enable_irq_wake failed %d\n", ret);
+ return ret;
+ }
+ sdiodev->irq_wake = true;
+
+ sdio_claim_host(sdiodev->func[1]);
+
+ if (sdiodev->bus_if->chip == BRCM_CC_43362_CHIP_ID) {
+ /* assign GPIO to SDIO core */
+ addr = CORE_CC_REG(SI_ENUM_BASE, gpiocontrol);
+ gpiocontrol = brcmf_sdiod_regrl(sdiodev, addr, &ret);
+ gpiocontrol |= 0x2;
+ brcmf_sdiod_regwl(sdiodev, addr, gpiocontrol, &ret);
+
+ brcmf_sdiod_regwb(sdiodev, SBSDIO_GPIO_SELECT, 0xf,
+ &ret);
+ brcmf_sdiod_regwb(sdiodev, SBSDIO_GPIO_OUT, 0, &ret);
+ brcmf_sdiod_regwb(sdiodev, SBSDIO_GPIO_EN, 0x2, &ret);
+ }
+
+ /* must configure SDIO_CCCR_IENx to enable irq */
+ data = brcmf_sdiod_regrb(sdiodev, SDIO_CCCR_IENx, &ret);
+ data |= 1 << SDIO_FUNC_1 | 1 << SDIO_FUNC_2 | 1;
+ brcmf_sdiod_regwb(sdiodev, SDIO_CCCR_IENx, data, &ret);
+
+ /* redirect, configure and enable io for interrupt signal */
+ data = SDIO_SEPINT_MASK | SDIO_SEPINT_OE;
+ if (pdata->oob_irq_flags & IRQF_TRIGGER_HIGH)
+ data |= SDIO_SEPINT_ACT_HI;
+ brcmf_sdiod_regwb(sdiodev, SDIO_CCCR_BRCM_SEPINT, data, &ret);
+
+ sdio_release_host(sdiodev->func[1]);
+ } else {
+ brcmf_dbg(SDIO, "Entering\n");
+ sdio_claim_host(sdiodev->func[1]);
+ sdio_claim_irq(sdiodev->func[1], brcmf_sdiod_ib_irqhandler);
+ sdio_claim_irq(sdiodev->func[2], brcmf_sdiod_dummy_irqhandler);
+ sdio_release_host(sdiodev->func[1]);
+ sdiodev->sd_irq_requested = true;
+ }
+
+ return 0;
+}
+
+void brcmf_sdiod_intr_unregister(struct brcmf_sdio_dev *sdiodev)
+{
+
+ brcmf_dbg(SDIO, "Entering oob=%d sd=%d\n",
+ sdiodev->oob_irq_requested,
+ sdiodev->sd_irq_requested);
+
+ if (sdiodev->oob_irq_requested) {
+ struct brcmfmac_sdio_pd *pdata;
+
+ pdata = &sdiodev->settings->bus.sdio;
+ sdio_claim_host(sdiodev->func[1]);
+ brcmf_sdiod_regwb(sdiodev, SDIO_CCCR_BRCM_SEPINT, 0, NULL);
+ brcmf_sdiod_regwb(sdiodev, SDIO_CCCR_IENx, 0, NULL);
+ sdio_release_host(sdiodev->func[1]);
+
+ sdiodev->oob_irq_requested = false;
+ if (sdiodev->irq_wake) {
+ disable_irq_wake(pdata->oob_irq_nr);
+ sdiodev->irq_wake = false;
+ }
+ free_irq(pdata->oob_irq_nr, &sdiodev->func[1]->dev);
+ sdiodev->irq_en = false;
+ sdiodev->oob_irq_requested = false;
+ }
+
+ if (sdiodev->sd_irq_requested) {
+ sdio_claim_host(sdiodev->func[1]);
+ sdio_release_irq(sdiodev->func[2]);
+ sdio_release_irq(sdiodev->func[1]);
+ sdio_release_host(sdiodev->func[1]);
+ sdiodev->sd_irq_requested = false;
+ }
+}
+
+void brcmf_sdiod_change_state(struct brcmf_sdio_dev *sdiodev,
+ enum brcmf_sdiod_state state)
+{
+ if (sdiodev->state == BRCMF_SDIOD_NOMEDIUM ||
+ state == sdiodev->state)
+ return;
+
+ brcmf_dbg(TRACE, "%d -> %d\n", sdiodev->state, state);
+ switch (sdiodev->state) {
+ case BRCMF_SDIOD_DATA:
+ /* any other state means bus interface is down */
+ brcmf_bus_change_state(sdiodev->bus_if, BRCMF_BUS_DOWN);
+ break;
+ case BRCMF_SDIOD_DOWN:
+ /* transition from DOWN to DATA means bus interface is up */
+ if (state == BRCMF_SDIOD_DATA)
+ brcmf_bus_change_state(sdiodev->bus_if, BRCMF_BUS_UP);
+ break;
+ default:
+ break;
+ }
+ sdiodev->state = state;
+}
+
+static inline int brcmf_sdiod_f0_writeb(struct sdio_func *func,
+ uint regaddr, u8 byte)
+{
+ int err_ret;
+
+ /*
+ * Can only directly write to some F0 registers.
+ * Handle CCCR_IENx and CCCR_ABORT command
+ * as a special case.
+ */
+ if ((regaddr == SDIO_CCCR_ABORT) ||
+ (regaddr == SDIO_CCCR_IENx))
+ sdio_writeb(func, byte, regaddr, &err_ret);
+ else
+ sdio_f0_writeb(func, byte, regaddr, &err_ret);
+
+ return err_ret;
+}
+
+static int brcmf_sdiod_request_data(struct brcmf_sdio_dev *sdiodev, u8 fn,
+ u32 addr, u8 regsz, void *data, bool write)
+{
+ struct sdio_func *func;
+ int ret = -EINVAL;
+
+ brcmf_dbg(SDIO, "rw=%d, func=%d, addr=0x%05x, nbytes=%d\n",
+ write, fn, addr, regsz);
+
+ /* only allow byte access on F0 */
+ if (WARN_ON(regsz > 1 && !fn))
+ return -EINVAL;
+ func = sdiodev->func[fn];
+
+ switch (regsz) {
+ case sizeof(u8):
+ if (write) {
+ if (fn)
+ sdio_writeb(func, *(u8 *)data, addr, &ret);
+ else
+ ret = brcmf_sdiod_f0_writeb(func, addr,
+ *(u8 *)data);
+ } else {
+ if (fn)
+ *(u8 *)data = sdio_readb(func, addr, &ret);
+ else
+ *(u8 *)data = sdio_f0_readb(func, addr, &ret);
+ }
+ break;
+ case sizeof(u16):
+ if (write)
+ sdio_writew(func, *(u16 *)data, addr, &ret);
+ else
+ *(u16 *)data = sdio_readw(func, addr, &ret);
+ break;
+ case sizeof(u32):
+ if (write)
+ sdio_writel(func, *(u32 *)data, addr, &ret);
+ else
+ *(u32 *)data = sdio_readl(func, addr, &ret);
+ break;
+ default:
+ brcmf_err("invalid size: %d\n", regsz);
+ break;
+ }
+
+ if (ret)
+ brcmf_dbg(SDIO, "failed to %s data F%d@0x%05x, err: %d\n",
+ write ? "write" : "read", fn, addr, ret);
+
+ return ret;
+}
+
+static int brcmf_sdiod_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr,
+ u8 regsz, void *data, bool write)
+{
+ u8 func;
+ s32 retry = 0;
+ int ret;
+
+ if (sdiodev->state == BRCMF_SDIOD_NOMEDIUM)
+ return -ENOMEDIUM;
+
+ /*
+ * figure out how to read the register based on address range
+ * 0x00 ~ 0x7FF: function 0 CCCR and FBR
+ * 0x10000 ~ 0x1FFFF: function 1 miscellaneous registers
+ * The rest: function 1 silicon backplane core registers
+ */
+ if ((addr & ~REG_F0_REG_MASK) == 0)
+ func = SDIO_FUNC_0;
+ else
+ func = SDIO_FUNC_1;
+
+ do {
+ if (!write)
+ memset(data, 0, regsz);
+ /* for retry wait for 1 ms till bus get settled down */
+ if (retry)
+ usleep_range(1000, 2000);
+ ret = brcmf_sdiod_request_data(sdiodev, func, addr, regsz,
+ data, write);
+ } while (ret != 0 && ret != -ENOMEDIUM &&
+ retry++ < SDIOH_API_ACCESS_RETRY_LIMIT);
+
+ if (ret == -ENOMEDIUM)
+ brcmf_sdiod_change_state(sdiodev, BRCMF_SDIOD_NOMEDIUM);
+ else if (ret != 0) {
+ /*
+ * SleepCSR register access can fail when
+ * waking up the device so reduce this noise
+ * in the logs.
+ */
+ if (addr != SBSDIO_FUNC1_SLEEPCSR)
+ brcmf_err("failed to %s data F%d@0x%05x, err: %d\n",
+ write ? "write" : "read", func, addr, ret);
+ else
+ brcmf_dbg(SDIO, "failed to %s data F%d@0x%05x, err: %d\n",
+ write ? "write" : "read", func, addr, ret);
+ }
+ return ret;
+}
+
+static int
+brcmf_sdiod_set_sbaddr_window(struct brcmf_sdio_dev *sdiodev, u32 address)
+{
+ int err = 0, i;
+ u8 addr[3];
+
+ if (sdiodev->state == BRCMF_SDIOD_NOMEDIUM)
+ return -ENOMEDIUM;
+
+ addr[0] = (address >> 8) & SBSDIO_SBADDRLOW_MASK;
+ addr[1] = (address >> 16) & SBSDIO_SBADDRMID_MASK;
+ addr[2] = (address >> 24) & SBSDIO_SBADDRHIGH_MASK;
+
+ for (i = 0; i < 3; i++) {
+ err = brcmf_sdiod_regrw_helper(sdiodev,
+ SBSDIO_FUNC1_SBADDRLOW + i,
+ sizeof(u8), &addr[i], true);
+ if (err) {
+ brcmf_err("failed at addr: 0x%0x\n",
+ SBSDIO_FUNC1_SBADDRLOW + i);
+ break;
+ }
+ }
+
+ return err;
+}
+
+static int
+brcmf_sdiod_addrprep(struct brcmf_sdio_dev *sdiodev, uint width, u32 *addr)
+{
+ uint bar0 = *addr & ~SBSDIO_SB_OFT_ADDR_MASK;
+ int err = 0;
+
+ if (bar0 != sdiodev->sbwad) {
+ err = brcmf_sdiod_set_sbaddr_window(sdiodev, bar0);
+ if (err)
+ return err;
+
+ sdiodev->sbwad = bar0;
+ }
+
+ *addr &= SBSDIO_SB_OFT_ADDR_MASK;
+
+ if (width == 4)
+ *addr |= SBSDIO_SB_ACCESS_2_4B_FLAG;
+
+ return 0;
+}
+
+u8 brcmf_sdiod_regrb(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret)
+{
+ u8 data;
+ int retval;
+
+ brcmf_dbg(SDIO, "addr:0x%08x\n", addr);
+ retval = brcmf_sdiod_regrw_helper(sdiodev, addr, sizeof(data), &data,
+ false);
+ brcmf_dbg(SDIO, "data:0x%02x\n", data);
+
+ if (ret)
+ *ret = retval;
+
+ return data;
+}
+
+u32 brcmf_sdiod_regrl(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret)
+{
+ u32 data = 0;
+ int retval;
+
+ brcmf_dbg(SDIO, "addr:0x%08x\n", addr);
+ retval = brcmf_sdiod_addrprep(sdiodev, sizeof(data), &addr);
+ if (retval)
+ goto done;
+ retval = brcmf_sdiod_regrw_helper(sdiodev, addr, sizeof(data), &data,
+ false);
+ brcmf_dbg(SDIO, "data:0x%08x\n", data);
+
+done:
+ if (ret)
+ *ret = retval;
+
+ return data;
+}
+
+void brcmf_sdiod_regwb(struct brcmf_sdio_dev *sdiodev, u32 addr,
+ u8 data, int *ret)
+{
+ int retval;
+
+ brcmf_dbg(SDIO, "addr:0x%08x, data:0x%02x\n", addr, data);
+ retval = brcmf_sdiod_regrw_helper(sdiodev, addr, sizeof(data), &data,
+ true);
+ if (ret)
+ *ret = retval;
+}
+
+void brcmf_sdiod_regwl(struct brcmf_sdio_dev *sdiodev, u32 addr,
+ u32 data, int *ret)
+{
+ int retval;
+
+ brcmf_dbg(SDIO, "addr:0x%08x, data:0x%08x\n", addr, data);
+ retval = brcmf_sdiod_addrprep(sdiodev, sizeof(data), &addr);
+ if (retval)
+ goto done;
+ retval = brcmf_sdiod_regrw_helper(sdiodev, addr, sizeof(data), &data,
+ true);
+
+done:
+ if (ret)
+ *ret = retval;
+}
+
+static int brcmf_sdiod_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn,
+ bool write, u32 addr, struct sk_buff *pkt)
+{
+ unsigned int req_sz;
+ int err;
+
+ /* Single skb use the standard mmc interface */
+ req_sz = pkt->len + 3;
+ req_sz &= (uint)~3;
+
+ if (write)
+ err = sdio_memcpy_toio(sdiodev->func[fn], addr,
+ ((u8 *)(pkt->data)), req_sz);
+ else if (fn == 1)
+ err = sdio_memcpy_fromio(sdiodev->func[fn], ((u8 *)(pkt->data)),
+ addr, req_sz);
+ else
+ /* function 2 read is FIFO operation */
+ err = sdio_readsb(sdiodev->func[fn], ((u8 *)(pkt->data)), addr,
+ req_sz);
+ if (err == -ENOMEDIUM)
+ brcmf_sdiod_change_state(sdiodev, BRCMF_SDIOD_NOMEDIUM);
+ return err;
+}
+
+/**
+ * brcmf_sdiod_sglist_rw - SDIO interface function for block data access
+ * @sdiodev: brcmfmac sdio device
+ * @fn: SDIO function number
+ * @write: direction flag
+ * @addr: dongle memory address as source/destination
+ * @pkt: skb pointer
+ *
+ * This function takes the respbonsibility as the interface function to MMC
+ * stack for block data access. It assumes that the skb passed down by the
+ * caller has already been padded and aligned.
+ */
+static int brcmf_sdiod_sglist_rw(struct brcmf_sdio_dev *sdiodev, uint fn,
+ bool write, u32 addr,
+ struct sk_buff_head *pktlist)
+{
+ unsigned int req_sz, func_blk_sz, sg_cnt, sg_data_sz, pkt_offset;
+ unsigned int max_req_sz, orig_offset, dst_offset;
+ unsigned short max_seg_cnt, seg_sz;
+ unsigned char *pkt_data, *orig_data, *dst_data;
+ struct sk_buff *pkt_next = NULL, *local_pkt_next;
+ struct sk_buff_head local_list, *target_list;
+ struct mmc_request mmc_req;
+ struct mmc_command mmc_cmd;
+ struct mmc_data mmc_dat;
+ struct scatterlist *sgl;
+ int ret = 0;
+
+ if (!pktlist->qlen)
+ return -EINVAL;
+
+ target_list = pktlist;
+ /* for host with broken sg support, prepare a page aligned list */
+ __skb_queue_head_init(&local_list);
+ if (!write && sdiodev->settings->bus.sdio.broken_sg_support) {
+ req_sz = 0;
+ skb_queue_walk(pktlist, pkt_next)
+ req_sz += pkt_next->len;
+ req_sz = ALIGN(req_sz, sdiodev->func[fn]->cur_blksize);
+ while (req_sz > PAGE_SIZE) {
+ pkt_next = brcmu_pkt_buf_get_skb(PAGE_SIZE);
+ if (pkt_next == NULL) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+ __skb_queue_tail(&local_list, pkt_next);
+ req_sz -= PAGE_SIZE;
+ }
+ pkt_next = brcmu_pkt_buf_get_skb(req_sz);
+ if (pkt_next == NULL) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+ __skb_queue_tail(&local_list, pkt_next);
+ target_list = &local_list;
+ }
+
+ func_blk_sz = sdiodev->func[fn]->cur_blksize;
+ max_req_sz = sdiodev->max_request_size;
+ max_seg_cnt = min_t(unsigned short, sdiodev->max_segment_count,
+ target_list->qlen);
+ seg_sz = target_list->qlen;
+ pkt_offset = 0;
+ pkt_next = target_list->next;
+
+ memset(&mmc_req, 0, sizeof(struct mmc_request));
+ memset(&mmc_cmd, 0, sizeof(struct mmc_command));
+ memset(&mmc_dat, 0, sizeof(struct mmc_data));
+
+ mmc_dat.sg = sdiodev->sgtable.sgl;
+ mmc_dat.blksz = func_blk_sz;
+ mmc_dat.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ;
+ mmc_cmd.opcode = SD_IO_RW_EXTENDED;
+ mmc_cmd.arg = write ? 1<<31 : 0; /* write flag */
+ mmc_cmd.arg |= (fn & 0x7) << 28; /* SDIO func num */
+ mmc_cmd.arg |= 1<<27; /* block mode */
+ /* for function 1 the addr will be incremented */
+ mmc_cmd.arg |= (fn == 1) ? 1<<26 : 0;
+ mmc_cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC;
+ mmc_req.cmd = &mmc_cmd;
+ mmc_req.data = &mmc_dat;
+
+ while (seg_sz) {
+ req_sz = 0;
+ sg_cnt = 0;
+ sgl = sdiodev->sgtable.sgl;
+ /* prep sg table */
+ while (pkt_next != (struct sk_buff *)target_list) {
+ pkt_data = pkt_next->data + pkt_offset;
+ sg_data_sz = pkt_next->len - pkt_offset;
+ if (sg_data_sz > sdiodev->max_segment_size)
+ sg_data_sz = sdiodev->max_segment_size;
+ if (sg_data_sz > max_req_sz - req_sz)
+ sg_data_sz = max_req_sz - req_sz;
+
+ sg_set_buf(sgl, pkt_data, sg_data_sz);
+
+ sg_cnt++;
+ sgl = sg_next(sgl);
+ req_sz += sg_data_sz;
+ pkt_offset += sg_data_sz;
+ if (pkt_offset == pkt_next->len) {
+ pkt_offset = 0;
+ pkt_next = pkt_next->next;
+ }
+
+ if (req_sz >= max_req_sz || sg_cnt >= max_seg_cnt)
+ break;
+ }
+ seg_sz -= sg_cnt;
+
+ if (req_sz % func_blk_sz != 0) {
+ brcmf_err("sg request length %u is not %u aligned\n",
+ req_sz, func_blk_sz);
+ ret = -ENOTBLK;
+ goto exit;
+ }
+
+ mmc_dat.sg_len = sg_cnt;
+ mmc_dat.blocks = req_sz / func_blk_sz;
+ mmc_cmd.arg |= (addr & 0x1FFFF) << 9; /* address */
+ mmc_cmd.arg |= mmc_dat.blocks & 0x1FF; /* block count */
+ /* incrementing addr for function 1 */
+ if (fn == 1)
+ addr += req_sz;
+
+ mmc_set_data_timeout(&mmc_dat, sdiodev->func[fn]->card);
+ mmc_wait_for_req(sdiodev->func[fn]->card->host, &mmc_req);
+
+ ret = mmc_cmd.error ? mmc_cmd.error : mmc_dat.error;
+ if (ret == -ENOMEDIUM) {
+ brcmf_sdiod_change_state(sdiodev, BRCMF_SDIOD_NOMEDIUM);
+ break;
+ } else if (ret != 0) {
+ brcmf_err("CMD53 sg block %s failed %d\n",
+ write ? "write" : "read", ret);
+ ret = -EIO;
+ break;
+ }
+ }
+
+ if (!write && sdiodev->settings->bus.sdio.broken_sg_support) {
+ local_pkt_next = local_list.next;
+ orig_offset = 0;
+ skb_queue_walk(pktlist, pkt_next) {
+ dst_offset = 0;
+ do {
+ req_sz = local_pkt_next->len - orig_offset;
+ req_sz = min_t(uint, pkt_next->len - dst_offset,
+ req_sz);
+ orig_data = local_pkt_next->data + orig_offset;
+ dst_data = pkt_next->data + dst_offset;
+ memcpy(dst_data, orig_data, req_sz);
+ orig_offset += req_sz;
+ dst_offset += req_sz;
+ if (orig_offset == local_pkt_next->len) {
+ orig_offset = 0;
+ local_pkt_next = local_pkt_next->next;
+ }
+ if (dst_offset == pkt_next->len)
+ break;
+ } while (!skb_queue_empty(&local_list));
+ }
+ }
+
+exit:
+ sg_init_table(sdiodev->sgtable.sgl, sdiodev->sgtable.orig_nents);
+ while ((pkt_next = __skb_dequeue(&local_list)) != NULL)
+ brcmu_pkt_buf_free_skb(pkt_next);
+
+ return ret;
+}
+
+int brcmf_sdiod_recv_buf(struct brcmf_sdio_dev *sdiodev, u8 *buf, uint nbytes)
+{
+ struct sk_buff *mypkt;
+ int err;
+
+ mypkt = brcmu_pkt_buf_get_skb(nbytes);
+ if (!mypkt) {
+ brcmf_err("brcmu_pkt_buf_get_skb failed: len %d\n",
+ nbytes);
+ return -EIO;
+ }
+
+ err = brcmf_sdiod_recv_pkt(sdiodev, mypkt);
+ if (!err)
+ memcpy(buf, mypkt->data, nbytes);
+
+ brcmu_pkt_buf_free_skb(mypkt);
+ return err;
+}
+
+int brcmf_sdiod_recv_pkt(struct brcmf_sdio_dev *sdiodev, struct sk_buff *pkt)
+{
+ u32 addr = sdiodev->sbwad;
+ int err = 0;
+
+ brcmf_dbg(SDIO, "addr = 0x%x, size = %d\n", addr, pkt->len);
+
+ err = brcmf_sdiod_addrprep(sdiodev, 4, &addr);
+ if (err)
+ goto done;
+
+ err = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_2, false, addr, pkt);
+
+done:
+ return err;
+}
+
+int brcmf_sdiod_recv_chain(struct brcmf_sdio_dev *sdiodev,
+ struct sk_buff_head *pktq, uint totlen)
+{
+ struct sk_buff *glom_skb;
+ struct sk_buff *skb;
+ u32 addr = sdiodev->sbwad;
+ int err = 0;
+
+ brcmf_dbg(SDIO, "addr = 0x%x, size = %d\n",
+ addr, pktq->qlen);
+
+ err = brcmf_sdiod_addrprep(sdiodev, 4, &addr);
+ if (err)
+ goto done;
+
+ if (pktq->qlen == 1)
+ err = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_2, false, addr,
+ pktq->next);
+ else if (!sdiodev->sg_support) {
+ glom_skb = brcmu_pkt_buf_get_skb(totlen);
+ if (!glom_skb)
+ return -ENOMEM;
+ err = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_2, false, addr,
+ glom_skb);
+ if (err) {
+ brcmu_pkt_buf_free_skb(glom_skb);
+ goto done;
+ }
+
+ skb_queue_walk(pktq, skb) {
+ memcpy(skb->data, glom_skb->data, skb->len);
+ skb_pull(glom_skb, skb->len);
+ }
+ } else
+ err = brcmf_sdiod_sglist_rw(sdiodev, SDIO_FUNC_2, false, addr,
+ pktq);
+
+done:
+ return err;
+}
+
+int brcmf_sdiod_send_buf(struct brcmf_sdio_dev *sdiodev, u8 *buf, uint nbytes)
+{
+ struct sk_buff *mypkt;
+ u32 addr = sdiodev->sbwad;
+ int err;
+
+ mypkt = brcmu_pkt_buf_get_skb(nbytes);
+ if (!mypkt) {
+ brcmf_err("brcmu_pkt_buf_get_skb failed: len %d\n",
+ nbytes);
+ return -EIO;
+ }
+
+ memcpy(mypkt->data, buf, nbytes);
+
+ err = brcmf_sdiod_addrprep(sdiodev, 4, &addr);
+
+ if (!err)
+ err = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_2, true, addr,
+ mypkt);
+
+ brcmu_pkt_buf_free_skb(mypkt);
+ return err;
+
+}
+
+int brcmf_sdiod_send_pkt(struct brcmf_sdio_dev *sdiodev,
+ struct sk_buff_head *pktq)
+{
+ struct sk_buff *skb;
+ u32 addr = sdiodev->sbwad;
+ int err;
+
+ brcmf_dbg(SDIO, "addr = 0x%x, size = %d\n", addr, pktq->qlen);
+
+ err = brcmf_sdiod_addrprep(sdiodev, 4, &addr);
+ if (err)
+ return err;
+
+ if (pktq->qlen == 1 || !sdiodev->sg_support)
+ skb_queue_walk(pktq, skb) {
+ err = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_2, true,
+ addr, skb);
+ if (err)
+ break;
+ }
+ else
+ err = brcmf_sdiod_sglist_rw(sdiodev, SDIO_FUNC_2, true, addr,
+ pktq);
+
+ return err;
+}
+
+int
+brcmf_sdiod_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address,
+ u8 *data, uint size)
+{
+ int bcmerror = 0;
+ struct sk_buff *pkt;
+ u32 sdaddr;
+ uint dsize;
+
+ dsize = min_t(uint, SBSDIO_SB_OFT_ADDR_LIMIT, size);
+ pkt = dev_alloc_skb(dsize);
+ if (!pkt) {
+ brcmf_err("dev_alloc_skb failed: len %d\n", dsize);
+ return -EIO;
+ }
+ pkt->priority = 0;
+
+ /* Determine initial transfer parameters */
+ sdaddr = address & SBSDIO_SB_OFT_ADDR_MASK;
+ if ((sdaddr + size) & SBSDIO_SBWINDOW_MASK)
+ dsize = (SBSDIO_SB_OFT_ADDR_LIMIT - sdaddr);
+ else
+ dsize = size;
+
+ sdio_claim_host(sdiodev->func[1]);
+
+ /* Do the transfer(s) */
+ while (size) {
+ /* Set the backplane window to include the start address */
+ bcmerror = brcmf_sdiod_set_sbaddr_window(sdiodev, address);
+ if (bcmerror)
+ break;
+
+ brcmf_dbg(SDIO, "%s %d bytes at offset 0x%08x in window 0x%08x\n",
+ write ? "write" : "read", dsize,
+ sdaddr, address & SBSDIO_SBWINDOW_MASK);
+
+ sdaddr &= SBSDIO_SB_OFT_ADDR_MASK;
+ sdaddr |= SBSDIO_SB_ACCESS_2_4B_FLAG;
+
+ skb_put(pkt, dsize);
+ if (write)
+ memcpy(pkt->data, data, dsize);
+ bcmerror = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_1, write,
+ sdaddr, pkt);
+ if (bcmerror) {
+ brcmf_err("membytes transfer failed\n");
+ break;
+ }
+ if (!write)
+ memcpy(data, pkt->data, dsize);
+ skb_trim(pkt, 0);
+
+ /* Adjust for next transfer (if any) */
+ size -= dsize;
+ if (size) {
+ data += dsize;
+ address += dsize;
+ sdaddr = 0;
+ dsize = min_t(uint, SBSDIO_SB_OFT_ADDR_LIMIT, size);
+ }
+ }
+
+ dev_kfree_skb(pkt);
+
+ /* Return the window to backplane enumeration space for core access */
+ if (brcmf_sdiod_set_sbaddr_window(sdiodev, sdiodev->sbwad))
+ brcmf_err("FAILED to set window back to 0x%x\n",
+ sdiodev->sbwad);
+
+ sdio_release_host(sdiodev->func[1]);
+
+ return bcmerror;
+}
+
+int brcmf_sdiod_abort(struct brcmf_sdio_dev *sdiodev, uint fn)
+{
+ char t_func = (char)fn;
+ brcmf_dbg(SDIO, "Enter\n");
+
+ /* issue abort cmd52 command through F0 */
+ brcmf_sdiod_request_data(sdiodev, SDIO_FUNC_0, SDIO_CCCR_ABORT,
+ sizeof(t_func), &t_func, true);
+
+ brcmf_dbg(SDIO, "Exit\n");
+ return 0;
+}
+
+void brcmf_sdiod_sgtable_alloc(struct brcmf_sdio_dev *sdiodev)
+{
+ struct sdio_func *func;
+ struct mmc_host *host;
+ uint max_blocks;
+ uint nents;
+ int err;
+
+ func = sdiodev->func[2];
+ host = func->card->host;
+ sdiodev->sg_support = host->max_segs > 1;
+ max_blocks = min_t(uint, host->max_blk_count, 511u);
+ sdiodev->max_request_size = min_t(uint, host->max_req_size,
+ max_blocks * func->cur_blksize);
+ sdiodev->max_segment_count = min_t(uint, host->max_segs,
+ SG_MAX_SINGLE_ALLOC);
+ sdiodev->max_segment_size = host->max_seg_size;
+
+ if (!sdiodev->sg_support)
+ return;
+
+ nents = max_t(uint, BRCMF_DEFAULT_RXGLOM_SIZE,
+ sdiodev->settings->bus.sdio.txglomsz);
+ nents += (nents >> 4) + 1;
+
+ WARN_ON(nents > sdiodev->max_segment_count);
+
+ brcmf_dbg(TRACE, "nents=%d\n", nents);
+ err = sg_alloc_table(&sdiodev->sgtable, nents, GFP_KERNEL);
+ if (err < 0) {
+ brcmf_err("allocation failed: disable scatter-gather");
+ sdiodev->sg_support = false;
+ }
+
+ sdiodev->txglomsz = sdiodev->settings->bus.sdio.txglomsz;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int brcmf_sdiod_freezer_attach(struct brcmf_sdio_dev *sdiodev)
+{
+ sdiodev->freezer = kzalloc(sizeof(*sdiodev->freezer), GFP_KERNEL);
+ if (!sdiodev->freezer)
+ return -ENOMEM;
+ atomic_set(&sdiodev->freezer->thread_count, 0);
+ atomic_set(&sdiodev->freezer->freezing, 0);
+ init_waitqueue_head(&sdiodev->freezer->thread_freeze);
+ init_completion(&sdiodev->freezer->resumed);
+ return 0;
+}
+
+static void brcmf_sdiod_freezer_detach(struct brcmf_sdio_dev *sdiodev)
+{
+ if (sdiodev->freezer) {
+ WARN_ON(atomic_read(&sdiodev->freezer->freezing));
+ kfree(sdiodev->freezer);
+ }
+}
+
+static int brcmf_sdiod_freezer_on(struct brcmf_sdio_dev *sdiodev)
+{
+ atomic_t *expect = &sdiodev->freezer->thread_count;
+ int res = 0;
+
+ sdiodev->freezer->frozen_count = 0;
+ reinit_completion(&sdiodev->freezer->resumed);
+ atomic_set(&sdiodev->freezer->freezing, 1);
+ brcmf_sdio_trigger_dpc(sdiodev->bus);
+ wait_event(sdiodev->freezer->thread_freeze,
+ atomic_read(expect) == sdiodev->freezer->frozen_count);
+ sdio_claim_host(sdiodev->func[1]);
+ res = brcmf_sdio_sleep(sdiodev->bus, true);
+ sdio_release_host(sdiodev->func[1]);
+ return res;
+}
+
+static void brcmf_sdiod_freezer_off(struct brcmf_sdio_dev *sdiodev)
+{
+ atomic_set(&sdiodev->freezer->freezing, 0);
+ complete_all(&sdiodev->freezer->resumed);
+ wait_event(sdiodev->freezer->thread_freeze, 0 == sdiodev->freezer->frozen_count);
+ sdio_claim_host(sdiodev->func[1]);
+ brcmf_sdio_sleep(sdiodev->bus, false);
+ sdio_release_host(sdiodev->func[1]);
+}
+
+bool brcmf_sdiod_freezing(struct brcmf_sdio_dev *sdiodev)
+{
+ return atomic_read(&sdiodev->freezer->freezing);
+}
+
+void brcmf_sdiod_try_freeze(struct brcmf_sdio_dev *sdiodev)
+{
+ if (!brcmf_sdiod_freezing(sdiodev))
+ return;
+ sdiodev->freezer->frozen_count++;
+ wake_up(&sdiodev->freezer->thread_freeze);
+ wait_for_completion(&sdiodev->freezer->resumed);
+ sdiodev->freezer->frozen_count--;
+ wake_up(&sdiodev->freezer->thread_freeze);
+}
+
+void brcmf_sdiod_freezer_count(struct brcmf_sdio_dev *sdiodev)
+{
+ atomic_inc(&sdiodev->freezer->thread_count);
+}
+
+void brcmf_sdiod_freezer_uncount(struct brcmf_sdio_dev *sdiodev)
+{
+ atomic_dec(&sdiodev->freezer->thread_count);
+}
+#else
+static int brcmf_sdiod_freezer_attach(struct brcmf_sdio_dev *sdiodev)
+{
+ return 0;
+}
+
+static void brcmf_sdiod_freezer_detach(struct brcmf_sdio_dev *sdiodev)
+{
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static int brcmf_sdiod_remove(struct brcmf_sdio_dev *sdiodev)
+{
+ sdiodev->state = BRCMF_SDIOD_DOWN;
+ if (sdiodev->bus) {
+ brcmf_sdio_remove(sdiodev->bus);
+ sdiodev->bus = NULL;
+ }
+
+ if (sdiodev->dev)
+ device_init_wakeup(sdiodev->dev, false);
+
+ brcmf_sdiod_freezer_detach(sdiodev);
+
+ /* Disable Function 2 */
+ sdio_claim_host(sdiodev->func[2]);
+ sdio_disable_func(sdiodev->func[2]);
+ sdio_release_host(sdiodev->func[2]);
+
+ /* Disable Function 1 */
+ sdio_claim_host(sdiodev->func[1]);
+ sdio_disable_func(sdiodev->func[1]);
+ sdio_release_host(sdiodev->func[1]);
+
+ sg_free_table(&sdiodev->sgtable);
+ sdiodev->sbwad = 0;
+
+ pm_runtime_allow(sdiodev->func[1]->card->host->parent);
+ return 0;
+}
+
+extern int g_poll_sdio_bus;
+
+static void brcmf_sdiod_host_fixup(struct mmc_host *host)
+{
+ /* runtime-pm powers off the device */
+ pm_runtime_forbid(host->parent);
+
+ /* MMC_CAP_NONREMOVABLE flag will break sdio bus poll loop */
+ if (!g_poll_sdio_bus) {
+ /* avoid removal detection upon resume */
+ host->caps |= MMC_CAP_NONREMOVABLE;
+ }
+}
+
+static int brcmf_sdiod_probe(struct brcmf_sdio_dev *sdiodev)
+{
+ int ret = 0;
+ unsigned int f2_blksz = SDIO_FUNC2_BLOCKSIZE;
+
+ sdiodev->num_funcs = 2;
+
+ sdio_claim_host(sdiodev->func[1]);
+
+ ret = sdio_set_block_size(sdiodev->func[1], SDIO_FUNC1_BLOCKSIZE);
+ if (ret) {
+ brcmf_err("Failed to set F1 blocksize\n");
+ sdio_release_host(sdiodev->func[1]);
+ goto out;
+ }
+
+ if (sdiodev->func[0]->device == SDIO_DEVICE_ID_CYPRESS_4373) {
+ f2_blksz = SDIO_4373_FUNC2_BLOCKSIZE;
+ }
+
+ ret = sdio_set_block_size(sdiodev->func[2], f2_blksz);
+ if (ret) {
+ brcmf_err("Failed to set F2 blocksize\n");
+ sdio_release_host(sdiodev->func[1]);
+ goto out;
+ } else {
+ brcmf_dbg(SDIO, "set F2 blocksize to %d\n", f2_blksz);
+ }
+
+ /* increase F2 timeout */
+ sdiodev->func[2]->enable_timeout = SDIO_WAIT_F2RDY;
+
+ /* Enable Function 1 */
+ ret = sdio_enable_func(sdiodev->func[1]);
+ sdio_release_host(sdiodev->func[1]);
+ if (ret) {
+ brcmf_err("Failed to enable F1: err=%d\n", ret);
+ goto out;
+ }
+
+ ret = brcmf_sdiod_freezer_attach(sdiodev);
+ if (ret)
+ goto out;
+
+ if (sdiodev->dev && device_init_wakeup(sdiodev->dev, true))
+ brcmf_err("Failed to init dev %p as wakeup source\n", sdiodev->dev);
+
+ /* try to attach to the target device */
+ sdiodev->bus = brcmf_sdio_probe(sdiodev);
+ if (!sdiodev->bus) {
+ ret = -ENODEV;
+ goto out;
+ }
+ brcmf_sdiod_host_fixup(sdiodev->func[2]->card->host);
+out:
+ if (ret)
+ brcmf_sdiod_remove(sdiodev);
+
+ return ret;
+}
+
+#define BRCMF_SDIO_DEVICE(dev_id) \
+ {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, dev_id)}
+
+/* devices we support, null terminated */
+static const struct sdio_device_id brcmf_sdmmc_ids[] = {
+ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43143),
+ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43241),
+ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4329),
+ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4330),
+ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4334),
+ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43340),
+ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43341),
+ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43362),
+ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4335_4339),
+ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4339),
+ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43430),
+ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4345),
+ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43455),
+ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4354),
+ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4356),
+ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_CYPRESS_4373),
+ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_CYPRESS_43012),
+ { /* end: all zeroes */ }
+};
+MODULE_DEVICE_TABLE(sdio, brcmf_sdmmc_ids);
+
+
+static void brcmf_sdiod_acpi_set_power_manageable(struct device *dev,
+ int val)
+{
+#if IS_ENABLED(CONFIG_ACPI)
+ struct acpi_device *adev;
+
+ adev = ACPI_COMPANION(dev);
+ if (adev)
+ adev->flags.power_manageable = 0;
+#endif
+}
+
+static int brcmf_ops_sdio_probe(struct sdio_func *func,
+ const struct sdio_device_id *id)
+{
+ int err;
+ struct brcmf_sdio_dev *sdiodev;
+ struct brcmf_bus *bus_if;
+ struct device *dev;
+
+ brcmf_dbg(SDIO, "Enter\n");
+ brcmf_dbg(SDIO, "Class=%x\n", func->class);
+ brcmf_dbg(SDIO, "sdio vendor ID: 0x%04x\n", func->vendor);
+ brcmf_dbg(SDIO, "sdio device ID: 0x%04x\n", func->device);
+ brcmf_dbg(SDIO, "Function#: %d\n", func->num);
+
+ dev = &func->dev;
+ /* prohibit ACPI power management for this device */
+ brcmf_sdiod_acpi_set_power_manageable(dev, 0);
+
+ /* Consume func num 1 but dont do anything with it. */
+ if (func->num == 1)
+ return 0;
+
+ /* Ignore anything but func 2 */
+ if (func->num != 2)
+ return -ENODEV;
+
+ bus_if = kzalloc(sizeof(struct brcmf_bus), GFP_KERNEL);
+ if (!bus_if)
+ return -ENOMEM;
+ sdiodev = kzalloc(sizeof(struct brcmf_sdio_dev), GFP_KERNEL);
+ if (!sdiodev) {
+ kfree(bus_if);
+ return -ENOMEM;
+ }
+
+ /* store refs to functions used. mmc_card does
+ * not hold the F0 function pointer.
+ */
+ sdiodev->func[0] = kmemdup(func, sizeof(*func), GFP_KERNEL);
+ sdiodev->func[0]->num = 0;
+ sdiodev->func[1] = func->card->sdio_func[0];
+ sdiodev->func[2] = func;
+
+ sdiodev->bus_if = bus_if;
+ bus_if->bus_priv.sdio = sdiodev;
+ bus_if->proto_type = BRCMF_PROTO_BCDC;
+ dev_set_drvdata(&func->dev, bus_if);
+ dev_set_drvdata(&sdiodev->func[1]->dev, bus_if);
+ sdiodev->dev = &sdiodev->func[1]->dev;
+
+ brcmf_sdiod_change_state(sdiodev, BRCMF_SDIOD_DOWN);
+
+ brcmf_dbg(SDIO, "F2 found, calling brcmf_sdiod_probe...\n");
+ err = brcmf_sdiod_probe(sdiodev);
+ if (err) {
+ brcmf_err("F2 error, probe failed %d...\n", err);
+ goto fail;
+ }
+
+ brcmf_dbg(SDIO, "F2 init completed...\n");
+ return 0;
+
+fail:
+ dev_set_drvdata(&func->dev, NULL);
+ dev_set_drvdata(&sdiodev->func[1]->dev, NULL);
+ kfree(sdiodev->func[0]);
+ kfree(sdiodev);
+ kfree(bus_if);
+ return err;
+}
+
+static void brcmf_ops_sdio_remove(struct sdio_func *func)
+{
+ struct brcmf_bus *bus_if;
+ struct brcmf_sdio_dev *sdiodev;
+
+ brcmf_dbg(SDIO, "Enter\n");
+ brcmf_dbg(SDIO, "sdio vendor ID: 0x%04x\n", func->vendor);
+ brcmf_dbg(SDIO, "sdio device ID: 0x%04x\n", func->device);
+ brcmf_dbg(SDIO, "Function: %d\n", func->num);
+
+ bus_if = dev_get_drvdata(&func->dev);
+ if (bus_if) {
+ sdiodev = bus_if->bus_priv.sdio;
+
+ /* start by unregistering irqs */
+ brcmf_sdiod_intr_unregister(sdiodev);
+
+ if (func->num != 1)
+ return;
+
+ /* only proceed with rest of cleanup if func 1 */
+ brcmf_sdiod_remove(sdiodev);
+
+ dev_set_drvdata(&sdiodev->func[1]->dev, NULL);
+ dev_set_drvdata(&sdiodev->func[2]->dev, NULL);
+
+ kfree(bus_if);
+ kfree(sdiodev->func[0]);
+ kfree(sdiodev);
+ }
+
+ brcmf_dbg(SDIO, "Exit\n");
+}
+
+void brcmf_sdio_wowl_config(struct device *dev, bool enabled)
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+ struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
+
+ brcmf_dbg(SDIO, "Configuring WOWL, enabled=%d\n", enabled);
+ sdiodev->wowl_enabled = enabled;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int brcmf_ops_sdio_suspend(struct device *dev)
+{
+ struct sdio_func *func;
+ struct brcmf_bus *bus_if;
+ struct brcmf_sdio_dev *sdiodev;
+ mmc_pm_flag_t sdio_flags;
+
+ func = container_of(dev, struct sdio_func, dev);
+ brcmf_dbg(SDIO, "Enter: F%d\n", func->num);
+ if (func->num != SDIO_FUNC_2)
+ return 0;
+
+
+ bus_if = dev_get_drvdata(dev);
+ sdiodev = bus_if->bus_priv.sdio;
+
+ brcmf_sdiod_freezer_on(sdiodev);
+ brcmf_sdio_wd_timer(sdiodev->bus, 0);
+
+ sdio_flags = MMC_PM_KEEP_POWER;
+ if (sdiodev->wowl_enabled) {
+ if (sdiodev->settings->bus.sdio.oob_irq_supported)
+ enable_irq_wake(sdiodev->settings->bus.sdio.oob_irq_nr);
+ else
+ sdio_flags |= MMC_PM_WAKE_SDIO_IRQ;
+ }
+ if (sdio_set_host_pm_flags(sdiodev->func[1], sdio_flags))
+ brcmf_err("Failed to set pm_flags %x\n", sdio_flags);
+ return 0;
+}
+
+static int brcmf_ops_sdio_resume(struct device *dev)
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+ struct brcmf_sdio_dev *sdiodev;
+ struct sdio_func *func = container_of(dev, struct sdio_func, dev);
+
+ brcmf_dbg(SDIO, "Enter: F%d\n", func->num);
+ if (func->num != SDIO_FUNC_2)
+ return 0;
+
+ sdiodev = bus_if->bus_priv.sdio;
+
+ brcmf_sdiod_freezer_off(sdiodev);
+ return 0;
+}
+
+static const struct dev_pm_ops brcmf_sdio_pm_ops = {
+ .suspend = brcmf_ops_sdio_suspend,
+ .resume = brcmf_ops_sdio_resume,
+};
+#endif /* CONFIG_PM_SLEEP */
+
+static struct sdio_driver brcmf_sdmmc_driver = {
+ .probe = brcmf_ops_sdio_probe,
+ .remove = brcmf_ops_sdio_remove,
+ .name = KBUILD_MODNAME,
+ .id_table = brcmf_sdmmc_ids,
+ .drv = {
+ .owner = THIS_MODULE,
+#ifdef CONFIG_PM_SLEEP
+ .pm = &brcmf_sdio_pm_ops,
+#endif /* CONFIG_PM_SLEEP */
+ },
+};
+
+void brcmf_sdio_register(void)
+{
+ int ret;
+
+ ret = sdio_register_driver(&brcmf_sdmmc_driver);
+ if (ret)
+ brcmf_err("sdio_register_driver failed: %d\n", ret);
+}
+
+void brcmf_sdio_exit(void)
+{
+ brcmf_dbg(SDIO, "Enter\n");
+
+ sdio_unregister_driver(&brcmf_sdmmc_driver);
+}
+
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/btcoex.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/btcoex.c
new file mode 100644
index 0000000..14a70d4
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/btcoex.c
@@ -0,0 +1,495 @@
+/*
+ * Copyright (c) 2013 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <linux/slab.h>
+#include <linux/netdevice.h>
+#include <net/cfg80211.h>
+
+#include <brcmu_wifi.h>
+#include <brcmu_utils.h>
+#include <defs.h>
+#include "core.h"
+#include "debug.h"
+#include "fwil.h"
+#include "fwil_types.h"
+#include "btcoex.h"
+#include "p2p.h"
+#include "cfg80211.h"
+
+/* T1 start SCO/eSCO priority suppression */
+#define BRCMF_BTCOEX_OPPR_WIN_TIME msecs_to_jiffies(2000)
+
+/* BT registers values during DHCP */
+#define BRCMF_BT_DHCP_REG50 0x8022
+#define BRCMF_BT_DHCP_REG51 0
+#define BRCMF_BT_DHCP_REG64 0
+#define BRCMF_BT_DHCP_REG65 0
+#define BRCMF_BT_DHCP_REG71 0
+#define BRCMF_BT_DHCP_REG66 0x2710
+#define BRCMF_BT_DHCP_REG41 0x33
+#define BRCMF_BT_DHCP_REG68 0x190
+
+/* number of samples for SCO detection */
+#define BRCMF_BT_SCO_SAMPLES 12
+
+/**
+* enum brcmf_btcoex_state - BT coex DHCP state machine states
+* @BRCMF_BT_DHCP_IDLE: DCHP is idle
+* @BRCMF_BT_DHCP_START: DHCP started, wait before
+* boosting wifi priority
+* @BRCMF_BT_DHCP_OPPR_WIN: graceful DHCP opportunity ended,
+* boost wifi priority
+* @BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT: wifi priority boost end,
+* restore defaults
+*/
+enum brcmf_btcoex_state {
+ BRCMF_BT_DHCP_IDLE,
+ BRCMF_BT_DHCP_START,
+ BRCMF_BT_DHCP_OPPR_WIN,
+ BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT
+};
+
+/**
+ * struct brcmf_btcoex_info - BT coex related information
+ * @vif: interface for which request was done.
+ * @timer: timer for DHCP state machine
+ * @timeout: configured timeout.
+ * @timer_on: DHCP timer active
+ * @dhcp_done: DHCP finished before T1/T2 timer expiration
+ * @bt_state: DHCP state machine state
+ * @work: DHCP state machine work
+ * @cfg: driver private data for cfg80211 interface
+ * @reg66: saved value of btc_params 66
+ * @reg41: saved value of btc_params 41
+ * @reg68: saved value of btc_params 68
+ * @saved_regs_part1: flag indicating regs 66,41,68
+ * have been saved
+ * @reg51: saved value of btc_params 51
+ * @reg64: saved value of btc_params 64
+ * @reg65: saved value of btc_params 65
+ * @reg71: saved value of btc_params 71
+ * @saved_regs_part1: flag indicating regs 50,51,64,65,71
+ * have been saved
+ */
+struct brcmf_btcoex_info {
+ struct brcmf_cfg80211_vif *vif;
+ struct timer_list timer;
+ u16 timeout;
+ bool timer_on;
+ bool dhcp_done;
+ enum brcmf_btcoex_state bt_state;
+ struct work_struct work;
+ struct brcmf_cfg80211_info *cfg;
+ u32 reg66;
+ u32 reg41;
+ u32 reg68;
+ bool saved_regs_part1;
+ u32 reg50;
+ u32 reg51;
+ u32 reg64;
+ u32 reg65;
+ u32 reg71;
+ bool saved_regs_part2;
+};
+
+/**
+ * brcmf_btcoex_params_write() - write btc_params firmware variable
+ * @ifp: interface
+ * @addr: btc_params register number
+ * @data: data to write
+ */
+static s32 brcmf_btcoex_params_write(struct brcmf_if *ifp, u32 addr, u32 data)
+{
+ struct {
+ __le32 addr;
+ __le32 data;
+ } reg_write;
+
+ reg_write.addr = cpu_to_le32(addr);
+ reg_write.data = cpu_to_le32(data);
+ return brcmf_fil_iovar_data_set(ifp, "btc_params",
+ ®_write, sizeof(reg_write));
+}
+
+/**
+ * brcmf_btcoex_params_read() - read btc_params firmware variable
+ * @ifp: interface
+ * @addr: btc_params register number
+ * @data: read data
+ */
+static s32 brcmf_btcoex_params_read(struct brcmf_if *ifp, u32 addr, u32 *data)
+{
+ *data = addr;
+
+ return brcmf_fil_iovar_int_get(ifp, "btc_params", data);
+}
+
+/**
+ * brcmf_btcoex_boost_wifi() - control BT SCO/eSCO parameters
+ * @btci: BT coex info
+ * @trump_sco:
+ * true - set SCO/eSCO parameters for compatibility
+ * during DHCP window
+ * false - restore saved parameter values
+ *
+ * Enhanced BT COEX settings for eSCO compatibility during DHCP window
+ */
+static void brcmf_btcoex_boost_wifi(struct brcmf_btcoex_info *btci,
+ bool trump_sco)
+{
+ struct brcmf_if *ifp = brcmf_get_ifp(btci->cfg->pub, 0);
+
+ if (trump_sco && !btci->saved_regs_part2) {
+ /* this should reduce eSCO agressive
+ * retransmit w/o breaking it
+ */
+
+ /* save current */
+ brcmf_dbg(INFO, "new SCO/eSCO coex algo {save & override}\n");
+ brcmf_btcoex_params_read(ifp, 50, &btci->reg50);
+ brcmf_btcoex_params_read(ifp, 51, &btci->reg51);
+ brcmf_btcoex_params_read(ifp, 64, &btci->reg64);
+ brcmf_btcoex_params_read(ifp, 65, &btci->reg65);
+ brcmf_btcoex_params_read(ifp, 71, &btci->reg71);
+
+ btci->saved_regs_part2 = true;
+ brcmf_dbg(INFO,
+ "saved bt_params[50,51,64,65,71]: 0x%x 0x%x 0x%x 0x%x 0x%x\n",
+ btci->reg50, btci->reg51, btci->reg64,
+ btci->reg65, btci->reg71);
+
+ /* pacify the eSco */
+ brcmf_btcoex_params_write(ifp, 50, BRCMF_BT_DHCP_REG50);
+ brcmf_btcoex_params_write(ifp, 51, BRCMF_BT_DHCP_REG51);
+ brcmf_btcoex_params_write(ifp, 64, BRCMF_BT_DHCP_REG64);
+ brcmf_btcoex_params_write(ifp, 65, BRCMF_BT_DHCP_REG65);
+ brcmf_btcoex_params_write(ifp, 71, BRCMF_BT_DHCP_REG71);
+
+ } else if (btci->saved_regs_part2) {
+ /* restore previously saved bt params */
+ brcmf_dbg(INFO, "Do new SCO/eSCO coex algo {restore}\n");
+ brcmf_btcoex_params_write(ifp, 50, btci->reg50);
+ brcmf_btcoex_params_write(ifp, 51, btci->reg51);
+ brcmf_btcoex_params_write(ifp, 64, btci->reg64);
+ brcmf_btcoex_params_write(ifp, 65, btci->reg65);
+ brcmf_btcoex_params_write(ifp, 71, btci->reg71);
+
+ brcmf_dbg(INFO,
+ "restored bt_params[50,51,64,65,71]: 0x%x 0x%x 0x%x 0x%x 0x%x\n",
+ btci->reg50, btci->reg51, btci->reg64,
+ btci->reg65, btci->reg71);
+
+ btci->saved_regs_part2 = false;
+ } else {
+ brcmf_dbg(INFO, "attempted to restore not saved BTCOEX params\n");
+ }
+}
+
+/**
+ * brcmf_btcoex_is_sco_active() - check if SCO/eSCO is active
+ * @ifp: interface
+ *
+ * return: true if SCO/eSCO session is active
+ */
+static bool brcmf_btcoex_is_sco_active(struct brcmf_if *ifp)
+{
+ int ioc_res = 0;
+ bool res = false;
+ int sco_id_cnt = 0;
+ u32 param27;
+ int i;
+
+ for (i = 0; i < BRCMF_BT_SCO_SAMPLES; i++) {
+ ioc_res = brcmf_btcoex_params_read(ifp, 27, ¶m27);
+
+ if (ioc_res < 0) {
+ brcmf_err("ioc read btc params error\n");
+ break;
+ }
+
+ brcmf_dbg(INFO, "sample[%d], btc_params 27:%x\n", i, param27);
+
+ if ((param27 & 0x6) == 2) { /* count both sco & esco */
+ sco_id_cnt++;
+ }
+
+ if (sco_id_cnt > 2) {
+ brcmf_dbg(INFO,
+ "sco/esco detected, pkt id_cnt:%d samples:%d\n",
+ sco_id_cnt, i);
+ res = true;
+ break;
+ }
+ }
+ brcmf_dbg(TRACE, "exit: result=%d\n", res);
+ return res;
+}
+
+/**
+ * btcmf_btcoex_save_part1() - save first step parameters.
+ */
+static void btcmf_btcoex_save_part1(struct brcmf_btcoex_info *btci)
+{
+ struct brcmf_if *ifp = btci->vif->ifp;
+
+ if (!btci->saved_regs_part1) {
+ /* Retrieve and save original reg value */
+ brcmf_btcoex_params_read(ifp, 66, &btci->reg66);
+ brcmf_btcoex_params_read(ifp, 41, &btci->reg41);
+ brcmf_btcoex_params_read(ifp, 68, &btci->reg68);
+ btci->saved_regs_part1 = true;
+ brcmf_dbg(INFO,
+ "saved btc_params regs (66,41,68) 0x%x 0x%x 0x%x\n",
+ btci->reg66, btci->reg41,
+ btci->reg68);
+ }
+}
+
+/**
+ * brcmf_btcoex_restore_part1() - restore first step parameters.
+ */
+static void brcmf_btcoex_restore_part1(struct brcmf_btcoex_info *btci)
+{
+ struct brcmf_if *ifp;
+
+ if (btci->saved_regs_part1) {
+ btci->saved_regs_part1 = false;
+ ifp = btci->vif->ifp;
+ brcmf_btcoex_params_write(ifp, 66, btci->reg66);
+ brcmf_btcoex_params_write(ifp, 41, btci->reg41);
+ brcmf_btcoex_params_write(ifp, 68, btci->reg68);
+ brcmf_dbg(INFO,
+ "restored btc_params regs {66,41,68} 0x%x 0x%x 0x%x\n",
+ btci->reg66, btci->reg41,
+ btci->reg68);
+ }
+}
+
+/**
+ * brcmf_btcoex_timerfunc() - BT coex timer callback
+ */
+static void brcmf_btcoex_timerfunc(ulong data)
+{
+ struct brcmf_btcoex_info *bt_local = (struct brcmf_btcoex_info *)data;
+ brcmf_dbg(TRACE, "enter\n");
+
+ bt_local->timer_on = false;
+ schedule_work(&bt_local->work);
+}
+
+/**
+ * brcmf_btcoex_handler() - BT coex state machine work handler
+ * @work: work
+ */
+static void brcmf_btcoex_handler(struct work_struct *work)
+{
+ struct brcmf_btcoex_info *btci;
+ btci = container_of(work, struct brcmf_btcoex_info, work);
+ if (btci->timer_on) {
+ btci->timer_on = false;
+ del_timer_sync(&btci->timer);
+ }
+
+ switch (btci->bt_state) {
+ case BRCMF_BT_DHCP_START:
+ /* DHCP started provide OPPORTUNITY window
+ to get DHCP address
+ */
+ brcmf_dbg(INFO, "DHCP started\n");
+ btci->bt_state = BRCMF_BT_DHCP_OPPR_WIN;
+ if (btci->timeout < BRCMF_BTCOEX_OPPR_WIN_TIME) {
+ mod_timer(&btci->timer, btci->timer.expires);
+ } else {
+ btci->timeout -= BRCMF_BTCOEX_OPPR_WIN_TIME;
+ mod_timer(&btci->timer,
+ jiffies + BRCMF_BTCOEX_OPPR_WIN_TIME);
+ }
+ btci->timer_on = true;
+ break;
+
+ case BRCMF_BT_DHCP_OPPR_WIN:
+ if (btci->dhcp_done) {
+ brcmf_dbg(INFO, "DHCP done before T1 expiration\n");
+ goto idle;
+ }
+
+ /* DHCP is not over yet, start lowering BT priority */
+ brcmf_dbg(INFO, "DHCP T1:%d expired\n",
+ jiffies_to_msecs(BRCMF_BTCOEX_OPPR_WIN_TIME));
+ brcmf_btcoex_boost_wifi(btci, true);
+
+ btci->bt_state = BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT;
+ mod_timer(&btci->timer, jiffies + btci->timeout);
+ btci->timer_on = true;
+ break;
+
+ case BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT:
+ if (btci->dhcp_done)
+ brcmf_dbg(INFO, "DHCP done before T2 expiration\n");
+ else
+ brcmf_dbg(INFO, "DHCP T2:%d expired\n",
+ BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT);
+
+ goto idle;
+
+ default:
+ brcmf_err("invalid state=%d !!!\n", btci->bt_state);
+ goto idle;
+ }
+
+ return;
+
+idle:
+ btci->bt_state = BRCMF_BT_DHCP_IDLE;
+ btci->timer_on = false;
+ brcmf_btcoex_boost_wifi(btci, false);
+ cfg80211_crit_proto_stopped(&btci->vif->wdev, GFP_KERNEL);
+ brcmf_btcoex_restore_part1(btci);
+ btci->vif = NULL;
+}
+
+/**
+ * brcmf_btcoex_attach() - initialize BT coex data
+ * @cfg: driver private cfg80211 data
+ *
+ * return: 0 on success
+ */
+int brcmf_btcoex_attach(struct brcmf_cfg80211_info *cfg)
+{
+ struct brcmf_btcoex_info *btci = NULL;
+ brcmf_dbg(TRACE, "enter\n");
+
+ btci = kmalloc(sizeof(struct brcmf_btcoex_info), GFP_KERNEL);
+ if (!btci)
+ return -ENOMEM;
+
+ btci->bt_state = BRCMF_BT_DHCP_IDLE;
+
+ /* Set up timer for BT */
+ btci->timer_on = false;
+ btci->timeout = BRCMF_BTCOEX_OPPR_WIN_TIME;
+ init_timer(&btci->timer);
+ btci->timer.data = (ulong)btci;
+ btci->timer.function = brcmf_btcoex_timerfunc;
+ btci->cfg = cfg;
+ btci->saved_regs_part1 = false;
+ btci->saved_regs_part2 = false;
+
+ INIT_WORK(&btci->work, brcmf_btcoex_handler);
+
+ cfg->btcoex = btci;
+ return 0;
+}
+
+/**
+ * brcmf_btcoex_detach - clean BT coex data
+ * @cfg: driver private cfg80211 data
+ */
+void brcmf_btcoex_detach(struct brcmf_cfg80211_info *cfg)
+{
+ brcmf_dbg(TRACE, "enter\n");
+
+ if (!cfg->btcoex)
+ return;
+
+ if (cfg->btcoex->timer_on) {
+ cfg->btcoex->timer_on = false;
+ del_timer_sync(&cfg->btcoex->timer);
+ }
+
+ cancel_work_sync(&cfg->btcoex->work);
+
+ brcmf_btcoex_boost_wifi(cfg->btcoex, false);
+ brcmf_btcoex_restore_part1(cfg->btcoex);
+
+ kfree(cfg->btcoex);
+ cfg->btcoex = NULL;
+}
+
+static void brcmf_btcoex_dhcp_start(struct brcmf_btcoex_info *btci)
+{
+ struct brcmf_if *ifp = btci->vif->ifp;
+
+ btcmf_btcoex_save_part1(btci);
+ /* set new regs values */
+ brcmf_btcoex_params_write(ifp, 66, BRCMF_BT_DHCP_REG66);
+ brcmf_btcoex_params_write(ifp, 41, BRCMF_BT_DHCP_REG41);
+ brcmf_btcoex_params_write(ifp, 68, BRCMF_BT_DHCP_REG68);
+ btci->dhcp_done = false;
+ btci->bt_state = BRCMF_BT_DHCP_START;
+ schedule_work(&btci->work);
+ brcmf_dbg(TRACE, "enable BT DHCP Timer\n");
+}
+
+static void brcmf_btcoex_dhcp_end(struct brcmf_btcoex_info *btci)
+{
+ /* Stop any bt timer because DHCP session is done */
+ btci->dhcp_done = true;
+ if (btci->timer_on) {
+ brcmf_dbg(INFO, "disable BT DHCP Timer\n");
+ btci->timer_on = false;
+ del_timer_sync(&btci->timer);
+
+ /* schedule worker if transition to IDLE is needed */
+ if (btci->bt_state != BRCMF_BT_DHCP_IDLE) {
+ brcmf_dbg(INFO, "bt_state:%d\n",
+ btci->bt_state);
+ schedule_work(&btci->work);
+ }
+ } else {
+ /* Restore original values */
+ brcmf_btcoex_restore_part1(btci);
+ }
+}
+
+/**
+ * brcmf_btcoex_set_mode - set BT coex mode
+ * @cfg: driver private cfg80211 data
+ * @mode: Wifi-Bluetooth coexistence mode
+ *
+ * return: 0 on success
+ */
+int brcmf_btcoex_set_mode(struct brcmf_cfg80211_vif *vif,
+ enum brcmf_btcoex_mode mode, u16 duration)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_priv(vif->wdev.wiphy);
+ struct brcmf_btcoex_info *btci = cfg->btcoex;
+ struct brcmf_if *ifp = brcmf_get_ifp(cfg->pub, 0);
+
+ switch (mode) {
+ case BRCMF_BTCOEX_DISABLED:
+ brcmf_dbg(INFO, "DHCP session starts\n");
+ if (btci->bt_state != BRCMF_BT_DHCP_IDLE)
+ return -EBUSY;
+ /* Start BT timer only for SCO connection */
+ if (brcmf_btcoex_is_sco_active(ifp)) {
+ btci->timeout = msecs_to_jiffies(duration);
+ btci->vif = vif;
+ brcmf_btcoex_dhcp_start(btci);
+ }
+ break;
+
+ case BRCMF_BTCOEX_ENABLED:
+ brcmf_dbg(INFO, "DHCP session ends\n");
+ if (btci->bt_state != BRCMF_BT_DHCP_IDLE &&
+ vif == btci->vif) {
+ brcmf_btcoex_dhcp_end(btci);
+ }
+ break;
+ default:
+ brcmf_dbg(INFO, "Unknown mode, ignored\n");
+ }
+ return 0;
+}
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/btcoex.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/btcoex.h
new file mode 100644
index 0000000..19647c6
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/btcoex.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2013 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#ifndef WL_BTCOEX_H_
+#define WL_BTCOEX_H_
+
+enum brcmf_btcoex_mode {
+ BRCMF_BTCOEX_DISABLED,
+ BRCMF_BTCOEX_ENABLED
+};
+
+int brcmf_btcoex_attach(struct brcmf_cfg80211_info *cfg);
+void brcmf_btcoex_detach(struct brcmf_cfg80211_info *cfg);
+int brcmf_btcoex_set_mode(struct brcmf_cfg80211_vif *vif,
+ enum brcmf_btcoex_mode mode, u16 duration);
+
+#endif /* WL_BTCOEX_H_ */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h
new file mode 100644
index 0000000..c733632
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h
@@ -0,0 +1,262 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef BRCMFMAC_BUS_H
+#define BRCMFMAC_BUS_H
+
+#include "debug.h"
+
+/* IDs of the 6 default common rings of msgbuf protocol */
+#define BRCMF_H2D_MSGRING_CONTROL_SUBMIT 0
+#define BRCMF_H2D_MSGRING_RXPOST_SUBMIT 1
+#define BRCMF_H2D_MSGRING_FLOWRING_IDSTART 2
+#define BRCMF_D2H_MSGRING_CONTROL_COMPLETE 2
+#define BRCMF_D2H_MSGRING_TX_COMPLETE 3
+#define BRCMF_D2H_MSGRING_RX_COMPLETE 4
+
+
+#define BRCMF_NROF_H2D_COMMON_MSGRINGS 2
+#define BRCMF_NROF_D2H_COMMON_MSGRINGS 3
+#define BRCMF_NROF_COMMON_MSGRINGS (BRCMF_NROF_H2D_COMMON_MSGRINGS + \
+ BRCMF_NROF_D2H_COMMON_MSGRINGS)
+
+/* The level of bus communication with the dongle */
+enum brcmf_bus_state {
+ BRCMF_BUS_DOWN, /* Not ready for frame transfers */
+ BRCMF_BUS_UP /* Ready for frame transfers */
+};
+
+/* The level of bus communication with the dongle */
+enum brcmf_bus_protocol_type {
+ BRCMF_PROTO_BCDC,
+ BRCMF_PROTO_MSGBUF
+};
+
+struct brcmf_mp_device;
+
+struct brcmf_bus_dcmd {
+ char *name;
+ char *param;
+ int param_len;
+ struct list_head list;
+};
+
+/**
+ * struct brcmf_bus_ops - bus callback operations.
+ *
+ * @preinit: execute bus/device specific dongle init commands (optional).
+ * @init: prepare for communication with dongle.
+ * @stop: clear pending frames, disable data flow.
+ * @txdata: send a data frame to the dongle. When the data
+ * has been transferred, the common driver must be
+ * notified using brcmf_txcomplete(). The common
+ * driver calls this function with interrupts
+ * disabled.
+ * @txctl: transmit a control request message to dongle.
+ * @rxctl: receive a control response message from dongle.
+ * @gettxq: obtain a reference of bus transmit queue (optional).
+ * @wowl_config: specify if dongle is configured for wowl when going to suspend
+ * @get_ramsize: obtain size of device memory.
+ * @get_memdump: obtain device memory dump in provided buffer.
+ * @get_fwname: obtain firmware name.
+ *
+ * This structure provides an abstract interface towards the
+ * bus specific driver. For control messages to common driver
+ * will assure there is only one active transaction. Unless
+ * indicated otherwise these callbacks are mandatory.
+ */
+struct brcmf_bus_ops {
+ int (*preinit)(struct device *dev);
+ void (*stop)(struct device *dev);
+ int (*txdata)(struct device *dev, struct sk_buff *skb);
+ int (*txctl)(struct device *dev, unsigned char *msg, uint len);
+ int (*rxctl)(struct device *dev, unsigned char *msg, uint len);
+ struct pktq * (*gettxq)(struct device *dev);
+ void (*wowl_config)(struct device *dev, bool enabled);
+ size_t (*get_ramsize)(struct device *dev);
+ int (*get_memdump)(struct device *dev, void *data, size_t len);
+ int (*get_fwname)(struct device *dev, uint chip, uint chiprev,
+ unsigned char *fw_name);
+};
+
+
+/**
+ * struct brcmf_bus_msgbuf - bus ringbuf if in case of msgbuf.
+ *
+ * @commonrings: commonrings which are always there.
+ * @flowrings: commonrings which are dynamically created and destroyed for data.
+ * @rx_dataoffset: if set then all rx data has this this offset.
+ * @max_rxbufpost: maximum number of buffers to post for rx.
+ * @max_flowrings: maximum number of tx flow rings supported.
+ * @max_submissionrings: maximum number of submission rings(h2d) supported.
+ * @max_completionrings: maximum number of completion rings(d2h) supported.
+ */
+struct brcmf_bus_msgbuf {
+ struct brcmf_commonring *commonrings[BRCMF_NROF_COMMON_MSGRINGS];
+ struct brcmf_commonring **flowrings;
+ u32 rx_dataoffset;
+ u32 max_rxbufpost;
+ u16 max_flowrings;
+ u16 max_submissionrings;
+ u16 max_completionrings;
+};
+
+
+/**
+ * struct brcmf_bus - interface structure between common and bus layer
+ *
+ * @bus_priv: pointer to private bus device.
+ * @proto_type: protocol type, bcdc or msgbuf
+ * @dev: device pointer of bus device.
+ * @drvr: public driver information.
+ * @state: operational state of the bus interface.
+ * @maxctl: maximum size for rxctl request message.
+ * @tx_realloc: number of tx packets realloced for headroom.
+ * @dstats: dongle-based statistical data.
+ * @dcmd_list: bus/device specific dongle initialization commands.
+ * @chip: device identifier of the dongle chip.
+ * @wowl_supported: is wowl supported by bus driver.
+ * @chiprev: revision of the dongle chip.
+ */
+struct brcmf_bus {
+ union {
+ struct brcmf_sdio_dev *sdio;
+ struct brcmf_usbdev *usb;
+ struct brcmf_pciedev *pcie;
+ } bus_priv;
+ enum brcmf_bus_protocol_type proto_type;
+ struct device *dev;
+ struct brcmf_pub *drvr;
+ enum brcmf_bus_state state;
+ uint maxctl;
+ unsigned long tx_realloc;
+ u32 chip;
+ u32 chiprev;
+ bool always_use_fws_queue;
+ bool wowl_supported;
+
+ const struct brcmf_bus_ops *ops;
+ struct brcmf_bus_msgbuf *msgbuf;
+};
+
+/*
+ * callback wrappers
+ */
+static inline int brcmf_bus_preinit(struct brcmf_bus *bus)
+{
+ if (!bus->ops->preinit)
+ return 0;
+ return bus->ops->preinit(bus->dev);
+}
+
+static inline void brcmf_bus_stop(struct brcmf_bus *bus)
+{
+ bus->ops->stop(bus->dev);
+}
+
+static inline int brcmf_bus_txdata(struct brcmf_bus *bus, struct sk_buff *skb)
+{
+ return bus->ops->txdata(bus->dev, skb);
+}
+
+static inline
+int brcmf_bus_txctl(struct brcmf_bus *bus, unsigned char *msg, uint len)
+{
+ return bus->ops->txctl(bus->dev, msg, len);
+}
+
+static inline
+int brcmf_bus_rxctl(struct brcmf_bus *bus, unsigned char *msg, uint len)
+{
+ return bus->ops->rxctl(bus->dev, msg, len);
+}
+
+static inline
+struct pktq *brcmf_bus_gettxq(struct brcmf_bus *bus)
+{
+ if (!bus->ops->gettxq)
+ return ERR_PTR(-ENOENT);
+
+ return bus->ops->gettxq(bus->dev);
+}
+
+static inline
+void brcmf_bus_wowl_config(struct brcmf_bus *bus, bool enabled)
+{
+ if (bus->ops->wowl_config)
+ bus->ops->wowl_config(bus->dev, enabled);
+}
+
+static inline size_t brcmf_bus_get_ramsize(struct brcmf_bus *bus)
+{
+ if (!bus->ops->get_ramsize)
+ return 0;
+
+ return bus->ops->get_ramsize(bus->dev);
+}
+
+static inline
+int brcmf_bus_get_memdump(struct brcmf_bus *bus, void *data, size_t len)
+{
+ if (!bus->ops->get_memdump)
+ return -EOPNOTSUPP;
+
+ return bus->ops->get_memdump(bus->dev, data, len);
+}
+
+static inline
+int brcmf_bus_get_fwname(struct brcmf_bus *bus, uint chip, uint chiprev,
+ unsigned char *fw_name)
+{
+ if (!bus->ops->get_fwname)
+ return -EOPNOTSUPP;
+
+ return bus->ops->get_fwname(bus->dev, chip, chiprev, fw_name);
+}
+
+/*
+ * interface functions from common layer
+ */
+
+/* Receive frame for delivery to OS. Callee disposes of rxp. */
+void brcmf_rx_frame(struct device *dev, struct sk_buff *rxp, bool handle_event);
+/* Receive async event packet from firmware. Callee disposes of rxp. */
+void brcmf_rx_event(struct device *dev, struct sk_buff *rxp);
+
+/* Indication from bus module regarding presence/insertion of dongle. */
+int brcmf_attach(struct device *dev, struct brcmf_mp_device *settings);
+/* Indication from bus module regarding removal/absence of dongle */
+void brcmf_detach(struct device *dev);
+/* Indication from bus module that dongle should be reset */
+void brcmf_dev_reset(struct device *dev);
+
+/* Configure the "global" bus state used by upper layers */
+void brcmf_bus_change_state(struct brcmf_bus *bus, enum brcmf_bus_state state);
+
+int brcmf_bus_started(struct device *dev);
+s32 brcmf_iovar_data_set(struct device *dev, char *name, void *data, u32 len);
+void brcmf_bus_add_txhdrlen(struct device *dev, uint len);
+
+#ifdef CPTCFG_BRCMFMAC_SDIO
+void brcmf_sdio_exit(void);
+void brcmf_sdio_register(void);
+#endif
+#ifdef CPTCFG_BRCMFMAC_USB
+void brcmf_usb_exit(void);
+void brcmf_usb_register(void);
+#endif
+
+#endif /* BRCMFMAC_BUS_H */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
new file mode 100644
index 0000000..6d52131
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
@@ -0,0 +1,8103 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* Toplevel file. Relies on dhd_linux.c to send commands to the dongle. */
+
+#include <linux/kernel.h>
+#include <linux/etherdevice.h>
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+#include <net/cfg80211.h>
+#include <net/netlink.h>
+
+#include <brcmu_utils.h>
+#include <defs.h>
+#include <brcmu_wifi.h>
+#include "core.h"
+#include "debug.h"
+#include "tracepoint.h"
+#include "fwil_types.h"
+#include "p2p.h"
+#include "btcoex.h"
+#include "pno.h"
+#include "cfg80211.h"
+#include "feature.h"
+#include "fwil.h"
+#include "proto.h"
+#include "vendor.h"
+#include "bus.h"
+#include "common.h"
+
+#define BRCMF_SCAN_IE_LEN_MAX 2048
+
+#define WPA_OUI "\x00\x50\xF2" /* WPA OUI */
+#define WPA_OUI_TYPE 1
+#define RSN_OUI "\x00\x0F\xAC" /* RSN OUI */
+#define WME_OUI_TYPE 2
+#define WPS_OUI_TYPE 4
+
+#define VS_IE_FIXED_HDR_LEN 6
+#define WPA_IE_VERSION_LEN 2
+#define WPA_IE_MIN_OUI_LEN 4
+#define WPA_IE_SUITE_COUNT_LEN 2
+
+#define WPA_CIPHER_NONE 0 /* None */
+#define WPA_CIPHER_WEP_40 1 /* WEP (40-bit) */
+#define WPA_CIPHER_TKIP 2 /* TKIP: default for WPA */
+#define WPA_CIPHER_AES_CCM 4 /* AES (CCM) */
+#define WPA_CIPHER_WEP_104 5 /* WEP (104-bit) */
+
+#define RSN_AKM_NONE 0 /* None (IBSS) */
+#define RSN_AKM_UNSPECIFIED 1 /* Over 802.1x */
+#define RSN_AKM_PSK 2 /* Pre-shared Key */
+#define RSN_AKM_SHA256_1X 5 /* SHA256, 802.1X */
+#define RSN_AKM_SHA256_PSK 6 /* SHA256, Pre-shared Key */
+#define RSN_CAP_LEN 2 /* Length of RSN capabilities */
+#define RSN_CAP_PTK_REPLAY_CNTR_MASK (BIT(2) | BIT(3))
+#define RSN_CAP_MFPR_MASK BIT(6)
+#define RSN_CAP_MFPC_MASK BIT(7)
+#define RSN_PMKID_COUNT_LEN 2
+
+#define VNDR_IE_CMD_LEN 4 /* length of the set command
+ * string :"add", "del" (+ NUL)
+ */
+#define VNDR_IE_COUNT_OFFSET 4
+#define VNDR_IE_PKTFLAG_OFFSET 8
+#define VNDR_IE_VSIE_OFFSET 12
+#define VNDR_IE_HDR_SIZE 12
+#define VNDR_IE_PARSE_LIMIT 5
+
+#define DOT11_MGMT_HDR_LEN 24 /* d11 management header len */
+#define DOT11_BCN_PRB_FIXED_LEN 12 /* beacon/probe fixed length */
+
+#define BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS 320
+#define BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS 400
+#define BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS 20
+
+#define BRCMF_SCAN_CHANNEL_TIME 40
+#define BRCMF_SCAN_UNASSOC_TIME 40
+#define BRCMF_SCAN_PASSIVE_TIME 120
+
+#define BRCMF_ND_INFO_TIMEOUT msecs_to_jiffies(2000)
+
+#define BRCMF_ASSOC_PARAMS_FIXED_SIZE \
+ (sizeof(struct brcmf_assoc_params_le) - sizeof(u16))
+
+static bool check_vif_up(struct brcmf_cfg80211_vif *vif)
+{
+ if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state)) {
+ brcmf_dbg(INFO, "device is not ready : status (%lu)\n",
+ vif->sme_state);
+ return false;
+ }
+ return true;
+}
+
+#define RATE_TO_BASE100KBPS(rate) (((rate) * 10) / 2)
+#define RATETAB_ENT(_rateid, _flags) \
+ { \
+ .bitrate = RATE_TO_BASE100KBPS(_rateid), \
+ .hw_value = (_rateid), \
+ .flags = (_flags), \
+ }
+
+static struct ieee80211_rate __wl_rates[] = {
+ RATETAB_ENT(BRCM_RATE_1M, 0),
+ RATETAB_ENT(BRCM_RATE_2M, IEEE80211_RATE_SHORT_PREAMBLE),
+ RATETAB_ENT(BRCM_RATE_5M5, IEEE80211_RATE_SHORT_PREAMBLE),
+ RATETAB_ENT(BRCM_RATE_11M, IEEE80211_RATE_SHORT_PREAMBLE),
+ RATETAB_ENT(BRCM_RATE_6M, 0),
+ RATETAB_ENT(BRCM_RATE_9M, 0),
+ RATETAB_ENT(BRCM_RATE_12M, 0),
+ RATETAB_ENT(BRCM_RATE_18M, 0),
+ RATETAB_ENT(BRCM_RATE_24M, 0),
+ RATETAB_ENT(BRCM_RATE_36M, 0),
+ RATETAB_ENT(BRCM_RATE_48M, 0),
+ RATETAB_ENT(BRCM_RATE_54M, 0),
+};
+
+#define wl_g_rates (__wl_rates + 0)
+#define wl_g_rates_size ARRAY_SIZE(__wl_rates)
+#define wl_a_rates (__wl_rates + 4)
+#define wl_a_rates_size (wl_g_rates_size - 4)
+
+#define CHAN2G(_channel, _freq) { \
+ .band = NL80211_BAND_2GHZ, \
+ .center_freq = (_freq), \
+ .hw_value = (_channel), \
+ .max_antenna_gain = 0, \
+ .max_power = 30, \
+}
+
+#define CHAN5G(_channel) { \
+ .band = NL80211_BAND_5GHZ, \
+ .center_freq = 5000 + (5 * (_channel)), \
+ .hw_value = (_channel), \
+ .max_antenna_gain = 0, \
+ .max_power = 30, \
+}
+
+static struct ieee80211_channel __wl_2ghz_channels[] = {
+ CHAN2G(1, 2412), CHAN2G(2, 2417), CHAN2G(3, 2422), CHAN2G(4, 2427),
+ CHAN2G(5, 2432), CHAN2G(6, 2437), CHAN2G(7, 2442), CHAN2G(8, 2447),
+ CHAN2G(9, 2452), CHAN2G(10, 2457), CHAN2G(11, 2462), CHAN2G(12, 2467),
+ CHAN2G(13, 2472), CHAN2G(14, 2484)
+};
+
+static struct ieee80211_channel __wl_5ghz_channels[] = {
+ CHAN5G(34), CHAN5G(36), CHAN5G(38), CHAN5G(40), CHAN5G(42),
+ CHAN5G(44), CHAN5G(46), CHAN5G(48), CHAN5G(52), CHAN5G(56),
+ CHAN5G(60), CHAN5G(64), CHAN5G(100), CHAN5G(104), CHAN5G(108),
+ CHAN5G(112), CHAN5G(116), CHAN5G(120), CHAN5G(124), CHAN5G(128),
+ CHAN5G(132), CHAN5G(136), CHAN5G(140), CHAN5G(144), CHAN5G(149),
+ CHAN5G(153), CHAN5G(157), CHAN5G(161), CHAN5G(165)
+};
+
+/* Band templates duplicated per wiphy. The channel info
+ * above is added to the band during setup.
+ */
+static const struct ieee80211_supported_band __wl_band_2ghz = {
+ .band = NL80211_BAND_2GHZ,
+ .bitrates = wl_g_rates,
+ .n_bitrates = wl_g_rates_size,
+};
+
+static const struct ieee80211_supported_band __wl_band_5ghz = {
+ .band = NL80211_BAND_5GHZ,
+ .bitrates = wl_a_rates,
+ .n_bitrates = wl_a_rates_size,
+};
+
+/* This is to override regulatory domains defined in cfg80211 module (reg.c)
+ * By default world regulatory domain defined in reg.c puts the flags
+ * NL80211_RRF_NO_IR for 5GHz channels (for * 36..48 and 149..165).
+ * With respect to these flags, wpa_supplicant doesn't * start p2p
+ * operations on 5GHz channels. All the changes in world regulatory
+ * domain are to be done here.
+ */
+static const struct ieee80211_regdomain brcmf_regdom = {
+ .n_reg_rules = 4,
+ .alpha2 = "99",
+ .reg_rules = {
+ /* IEEE 802.11b/g, channels 1..11 */
+ REG_RULE(2412-10, 2472+10, 40, 6, 20, 0),
+ /* If any */
+ /* IEEE 802.11 channel 14 - Only JP enables
+ * this and for 802.11b only
+ */
+ REG_RULE(2484-10, 2484+10, 20, 6, 20, 0),
+ /* IEEE 802.11a, channel 36..64 */
+ REG_RULE(5150-10, 5350+10, 80, 6, 20, 0),
+ /* IEEE 802.11a, channel 100..165 */
+ REG_RULE(5470-10, 5850+10, 80, 6, 20, 0), }
+};
+
+/* Note: brcmf_cipher_suites is an array of int defining which cipher suites
+ * are supported. A pointer to this array and the number of entries is passed
+ * on to upper layers. AES_CMAC defines whether or not the driver supports MFP.
+ * So the cipher suite AES_CMAC has to be the last one in the array, and when
+ * device does not support MFP then the number of suites will be decreased by 1
+ */
+static const u32 brcmf_cipher_suites[] = {
+ WLAN_CIPHER_SUITE_WEP40,
+ WLAN_CIPHER_SUITE_WEP104,
+ WLAN_CIPHER_SUITE_TKIP,
+ WLAN_CIPHER_SUITE_CCMP,
+ WLAN_CIPHER_SUITE_PMK,
+ /* Keep as last entry: */
+ WLAN_CIPHER_SUITE_AES_CMAC
+};
+
+/* Vendor specific ie. id = 221, oui and type defines exact ie */
+struct brcmf_vs_tlv {
+ u8 id;
+ u8 len;
+ u8 oui[3];
+ u8 oui_type;
+};
+
+struct parsed_vndr_ie_info {
+ u8 *ie_ptr;
+ u32 ie_len; /* total length including id & length field */
+ struct brcmf_vs_tlv vndrie;
+};
+
+struct parsed_vndr_ies {
+ u32 count;
+ struct parsed_vndr_ie_info ie_info[VNDR_IE_PARSE_LIMIT];
+};
+
+u8 g_wakeup_irq = 0;
+u8 g_dump_packet = 0;
+int g_TCPKATimeout = false;
+u32 tcpka_intr_enabled = false;
+extern int g_keepalive_debug;
+extern int g_remote_ka_timeout;
+extern int g_devtype;
+extern int dhd_power_mode;
+
+static u8 nl80211_band_to_fwil(enum nl80211_band band)
+{
+ switch (band) {
+ case NL80211_BAND_2GHZ:
+ return WLC_BAND_2G;
+ case NL80211_BAND_5GHZ:
+ return WLC_BAND_5G;
+ default:
+ WARN_ON(1);
+ break;
+ }
+ return 0;
+}
+
+static u16 chandef_to_chanspec(struct brcmu_d11inf *d11inf,
+ struct cfg80211_chan_def *ch)
+{
+ struct brcmu_chan ch_inf;
+ s32 primary_offset;
+
+ brcmf_dbg(TRACE, "chandef: control %d center %d width %d\n",
+ ch->chan->center_freq, ch->center_freq1, ch->width);
+ ch_inf.chnum = ieee80211_frequency_to_channel(ch->center_freq1);
+ primary_offset = ch->chan->center_freq - ch->center_freq1;
+ switch (ch->width) {
+ case NL80211_CHAN_WIDTH_20:
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ ch_inf.bw = BRCMU_CHAN_BW_20;
+ WARN_ON(primary_offset != 0);
+ break;
+ case NL80211_CHAN_WIDTH_40:
+ ch_inf.bw = BRCMU_CHAN_BW_40;
+ if (primary_offset > 0)
+ ch_inf.sb = BRCMU_CHAN_SB_U;
+ else
+ ch_inf.sb = BRCMU_CHAN_SB_L;
+ break;
+ case NL80211_CHAN_WIDTH_80:
+ ch_inf.bw = BRCMU_CHAN_BW_80;
+ if (primary_offset == -30)
+ ch_inf.sb = BRCMU_CHAN_SB_LL;
+ else if (primary_offset == -10)
+ ch_inf.sb = BRCMU_CHAN_SB_LU;
+ else if (primary_offset == 10)
+ ch_inf.sb = BRCMU_CHAN_SB_UL;
+ else
+ ch_inf.sb = BRCMU_CHAN_SB_UU;
+ break;
+ case NL80211_CHAN_WIDTH_80P80:
+ case NL80211_CHAN_WIDTH_160:
+ case NL80211_CHAN_WIDTH_5:
+ case NL80211_CHAN_WIDTH_10:
+ default:
+ WARN_ON_ONCE(1);
+ }
+ switch (ch->chan->band) {
+ case NL80211_BAND_2GHZ:
+ ch_inf.band = BRCMU_CHAN_BAND_2G;
+ break;
+ case NL80211_BAND_5GHZ:
+ ch_inf.band = BRCMU_CHAN_BAND_5G;
+ break;
+ case NL80211_BAND_60GHZ:
+ default:
+ WARN_ON_ONCE(1);
+ }
+ d11inf->encchspec(&ch_inf);
+
+ return ch_inf.chspec;
+}
+
+u16 channel_to_chanspec(struct brcmu_d11inf *d11inf,
+ struct ieee80211_channel *ch)
+{
+ struct brcmu_chan ch_inf;
+
+ ch_inf.chnum = ieee80211_frequency_to_channel(ch->center_freq);
+ ch_inf.bw = BRCMU_CHAN_BW_20;
+ d11inf->encchspec(&ch_inf);
+
+ return ch_inf.chspec;
+}
+
+/* Traverse a string of 1-byte tag/1-byte length/variable-length value
+ * triples, returning a pointer to the substring whose first element
+ * matches tag
+ */
+static const struct brcmf_tlv *
+brcmf_parse_tlvs(const void *buf, int buflen, uint key)
+{
+ const struct brcmf_tlv *elt = buf;
+ int totlen = buflen;
+
+ /* find tagged parameter */
+ while (totlen >= TLV_HDR_LEN) {
+ int len = elt->len;
+
+ /* validate remaining totlen */
+ if ((elt->id == key) && (totlen >= (len + TLV_HDR_LEN)))
+ return elt;
+
+ elt = (struct brcmf_tlv *)((u8 *)elt + (len + TLV_HDR_LEN));
+ totlen -= (len + TLV_HDR_LEN);
+ }
+
+ return NULL;
+}
+
+/* Is any of the tlvs the expected entry? If
+ * not update the tlvs buffer pointer/length.
+ */
+static bool
+brcmf_tlv_has_ie(const u8 *ie, const u8 **tlvs, u32 *tlvs_len,
+ const u8 *oui, u32 oui_len, u8 type)
+{
+ /* If the contents match the OUI and the type */
+ if (ie[TLV_LEN_OFF] >= oui_len + 1 &&
+ !memcmp(&ie[TLV_BODY_OFF], oui, oui_len) &&
+ type == ie[TLV_BODY_OFF + oui_len]) {
+ return true;
+ }
+
+ if (tlvs == NULL)
+ return false;
+ /* point to the next ie */
+ ie += ie[TLV_LEN_OFF] + TLV_HDR_LEN;
+ /* calculate the length of the rest of the buffer */
+ *tlvs_len -= (int)(ie - *tlvs);
+ /* update the pointer to the start of the buffer */
+ *tlvs = ie;
+
+ return false;
+}
+
+static struct brcmf_vs_tlv *
+brcmf_find_wpaie(const u8 *parse, u32 len)
+{
+ const struct brcmf_tlv *ie;
+
+ while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) {
+ if (brcmf_tlv_has_ie((const u8 *)ie, &parse, &len,
+ WPA_OUI, TLV_OUI_LEN, WPA_OUI_TYPE))
+ return (struct brcmf_vs_tlv *)ie;
+ }
+ return NULL;
+}
+
+static struct brcmf_vs_tlv *
+brcmf_find_wpsie(const u8 *parse, u32 len)
+{
+ const struct brcmf_tlv *ie;
+
+ while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) {
+ if (brcmf_tlv_has_ie((u8 *)ie, &parse, &len,
+ WPA_OUI, TLV_OUI_LEN, WPS_OUI_TYPE))
+ return (struct brcmf_vs_tlv *)ie;
+ }
+ return NULL;
+}
+
+static int brcmf_vif_change_validate(struct brcmf_cfg80211_info *cfg,
+ struct brcmf_cfg80211_vif *vif,
+ enum nl80211_iftype new_type)
+{
+ struct brcmf_cfg80211_vif *pos;
+ bool check_combos = false;
+ int ret = 0;
+ struct iface_combination_params params = {
+ .num_different_channels = 1,
+ };
+
+ list_for_each_entry(pos, &cfg->vif_list, list)
+ if (pos == vif) {
+ params.iftype_num[new_type]++;
+ } else {
+ /* concurrent interfaces so need check combinations */
+ check_combos = true;
+ params.iftype_num[pos->wdev.iftype]++;
+ }
+
+ if (check_combos)
+ ret = cfg80211_check_combinations(cfg->wiphy, ¶ms);
+
+ return ret;
+}
+
+static int brcmf_vif_add_validate(struct brcmf_cfg80211_info *cfg,
+ enum nl80211_iftype new_type)
+{
+ struct brcmf_cfg80211_vif *pos;
+ struct iface_combination_params params = {
+ .num_different_channels = 1,
+ };
+
+ list_for_each_entry(pos, &cfg->vif_list, list)
+ params.iftype_num[pos->wdev.iftype]++;
+
+ params.iftype_num[new_type]++;
+ return cfg80211_check_combinations(cfg->wiphy, ¶ms);
+}
+
+static void convert_key_from_CPU(struct brcmf_wsec_key *key,
+ struct brcmf_wsec_key_le *key_le)
+{
+ key_le->index = cpu_to_le32(key->index);
+ key_le->len = cpu_to_le32(key->len);
+ key_le->algo = cpu_to_le32(key->algo);
+ key_le->flags = cpu_to_le32(key->flags);
+ key_le->rxiv.hi = cpu_to_le32(key->rxiv.hi);
+ key_le->rxiv.lo = cpu_to_le16(key->rxiv.lo);
+ key_le->iv_initialized = cpu_to_le32(key->iv_initialized);
+ memcpy(key_le->data, key->data, sizeof(key->data));
+ memcpy(key_le->ea, key->ea, sizeof(key->ea));
+}
+
+static int
+send_key_to_dongle(struct brcmf_if *ifp, struct brcmf_wsec_key *key)
+{
+ int err;
+ struct brcmf_wsec_key_le key_le;
+
+ convert_key_from_CPU(key, &key_le);
+
+ brcmf_netdev_wait_pend8021x(ifp);
+
+ err = brcmf_fil_bsscfg_data_set(ifp, "wsec_key", &key_le,
+ sizeof(key_le));
+
+ if (err)
+ brcmf_err("wsec_key error (%d)\n", err);
+ return err;
+}
+
+static s32
+brcmf_configure_arp_nd_offload(struct brcmf_if *ifp, bool enable)
+{
+ s32 err;
+ u32 mode;
+
+ if (enable)
+ mode = BRCMF_ARP_OL_AGENT | BRCMF_ARP_OL_PEER_AUTO_REPLY;
+ else
+ mode = 0;
+
+ /* Try to set and enable ARP offload feature, this may fail, then it */
+ /* is simply not supported and err 0 will be returned */
+ err = brcmf_fil_iovar_int_set(ifp, "arp_ol", mode);
+ if (err) {
+ brcmf_dbg(TRACE, "failed to set ARP offload mode to 0x%x, err = %d\n",
+ mode, err);
+ err = 0;
+ } else {
+ err = brcmf_fil_iovar_int_set(ifp, "arpoe", enable);
+ if (err) {
+ brcmf_dbg(TRACE, "failed to configure (%d) ARP offload err = %d\n",
+ enable, err);
+ err = 0;
+ } else
+ brcmf_dbg(TRACE, "successfully configured (%d) ARP offload to 0x%x\n",
+ enable, mode);
+ }
+
+ err = brcmf_fil_iovar_int_set(ifp, "ndoe", enable);
+ if (err) {
+ brcmf_dbg(TRACE, "failed to configure (%d) ND offload err = %d\n",
+ enable, err);
+ err = 0;
+ } else
+ brcmf_dbg(TRACE, "successfully configured (%d) ND offload to 0x%x\n",
+ enable, mode);
+
+ return err;
+}
+
+static void
+brcmf_cfg80211_update_proto_addr_mode(struct wireless_dev *wdev)
+{
+ struct brcmf_cfg80211_vif *vif;
+ struct brcmf_if *ifp;
+
+ vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
+ ifp = vif->ifp;
+
+ if ((wdev->iftype == NL80211_IFTYPE_ADHOC) ||
+ (wdev->iftype == NL80211_IFTYPE_AP) ||
+ (wdev->iftype == NL80211_IFTYPE_P2P_GO))
+ brcmf_proto_configure_addr_mode(ifp->drvr, ifp->ifidx,
+ ADDR_DIRECT);
+ else
+ brcmf_proto_configure_addr_mode(ifp->drvr, ifp->ifidx,
+ ADDR_INDIRECT);
+}
+
+static int brcmf_get_first_free_bsscfgidx(struct brcmf_pub *drvr)
+{
+ int bsscfgidx;
+
+ for (bsscfgidx = 0; bsscfgidx < BRCMF_MAX_IFS; bsscfgidx++) {
+ /* bsscfgidx 1 is reserved for legacy P2P */
+ if (bsscfgidx == 1)
+ continue;
+ if (!drvr->iflist[bsscfgidx])
+ return bsscfgidx;
+ }
+
+ return -ENOMEM;
+}
+
+static int brcmf_cfg80211_request_ap_if(struct brcmf_if *ifp)
+{
+ struct brcmf_mbss_ssid_le mbss_ssid_le;
+ int bsscfgidx;
+ int err;
+
+ memset(&mbss_ssid_le, 0, sizeof(mbss_ssid_le));
+ bsscfgidx = brcmf_get_first_free_bsscfgidx(ifp->drvr);
+ if (bsscfgidx < 0)
+ return bsscfgidx;
+
+ mbss_ssid_le.bsscfgidx = cpu_to_le32(bsscfgidx);
+ mbss_ssid_le.SSID_len = cpu_to_le32(5);
+ sprintf(mbss_ssid_le.SSID, "ssid%d" , bsscfgidx);
+
+ err = brcmf_fil_bsscfg_data_set(ifp, "bsscfg:ssid", &mbss_ssid_le,
+ sizeof(mbss_ssid_le));
+ if (err < 0)
+ brcmf_err("setting ssid failed %d\n", err);
+
+ return err;
+}
+
+/**
+ * brcmf_ap_add_vif() - create a new AP virtual interface for multiple BSS
+ *
+ * @wiphy: wiphy device of new interface.
+ * @name: name of the new interface.
+ * @params: contains mac address for AP device.
+ */
+static
+struct wireless_dev *brcmf_ap_add_vif(struct wiphy *wiphy, const char *name,
+ struct vif_params *params)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
+ struct brcmf_cfg80211_vif *vif;
+ int err;
+
+ if (brcmf_cfg80211_vif_event_armed(cfg))
+ return ERR_PTR(-EBUSY);
+
+ brcmf_dbg(INFO, "Adding vif \"%s\"\n", name);
+
+ vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_AP);
+ if (IS_ERR(vif))
+ return (struct wireless_dev *)vif;
+
+ brcmf_cfg80211_arm_vif_event(cfg, vif);
+
+ err = brcmf_cfg80211_request_ap_if(ifp);
+ if (err) {
+ brcmf_cfg80211_arm_vif_event(cfg, NULL);
+ goto fail;
+ }
+
+ /* wait for firmware event */
+ err = brcmf_cfg80211_wait_vif_event(cfg, BRCMF_E_IF_ADD,
+ BRCMF_VIF_EVENT_TIMEOUT);
+ brcmf_cfg80211_arm_vif_event(cfg, NULL);
+ if (!err) {
+ brcmf_err("timeout occurred\n");
+ err = -EIO;
+ goto fail;
+ }
+
+ /* interface created in firmware */
+ ifp = vif->ifp;
+ if (!ifp) {
+ brcmf_err("no if pointer provided\n");
+ err = -ENOENT;
+ goto fail;
+ }
+
+ strncpy(ifp->ndev->name, name, sizeof(ifp->ndev->name) - 1);
+ err = brcmf_net_attach(ifp, true);
+ if (err) {
+ brcmf_err("Registering netdevice failed\n");
+ goto fail;
+ }
+
+ return &ifp->vif->wdev;
+
+fail:
+ brcmf_free_vif(vif);
+ return ERR_PTR(err);
+}
+
+static bool brcmf_is_apmode(struct brcmf_cfg80211_vif *vif)
+{
+ enum nl80211_iftype iftype;
+
+ iftype = vif->wdev.iftype;
+ return iftype == NL80211_IFTYPE_AP || iftype == NL80211_IFTYPE_P2P_GO;
+}
+
+static bool brcmf_is_ibssmode(struct brcmf_cfg80211_vif *vif)
+{
+ return vif->wdev.iftype == NL80211_IFTYPE_ADHOC;
+}
+
+static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy,
+ const char *name,
+ unsigned char name_assign_type,
+ enum nl80211_iftype type,
+ struct vif_params *params)
+{
+ struct wireless_dev *wdev;
+ int err;
+
+ brcmf_dbg(TRACE, "enter: %s type %d\n", name, type);
+ err = brcmf_vif_add_validate(wiphy_to_cfg(wiphy), type);
+ if (err) {
+ brcmf_err("iface validation failed: err=%d\n", err);
+ return ERR_PTR(err);
+ }
+ switch (type) {
+ case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_AP_VLAN:
+ case NL80211_IFTYPE_WDS:
+ case NL80211_IFTYPE_MONITOR:
+ case NL80211_IFTYPE_MESH_POINT:
+ return ERR_PTR(-EOPNOTSUPP);
+ case NL80211_IFTYPE_AP:
+ wdev = brcmf_ap_add_vif(wiphy, name, params);
+ break;
+ case NL80211_IFTYPE_P2P_CLIENT:
+ case NL80211_IFTYPE_P2P_GO:
+ case NL80211_IFTYPE_P2P_DEVICE:
+ wdev = brcmf_p2p_add_vif(wiphy, name, name_assign_type, type, params);
+ break;
+ case NL80211_IFTYPE_UNSPECIFIED:
+ default:
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (IS_ERR(wdev))
+ brcmf_err("add iface %s type %d failed: err=%d\n",
+ name, type, (int)PTR_ERR(wdev));
+ else
+ brcmf_cfg80211_update_proto_addr_mode(wdev);
+
+ return wdev;
+}
+
+static void brcmf_scan_config_mpc(struct brcmf_if *ifp, int mpc)
+{
+ if (brcmf_feat_is_quirk_enabled(ifp, BRCMF_FEAT_QUIRK_NEED_MPC))
+ brcmf_set_mpc(ifp, mpc);
+}
+
+void brcmf_set_mpc(struct brcmf_if *ifp, int mpc)
+{
+ s32 err = 0;
+
+ if (check_vif_up(ifp->vif)) {
+ err = brcmf_fil_iovar_int_set(ifp, "mpc", mpc);
+ if (err) {
+ brcmf_err("fail to set mpc\n");
+ return;
+ }
+ brcmf_dbg(INFO, "MPC : %d\n", mpc);
+ }
+}
+
+s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg,
+ struct brcmf_if *ifp, bool aborted,
+ bool fw_abort)
+{
+ struct brcmf_scan_params_le params_le;
+ struct cfg80211_scan_request *scan_request;
+ s32 err = 0;
+
+ brcmf_dbg(SCAN, "Enter\n");
+
+ /* clear scan request, because the FW abort can cause a second call */
+ /* to this functon and might cause a double cfg80211_scan_done */
+ scan_request = cfg->scan_request;
+ cfg->scan_request = NULL;
+
+ if (timer_pending(&cfg->escan_timeout))
+ del_timer_sync(&cfg->escan_timeout);
+
+ if (fw_abort) {
+ /* Do a scan abort to stop the driver's scan engine */
+ brcmf_dbg(SCAN, "ABORT scan in firmware\n");
+ memset(¶ms_le, 0, sizeof(params_le));
+ eth_broadcast_addr(params_le.bssid);
+ params_le.bss_type = DOT11_BSSTYPE_ANY;
+ params_le.scan_type = 0;
+ params_le.channel_num = cpu_to_le32(1);
+ params_le.nprobes = cpu_to_le32(1);
+ params_le.active_time = cpu_to_le32(-1);
+ params_le.passive_time = cpu_to_le32(-1);
+ params_le.home_time = cpu_to_le32(-1);
+ /* Scan is aborted by setting channel_list[0] to -1 */
+ params_le.channel_list[0] = cpu_to_le16(-1);
+ /* E-Scan (or anyother type) can be aborted by SCAN */
+ err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN,
+ ¶ms_le, sizeof(params_le));
+ if (err)
+ brcmf_err("Scan abort failed\n");
+ }
+
+ brcmf_scan_config_mpc(ifp, 1);
+
+ /*
+ * e-scan can be initiated internally
+ * which takes precedence.
+ */
+ if (cfg->internal_escan) {
+ brcmf_dbg(SCAN, "scheduled scan completed\n");
+ cfg->internal_escan = false;
+ if (!aborted)
+ cfg80211_sched_scan_results(cfg_to_wiphy(cfg), 0);
+ } else if (scan_request) {
+ struct cfg80211_scan_info info = {
+ .aborted = aborted,
+ };
+
+ brcmf_dbg(SCAN, "ESCAN Completed scan: %s\n",
+ aborted ? "Aborted" : "Done");
+ cfg80211_scan_done(scan_request, &info);
+ }
+ if (!test_and_clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status))
+ brcmf_dbg(SCAN, "Scan complete, probably P2P scan\n");
+
+ return err;
+}
+
+static int brcmf_cfg80211_del_ap_iface(struct wiphy *wiphy,
+ struct wireless_dev *wdev)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
+ struct net_device *ndev = wdev->netdev;
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ int ret;
+ int err;
+
+ brcmf_cfg80211_arm_vif_event(cfg, ifp->vif);
+
+ err = brcmf_fil_bsscfg_data_set(ifp, "interface_remove", NULL, 0);
+ if (err) {
+ brcmf_err("interface_remove failed %d\n", err);
+ goto err_unarm;
+ }
+
+ /* wait for firmware event */
+ ret = brcmf_cfg80211_wait_vif_event(cfg, BRCMF_E_IF_DEL,
+ BRCMF_VIF_EVENT_TIMEOUT);
+ if (!ret) {
+ brcmf_err("timeout occurred\n");
+ err = -EIO;
+ goto err_unarm;
+ }
+
+ brcmf_remove_interface(ifp, true);
+
+err_unarm:
+ brcmf_cfg80211_arm_vif_event(cfg, NULL);
+ return err;
+}
+
+static
+int brcmf_cfg80211_del_iface(struct wiphy *wiphy, struct wireless_dev *wdev)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
+ struct net_device *ndev = wdev->netdev;
+
+ if (ndev && ndev == cfg_to_ndev(cfg))
+ return -ENOTSUPP;
+
+ /* vif event pending in firmware */
+ if (brcmf_cfg80211_vif_event_armed(cfg))
+ return -EBUSY;
+
+ if (ndev) {
+ if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status) &&
+ cfg->escan_info.ifp == netdev_priv(ndev))
+ brcmf_notify_escan_complete(cfg, netdev_priv(ndev),
+ true, true);
+
+ brcmf_fil_iovar_int_set(netdev_priv(ndev), "mpc", 1);
+ }
+
+ switch (wdev->iftype) {
+ case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_AP_VLAN:
+ case NL80211_IFTYPE_WDS:
+ case NL80211_IFTYPE_MONITOR:
+ case NL80211_IFTYPE_MESH_POINT:
+ return -EOPNOTSUPP;
+ case NL80211_IFTYPE_AP:
+ return brcmf_cfg80211_del_ap_iface(wiphy, wdev);
+ case NL80211_IFTYPE_P2P_CLIENT:
+ case NL80211_IFTYPE_P2P_GO:
+ case NL80211_IFTYPE_P2P_DEVICE:
+ return brcmf_p2p_del_vif(wiphy, wdev);
+ case NL80211_IFTYPE_UNSPECIFIED:
+ default:
+ return -EINVAL;
+ }
+ return -EOPNOTSUPP;
+}
+
+static s32
+brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev,
+ enum nl80211_iftype type,
+ struct vif_params *params)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ struct brcmf_cfg80211_vif *vif = ifp->vif;
+ s32 infra = 0;
+ s32 ap = 0;
+ s32 err = 0;
+
+ brcmf_dbg(TRACE, "Enter, bsscfgidx=%d, type=%d\n", ifp->bsscfgidx,
+ type);
+
+ /* WAR: There are a number of p2p interface related problems which
+ * need to be handled initially (before doing the validate).
+ * wpa_supplicant tends to do iface changes on p2p device/client/go
+ * which are not always possible/allowed. However we need to return
+ * OK otherwise the wpa_supplicant wont start. The situation differs
+ * on configuration and setup (p2pon=1 module param). The first check
+ * is to see if the request is a change to station for p2p iface.
+ */
+ if ((type == NL80211_IFTYPE_STATION) &&
+ ((vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) ||
+ (vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) ||
+ (vif->wdev.iftype == NL80211_IFTYPE_P2P_DEVICE))) {
+ brcmf_dbg(TRACE, "Ignoring cmd for p2p if\n");
+ /* Now depending on whether module param p2pon=1 was used the
+ * response needs to be either 0 or EOPNOTSUPP. The reason is
+ * that if p2pon=1 is used, but a newer supplicant is used then
+ * we should return an error, as this combination wont work.
+ * In other situations 0 is returned and supplicant will start
+ * normally. It will give a trace in cfg80211, but it is the
+ * only way to get it working. Unfortunately this will result
+ * in situation where we wont support new supplicant in
+ * combination with module param p2pon=1, but that is the way
+ * it is. If the user tries this then unloading of driver might
+ * fail/lock.
+ */
+ if (cfg->p2p.p2pdev_dynamically)
+ return -EOPNOTSUPP;
+ else
+ return 0;
+ }
+ err = brcmf_vif_change_validate(wiphy_to_cfg(wiphy), vif, type);
+ if (err) {
+ brcmf_err("iface validation failed: err=%d\n", err);
+ return err;
+ }
+ switch (type) {
+ case NL80211_IFTYPE_MONITOR:
+ case NL80211_IFTYPE_WDS:
+ brcmf_err("type (%d) : currently we do not support this type\n",
+ type);
+ return -EOPNOTSUPP;
+ case NL80211_IFTYPE_ADHOC:
+ infra = 0;
+ break;
+ case NL80211_IFTYPE_STATION:
+ infra = 1;
+ break;
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_P2P_GO:
+ ap = 1;
+ break;
+ default:
+ err = -EINVAL;
+ goto done;
+ }
+
+ if (ap) {
+ if (type == NL80211_IFTYPE_P2P_GO) {
+ brcmf_dbg(INFO, "IF Type = P2P GO\n");
+ err = brcmf_p2p_ifchange(cfg, BRCMF_FIL_P2P_IF_GO);
+ }
+ if (!err) {
+ brcmf_dbg(INFO, "IF Type = AP\n");
+ }
+ } else {
+ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, infra);
+ if (err) {
+ brcmf_err("WLC_SET_INFRA error (%d)\n", err);
+ err = -EAGAIN;
+ goto done;
+ }
+ brcmf_dbg(INFO, "IF Type = %s\n", brcmf_is_ibssmode(vif) ?
+ "Adhoc" : "Infra");
+ }
+ ndev->ieee80211_ptr->iftype = type;
+
+ brcmf_cfg80211_update_proto_addr_mode(&vif->wdev);
+
+done:
+ brcmf_dbg(TRACE, "Exit\n");
+
+ return err;
+}
+
+static void brcmf_escan_prep(struct brcmf_cfg80211_info *cfg,
+ struct brcmf_scan_params_le *params_le,
+ struct cfg80211_scan_request *request)
+{
+ u32 n_ssids;
+ u32 n_channels;
+ s32 i;
+ s32 offset;
+ u16 chanspec;
+ char *ptr;
+ struct brcmf_ssid_le ssid_le;
+
+ eth_broadcast_addr(params_le->bssid);
+ params_le->bss_type = DOT11_BSSTYPE_ANY;
+ params_le->scan_type = 0;
+ params_le->channel_num = 0;
+ params_le->nprobes = cpu_to_le32(-1);
+ params_le->active_time = cpu_to_le32(-1);
+ params_le->passive_time = cpu_to_le32(-1);
+ params_le->home_time = cpu_to_le32(-1);
+ memset(¶ms_le->ssid_le, 0, sizeof(params_le->ssid_le));
+
+ /* if request is null exit so it will be all channel broadcast scan */
+ if (!request)
+ return;
+
+ n_ssids = request->n_ssids;
+ n_channels = request->n_channels;
+ /* Copy channel array if applicable */
+ brcmf_dbg(SCAN, "### List of channelspecs to scan ### %d\n",
+ n_channels);
+ if (n_channels > 0) {
+ for (i = 0; i < n_channels; i++) {
+ chanspec = channel_to_chanspec(&cfg->d11inf,
+ request->channels[i]);
+ brcmf_dbg(SCAN, "Chan : %d, Channel spec: %x\n",
+ request->channels[i]->hw_value, chanspec);
+ params_le->channel_list[i] = cpu_to_le16(chanspec);
+ }
+ } else {
+ brcmf_dbg(SCAN, "Scanning all channels\n");
+ }
+ /* Copy ssid array if applicable */
+ brcmf_dbg(SCAN, "### List of SSIDs to scan ### %d\n", n_ssids);
+ if (n_ssids > 0) {
+ offset = offsetof(struct brcmf_scan_params_le, channel_list) +
+ n_channels * sizeof(u16);
+ offset = roundup(offset, sizeof(u32));
+ ptr = (char *)params_le + offset;
+ for (i = 0; i < n_ssids; i++) {
+ memset(&ssid_le, 0, sizeof(ssid_le));
+ ssid_le.SSID_len =
+ cpu_to_le32(request->ssids[i].ssid_len);
+ memcpy(ssid_le.SSID, request->ssids[i].ssid,
+ request->ssids[i].ssid_len);
+ if (!ssid_le.SSID_len)
+ brcmf_dbg(SCAN, "%d: Broadcast scan\n", i);
+ else
+ brcmf_dbg(SCAN, "%d: scan for %s size =%d\n",
+ i, ssid_le.SSID, ssid_le.SSID_len);
+ memcpy(ptr, &ssid_le, sizeof(ssid_le));
+ ptr += sizeof(ssid_le);
+ }
+ } else {
+ brcmf_dbg(SCAN, "Broadcast scan %p\n", request->ssids);
+ if ((request->ssids) && request->ssids->ssid_len) {
+ brcmf_dbg(SCAN, "SSID %s len=%d\n",
+ params_le->ssid_le.SSID,
+ request->ssids->ssid_len);
+ params_le->ssid_le.SSID_len =
+ cpu_to_le32(request->ssids->ssid_len);
+ memcpy(¶ms_le->ssid_le.SSID, request->ssids->ssid,
+ request->ssids->ssid_len);
+ }
+ }
+ /* Adding mask to channel numbers */
+ params_le->channel_num =
+ cpu_to_le32((n_ssids << BRCMF_SCAN_PARAMS_NSSID_SHIFT) |
+ (n_channels & BRCMF_SCAN_PARAMS_COUNT_MASK));
+}
+
+static s32
+brcmf_run_escan(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp,
+ struct cfg80211_scan_request *request)
+{
+ s32 params_size = BRCMF_SCAN_PARAMS_FIXED_SIZE +
+ offsetof(struct brcmf_escan_params_le, params_le);
+ struct brcmf_escan_params_le *params;
+ s32 err = 0;
+
+ brcmf_dbg(SCAN, "E-SCAN START\n");
+
+ if (request != NULL) {
+ /* Allocate space for populating ssids in struct */
+ params_size += sizeof(u32) * ((request->n_channels + 1) / 2);
+
+ /* Allocate space for populating ssids in struct */
+ params_size += sizeof(struct brcmf_ssid_le) * request->n_ssids;
+ }
+
+ params = kzalloc(params_size, GFP_KERNEL);
+ if (!params) {
+ err = -ENOMEM;
+ goto exit;
+ }
+ BUG_ON(params_size + sizeof("escan") >= BRCMF_DCMD_MEDLEN);
+ brcmf_escan_prep(cfg, ¶ms->params_le, request);
+ params->version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION);
+ params->action = cpu_to_le16(WL_ESCAN_ACTION_START);
+ params->sync_id = cpu_to_le16(0x1234);
+
+ err = brcmf_fil_iovar_data_set(ifp, "escan", params, params_size);
+ if (err) {
+ if (err == -EBUSY)
+ brcmf_dbg(INFO, "system busy : escan canceled\n");
+ else
+ brcmf_err("error (%d)\n", err);
+ }
+
+ kfree(params);
+exit:
+ return err;
+}
+
+static s32
+brcmf_do_escan(struct brcmf_if *ifp, struct cfg80211_scan_request *request)
+{
+ struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
+ s32 err;
+ u32 passive_scan;
+ struct brcmf_scan_results *results;
+ struct escan_info *escan = &cfg->escan_info;
+
+ brcmf_dbg(SCAN, "Enter\n");
+ escan->ifp = ifp;
+ escan->wiphy = cfg->wiphy;
+ escan->escan_state = WL_ESCAN_STATE_SCANNING;
+ passive_scan = cfg->active_scan ? 0 : 1;
+ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PASSIVE_SCAN,
+ passive_scan);
+ if (err) {
+ brcmf_err("error (%d)\n", err);
+ return err;
+ }
+ brcmf_scan_config_mpc(ifp, 0);
+ results = (struct brcmf_scan_results *)cfg->escan_info.escan_buf;
+ results->version = 0;
+ results->count = 0;
+ results->buflen = WL_ESCAN_RESULTS_FIXED_SIZE;
+
+ err = escan->run(cfg, ifp, request);
+ if (err)
+ brcmf_scan_config_mpc(ifp, 1);
+ return err;
+}
+
+static s32
+brcmf_cfg80211_escan(struct wiphy *wiphy, struct brcmf_cfg80211_vif *vif,
+ struct cfg80211_scan_request *request,
+ struct cfg80211_ssid *this_ssid)
+{
+ struct brcmf_if *ifp = vif->ifp;
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct cfg80211_ssid *ssids;
+ u32 passive_scan;
+ bool escan_req;
+ bool spec_scan;
+ s32 err;
+ struct brcmf_ssid_le ssid_le;
+ u32 SSID_len;
+
+ brcmf_dbg(SCAN, "START ESCAN\n");
+
+ if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
+ brcmf_err("Scanning already: status (%lu)\n", cfg->scan_status);
+ return -EAGAIN;
+ }
+ if (test_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status)) {
+ brcmf_err("Scanning being aborted: status (%lu)\n",
+ cfg->scan_status);
+ return -EAGAIN;
+ }
+ if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) {
+ brcmf_err("Scanning suppressed: status (%lu)\n",
+ cfg->scan_status);
+ return -EAGAIN;
+ }
+ if (test_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state)) {
+ brcmf_err("Connecting: status (%lu)\n", ifp->vif->sme_state);
+ return -EAGAIN;
+ }
+
+ /* If scan req comes for p2p0, send it over primary I/F */
+ if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif)
+ vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif;
+
+ escan_req = false;
+ if (request) {
+ /* scan bss */
+ ssids = request->ssids;
+ escan_req = true;
+ } else {
+ /* scan in ibss */
+ /* we don't do escan in ibss */
+ ssids = this_ssid;
+ }
+
+ cfg->scan_request = request;
+ cfg->scan_retry_cnt = 0;
+ set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
+ if (escan_req) {
+ cfg->escan_info.run = brcmf_run_escan;
+ err = brcmf_p2p_scan_prep(wiphy, request, vif);
+ if (err)
+ goto scan_out;
+
+ err = brcmf_do_escan(vif->ifp, request);
+ if (err)
+ goto scan_out;
+ } else {
+ brcmf_dbg(SCAN, "ssid \"%s\", ssid_len (%d)\n",
+ ssids->ssid, ssids->ssid_len);
+ memset(&ssid_le, 0, sizeof(ssid_le));
+ SSID_len = min_t(u8, sizeof(ssid_le.SSID), ssids->ssid_len);
+ ssid_le.SSID_len = cpu_to_le32(0);
+ spec_scan = false;
+ if (SSID_len) {
+ memcpy(ssid_le.SSID, ssids->ssid, SSID_len);
+ ssid_le.SSID_len = cpu_to_le32(SSID_len);
+ spec_scan = true;
+ } else
+ brcmf_dbg(SCAN, "Broadcast scan\n");
+
+ passive_scan = cfg->active_scan ? 0 : 1;
+ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PASSIVE_SCAN,
+ passive_scan);
+ if (err) {
+ brcmf_err("WLC_SET_PASSIVE_SCAN error (%d)\n", err);
+ goto scan_out;
+ }
+ brcmf_scan_config_mpc(ifp, 0);
+ err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN, &ssid_le,
+ sizeof(ssid_le));
+ if (err) {
+ if (err == -EBUSY)
+ brcmf_dbg(INFO, "BUSY: scan for \"%s\" canceled\n",
+ ssid_le.SSID);
+ else
+ brcmf_err("WLC_SCAN error (%d)\n", err);
+
+ brcmf_scan_config_mpc(ifp, 1);
+ goto scan_out;
+ }
+ }
+
+ /* Arm scan timeout timer */
+ mod_timer(&cfg->escan_timeout, jiffies +
+ BRCMF_ESCAN_TIMER_INTERVAL_MS * HZ / 1000);
+
+ return 0;
+
+scan_out:
+ clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
+ cfg->scan_request = NULL;
+ return err;
+}
+
+static s32
+brcmf_cfg80211_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request)
+{
+ struct brcmf_cfg80211_vif *vif;
+ s32 err = 0;
+
+ brcmf_dbg(TRACE, "Enter\n");
+ vif = container_of(request->wdev, struct brcmf_cfg80211_vif, wdev);
+ if (!check_vif_up(vif))
+ return -EIO;
+
+ err = brcmf_cfg80211_escan(wiphy, vif, request, NULL);
+
+ if (err)
+ brcmf_err("scan error (%d)\n", err);
+
+ brcmf_dbg(TRACE, "Exit\n");
+ return err;
+}
+
+static s32 brcmf_set_rts(struct net_device *ndev, u32 rts_threshold)
+{
+ s32 err = 0;
+
+ err = brcmf_fil_iovar_int_set(netdev_priv(ndev), "rtsthresh",
+ rts_threshold);
+ if (err)
+ brcmf_err("Error (%d)\n", err);
+
+ return err;
+}
+
+static s32 brcmf_set_frag(struct net_device *ndev, u32 frag_threshold)
+{
+ s32 err = 0;
+
+ err = brcmf_fil_iovar_int_set(netdev_priv(ndev), "fragthresh",
+ frag_threshold);
+ if (err)
+ brcmf_err("Error (%d)\n", err);
+
+ return err;
+}
+
+static s32 brcmf_set_retry(struct net_device *ndev, u32 retry, bool l)
+{
+ s32 err = 0;
+ u32 cmd = (l ? BRCMF_C_SET_LRL : BRCMF_C_SET_SRL);
+
+ err = brcmf_fil_cmd_int_set(netdev_priv(ndev), cmd, retry);
+ if (err) {
+ brcmf_err("cmd (%d) , error (%d)\n", cmd, err);
+ return err;
+ }
+ return err;
+}
+
+static s32 brcmf_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct net_device *ndev = cfg_to_ndev(cfg);
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ s32 err = 0;
+
+ brcmf_dbg(TRACE, "Enter\n");
+ if (!check_vif_up(ifp->vif))
+ return -EIO;
+
+ if (changed & WIPHY_PARAM_RTS_THRESHOLD &&
+ (cfg->conf->rts_threshold != wiphy->rts_threshold)) {
+ cfg->conf->rts_threshold = wiphy->rts_threshold;
+ err = brcmf_set_rts(ndev, cfg->conf->rts_threshold);
+ if (!err)
+ goto done;
+ }
+ if (changed & WIPHY_PARAM_FRAG_THRESHOLD &&
+ (cfg->conf->frag_threshold != wiphy->frag_threshold)) {
+ cfg->conf->frag_threshold = wiphy->frag_threshold;
+ err = brcmf_set_frag(ndev, cfg->conf->frag_threshold);
+ if (!err)
+ goto done;
+ }
+ if (changed & WIPHY_PARAM_RETRY_LONG
+ && (cfg->conf->retry_long != wiphy->retry_long)) {
+ cfg->conf->retry_long = wiphy->retry_long;
+ err = brcmf_set_retry(ndev, cfg->conf->retry_long, true);
+ if (!err)
+ goto done;
+ }
+ if (changed & WIPHY_PARAM_RETRY_SHORT
+ && (cfg->conf->retry_short != wiphy->retry_short)) {
+ cfg->conf->retry_short = wiphy->retry_short;
+ err = brcmf_set_retry(ndev, cfg->conf->retry_short, false);
+ if (!err)
+ goto done;
+ }
+
+done:
+ brcmf_dbg(TRACE, "Exit\n");
+ return err;
+}
+
+static void brcmf_init_prof(struct brcmf_cfg80211_profile *prof)
+{
+ memset(prof, 0, sizeof(*prof));
+}
+
+static u16 brcmf_map_fw_linkdown_reason(const struct brcmf_event_msg *e)
+{
+ u16 reason;
+
+ switch (e->event_code) {
+ case BRCMF_E_DEAUTH:
+ case BRCMF_E_DEAUTH_IND:
+ case BRCMF_E_DISASSOC_IND:
+ reason = e->reason;
+ break;
+ case BRCMF_E_LINK:
+ default:
+ reason = 0;
+ break;
+ }
+ return reason;
+}
+
+static int brcmf_set_pmk(struct brcmf_if *ifp, const u8 *pmk_data, u16 pmk_len)
+{
+ struct brcmf_wsec_pmk_le pmk;
+ int i, err;
+
+ /* convert to firmware key format */
+ pmk.key_len = cpu_to_le16(pmk_len << 1);
+ pmk.flags = cpu_to_le16(BRCMF_WSEC_PASSPHRASE);
+ for (i = 0; i < pmk_len; i++)
+ snprintf(&pmk.key[2 * i], 3, "%02x", pmk_data[i]);
+
+ /* store psk in firmware */
+ err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_WSEC_PMK,
+ &pmk, sizeof(pmk));
+ if (err < 0)
+ brcmf_err("failed to change PSK in firmware (len=%u)\n",
+ pmk_len);
+
+ return err;
+}
+
+static void brcmf_link_down(struct brcmf_cfg80211_vif *vif, u16 reason)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(vif->wdev.wiphy);
+ s32 err = 0;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state)) {
+ brcmf_dbg(INFO, "Call WLC_DISASSOC to stop excess roaming\n ");
+ err = brcmf_fil_cmd_data_set(vif->ifp,
+ BRCMF_C_DISASSOC, NULL, 0);
+ if (err) {
+ brcmf_err("WLC_DISASSOC failed (%d)\n", err);
+ }
+ if ((vif->wdev.iftype == NL80211_IFTYPE_STATION) ||
+ (vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT))
+ cfg80211_disconnected(vif->wdev.netdev, reason, NULL, 0,
+ true, GFP_KERNEL);
+ }
+ clear_bit(BRCMF_VIF_STATUS_CONNECTING, &vif->sme_state);
+ clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
+ brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0);
+ if (vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_NONE) {
+ brcmf_set_pmk(vif->ifp, NULL, 0);
+ vif->profile.use_fwsup = BRCMF_PROFILE_FWSUP_NONE;
+ }
+ brcmf_dbg(TRACE, "Exit\n");
+}
+
+static s32
+brcmf_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *ndev,
+ struct cfg80211_ibss_params *params)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
+ struct brcmf_join_params join_params;
+ size_t join_params_size = 0;
+ s32 err = 0;
+ s32 wsec = 0;
+ s32 bcnprd;
+ u16 chanspec;
+ u32 ssid_len;
+
+ brcmf_dbg(TRACE, "Enter\n");
+ if (!check_vif_up(ifp->vif))
+ return -EIO;
+
+ if (params->ssid)
+ brcmf_dbg(CONN, "SSID: %s\n", params->ssid);
+ else {
+ brcmf_dbg(CONN, "SSID: NULL, Not supported\n");
+ return -EOPNOTSUPP;
+ }
+
+ set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
+
+ if (params->bssid)
+ brcmf_dbg(CONN, "BSSID: %pM\n", params->bssid);
+ else
+ brcmf_dbg(CONN, "No BSSID specified\n");
+
+ if (params->chandef.chan)
+ brcmf_dbg(CONN, "channel: %d\n",
+ params->chandef.chan->center_freq);
+ else
+ brcmf_dbg(CONN, "no channel specified\n");
+
+ if (params->channel_fixed)
+ brcmf_dbg(CONN, "fixed channel required\n");
+ else
+ brcmf_dbg(CONN, "no fixed channel required\n");
+
+ if (params->ie && params->ie_len)
+ brcmf_dbg(CONN, "ie len: %d\n", params->ie_len);
+ else
+ brcmf_dbg(CONN, "no ie specified\n");
+
+ if (params->beacon_interval)
+ brcmf_dbg(CONN, "beacon interval: %d\n",
+ params->beacon_interval);
+ else
+ brcmf_dbg(CONN, "no beacon interval specified\n");
+
+ if (params->basic_rates)
+ brcmf_dbg(CONN, "basic rates: %08X\n", params->basic_rates);
+ else
+ brcmf_dbg(CONN, "no basic rates specified\n");
+
+ if (params->privacy)
+ brcmf_dbg(CONN, "privacy required\n");
+ else
+ brcmf_dbg(CONN, "no privacy required\n");
+
+ /* Configure Privacy for starter */
+ if (params->privacy)
+ wsec |= WEP_ENABLED;
+
+ err = brcmf_fil_iovar_int_set(ifp, "wsec", wsec);
+ if (err) {
+ brcmf_err("wsec failed (%d)\n", err);
+ goto done;
+ }
+
+ /* Configure Beacon Interval for starter */
+ if (params->beacon_interval)
+ bcnprd = params->beacon_interval;
+ else
+ bcnprd = 100;
+
+ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD, bcnprd);
+ if (err) {
+ brcmf_err("WLC_SET_BCNPRD failed (%d)\n", err);
+ goto done;
+ }
+
+ /* Configure required join parameter */
+ memset(&join_params, 0, sizeof(struct brcmf_join_params));
+
+ /* SSID */
+ ssid_len = min_t(u32, params->ssid_len, IEEE80211_MAX_SSID_LEN);
+ memcpy(join_params.ssid_le.SSID, params->ssid, ssid_len);
+ join_params.ssid_le.SSID_len = cpu_to_le32(ssid_len);
+ join_params_size = sizeof(join_params.ssid_le);
+
+ /* BSSID */
+ if (params->bssid) {
+ memcpy(join_params.params_le.bssid, params->bssid, ETH_ALEN);
+ join_params_size += BRCMF_ASSOC_PARAMS_FIXED_SIZE;
+ memcpy(profile->bssid, params->bssid, ETH_ALEN);
+ } else {
+ eth_broadcast_addr(join_params.params_le.bssid);
+ eth_zero_addr(profile->bssid);
+ }
+
+ /* Channel */
+ if (params->chandef.chan) {
+ u32 target_channel;
+
+ cfg->channel =
+ ieee80211_frequency_to_channel(
+ params->chandef.chan->center_freq);
+ if (params->channel_fixed) {
+ /* adding chanspec */
+ chanspec = chandef_to_chanspec(&cfg->d11inf,
+ ¶ms->chandef);
+ join_params.params_le.chanspec_list[0] =
+ cpu_to_le16(chanspec);
+ join_params.params_le.chanspec_num = cpu_to_le32(1);
+ join_params_size += sizeof(join_params.params_le);
+ }
+
+ /* set channel for starter */
+ target_channel = cfg->channel;
+ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_CHANNEL,
+ target_channel);
+ if (err) {
+ brcmf_err("WLC_SET_CHANNEL failed (%d)\n", err);
+ goto done;
+ }
+ } else
+ cfg->channel = 0;
+
+ cfg->ibss_starter = false;
+
+
+ err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
+ &join_params, join_params_size);
+ if (err) {
+ brcmf_err("WLC_SET_SSID failed (%d)\n", err);
+ goto done;
+ }
+
+done:
+ if (err)
+ clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
+ brcmf_dbg(TRACE, "Exit\n");
+ return err;
+}
+
+static s32
+brcmf_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *ndev)
+{
+ struct brcmf_if *ifp = netdev_priv(ndev);
+
+ brcmf_dbg(TRACE, "Enter\n");
+ if (!check_vif_up(ifp->vif)) {
+ /* When driver is being unloaded, it can end up here. If an
+ * error is returned then later on a debug trace in the wireless
+ * core module will be printed. To avoid this 0 is returned.
+ */
+ return 0;
+ }
+
+ brcmf_link_down(ifp->vif, WLAN_REASON_DEAUTH_LEAVING);
+ brcmf_net_setcarrier(ifp, false);
+
+ brcmf_dbg(TRACE, "Exit\n");
+
+ return 0;
+}
+
+static s32 brcmf_set_wpa_version(struct net_device *ndev,
+ struct cfg80211_connect_params *sme)
+{
+ struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
+ struct brcmf_cfg80211_security *sec;
+ s32 val = 0;
+ s32 err = 0;
+
+ if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_1)
+ val = WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED;
+ else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2)
+ val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED;
+ else
+ val = WPA_AUTH_DISABLED;
+ brcmf_dbg(CONN, "setting wpa_auth to 0x%0x\n", val);
+ err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wpa_auth", val);
+ if (err) {
+ brcmf_err("set wpa_auth failed (%d)\n", err);
+ return err;
+ }
+ sec = &profile->sec;
+ sec->wpa_versions = sme->crypto.wpa_versions;
+ return err;
+}
+
+static s32 brcmf_set_auth_type(struct net_device *ndev,
+ struct cfg80211_connect_params *sme)
+{
+ struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
+ struct brcmf_cfg80211_security *sec;
+ s32 val = 0;
+ s32 err = 0;
+
+ switch (sme->auth_type) {
+ case NL80211_AUTHTYPE_OPEN_SYSTEM:
+ val = 0;
+ brcmf_dbg(CONN, "open system\n");
+ break;
+ case NL80211_AUTHTYPE_SHARED_KEY:
+ val = 1;
+ brcmf_dbg(CONN, "shared key\n");
+ break;
+ default:
+ val = 2;
+ brcmf_dbg(CONN, "automatic, auth type (%d)\n", sme->auth_type);
+ break;
+ }
+
+ err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "auth", val);
+ if (err) {
+ brcmf_err("set auth failed (%d)\n", err);
+ return err;
+ }
+ sec = &profile->sec;
+ sec->auth_type = sme->auth_type;
+ return err;
+}
+
+static s32
+brcmf_set_wsec_mode(struct net_device *ndev,
+ struct cfg80211_connect_params *sme)
+{
+ struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
+ struct brcmf_cfg80211_security *sec;
+ s32 pval = 0;
+ s32 gval = 0;
+ s32 wsec;
+ s32 err = 0;
+
+ if (sme->crypto.n_ciphers_pairwise) {
+ switch (sme->crypto.ciphers_pairwise[0]) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ pval = WEP_ENABLED;
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ pval = TKIP_ENABLED;
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ pval = AES_ENABLED;
+ break;
+ case WLAN_CIPHER_SUITE_AES_CMAC:
+ pval = AES_ENABLED;
+ break;
+ default:
+ brcmf_err("invalid cipher pairwise (%d)\n",
+ sme->crypto.ciphers_pairwise[0]);
+ return -EINVAL;
+ }
+ }
+ if (sme->crypto.cipher_group) {
+ switch (sme->crypto.cipher_group) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ gval = WEP_ENABLED;
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ gval = TKIP_ENABLED;
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ gval = AES_ENABLED;
+ break;
+ case WLAN_CIPHER_SUITE_AES_CMAC:
+ gval = AES_ENABLED;
+ break;
+ default:
+ brcmf_err("invalid cipher group (%d)\n",
+ sme->crypto.cipher_group);
+ return -EINVAL;
+ }
+ }
+
+ brcmf_dbg(CONN, "pval (%d) gval (%d)\n", pval, gval);
+ /* In case of privacy, but no security and WPS then simulate */
+ /* setting AES. WPS-2.0 allows no security */
+ if (brcmf_find_wpsie(sme->ie, sme->ie_len) && !pval && !gval &&
+ sme->privacy)
+ pval = AES_ENABLED;
+
+ wsec = pval | gval;
+ err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wsec", wsec);
+ if (err) {
+ brcmf_err("error (%d)\n", err);
+ return err;
+ }
+
+ sec = &profile->sec;
+ sec->cipher_pairwise = sme->crypto.ciphers_pairwise[0];
+ sec->cipher_group = sme->crypto.cipher_group;
+
+ return err;
+}
+
+static s32
+brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme)
+{
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
+ s32 val;
+ s32 err;
+ const struct brcmf_tlv *rsn_ie;
+ const u8 *ie;
+ u32 ie_len;
+ u32 offset;
+ u16 rsn_cap;
+ u32 mfp;
+ u16 count;
+
+ profile->use_fwsup = BRCMF_PROFILE_FWSUP_NONE;
+
+ if (!sme->crypto.n_akm_suites)
+ return 0;
+
+ err = brcmf_fil_bsscfg_int_get(netdev_priv(ndev), "wpa_auth", &val);
+ if (err) {
+ brcmf_err("could not get wpa_auth (%d)\n", err);
+ return err;
+ }
+ if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED)) {
+ switch (sme->crypto.akm_suites[0]) {
+ case WLAN_AKM_SUITE_8021X:
+ val = WPA_AUTH_UNSPECIFIED;
+ if (sme->want_1x)
+ profile->use_fwsup = BRCMF_PROFILE_FWSUP_1X;
+ break;
+ case WLAN_AKM_SUITE_PSK:
+ val = WPA_AUTH_PSK;
+ break;
+ default:
+ brcmf_err("invalid cipher group (%d)\n",
+ sme->crypto.cipher_group);
+ return -EINVAL;
+ }
+ } else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) {
+ switch (sme->crypto.akm_suites[0]) {
+ case WLAN_AKM_SUITE_8021X:
+ val = WPA2_AUTH_UNSPECIFIED;
+ if (sme->want_1x)
+ profile->use_fwsup = BRCMF_PROFILE_FWSUP_1X;
+ break;
+ case WLAN_AKM_SUITE_8021X_SHA256:
+ val = WPA2_AUTH_1X_SHA256;
+ if (sme->want_1x)
+ profile->use_fwsup = BRCMF_PROFILE_FWSUP_1X;
+ break;
+ case WLAN_AKM_SUITE_PSK_SHA256:
+ val = WPA2_AUTH_PSK_SHA256;
+ break;
+ case WLAN_AKM_SUITE_PSK:
+ val = WPA2_AUTH_PSK;
+ break;
+ case WLAN_AKM_SUITE_FT_8021X:
+ val = WPA2_AUTH_UNSPECIFIED | WPA2_AUTH_FT;
+ break;
+ case WLAN_AKM_SUITE_FT_PSK:
+ val = WPA2_AUTH_PSK | WPA2_AUTH_FT;
+ break;
+ default:
+ brcmf_err("invalid cipher group (%d)\n",
+ sme->crypto.cipher_group);
+ return -EINVAL;
+ }
+ }
+
+ if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_1X)
+ brcmf_dbg(INFO, "using 1X offload\n");
+
+ if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP))
+ goto skip_mfp_config;
+ /* The MFP mode (1 or 2) needs to be determined, parse IEs. The
+ * IE will not be verified, just a quick search for MFP config
+ */
+ rsn_ie = brcmf_parse_tlvs((const u8 *)sme->ie, sme->ie_len,
+ WLAN_EID_RSN);
+ if (!rsn_ie)
+ goto skip_mfp_config;
+ ie = (const u8 *)rsn_ie;
+ ie_len = rsn_ie->len + TLV_HDR_LEN;
+ /* Skip unicast suite */
+ offset = TLV_HDR_LEN + WPA_IE_VERSION_LEN + WPA_IE_MIN_OUI_LEN;
+ if (offset + WPA_IE_SUITE_COUNT_LEN >= ie_len)
+ goto skip_mfp_config;
+ /* Skip multicast suite */
+ count = ie[offset] + (ie[offset + 1] << 8);
+ offset += WPA_IE_SUITE_COUNT_LEN + (count * WPA_IE_MIN_OUI_LEN);
+ if (offset + WPA_IE_SUITE_COUNT_LEN >= ie_len)
+ goto skip_mfp_config;
+ /* Skip auth key management suite(s) */
+ count = ie[offset] + (ie[offset + 1] << 8);
+ offset += WPA_IE_SUITE_COUNT_LEN + (count * WPA_IE_MIN_OUI_LEN);
+ if (offset + WPA_IE_SUITE_COUNT_LEN > ie_len)
+ goto skip_mfp_config;
+ /* Ready to read capabilities */
+ mfp = BRCMF_MFP_NONE;
+ rsn_cap = ie[offset] + (ie[offset + 1] << 8);
+ if (rsn_cap & RSN_CAP_MFPR_MASK)
+ mfp = BRCMF_MFP_REQUIRED;
+ else if (rsn_cap & RSN_CAP_MFPC_MASK)
+ mfp = BRCMF_MFP_CAPABLE;
+ brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "mfp", mfp);
+
+skip_mfp_config:
+ brcmf_dbg(CONN, "setting wpa_auth to %d\n", val);
+ err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wpa_auth", val);
+ if (err) {
+ brcmf_err("could not set wpa_auth (%d)\n", err);
+ return err;
+ }
+
+ return err;
+}
+
+static s32
+brcmf_set_sharedkey(struct net_device *ndev,
+ struct cfg80211_connect_params *sme)
+{
+ struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
+ struct brcmf_cfg80211_security *sec;
+ struct brcmf_wsec_key key;
+ s32 val;
+ s32 err = 0;
+
+ brcmf_dbg(CONN, "key len (%d)\n", sme->key_len);
+
+ if (sme->key_len == 0)
+ return 0;
+
+ sec = &profile->sec;
+ brcmf_dbg(CONN, "wpa_versions 0x%x cipher_pairwise 0x%x\n",
+ sec->wpa_versions, sec->cipher_pairwise);
+
+ if (sec->wpa_versions & (NL80211_WPA_VERSION_1 | NL80211_WPA_VERSION_2))
+ return 0;
+
+ if (!(sec->cipher_pairwise &
+ (WLAN_CIPHER_SUITE_WEP40 | WLAN_CIPHER_SUITE_WEP104)))
+ return 0;
+
+ memset(&key, 0, sizeof(key));
+ key.len = (u32) sme->key_len;
+ key.index = (u32) sme->key_idx;
+ if (key.len > sizeof(key.data)) {
+ brcmf_err("Too long key length (%u)\n", key.len);
+ return -EINVAL;
+ }
+ memcpy(key.data, sme->key, key.len);
+ key.flags = BRCMF_PRIMARY_KEY;
+ switch (sec->cipher_pairwise) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ key.algo = CRYPTO_ALGO_WEP1;
+ break;
+ case WLAN_CIPHER_SUITE_WEP104:
+ key.algo = CRYPTO_ALGO_WEP128;
+ break;
+ default:
+ brcmf_err("Invalid algorithm (%d)\n",
+ sme->crypto.ciphers_pairwise[0]);
+ return -EINVAL;
+ }
+ /* Set the new key/index */
+ brcmf_dbg(CONN, "key length (%d) key index (%d) algo (%d)\n",
+ key.len, key.index, key.algo);
+ brcmf_dbg(CONN, "key \"%s\"\n", key.data);
+ err = send_key_to_dongle(netdev_priv(ndev), &key);
+ if (err)
+ return err;
+
+ if (sec->auth_type == NL80211_AUTHTYPE_SHARED_KEY) {
+ brcmf_dbg(CONN, "set auth_type to shared key\n");
+ val = WL_AUTH_SHARED_KEY; /* shared key */
+ err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "auth", val);
+ if (err)
+ brcmf_err("set auth failed (%d)\n", err);
+ }
+ return err;
+}
+
+static
+enum nl80211_auth_type brcmf_war_auth_type(struct brcmf_if *ifp,
+ enum nl80211_auth_type type)
+{
+ if (type == NL80211_AUTHTYPE_AUTOMATIC &&
+ brcmf_feat_is_quirk_enabled(ifp, BRCMF_FEAT_QUIRK_AUTO_AUTH)) {
+ brcmf_dbg(CONN, "WAR: use OPEN instead of AUTO\n");
+ type = NL80211_AUTHTYPE_OPEN_SYSTEM;
+ }
+ return type;
+}
+
+static void brcmf_set_join_pref(struct brcmf_if *ifp,
+ struct cfg80211_bss_selection *bss_select)
+{
+ struct brcmf_join_pref_params join_pref_params[2];
+ enum nl80211_band band;
+ int err, i = 0;
+
+ join_pref_params[i].len = 2;
+ join_pref_params[i].rssi_gain = 0;
+
+ if (bss_select->behaviour != NL80211_BSS_SELECT_ATTR_BAND_PREF)
+ brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_ASSOC_PREFER, WLC_BAND_AUTO);
+
+ switch (bss_select->behaviour) {
+ case __NL80211_BSS_SELECT_ATTR_INVALID:
+ brcmf_c_set_joinpref_default(ifp);
+ return;
+ case NL80211_BSS_SELECT_ATTR_BAND_PREF:
+ join_pref_params[i].type = BRCMF_JOIN_PREF_BAND;
+ band = bss_select->param.band_pref;
+ join_pref_params[i].band = nl80211_band_to_fwil(band);
+ i++;
+ break;
+ case NL80211_BSS_SELECT_ATTR_RSSI_ADJUST:
+ join_pref_params[i].type = BRCMF_JOIN_PREF_RSSI_DELTA;
+ band = bss_select->param.adjust.band;
+ join_pref_params[i].band = nl80211_band_to_fwil(band);
+ join_pref_params[i].rssi_gain = bss_select->param.adjust.delta;
+ i++;
+ break;
+ case NL80211_BSS_SELECT_ATTR_RSSI:
+ default:
+ break;
+ }
+ join_pref_params[i].type = BRCMF_JOIN_PREF_RSSI;
+ join_pref_params[i].len = 2;
+ join_pref_params[i].rssi_gain = 0;
+ join_pref_params[i].band = 0;
+ err = brcmf_fil_iovar_data_set(ifp, "join_pref", join_pref_params,
+ sizeof(join_pref_params));
+ if (err)
+ brcmf_err("Set join_pref error (%d)\n", err);
+}
+
+static s32
+brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev,
+ struct cfg80211_connect_params *sme)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
+ struct ieee80211_channel *chan = sme->channel;
+ struct brcmf_join_params join_params;
+ size_t join_params_size;
+ const struct brcmf_tlv *rsn_ie;
+ const struct brcmf_vs_tlv *wpa_ie;
+ const void *ie;
+ u32 ie_len;
+ struct brcmf_ext_join_params_le *ext_join_params;
+ u16 chanspec;
+ s32 err = 0;
+ u32 ssid_len;
+
+ brcmf_dbg(TRACE, "Enter\n");
+ if (!check_vif_up(ifp->vif))
+ return -EIO;
+
+ if (!sme->ssid) {
+ brcmf_err("Invalid ssid\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif) {
+ /* A normal (non P2P) connection request setup. */
+ ie = NULL;
+ ie_len = 0;
+ /* find the WPA_IE */
+ wpa_ie = brcmf_find_wpaie((u8 *)sme->ie, sme->ie_len);
+ if (wpa_ie) {
+ ie = wpa_ie;
+ ie_len = wpa_ie->len + TLV_HDR_LEN;
+ } else {
+ /* find the RSN_IE */
+ rsn_ie = brcmf_parse_tlvs((const u8 *)sme->ie,
+ sme->ie_len,
+ WLAN_EID_RSN);
+ if (rsn_ie) {
+ ie = rsn_ie;
+ ie_len = rsn_ie->len + TLV_HDR_LEN;
+ }
+ }
+ brcmf_fil_iovar_data_set(ifp, "wpaie", ie, ie_len);
+ }
+
+ err = brcmf_vif_set_mgmt_ie(ifp->vif, BRCMF_VNDR_IE_ASSOCREQ_FLAG,
+ sme->ie, sme->ie_len);
+ if (err)
+ brcmf_err("Set Assoc REQ IE Failed\n");
+ else
+ brcmf_dbg(TRACE, "Applied Vndr IEs for Assoc request\n");
+
+ set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
+
+ if (chan) {
+ cfg->channel =
+ ieee80211_frequency_to_channel(chan->center_freq);
+ chanspec = channel_to_chanspec(&cfg->d11inf, chan);
+ brcmf_dbg(CONN, "channel=%d, center_req=%d, chanspec=0x%04x\n",
+ cfg->channel, chan->center_freq, chanspec);
+ } else {
+ cfg->channel = 0;
+ chanspec = 0;
+ }
+
+ brcmf_dbg(INFO, "ie (%p), ie_len (%zd)\n", sme->ie, sme->ie_len);
+
+ err = brcmf_set_wpa_version(ndev, sme);
+ if (err) {
+ brcmf_err("wl_set_wpa_version failed (%d)\n", err);
+ goto done;
+ }
+
+ sme->auth_type = brcmf_war_auth_type(ifp, sme->auth_type);
+ err = brcmf_set_auth_type(ndev, sme);
+ if (err) {
+ brcmf_err("wl_set_auth_type failed (%d)\n", err);
+ goto done;
+ }
+
+ err = brcmf_set_wsec_mode(ndev, sme);
+ if (err) {
+ brcmf_err("wl_set_set_cipher failed (%d)\n", err);
+ goto done;
+ }
+
+ err = brcmf_set_key_mgmt(ndev, sme);
+ if (err) {
+ brcmf_err("wl_set_key_mgmt failed (%d)\n", err);
+ goto done;
+ }
+
+ err = brcmf_set_sharedkey(ndev, sme);
+ if (err) {
+ brcmf_err("brcmf_set_sharedkey failed (%d)\n", err);
+ goto done;
+ }
+
+ if (sme->crypto.psk) {
+ if (WARN_ON(profile->use_fwsup != BRCMF_PROFILE_FWSUP_NONE)) {
+ err = -EINVAL;
+ goto done;
+ }
+ brcmf_dbg(INFO, "using PSK offload\n");
+ profile->use_fwsup = BRCMF_PROFILE_FWSUP_PSK;
+ }
+
+ if (profile->use_fwsup != BRCMF_PROFILE_FWSUP_NONE) {
+ /* enable firmware supplicant for this interface */
+ err = brcmf_fil_iovar_int_set(ifp, "sup_wpa", 1);
+ if (err < 0) {
+ brcmf_err("failed to enable fw supplicant\n");
+ goto done;
+ }
+ }
+
+ if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_PSK) {
+ err = brcmf_set_pmk(ifp, sme->crypto.psk,
+ BRCMF_WSEC_MAX_PSK_LEN);
+ if (err)
+ goto done;
+ }
+
+ if (sme->bssid_hint) {
+ sme->bssid = sme->bssid_hint;
+ brcmf_dbg(CONN, "using bssid_hint\n");
+ }
+
+ /* Join with specific BSSID and cached SSID
+ * If SSID is zero join based on BSSID only
+ */
+ join_params_size = offsetof(struct brcmf_ext_join_params_le, assoc_le) +
+ offsetof(struct brcmf_assoc_params_le, chanspec_list);
+ if (cfg->channel)
+ join_params_size += sizeof(u16);
+ ext_join_params = kzalloc(join_params_size, GFP_KERNEL);
+ if (ext_join_params == NULL) {
+ err = -ENOMEM;
+ goto done;
+ }
+ ssid_len = min_t(u32, sme->ssid_len, IEEE80211_MAX_SSID_LEN);
+ ext_join_params->ssid_le.SSID_len = cpu_to_le32(ssid_len);
+ memcpy(&ext_join_params->ssid_le.SSID, sme->ssid, ssid_len);
+ if (ssid_len < IEEE80211_MAX_SSID_LEN)
+ brcmf_dbg(CONN, "SSID \"%s\", len (%d)\n",
+ ext_join_params->ssid_le.SSID, ssid_len);
+
+ /* Set up join scan parameters */
+ ext_join_params->scan_le.scan_type = -1;
+ ext_join_params->scan_le.home_time = cpu_to_le32(-1);
+
+ if (sme->bssid)
+ memcpy(&ext_join_params->assoc_le.bssid, sme->bssid, ETH_ALEN);
+ else
+ eth_broadcast_addr(ext_join_params->assoc_le.bssid);
+
+ if (cfg->channel) {
+ ext_join_params->assoc_le.chanspec_num = cpu_to_le32(1);
+
+ ext_join_params->assoc_le.chanspec_list[0] =
+ cpu_to_le16(chanspec);
+ /* Increase dwell time to receive probe response or detect
+ * beacon from target AP at a noisy air only during connect
+ * command.
+ */
+ ext_join_params->scan_le.active_time =
+ cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS);
+ ext_join_params->scan_le.passive_time =
+ cpu_to_le32(BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS);
+ /* To sync with presence period of VSDB GO send probe request
+ * more frequently. Probe request will be stopped when it gets
+ * probe response from target AP/GO.
+ */
+ ext_join_params->scan_le.nprobes =
+ cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS /
+ BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS);
+ } else {
+ ext_join_params->scan_le.active_time = cpu_to_le32(-1);
+ ext_join_params->scan_le.passive_time = cpu_to_le32(-1);
+ ext_join_params->scan_le.nprobes = cpu_to_le32(-1);
+ }
+
+ brcmf_set_join_pref(ifp, &sme->bss_select);
+
+ err = brcmf_fil_bsscfg_data_set(ifp, "join", ext_join_params,
+ join_params_size);
+ kfree(ext_join_params);
+ if (!err)
+ /* This is it. join command worked, we are done */
+ goto done;
+
+ /* join command failed, fallback to set ssid */
+ memset(&join_params, 0, sizeof(join_params));
+ join_params_size = sizeof(join_params.ssid_le);
+
+ memcpy(&join_params.ssid_le.SSID, sme->ssid, ssid_len);
+ join_params.ssid_le.SSID_len = cpu_to_le32(ssid_len);
+
+ if (sme->bssid)
+ memcpy(join_params.params_le.bssid, sme->bssid, ETH_ALEN);
+ else
+ eth_broadcast_addr(join_params.params_le.bssid);
+
+ if (cfg->channel) {
+ join_params.params_le.chanspec_list[0] = cpu_to_le16(chanspec);
+ join_params.params_le.chanspec_num = cpu_to_le32(1);
+ join_params_size += sizeof(join_params.params_le);
+ }
+ err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
+ &join_params, join_params_size);
+ if (err)
+ brcmf_err("BRCMF_C_SET_SSID failed (%d)\n", err);
+
+done:
+ if (err)
+ clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
+ brcmf_dbg(TRACE, "Exit\n");
+ return err;
+}
+
+static s32
+brcmf_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *ndev,
+ u16 reason_code)
+{
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
+ struct brcmf_scb_val_le scbval;
+ s32 err = 0;
+
+ brcmf_dbg(TRACE, "Enter. Reason code = %d\n", reason_code);
+ if (!check_vif_up(ifp->vif))
+ return -EIO;
+
+ clear_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state);
+ clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
+ cfg80211_disconnected(ndev, reason_code, NULL, 0, true, GFP_KERNEL);
+
+ memcpy(&scbval.ea, &profile->bssid, ETH_ALEN);
+ scbval.val = cpu_to_le32(reason_code);
+ err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_DISASSOC,
+ &scbval, sizeof(scbval));
+ if (err)
+ brcmf_err("error (%d)\n", err);
+
+ brcmf_dbg(TRACE, "Exit\n");
+ return err;
+}
+
+static s32
+brcmf_cfg80211_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
+ enum nl80211_tx_power_setting type, s32 mbm)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct net_device *ndev = cfg_to_ndev(cfg);
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ s32 err;
+ s32 disable;
+ u32 qdbm = 127;
+
+ brcmf_dbg(TRACE, "Enter %d %d\n", type, mbm);
+ if (!check_vif_up(ifp->vif))
+ return -EIO;
+
+ switch (type) {
+ case NL80211_TX_POWER_AUTOMATIC:
+ break;
+ case NL80211_TX_POWER_LIMITED:
+ case NL80211_TX_POWER_FIXED:
+ if (mbm < 0) {
+ brcmf_err("TX_POWER_FIXED - dbm is negative\n");
+ err = -EINVAL;
+ goto done;
+ }
+ qdbm = MBM_TO_DBM(4 * mbm);
+ if (qdbm > 127)
+ qdbm = 127;
+ qdbm |= WL_TXPWR_OVERRIDE;
+ break;
+ default:
+ brcmf_err("Unsupported type %d\n", type);
+ err = -EINVAL;
+ goto done;
+ }
+ /* Make sure radio is off or on as far as software is concerned */
+ disable = WL_RADIO_SW_DISABLE << 16;
+ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_RADIO, disable);
+ if (err)
+ brcmf_err("WLC_SET_RADIO error (%d)\n", err);
+
+ err = brcmf_fil_iovar_int_set(ifp, "qtxpower", qdbm);
+ if (err)
+ brcmf_err("qtxpower error (%d)\n", err);
+
+done:
+ brcmf_dbg(TRACE, "Exit %d (qdbm)\n", qdbm & ~WL_TXPWR_OVERRIDE);
+ return err;
+}
+
+static s32
+brcmf_cfg80211_get_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
+ s32 *dbm)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct net_device *ndev = cfg_to_ndev(cfg);
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ s32 qdbm = 0;
+ s32 err;
+
+ brcmf_dbg(TRACE, "Enter\n");
+ if (!check_vif_up(ifp->vif))
+ return -EIO;
+
+ err = brcmf_fil_iovar_int_get(ifp, "qtxpower", &qdbm);
+ if (err) {
+ brcmf_err("error (%d)\n", err);
+ goto done;
+ }
+ *dbm = (qdbm & ~WL_TXPWR_OVERRIDE) / 4;
+
+done:
+ brcmf_dbg(TRACE, "Exit (0x%x %d)\n", qdbm, *dbm);
+ return err;
+}
+
+static s32
+brcmf_cfg80211_config_default_key(struct wiphy *wiphy, struct net_device *ndev,
+ u8 key_idx, bool unicast, bool multicast)
+{
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ u32 index;
+ u32 wsec;
+ s32 err = 0;
+
+ brcmf_dbg(TRACE, "Enter\n");
+ brcmf_dbg(CONN, "key index (%d)\n", key_idx);
+ if (!check_vif_up(ifp->vif))
+ return -EIO;
+
+ err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
+ if (err) {
+ brcmf_err("WLC_GET_WSEC error (%d)\n", err);
+ goto done;
+ }
+
+ if (wsec & WEP_ENABLED) {
+ /* Just select a new current key */
+ index = key_idx;
+ err = brcmf_fil_cmd_int_set(ifp,
+ BRCMF_C_SET_KEY_PRIMARY, index);
+ if (err)
+ brcmf_err("error (%d)\n", err);
+ }
+done:
+ brcmf_dbg(TRACE, "Exit\n");
+ return err;
+}
+
+static s32
+brcmf_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
+ u8 key_idx, bool pairwise, const u8 *mac_addr)
+{
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ struct brcmf_wsec_key *key;
+ s32 err;
+
+ brcmf_dbg(TRACE, "Enter\n");
+ brcmf_dbg(CONN, "key index (%d)\n", key_idx);
+
+ if (!check_vif_up(ifp->vif))
+ return -EIO;
+
+ if (key_idx >= BRCMF_MAX_DEFAULT_KEYS) {
+ /* we ignore this key index in this case */
+ return -EINVAL;
+ }
+
+ key = &ifp->vif->profile.key[key_idx];
+
+ if (key->algo == CRYPTO_ALGO_OFF) {
+ brcmf_dbg(CONN, "Ignore clearing of (never configured) key\n");
+ return -EINVAL;
+ }
+
+ memset(key, 0, sizeof(*key));
+ key->index = (u32)key_idx;
+ key->flags = BRCMF_PRIMARY_KEY;
+
+ /* Clear the key/index */
+ err = send_key_to_dongle(ifp, key);
+
+ brcmf_dbg(TRACE, "Exit\n");
+ return err;
+}
+
+static s32
+brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
+ u8 key_idx, bool pairwise, const u8 *mac_addr,
+ struct key_params *params)
+{
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ struct brcmf_wsec_key *key;
+ s32 val;
+ s32 wsec;
+ s32 err;
+ u8 keybuf[8];
+ bool ext_key;
+
+ brcmf_dbg(TRACE, "Enter\n");
+ brcmf_dbg(CONN, "key index (%d)\n", key_idx);
+ if (!check_vif_up(ifp->vif))
+ return -EIO;
+
+ if (key_idx >= BRCMF_MAX_DEFAULT_KEYS) {
+ /* we ignore this key index in this case */
+ brcmf_err("invalid key index (%d)\n", key_idx);
+ return -EINVAL;
+ }
+
+ if (params->key_len == 0)
+ return brcmf_cfg80211_del_key(wiphy, ndev, key_idx, pairwise,
+ mac_addr);
+
+ if (params->key_len > sizeof(key->data)) {
+ brcmf_err("Too long key length (%u)\n", params->key_len);
+ return -EINVAL;
+ }
+
+ ext_key = false;
+ if (mac_addr && (params->cipher != WLAN_CIPHER_SUITE_WEP40) &&
+ (params->cipher != WLAN_CIPHER_SUITE_WEP104)) {
+ brcmf_dbg(TRACE, "Ext key, mac %pM", mac_addr);
+ ext_key = true;
+ }
+
+ key = &ifp->vif->profile.key[key_idx];
+ memset(key, 0, sizeof(*key));
+ if ((ext_key) && (!is_multicast_ether_addr(mac_addr)))
+ memcpy((char *)&key->ea, (void *)mac_addr, ETH_ALEN);
+ key->len = params->key_len;
+ key->index = key_idx;
+ memcpy(key->data, params->key, key->len);
+ if (!ext_key)
+ key->flags = BRCMF_PRIMARY_KEY;
+
+ switch (params->cipher) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ key->algo = CRYPTO_ALGO_WEP1;
+ val = WEP_ENABLED;
+ brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
+ break;
+ case WLAN_CIPHER_SUITE_WEP104:
+ key->algo = CRYPTO_ALGO_WEP128;
+ val = WEP_ENABLED;
+ brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ if (!brcmf_is_apmode(ifp->vif)) {
+ brcmf_dbg(CONN, "Swapping RX/TX MIC key\n");
+ memcpy(keybuf, &key->data[24], sizeof(keybuf));
+ memcpy(&key->data[24], &key->data[16], sizeof(keybuf));
+ memcpy(&key->data[16], keybuf, sizeof(keybuf));
+ }
+ key->algo = CRYPTO_ALGO_TKIP;
+ val = TKIP_ENABLED;
+ brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
+ break;
+ case WLAN_CIPHER_SUITE_AES_CMAC:
+ key->algo = CRYPTO_ALGO_AES_CCM;
+ val = AES_ENABLED;
+ brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ key->algo = CRYPTO_ALGO_AES_CCM;
+ val = AES_ENABLED;
+ brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_CCMP\n");
+ break;
+ case WLAN_CIPHER_SUITE_PMK:
+ val = 0;
+ brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_PMK\n");
+ err = brcmf_set_pmk(ifp, params->key, BRCMF_WSEC_MAX_PSK_LEN);
+ if (err)
+ goto done;
+ break;
+ default:
+ brcmf_err("Invalid cipher (0x%x)\n", params->cipher);
+ err = -EINVAL;
+ goto done;
+ }
+
+ err = send_key_to_dongle(ifp, key);
+ if (ext_key || err)
+ goto done;
+
+ err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
+ if (err) {
+ brcmf_err("get wsec error (%d)\n", err);
+ goto done;
+ }
+ wsec |= val;
+ err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
+ if (err) {
+ brcmf_err("set wsec error (%d)\n", err);
+ goto done;
+ }
+
+done:
+ brcmf_dbg(TRACE, "Exit\n");
+ return err;
+}
+
+static s32
+brcmf_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev, u8 key_idx,
+ bool pairwise, const u8 *mac_addr, void *cookie,
+ void (*callback)(void *cookie,
+ struct key_params *params))
+{
+ struct key_params params;
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
+ struct brcmf_cfg80211_security *sec;
+ s32 wsec;
+ s32 err = 0;
+
+ brcmf_dbg(TRACE, "Enter\n");
+ brcmf_dbg(CONN, "key index (%d)\n", key_idx);
+ if (!check_vif_up(ifp->vif))
+ return -EIO;
+
+ memset(¶ms, 0, sizeof(params));
+
+ err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
+ if (err) {
+ brcmf_err("WLC_GET_WSEC error (%d)\n", err);
+ /* Ignore this error, may happen during DISASSOC */
+ err = -EAGAIN;
+ goto done;
+ }
+ if (wsec & WEP_ENABLED) {
+ sec = &profile->sec;
+ if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP40) {
+ params.cipher = WLAN_CIPHER_SUITE_WEP40;
+ brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
+ } else if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP104) {
+ params.cipher = WLAN_CIPHER_SUITE_WEP104;
+ brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
+ }
+ } else if (wsec & TKIP_ENABLED) {
+ params.cipher = WLAN_CIPHER_SUITE_TKIP;
+ brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
+ } else if (wsec & AES_ENABLED) {
+ params.cipher = WLAN_CIPHER_SUITE_AES_CMAC;
+ brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
+ } else {
+ brcmf_err("Invalid algo (0x%x)\n", wsec);
+ err = -EINVAL;
+ goto done;
+ }
+ callback(cookie, ¶ms);
+
+done:
+ brcmf_dbg(TRACE, "Exit\n");
+ return err;
+}
+
+static s32
+brcmf_cfg80211_config_default_mgmt_key(struct wiphy *wiphy,
+ struct net_device *ndev, u8 key_idx)
+{
+ struct brcmf_if *ifp = netdev_priv(ndev);
+
+ brcmf_dbg(TRACE, "Enter key_idx %d\n", key_idx);
+
+ if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP))
+ return 0;
+
+ brcmf_dbg(INFO, "Not supported\n");
+
+ return -EOPNOTSUPP;
+}
+
+static void
+brcmf_cfg80211_reconfigure_wep(struct brcmf_if *ifp)
+{
+ s32 err;
+ u8 key_idx;
+ struct brcmf_wsec_key *key;
+ s32 wsec;
+
+ for (key_idx = 0; key_idx < BRCMF_MAX_DEFAULT_KEYS; key_idx++) {
+ key = &ifp->vif->profile.key[key_idx];
+ if ((key->algo == CRYPTO_ALGO_WEP1) ||
+ (key->algo == CRYPTO_ALGO_WEP128))
+ break;
+ }
+ if (key_idx == BRCMF_MAX_DEFAULT_KEYS)
+ return;
+
+ err = send_key_to_dongle(ifp, key);
+ if (err) {
+ brcmf_err("Setting WEP key failed (%d)\n", err);
+ return;
+ }
+ err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
+ if (err) {
+ brcmf_err("get wsec error (%d)\n", err);
+ return;
+ }
+ wsec |= WEP_ENABLED;
+ err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
+ if (err)
+ brcmf_err("set wsec error (%d)\n", err);
+}
+
+static void brcmf_convert_sta_flags(u32 fw_sta_flags, struct station_info *si)
+{
+ struct nl80211_sta_flag_update *sfu;
+
+ brcmf_dbg(TRACE, "flags %08x\n", fw_sta_flags);
+ si->filled |= BIT(NL80211_STA_INFO_STA_FLAGS);
+ sfu = &si->sta_flags;
+ sfu->mask = BIT(NL80211_STA_FLAG_WME) |
+ BIT(NL80211_STA_FLAG_AUTHENTICATED) |
+ BIT(NL80211_STA_FLAG_ASSOCIATED) |
+ BIT(NL80211_STA_FLAG_AUTHORIZED);
+ if (fw_sta_flags & BRCMF_STA_WME)
+ sfu->set |= BIT(NL80211_STA_FLAG_WME);
+ if (fw_sta_flags & BRCMF_STA_AUTHE)
+ sfu->set |= BIT(NL80211_STA_FLAG_AUTHENTICATED);
+ if (fw_sta_flags & BRCMF_STA_ASSOC)
+ sfu->set |= BIT(NL80211_STA_FLAG_ASSOCIATED);
+ if (fw_sta_flags & BRCMF_STA_AUTHO)
+ sfu->set |= BIT(NL80211_STA_FLAG_AUTHORIZED);
+}
+
+static void brcmf_fill_bss_param(struct brcmf_if *ifp, struct station_info *si)
+{
+ struct {
+ __le32 len;
+ struct brcmf_bss_info_le bss_le;
+ } *buf;
+ u16 capability;
+ int err;
+
+ buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
+ if (!buf)
+ return;
+
+ buf->len = cpu_to_le32(WL_BSS_INFO_MAX);
+ err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO, buf,
+ WL_BSS_INFO_MAX);
+ if (err) {
+ brcmf_err("Failed to get bss info (%d)\n", err);
+ goto out_kfree;
+ }
+ si->filled |= BIT(NL80211_STA_INFO_BSS_PARAM);
+ si->bss_param.beacon_interval = le16_to_cpu(buf->bss_le.beacon_period);
+ si->bss_param.dtim_period = buf->bss_le.dtim_period;
+ capability = le16_to_cpu(buf->bss_le.capability);
+ if (capability & IEEE80211_HT_STBC_PARAM_DUAL_CTS_PROT)
+ si->bss_param.flags |= BSS_PARAM_FLAGS_CTS_PROT;
+ if (capability & WLAN_CAPABILITY_SHORT_PREAMBLE)
+ si->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_PREAMBLE;
+ if (capability & WLAN_CAPABILITY_SHORT_SLOT_TIME)
+ si->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_SLOT_TIME;
+
+out_kfree:
+ kfree(buf);
+}
+
+static s32
+brcmf_cfg80211_get_station_ibss(struct brcmf_if *ifp,
+ struct station_info *sinfo)
+{
+ struct brcmf_scb_val_le scbval;
+ struct brcmf_pktcnt_le pktcnt;
+ s32 err;
+ u32 rate;
+ u32 rssi;
+
+ /* Get the current tx rate */
+ err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_RATE, &rate);
+ if (err < 0) {
+ brcmf_err("BRCMF_C_GET_RATE error (%d)\n", err);
+ return err;
+ }
+ sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE);
+ sinfo->txrate.legacy = rate * 5;
+
+ memset(&scbval, 0, sizeof(scbval));
+ err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_RSSI, &scbval,
+ sizeof(scbval));
+ if (err) {
+ brcmf_err("BRCMF_C_GET_RSSI error (%d)\n", err);
+ return err;
+ }
+ rssi = le32_to_cpu(scbval.val);
+ sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL);
+ sinfo->signal = rssi;
+
+ err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_GET_PKTCNTS, &pktcnt,
+ sizeof(pktcnt));
+ if (err) {
+ brcmf_err("BRCMF_C_GET_GET_PKTCNTS error (%d)\n", err);
+ return err;
+ }
+ sinfo->filled |= BIT(NL80211_STA_INFO_RX_PACKETS) |
+ BIT(NL80211_STA_INFO_RX_DROP_MISC) |
+ BIT(NL80211_STA_INFO_TX_PACKETS) |
+ BIT(NL80211_STA_INFO_TX_FAILED);
+ sinfo->rx_packets = le32_to_cpu(pktcnt.rx_good_pkt);
+ sinfo->rx_dropped_misc = le32_to_cpu(pktcnt.rx_bad_pkt);
+ sinfo->tx_packets = le32_to_cpu(pktcnt.tx_good_pkt);
+ sinfo->tx_failed = le32_to_cpu(pktcnt.tx_bad_pkt);
+
+ return 0;
+}
+
+static s32
+brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev,
+ const u8 *mac, struct station_info *sinfo)
+{
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ struct brcmf_scb_val_le scb_val;
+ s32 err = 0;
+ struct brcmf_sta_info_le sta_info_le;
+ u32 sta_flags;
+ u32 is_tdls_peer;
+ s32 total_rssi;
+ s32 count_rssi;
+ int rssi;
+ u32 i;
+
+ brcmf_dbg(TRACE, "Enter, MAC %pM\n", mac);
+ if (!check_vif_up(ifp->vif))
+ return -EIO;
+
+ if (brcmf_is_ibssmode(ifp->vif))
+ return brcmf_cfg80211_get_station_ibss(ifp, sinfo);
+
+ memset(&sta_info_le, 0, sizeof(sta_info_le));
+ memcpy(&sta_info_le, mac, ETH_ALEN);
+ err = brcmf_fil_iovar_data_get(ifp, "tdls_sta_info",
+ &sta_info_le,
+ sizeof(sta_info_le));
+ is_tdls_peer = !err;
+ if (err) {
+ err = brcmf_fil_iovar_data_get(ifp, "sta_info",
+ &sta_info_le,
+ sizeof(sta_info_le));
+ if (err < 0) {
+ brcmf_err("GET STA INFO failed, %d\n", err);
+ goto done;
+ }
+ }
+ brcmf_dbg(TRACE, "version %d\n", le16_to_cpu(sta_info_le.ver));
+ sinfo->filled = BIT(NL80211_STA_INFO_INACTIVE_TIME);
+ sinfo->inactive_time = le32_to_cpu(sta_info_le.idle) * 1000;
+ sta_flags = le32_to_cpu(sta_info_le.flags);
+ brcmf_convert_sta_flags(sta_flags, sinfo);
+ sinfo->sta_flags.mask |= BIT(NL80211_STA_FLAG_TDLS_PEER);
+ if (is_tdls_peer)
+ sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_TDLS_PEER);
+ else
+ sinfo->sta_flags.set &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
+ if (sta_flags & BRCMF_STA_ASSOC) {
+ sinfo->filled |= BIT(NL80211_STA_INFO_CONNECTED_TIME);
+ sinfo->connected_time = le32_to_cpu(sta_info_le.in);
+ brcmf_fill_bss_param(ifp, sinfo);
+ }
+ if (sta_flags & BRCMF_STA_SCBSTATS) {
+ sinfo->filled |= BIT(NL80211_STA_INFO_TX_FAILED);
+ sinfo->tx_failed = le32_to_cpu(sta_info_le.tx_failures);
+ sinfo->filled |= BIT(NL80211_STA_INFO_TX_PACKETS);
+ sinfo->tx_packets = le32_to_cpu(sta_info_le.tx_pkts);
+ sinfo->tx_packets += le32_to_cpu(sta_info_le.tx_mcast_pkts);
+ sinfo->filled |= BIT(NL80211_STA_INFO_RX_PACKETS);
+ sinfo->rx_packets = le32_to_cpu(sta_info_le.rx_ucast_pkts);
+ sinfo->rx_packets += le32_to_cpu(sta_info_le.rx_mcast_pkts);
+ if (sinfo->tx_packets) {
+ sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE);
+ sinfo->txrate.legacy =
+ le32_to_cpu(sta_info_le.tx_rate) / 100;
+ }
+ if (sinfo->rx_packets) {
+ sinfo->filled |= BIT(NL80211_STA_INFO_RX_BITRATE);
+ sinfo->rxrate.legacy =
+ le32_to_cpu(sta_info_le.rx_rate) / 100;
+ }
+ if (le16_to_cpu(sta_info_le.ver) >= 4) {
+ sinfo->filled |= BIT(NL80211_STA_INFO_TX_BYTES);
+ sinfo->tx_bytes = le64_to_cpu(sta_info_le.tx_tot_bytes);
+ sinfo->filled |= BIT(NL80211_STA_INFO_RX_BYTES);
+ sinfo->rx_bytes = le64_to_cpu(sta_info_le.rx_tot_bytes);
+ }
+ total_rssi = 0;
+ count_rssi = 0;
+ for (i = 0; i < BRCMF_ANT_MAX; i++) {
+ if (sta_info_le.rssi[i]) {
+ sinfo->chain_signal_avg[count_rssi] =
+ sta_info_le.rssi[i];
+ sinfo->chain_signal[count_rssi] =
+ sta_info_le.rssi[i];
+ total_rssi += sta_info_le.rssi[i];
+ count_rssi++;
+ }
+ }
+ if (count_rssi) {
+ sinfo->filled |= BIT(NL80211_STA_INFO_CHAIN_SIGNAL);
+ sinfo->chains = count_rssi;
+
+ sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL);
+ total_rssi /= count_rssi;
+ sinfo->signal = total_rssi;
+ } else if (test_bit(BRCMF_VIF_STATUS_CONNECTED,
+ &ifp->vif->sme_state)) {
+ memset(&scb_val, 0, sizeof(scb_val));
+ err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_RSSI,
+ &scb_val, sizeof(scb_val));
+ if (err) {
+ brcmf_err("Could not get rssi (%d)\n", err);
+ goto done;
+ } else {
+ rssi = le32_to_cpu(scb_val.val);
+ sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL);
+ sinfo->signal = rssi;
+ brcmf_dbg(CONN, "RSSI %d dBm\n", rssi);
+ }
+ }
+ }
+done:
+ brcmf_dbg(TRACE, "Exit\n");
+ return err;
+}
+
+static int
+brcmf_cfg80211_dump_station(struct wiphy *wiphy, struct net_device *ndev,
+ int idx, u8 *mac, struct station_info *sinfo)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ s32 err;
+
+ brcmf_dbg(TRACE, "Enter, idx %d\n", idx);
+
+ if (idx == 0) {
+ cfg->assoclist.count = cpu_to_le32(BRCMF_MAX_ASSOCLIST);
+ err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_ASSOCLIST,
+ &cfg->assoclist,
+ sizeof(cfg->assoclist));
+ if (err) {
+ brcmf_err("BRCMF_C_GET_ASSOCLIST unsupported, err=%d\n",
+ err);
+ cfg->assoclist.count = 0;
+ return -EOPNOTSUPP;
+ }
+ }
+ if (idx < le32_to_cpu(cfg->assoclist.count)) {
+ memcpy(mac, cfg->assoclist.mac[idx], ETH_ALEN);
+ return brcmf_cfg80211_get_station(wiphy, ndev, mac, sinfo);
+ }
+ return -ENOENT;
+}
+
+static s32
+brcmf_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *ndev,
+ bool enabled, s32 timeout)
+{
+ s32 pm;
+ s32 err = 0;
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct brcmf_if *ifp = netdev_priv(ndev);
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ /*
+ * Powersave enable/disable request is coming from the
+ * cfg80211 even before the interface is up. In that
+ * scenario, driver will be storing the power save
+ * preference in cfg struct to apply this to
+ * FW later while initializing the dongle
+ */
+ cfg->pwr_save = enabled;
+ if (!check_vif_up(ifp->vif)) {
+
+ brcmf_dbg(INFO, "Device is not ready, storing the value in cfg_info struct\n");
+ goto done;
+ }
+
+ pm = enabled ? dhd_power_mode : PM_OFF;
+ /* Do not enable the power save after assoc if it is a p2p interface */
+ if (ifp->vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) {
+ brcmf_dbg(INFO, "Do not enable power save for P2P clients\n");
+ pm = PM_OFF;
+ }
+ brcmf_dbg(INFO, "power save %s\n", (pm ? "enabled" : "disabled"));
+
+ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, pm);
+ if (err) {
+ if (err == -ENODEV)
+ brcmf_err("net_device is not ready yet\n");
+ else
+ brcmf_err("error (%d)\n", err);
+ }
+done:
+ brcmf_dbg(TRACE, "Exit\n");
+ return err;
+}
+
+static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg,
+ struct brcmf_bss_info_le *bi)
+{
+ struct wiphy *wiphy = cfg_to_wiphy(cfg);
+ struct ieee80211_channel *notify_channel;
+ struct cfg80211_bss *bss;
+ struct ieee80211_supported_band *band;
+ struct brcmu_chan ch;
+ u16 channel;
+ u32 freq;
+ u16 notify_capability;
+ u16 notify_interval;
+ u8 *notify_ie;
+ size_t notify_ielen;
+ s32 notify_signal;
+
+ if (le32_to_cpu(bi->length) > WL_BSS_INFO_MAX) {
+ brcmf_err("Bss info is larger than buffer. Discarding\n");
+ return 0;
+ }
+
+ if (!bi->ctl_ch) {
+ ch.chspec = le16_to_cpu(bi->chanspec);
+ cfg->d11inf.decchspec(&ch);
+ bi->ctl_ch = ch.control_ch_num;
+ }
+ channel = bi->ctl_ch;
+
+ if (channel <= CH_MAX_2G_CHANNEL)
+ band = wiphy->bands[NL80211_BAND_2GHZ];
+ else
+ band = wiphy->bands[NL80211_BAND_5GHZ];
+
+ freq = ieee80211_channel_to_frequency(channel, band->band);
+ notify_channel = ieee80211_get_channel(wiphy, freq);
+
+ notify_capability = le16_to_cpu(bi->capability);
+ notify_interval = le16_to_cpu(bi->beacon_period);
+ notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
+ notify_ielen = le32_to_cpu(bi->ie_length);
+ notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100;
+
+ brcmf_dbg(SCAN, "bssid: %pM\n", bi->BSSID);
+ brcmf_dbg(SCAN, "Channel: %d(%d)\n", channel, freq);
+ brcmf_dbg(SCAN, "Capability: %X\n", notify_capability);
+ brcmf_dbg(SCAN, "Beacon interval: %d\n", notify_interval);
+ brcmf_dbg(SCAN, "Signal: %d\n", notify_signal);
+
+ bss = cfg80211_inform_bss(wiphy, notify_channel,
+ CFG80211_BSS_FTYPE_UNKNOWN,
+ (const u8 *)bi->BSSID,
+ 0, notify_capability,
+ notify_interval, notify_ie,
+ notify_ielen, notify_signal,
+ GFP_KERNEL);
+
+ if (!bss)
+ return -ENOMEM;
+
+ cfg80211_put_bss(wiphy, bss);
+
+ return 0;
+}
+
+static struct brcmf_bss_info_le *
+next_bss_le(struct brcmf_scan_results *list, struct brcmf_bss_info_le *bss)
+{
+ if (bss == NULL)
+ return list->bss_info_le;
+ return (struct brcmf_bss_info_le *)((unsigned long)bss +
+ le32_to_cpu(bss->length));
+}
+
+static s32 brcmf_inform_bss(struct brcmf_cfg80211_info *cfg)
+{
+ struct brcmf_scan_results *bss_list;
+ struct brcmf_bss_info_le *bi = NULL; /* must be initialized */
+ s32 err = 0;
+ int i;
+
+ bss_list = (struct brcmf_scan_results *)cfg->escan_info.escan_buf;
+ if (bss_list->count != 0 &&
+ bss_list->version != BRCMF_BSS_INFO_VERSION) {
+ brcmf_err("Version %d != WL_BSS_INFO_VERSION\n",
+ bss_list->version);
+ return -EOPNOTSUPP;
+ }
+ brcmf_dbg(SCAN, "scanned AP count (%d)\n", bss_list->count);
+ for (i = 0; i < bss_list->count; i++) {
+ bi = next_bss_le(bss_list, bi);
+ err = brcmf_inform_single_bss(cfg, bi);
+ if (err)
+ break;
+ }
+ return err;
+}
+
+static s32 brcmf_inform_ibss(struct brcmf_cfg80211_info *cfg,
+ struct net_device *ndev, const u8 *bssid)
+{
+ struct wiphy *wiphy = cfg_to_wiphy(cfg);
+ struct ieee80211_channel *notify_channel;
+ struct brcmf_bss_info_le *bi = NULL;
+ struct ieee80211_supported_band *band;
+ struct cfg80211_bss *bss;
+ struct brcmu_chan ch;
+ u8 *buf = NULL;
+ s32 err = 0;
+ u32 freq;
+ u16 notify_capability;
+ u16 notify_interval;
+ u8 *notify_ie;
+ size_t notify_ielen;
+ s32 notify_signal;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
+ if (buf == NULL) {
+ err = -ENOMEM;
+ goto CleanUp;
+ }
+
+ *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);
+
+ err = brcmf_fil_cmd_data_get(netdev_priv(ndev), BRCMF_C_GET_BSS_INFO,
+ buf, WL_BSS_INFO_MAX);
+ if (err) {
+ brcmf_err("WLC_GET_BSS_INFO failed: %d\n", err);
+ goto CleanUp;
+ }
+
+ bi = (struct brcmf_bss_info_le *)(buf + 4);
+
+ ch.chspec = le16_to_cpu(bi->chanspec);
+ cfg->d11inf.decchspec(&ch);
+
+ if (ch.band == BRCMU_CHAN_BAND_2G)
+ band = wiphy->bands[NL80211_BAND_2GHZ];
+ else
+ band = wiphy->bands[NL80211_BAND_5GHZ];
+
+ freq = ieee80211_channel_to_frequency(ch.control_ch_num, band->band);
+ cfg->channel = freq;
+ notify_channel = ieee80211_get_channel(wiphy, freq);
+
+ notify_capability = le16_to_cpu(bi->capability);
+ notify_interval = le16_to_cpu(bi->beacon_period);
+ notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
+ notify_ielen = le32_to_cpu(bi->ie_length);
+ notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100;
+
+ brcmf_dbg(SCAN, "channel: %d(%d)\n", ch.control_ch_num, freq);
+ brcmf_dbg(SCAN, "capability: %X\n", notify_capability);
+ brcmf_dbg(SCAN, "beacon interval: %d\n", notify_interval);
+ brcmf_dbg(SCAN, "signal: %d\n", notify_signal);
+
+ bss = cfg80211_inform_bss(wiphy, notify_channel,
+ CFG80211_BSS_FTYPE_UNKNOWN, bssid, 0,
+ notify_capability, notify_interval,
+ notify_ie, notify_ielen, notify_signal,
+ GFP_KERNEL);
+
+ if (!bss) {
+ err = -ENOMEM;
+ goto CleanUp;
+ }
+
+ cfg80211_put_bss(wiphy, bss);
+
+CleanUp:
+
+ kfree(buf);
+
+ brcmf_dbg(TRACE, "Exit\n");
+
+ return err;
+}
+
+static s32 brcmf_update_bss_info(struct brcmf_cfg80211_info *cfg,
+ struct brcmf_if *ifp)
+{
+ struct brcmf_bss_info_le *bi;
+ const struct brcmf_tlv *tim;
+ u16 beacon_interval;
+ u8 dtim_period;
+ size_t ie_len;
+ u8 *ie;
+ s32 err = 0;
+
+ brcmf_dbg(TRACE, "Enter\n");
+ if (brcmf_is_ibssmode(ifp->vif))
+ return err;
+
+ *(__le32 *)cfg->extra_buf = cpu_to_le32(WL_EXTRA_BUF_MAX);
+ err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO,
+ cfg->extra_buf, WL_EXTRA_BUF_MAX);
+ if (err) {
+ brcmf_err("Could not get bss info %d\n", err);
+ goto update_bss_info_out;
+ }
+
+ bi = (struct brcmf_bss_info_le *)(cfg->extra_buf + 4);
+ err = brcmf_inform_single_bss(cfg, bi);
+ if (err)
+ goto update_bss_info_out;
+
+ ie = ((u8 *)bi) + le16_to_cpu(bi->ie_offset);
+ ie_len = le32_to_cpu(bi->ie_length);
+ beacon_interval = le16_to_cpu(bi->beacon_period);
+
+ tim = brcmf_parse_tlvs(ie, ie_len, WLAN_EID_TIM);
+ if (tim)
+ dtim_period = tim->data[1];
+ else {
+ /*
+ * active scan was done so we could not get dtim
+ * information out of probe response.
+ * so we speficially query dtim information to dongle.
+ */
+ u32 var;
+ err = brcmf_fil_iovar_int_get(ifp, "dtim_assoc", &var);
+ if (err) {
+ brcmf_err("wl dtim_assoc failed (%d)\n", err);
+ goto update_bss_info_out;
+ }
+ dtim_period = (u8)var;
+ }
+
+update_bss_info_out:
+ brcmf_dbg(TRACE, "Exit");
+ return err;
+}
+
+void brcmf_abort_scanning(struct brcmf_cfg80211_info *cfg)
+{
+ struct escan_info *escan = &cfg->escan_info;
+
+ set_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status);
+ if (cfg->internal_escan || cfg->scan_request) {
+ escan->escan_state = WL_ESCAN_STATE_IDLE;
+ brcmf_notify_escan_complete(cfg, escan->ifp, true, true);
+ }
+ clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
+ clear_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status);
+}
+
+static void brcmf_cfg80211_escan_timeout_worker(struct work_struct *work)
+{
+ struct brcmf_cfg80211_info *cfg =
+ container_of(work, struct brcmf_cfg80211_info,
+ escan_timeout_work);
+
+ brcmf_inform_bss(cfg);
+ brcmf_notify_escan_complete(cfg, cfg->escan_info.ifp, true, true);
+}
+
+static void brcmf_escan_timeout(unsigned long data)
+{
+ struct brcmf_cfg80211_info *cfg =
+ (struct brcmf_cfg80211_info *)data;
+
+ if (cfg->internal_escan || cfg->scan_request) {
+ brcmf_err("timer expired\n");
+ schedule_work(&cfg->escan_timeout_work);
+ }
+}
+
+static s32
+brcmf_compare_update_same_bss(struct brcmf_cfg80211_info *cfg,
+ struct brcmf_bss_info_le *bss,
+ struct brcmf_bss_info_le *bss_info_le)
+{
+ struct brcmu_chan ch_bss, ch_bss_info_le;
+
+ ch_bss.chspec = le16_to_cpu(bss->chanspec);
+ cfg->d11inf.decchspec(&ch_bss);
+ ch_bss_info_le.chspec = le16_to_cpu(bss_info_le->chanspec);
+ cfg->d11inf.decchspec(&ch_bss_info_le);
+
+ if (!memcmp(&bss_info_le->BSSID, &bss->BSSID, ETH_ALEN) &&
+ ch_bss.band == ch_bss_info_le.band &&
+ bss_info_le->SSID_len == bss->SSID_len &&
+ !memcmp(bss_info_le->SSID, bss->SSID, bss_info_le->SSID_len)) {
+
+ /* Merge RSSI if any new or previous value is 0 */
+ if (bss_info_le->RSSI == 0) {
+ bss_info_le->RSSI = bss->RSSI;
+ bss_info_le->flags = (bss_info_le->flags &
+ ~BRCMF_BSS_RSSI_ON_CHANNEL) |
+ (bss->flags & BRCMF_BSS_RSSI_ON_CHANNEL);
+ } else if (bss->RSSI == 0) {
+ bss->RSSI = bss_info_le->RSSI;
+ bss->flags = (bss->flags & ~BRCMF_BSS_RSSI_ON_CHANNEL) |
+ (bss_info_le->flags & BRCMF_BSS_RSSI_ON_CHANNEL);
+ }
+
+ /* do not allow beacon data to update
+ * the data recd from a probe response
+ */
+ if (((bss->flags & BRCMF_BSS_FROM_BEACON) == 0) &&
+ (bss_info_le->flags & BRCMF_BSS_FROM_BEACON))
+ return -1;
+
+ if ((bss->flags & BRCMF_BSS_RSSI_ON_CHANNEL) ==
+ (bss_info_le->flags & BRCMF_BSS_RSSI_ON_CHANNEL)) {
+ s16 bss_rssi = le16_to_cpu(bss->RSSI);
+ s16 bss_info_rssi = le16_to_cpu(bss_info_le->RSSI);
+
+ /* preserve max RSSI if the measurements are
+ * both on-channel or both off-channel
+ */
+ if (bss_rssi > bss_info_rssi)
+ bss_info_le->RSSI = bss->RSSI;
+ } else if ((bss->flags & BRCMF_BSS_RSSI_ON_CHANNEL) &&
+ (bss_info_le->flags & BRCMF_BSS_RSSI_ON_CHANNEL) == 0) {
+ /* preserve the on-channel rssi measurement
+ * if the new measurement is off channel
+ */
+ bss_info_le->RSSI = bss->RSSI;
+ bss_info_le->flags |= BRCMF_BSS_RSSI_ON_CHANNEL;
+ }
+ return 1;
+ }
+ return 0;
+}
+
+static s32 brcmf_patch_null_ssid(struct brcmf_bss_info_le *bss_info_le,
+ u32 *p_bi_length)
+{
+ u8 *ie, *ssidie;
+ u32 ie_len;
+
+ if (!bss_info_le || !p_bi_length) {
+ brcmf_err("Invalid input parameter, bss_info_le %p p_bi_length %p",
+ bss_info_le, p_bi_length);
+ return -1;
+ }
+
+ /* patch hidden ssid ie */
+ ie = ((u8 *)bss_info_le) + le16_to_cpu(bss_info_le->ie_offset);
+ ie_len = le32_to_cpu(bss_info_le->ie_length);
+
+ ssidie = (u8 *)cfg80211_find_ie(WLAN_EID_SSID, ie, ie_len);
+ if (ssidie) {
+ if (ssidie[1] != bss_info_le->SSID_len) {
+ brcmf_dbg(SCAN, "ssidie length %d is not equal to ssid length %d\n",
+ ssidie[1], bss_info_le->SSID_len);
+ memmove(ssidie + bss_info_le->SSID_len + 2,
+ (ssidie + 2) + ssidie[1],
+ ie_len - (ssidie + 2 + ssidie[1] - ie));
+ memcpy(ssidie + 2, bss_info_le->SSID, bss_info_le->SSID_len);
+ bss_info_le->ie_length =
+ cpu_to_le32(ie_len + bss_info_le->SSID_len - ssidie[1]);
+ *p_bi_length = *p_bi_length - ssidie[1] + bss_info_le->SSID_len;
+ bss_info_le->length = cpu_to_le32(*p_bi_length);
+ ssidie[1] = bss_info_le->SSID_len;
+ } else if (ssidie[2] == '\0') {
+ /* ssidie[1] is correct, but ssid is empty */
+ brcmf_dbg(SCAN, "ssidie length %d, ssidie data empty, actual ssid %s\n",
+ ssidie[1], bss_info_le->SSID);
+ memcpy(ssidie + 2, bss_info_le->SSID, bss_info_le->SSID_len);
+ }
+ }
+
+ return 0;
+}
+
+#define MAX_TARGET_SCAN_COUNT 16
+#define MAX_SCAN_RETRY_COUNT 3
+
+static s32 brcmf_process_invalid_rssi(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp)
+{
+ struct cfg80211_scan_request *request = NULL;
+ struct cfg80211_ssid ssid[MAX_TARGET_SCAN_COUNT];
+ struct ieee80211_channel channel[MAX_TARGET_SCAN_COUNT];
+ struct brcmf_bss_info_le *bss = NULL;
+ struct brcmf_scan_results *list;
+ u32 i, j, ssid_idx = 0, chan_idx = 0;
+ s32 err = 0;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ memset(&ssid, 0x00, sizeof(ssid));
+ ssid_idx = 0;
+ chan_idx = 0 ;
+
+ list = (struct brcmf_scan_results *)cfg->escan_info.escan_buf;
+ for (i = 0; i < list->count; i++) {
+ bss = bss ? (struct brcmf_bss_info_le *)
+ ((unsigned char *)bss +
+ le32_to_cpu(bss->length)) : list->bss_info_le;
+ if (bss->RSSI == 0) {
+ /* record the ssid and chanspec for later target scan */
+ if (ssid_idx < MAX_TARGET_SCAN_COUNT) {
+ ssid[ssid_idx].ssid_len = min_t(u8, bss->SSID_len, IEEE80211_MAX_SSID_LEN);
+ memcpy(ssid[ssid_idx].ssid, bss->SSID, ssid[ssid_idx].ssid_len);
+ brcmf_dbg(INFO, "target scan ssid (%s)\n", ssid[ssid_idx].ssid);
+ ssid_idx++;
+ }
+
+ if (chan_idx < MAX_TARGET_SCAN_COUNT) {
+ struct brcmu_chan ch;
+ enum nl80211_band band;
+ u32 center_freq =0;
+
+ ch.chspec = le16_to_cpu(bss->chanspec);
+ cfg->d11inf.decchspec(&ch);
+
+ if (ch.band == BRCMU_CHAN_BAND_2G)
+ band = NL80211_BAND_2GHZ;
+ else
+ band = NL80211_BAND_5GHZ;
+
+ center_freq = ieee80211_channel_to_frequency(ch.control_ch_num, band);
+ for (j = 0; j < chan_idx; j++) {
+ if (center_freq == channel[j].center_freq) {
+ break;
+ }
+ }
+ if (j == chan_idx) {
+ brcmf_dbg(INFO, "target scan channel (%d)\n", ch.control_ch_num);
+ channel[chan_idx].center_freq = center_freq;
+ channel[chan_idx].band = band;
+ channel[chan_idx].flags |= IEEE80211_CHAN_NO_HT40;
+ chan_idx++;
+ }
+ }
+ }
+ }
+
+ if (ssid_idx == 0 && chan_idx == 0) {
+ brcmf_dbg(SCAN, "all rssi are valid, no target scan issued!\n");
+ err = -1;
+ goto out_err;
+ }
+
+ request = kzalloc(sizeof(*request) + sizeof(*request->channels) * chan_idx, GFP_KERNEL);
+ if (!request) {
+ brcmf_err("No memory\n");
+ err = -2;
+ goto out_err;
+ }
+
+ request->ssids = &ssid[0];
+ request->n_ssids = ssid_idx;
+
+ for (i =0; i < chan_idx; i++) {
+ request->channels[i] = &channel[i];
+ }
+ request->n_channels = chan_idx;
+
+ set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
+ cfg->escan_info.run = brcmf_run_escan;
+ err = brcmf_do_escan(ifp, request);
+ if (err) {
+ clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
+ goto out_err;
+ }
+
+out_err:
+ if (request)
+ kfree(request);
+ return err;
+}
+
+static s32
+brcmf_cfg80211_escan_handler(struct brcmf_if *ifp,
+ const struct brcmf_event_msg *e, void *data)
+{
+ struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
+ s32 status;
+ struct brcmf_escan_result_le *escan_result_le;
+ struct brcmf_bss_info_le *bss_info_le;
+ struct brcmf_bss_info_le *bss = NULL;
+ u32 bi_length;
+ struct brcmf_scan_results *list;
+ u32 i;
+ bool aborted;
+ s32 ret;
+ u32 cur_len;
+
+ status = e->status;
+
+ if (status == BRCMF_E_STATUS_ABORT)
+ goto exit;
+
+ if (!test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
+ brcmf_err("scan not ready, bsscfgidx=%d\n", ifp->bsscfgidx);
+ return -EPERM;
+ }
+
+ if (status == BRCMF_E_STATUS_PARTIAL) {
+ brcmf_dbg(SCAN, "ESCAN Partial result\n");
+ escan_result_le = (struct brcmf_escan_result_le *) data;
+ if (!escan_result_le) {
+ brcmf_err("Invalid escan result (NULL pointer)\n");
+ goto exit;
+ }
+ if (le16_to_cpu(escan_result_le->bss_count) != 1) {
+ brcmf_err("Invalid bss_count %d: ignoring\n",
+ escan_result_le->bss_count);
+ goto exit;
+ }
+ bss_info_le = &escan_result_le->bss_info_le;
+
+ if (brcmf_p2p_scan_finding_common_channel(cfg, bss_info_le))
+ goto exit;
+
+ if (!cfg->internal_escan && !cfg->scan_request) {
+ brcmf_dbg(SCAN, "result without cfg80211 request\n");
+ goto exit;
+ }
+
+ bi_length = le32_to_cpu(bss_info_le->length);
+ if (bi_length != (le32_to_cpu(escan_result_le->buflen) -
+ WL_ESCAN_RESULTS_FIXED_SIZE)) {
+ brcmf_err("Invalid bss_info length %d: ignoring\n",
+ bi_length);
+ goto exit;
+ }
+
+ if (!(cfg_to_wiphy(cfg)->interface_modes &
+ BIT(NL80211_IFTYPE_ADHOC))) {
+ if (le16_to_cpu(bss_info_le->capability) &
+ WLAN_CAPABILITY_IBSS) {
+ brcmf_err("Ignoring IBSS result\n");
+ goto exit;
+ }
+ }
+
+ list = (struct brcmf_scan_results *)
+ cfg->escan_info.escan_buf;
+ if (bi_length > BRCMF_ESCAN_BUF_SIZE - list->buflen) {
+ brcmf_err("Buffer is too small: ignoring\n");
+ goto exit;
+ }
+
+ brcmf_patch_null_ssid(bss_info_le, &bi_length);
+
+ cur_len = WL_ESCAN_RESULTS_FIXED_SIZE;
+ for (i = 0; i < list->count; i++) {
+ bss = bss ? (struct brcmf_bss_info_le *)
+ ((unsigned char *)bss +
+ le32_to_cpu(bss->length)) : list->bss_info_le;
+
+ ret = brcmf_compare_update_same_bss(cfg, bss, bss_info_le);
+ if (ret < 0)
+ /* skip this bss update */
+ goto exit;
+
+ if (ret > 0) {
+ /* found match record */
+ u32 prev_len = le32_to_cpu(bss->length);
+ if (prev_len != bi_length) {
+ brcmf_dbg(SCAN, "ssid %s, bss %x, bi %x\n",
+ bss->SSID, bss->flags, bss_info_le->flags);
+ brcmf_dbg(SCAN, "bss info replacement occured(bss:%d->bi:%d)\n",
+ bss->ie_length, bss_info_le->ie_length);
+
+ if (list->buflen - prev_len + bi_length > BRCMF_ESCAN_BUF_SIZE) {
+ brcmf_err("Buffer is too small:"
+ "keep the previous result of this AP\n");
+ /* Only update RSSI */
+ bss->RSSI = bss_info_le->RSSI;
+ bss->flags |=
+ (bss_info_le->flags & BRCMF_BSS_RSSI_ON_CHANNEL);
+ goto exit;
+ }
+
+ if (i < list->count - 1) {
+ /* memory copy required by this case only */
+ memmove((u8 *)bss + bi_length,
+ (u8 *)bss + prev_len,
+ list->buflen - cur_len - prev_len);
+ }
+ list->buflen -= prev_len;
+ list->buflen += bi_length;
+ }
+ list->version = le32_to_cpu(bss_info_le->version);
+ memcpy(bss, bss_info_le, bi_length);
+ goto exit;
+ }
+ cur_len += le32_to_cpu(bss->length);
+ }
+ memcpy(&cfg->escan_info.escan_buf[list->buflen], bss_info_le,
+ bi_length);
+ list->version = le32_to_cpu(bss_info_le->version);
+ list->buflen += bi_length;
+ list->count++;
+ } else {
+ if (cfg->scan_request && (cfg->scan_retry_cnt < MAX_SCAN_RETRY_COUNT)) {
+ brcmf_dbg(SCAN, "Scan done, checking invalid rssi in scan results\n");
+ if (brcmf_process_invalid_rssi(cfg, ifp) == 0) {
+ cfg->scan_retry_cnt++;
+ brcmf_dbg(INFO, "Scanning %d time due to invalid rssi\n",
+ cfg->scan_retry_cnt);
+ goto exit;
+ }
+ }
+
+ cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
+ if (brcmf_p2p_scan_finding_common_channel(cfg, NULL))
+ goto exit;
+ if (cfg->internal_escan || cfg->scan_request) {
+ brcmf_inform_bss(cfg);
+ aborted = status != BRCMF_E_STATUS_SUCCESS;
+ brcmf_notify_escan_complete(cfg, ifp, aborted, false);
+ } else
+ brcmf_dbg(SCAN, "Ignored scan complete result 0x%x\n",
+ status);
+ }
+exit:
+ return 0;
+}
+
+static void brcmf_init_escan(struct brcmf_cfg80211_info *cfg)
+{
+ brcmf_fweh_register(cfg->pub, BRCMF_E_ESCAN_RESULT,
+ brcmf_cfg80211_escan_handler);
+ cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
+ /* Init scan_timeout timer */
+ init_timer(&cfg->escan_timeout);
+ cfg->escan_timeout.data = (unsigned long) cfg;
+ cfg->escan_timeout.function = brcmf_escan_timeout;
+ INIT_WORK(&cfg->escan_timeout_work,
+ brcmf_cfg80211_escan_timeout_worker);
+}
+
+static struct cfg80211_scan_request *
+brcmf_alloc_internal_escan_request(struct wiphy *wiphy, u32 n_netinfo) {
+ struct cfg80211_scan_request *req;
+ size_t req_size;
+
+ req_size = sizeof(*req) +
+ n_netinfo * sizeof(req->channels[0]) +
+ n_netinfo * sizeof(*req->ssids);
+
+ req = kzalloc(req_size, GFP_KERNEL);
+ if (req) {
+ req->wiphy = wiphy;
+ req->ssids = (void *)(&req->channels[0]) +
+ n_netinfo * sizeof(req->channels[0]);
+ }
+ return req;
+}
+
+static int brcmf_internal_escan_add_info(struct cfg80211_scan_request *req,
+ u8 *ssid, u8 ssid_len, u8 channel)
+{
+ struct ieee80211_channel *chan;
+ enum nl80211_band band;
+ int freq, i;
+
+ if (channel <= CH_MAX_2G_CHANNEL)
+ band = NL80211_BAND_2GHZ;
+ else
+ band = NL80211_BAND_5GHZ;
+
+ freq = ieee80211_channel_to_frequency(channel, band);
+ if (!freq)
+ return -EINVAL;
+
+ chan = ieee80211_get_channel(req->wiphy, freq);
+ if (!chan)
+ return -EINVAL;
+
+ for (i = 0; i < req->n_channels; i++) {
+ if (req->channels[i] == chan)
+ break;
+ }
+ if (i == req->n_channels)
+ req->channels[req->n_channels++] = chan;
+
+ for (i = 0; i < req->n_ssids; i++) {
+ if (req->ssids[i].ssid_len == ssid_len &&
+ !memcmp(req->ssids[i].ssid, ssid, ssid_len))
+ break;
+ }
+ if (i == req->n_ssids) {
+ memcpy(req->ssids[req->n_ssids].ssid, ssid, ssid_len);
+ req->ssids[req->n_ssids++].ssid_len = ssid_len;
+ }
+ return 0;
+}
+
+static int brcmf_start_internal_escan(struct brcmf_if *ifp,
+ struct cfg80211_scan_request *request)
+{
+ struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
+ int err;
+
+ if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
+ /* Abort any on-going scan */
+ brcmf_abort_scanning(cfg);
+ }
+
+ set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
+ cfg->escan_info.run = brcmf_run_escan;
+ err = brcmf_do_escan(ifp, request);
+ if (err) {
+ clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
+ return err;
+ }
+ cfg->internal_escan = true;
+ return 0;
+}
+
+static struct brcmf_pno_net_info_le *
+brcmf_get_netinfo_array(struct brcmf_pno_scanresults_le *pfn_v1)
+{
+ struct brcmf_pno_scanresults_v2_le *pfn_v2;
+ struct brcmf_pno_net_info_le *netinfo;
+
+ switch (pfn_v1->version) {
+ default:
+ WARN_ON(1);
+ /* fall-thru */
+ case cpu_to_le32(1):
+ netinfo = (struct brcmf_pno_net_info_le *)(pfn_v1 + 1);
+ break;
+ case cpu_to_le32(2):
+ pfn_v2 = (struct brcmf_pno_scanresults_v2_le *)pfn_v1;
+ netinfo = (struct brcmf_pno_net_info_le *)(pfn_v2 + 1);
+ break;
+ }
+
+ return netinfo;
+}
+
+/* PFN result doesn't have all the info which are required by the supplicant
+ * (For e.g IEs) Do a target Escan so that sched scan results are reported
+ * via wl_inform_single_bss in the required format. Escan does require the
+ * scan request in the form of cfg80211_scan_request. For timebeing, create
+ * cfg80211_scan_request one out of the received PNO event.
+ */
+static s32
+brcmf_notify_sched_scan_results(struct brcmf_if *ifp,
+ const struct brcmf_event_msg *e, void *data)
+{
+ struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
+ struct brcmf_pno_net_info_le *netinfo, *netinfo_start;
+ struct cfg80211_scan_request *request = NULL;
+ struct wiphy *wiphy = cfg_to_wiphy(cfg);
+ int i, err = 0;
+ struct brcmf_pno_scanresults_le *pfn_result;
+ u32 result_count;
+ u32 status;
+ u32 datalen;
+
+ brcmf_dbg(SCAN, "Enter\n");
+
+ if (e->datalen < (sizeof(*pfn_result) + sizeof(*netinfo))) {
+ brcmf_dbg(SCAN, "Event data to small. Ignore\n");
+ return 0;
+ }
+
+ if (e->event_code == BRCMF_E_PFN_NET_LOST) {
+ brcmf_dbg(SCAN, "PFN NET LOST event. Do Nothing\n");
+ return 0;
+ }
+
+ pfn_result = (struct brcmf_pno_scanresults_le *)data;
+ result_count = le32_to_cpu(pfn_result->count);
+ status = le32_to_cpu(pfn_result->status);
+
+ /* PFN event is limited to fit 512 bytes so we may get
+ * multiple NET_FOUND events. For now place a warning here.
+ */
+ WARN_ON(status != BRCMF_PNO_SCAN_COMPLETE);
+ brcmf_dbg(SCAN, "PFN NET FOUND event. count: %d\n", result_count);
+ if (!result_count) {
+ brcmf_err("FALSE PNO Event. (pfn_count == 0)\n");
+ goto out_err;
+ }
+
+ netinfo_start = brcmf_get_netinfo_array(pfn_result);
+ datalen = e->datalen - ((void *)netinfo_start - (void *)pfn_result);
+ if (datalen < result_count * sizeof(*netinfo)) {
+ brcmf_err("insufficient event data\n");
+ goto out_err;
+ }
+
+ request = brcmf_alloc_internal_escan_request(wiphy,
+ result_count);
+ if (!request) {
+ err = -ENOMEM;
+ goto out_err;
+ }
+
+ for (i = 0; i < result_count; i++) {
+ netinfo = &netinfo_start[i];
+
+ if (netinfo->SSID_len > IEEE80211_MAX_SSID_LEN)
+ netinfo->SSID_len = IEEE80211_MAX_SSID_LEN;
+ brcmf_dbg(SCAN, "SSID:%.32s Channel:%d\n",
+ netinfo->SSID, netinfo->channel);
+ err = brcmf_internal_escan_add_info(request,
+ netinfo->SSID,
+ netinfo->SSID_len,
+ netinfo->channel);
+ if (err)
+ goto out_err;
+ }
+
+ err = brcmf_start_internal_escan(ifp, request);
+ if (!err)
+ goto free_req;
+
+out_err:
+ cfg80211_sched_scan_stopped(wiphy, 0);
+free_req:
+ kfree(request);
+ return err;
+}
+
+static int
+brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy,
+ struct net_device *ndev,
+ struct cfg80211_sched_scan_request *req)
+{
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
+
+ brcmf_dbg(SCAN, "Enter n_match_sets:%d n_ssids:%d\n",
+ req->n_match_sets, req->n_ssids);
+
+ if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) {
+ brcmf_err("Scanning suppressed: status (%lu)\n",
+ cfg->scan_status);
+ return -EAGAIN;
+ }
+
+ if (req->n_match_sets <= 0) {
+ brcmf_dbg(SCAN, "invalid number of matchsets specified: %d\n",
+ req->n_match_sets);
+ return -EINVAL;
+ }
+
+ return brcmf_pno_start_sched_scan(ifp, req);
+}
+
+static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy,
+ struct net_device *ndev, u64 reqid)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct brcmf_if *ifp = netdev_priv(ndev);
+
+ brcmf_dbg(SCAN, "enter\n");
+ brcmf_pno_clean(ifp);
+ if (cfg->internal_escan)
+ brcmf_notify_escan_complete(cfg, ifp, true, true);
+ return 0;
+}
+
+static __always_inline void brcmf_delay(u32 ms)
+{
+ if (ms < 1000 / HZ) {
+ cond_resched();
+ mdelay(ms);
+ } else {
+ msleep(ms);
+ }
+}
+
+static s32 brcmf_config_wowl_pattern(struct brcmf_if *ifp, u8 cmd[4],
+ u8 *pattern, u32 patternsize, u8 *mask,
+ u32 packet_offset)
+{
+ struct brcmf_fil_wowl_pattern_le *filter;
+ u32 masksize;
+ u32 patternoffset;
+ u8 *buf;
+ u32 bufsize;
+ s32 ret;
+
+ masksize = (patternsize + 7) / 8;
+ patternoffset = sizeof(*filter) - sizeof(filter->cmd) + masksize;
+
+ bufsize = sizeof(*filter) + patternsize + masksize;
+ buf = kzalloc(bufsize, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+ filter = (struct brcmf_fil_wowl_pattern_le *)buf;
+
+ memcpy(filter->cmd, cmd, 4);
+ filter->masksize = cpu_to_le32(masksize);
+ filter->offset = cpu_to_le32(packet_offset);
+ filter->patternoffset = cpu_to_le32(patternoffset);
+ filter->patternsize = cpu_to_le32(patternsize);
+ filter->type = cpu_to_le32(BRCMF_WOWL_PATTERN_TYPE_BITMAP);
+
+ if ((mask) && (masksize))
+ memcpy(buf + sizeof(*filter), mask, masksize);
+ if ((pattern) && (patternsize))
+ memcpy(buf + sizeof(*filter) + masksize, pattern, patternsize);
+
+ ret = brcmf_fil_iovar_data_set(ifp, "wowl_pattern", buf, bufsize);
+
+ kfree(buf);
+ return ret;
+}
+
+static s32
+brcmf_wowl_nd_results(struct brcmf_if *ifp, const struct brcmf_event_msg *e,
+ void *data)
+{
+ struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
+ struct brcmf_pno_scanresults_le *pfn_result;
+ struct brcmf_pno_net_info_le *netinfo;
+
+ brcmf_dbg(SCAN, "Enter\n");
+
+ if (e->datalen < (sizeof(*pfn_result) + sizeof(*netinfo))) {
+ brcmf_dbg(SCAN, "Event data to small. Ignore\n");
+ return 0;
+ }
+
+ pfn_result = (struct brcmf_pno_scanresults_le *)data;
+
+ if (e->event_code == BRCMF_E_PFN_NET_LOST) {
+ brcmf_dbg(SCAN, "PFN NET LOST event. Ignore\n");
+ return 0;
+ }
+
+ if (le32_to_cpu(pfn_result->count) < 1) {
+ brcmf_err("Invalid result count, expected 1 (%d)\n",
+ le32_to_cpu(pfn_result->count));
+ return -EINVAL;
+ }
+
+ netinfo = brcmf_get_netinfo_array(pfn_result);
+ memcpy(cfg->wowl.nd->ssid.ssid, netinfo->SSID, netinfo->SSID_len);
+ cfg->wowl.nd->ssid.ssid_len = netinfo->SSID_len;
+ cfg->wowl.nd->n_channels = 1;
+ cfg->wowl.nd->channels[0] =
+ ieee80211_channel_to_frequency(netinfo->channel,
+ netinfo->channel <= CH_MAX_2G_CHANNEL ?
+ NL80211_BAND_2GHZ : NL80211_BAND_5GHZ);
+ cfg->wowl.nd_info->n_matches = 1;
+ cfg->wowl.nd_info->matches[0] = cfg->wowl.nd;
+
+ /* Inform (the resume task) that the net detect information was recvd */
+ cfg->wowl.nd_data_completed = true;
+ wake_up(&cfg->wowl.nd_data_wait);
+
+ return 0;
+}
+
+/**
+ * Pattern matching filter. Specifies an offset within received packets to
+ * start matching, the pattern to match, the size of the pattern, and a bitmask
+ * that indicates which bits within the pattern should be matched.
+ */
+typedef struct wl_pkt_filter_pattern {
+ __le32 offset; /**< Offset within received packet to start pattern matching.
+ * Offset '0' is the first byte of the ethernet header.
+ */
+ __le32 size_bytes; /**< Size of the pattern. Bitmask must be the same size. */
+ u8 mask_and_pattern[1]; /**< Variable length mask and pattern data. mask starts
+ * at offset 0. Pattern immediately follows mask. for
+ * secured pattern, put the descrypter pointer to the
+ * beginning, mask and pattern postponed correspondingly
+ */
+} wl_pkt_filter_pattern_t;
+
+/** A pattern list is a numerically specified list of modified pattern structures. */
+typedef struct wl_pkt_filter_pattern_listel {
+ __le16 rel_offs; /**< Offset to begin match (relative to 'base' below) */
+ __le16 base_offs; /**< Base for offset (defined below) */
+ __le16 size_bytes; /**< Size of mask/pattern */
+ __le16 match_flags; /**< Addition flags controlling the match */
+ u8 mask_and_data[1]; /**< Variable length mask followed by data, each size_bytes */
+} wl_pkt_filter_pattern_listel_t;
+
+typedef struct wl_pkt_filter_pattern_list {
+ u8 list_cnt; /**< Number of elements in the list */
+ u8 PAD1[1]; /**< Reserved (possible version: reserved) */
+ __le16 totsize; /**< Total size of this pattern list (includes this struct) */
+ wl_pkt_filter_pattern_listel_t patterns[1]; /**< Variable number of list elements */
+} wl_pkt_filter_pattern_list_t;
+
+typedef struct wl_apf_program {
+ __le16 version;
+ __le16 instr_len; /* number of instruction blocks */
+ __le32 inst_ts; /* program installation timestamp */
+ u8 instrs[1]; /* variable length instructions */
+} wl_apf_program_t;
+
+typedef struct wl_pkt_filter_pattern_timeout {
+ __le32 offset; /* Offset within received packet to start pattern matching.
+ * Offset '0' is the first byte of the ethernet header.
+ */
+ __le32 size_bytes; /* Size of the pattern. Bitmask must be the same size. */
+ __le32 timeout; /* Timeout(seconds) */
+ u8 mask_and_pattern[1]; /* Variable length mask and pattern data.
+ * mask starts at offset 0. Pattern
+ * immediately follows mask.
+ */
+} wl_pkt_filter_pattern_timeout_t;
+
+/** IOVAR "pkt_filter_add" parameter. Used to install packet filters. */
+typedef struct wl_pkt_filter {
+ __le32 id; /**< Unique filter id, specified by app. */
+ __le32 type; /**< Filter type (WL_PKT_FILTER_TYPE_xxx). */
+ __le32 negate_match; /**< Negate the result of filter matches */
+ union { /* Filter definitions */
+ wl_pkt_filter_pattern_t pattern; /**< Pattern matching filter */
+ wl_pkt_filter_pattern_list_t patlist; /**< List of patterns to match */
+ wl_apf_program_t apf_program; /* apf program */
+ wl_pkt_filter_pattern_timeout_t pattern_timeout; /* Pattern timeout event filter */
+ } u;
+} wl_pkt_filter_t;
+
+#define WL_PKT_FILTER_FIXED_LEN offsetof(wl_pkt_filter_t, u)
+#define WL_PKT_FILTER_PATTERN_FIXED_LEN offsetof(wl_pkt_filter_pattern_t, mask_and_pattern)
+#define WL_PKT_FILTER_PATTERN_LIST_FIXED_LEN offsetof(wl_pkt_filter_pattern_list_t, patterns)
+#define WL_PKT_FILTER_PATTERN_LISTEL_FIXED_LEN \
+ offsetof(wl_pkt_filter_pattern_listel_t, mask_and_data)
+#define WL_PKT_FILTER_PATTERN_TIMEOUT_FIXED_LEN \
+ offsetof(wl_pkt_filter_pattern_timeout_t, mask_and_pattern)
+
+/* IOVAR "pkt_filter_enable" parameter. */
+typedef struct wl_pkt_filter_enable {
+ __le32 id; /* Unique filter id */
+ __le32 enable; /* Enable/disable bool */
+} wl_pkt_filter_enable_t;
+
+
+/* IOVAR "pkt_filter_stats" parameter. Used to retrieve debug statistics. */
+typedef struct wl_pkt_filter_stats {
+ __le32 num_pkts_matched; /* # filter matches for specified filter id */
+ __le32 num_pkts_forwarded; /* # packets fwded from dongle to host for all filters */
+ __le32 num_pkts_discarded; /* # packets discarded by dongle for all filters */
+} wl_pkt_filter_stats_t;
+
+#define WL_WOWLAN_MAX_PATTERNS 8
+#define WL_WOWLAN_MIN_PATTERN_LEN 1
+#define WL_WOWLAN_MAX_PATTERN_LEN 255
+#define WL_WOWLAN_PKT_FILTER_ID_FIRST 201
+#define WL_WOWLAN_PKT_FILTER_ID_LAST (WL_WOWLAN_PKT_FILTER_ID_FIRST + \
+ WL_WOWLAN_MAX_PATTERNS - 1)
+
+/* Packet filter types. Currently, only pattern matching is supported. */
+typedef enum wl_pkt_filter_type {
+ WL_PKT_FILTER_TYPE_PATTERN_MATCH=0, /* Pattern matching filter */
+ WL_PKT_FILTER_TYPE_MAGIC_PATTERN_MATCH=1, /* Magic packet match */
+ WL_PKT_FILTER_TYPE_PATTERN_LIST_MATCH=2, /* A pattern list (match all to match filter) */
+ WL_PKT_FILTER_TYPE_ENCRYPTED_PATTERN_MATCH=3, /* SECURE WOWL magic / net pattern match */
+ WL_PKT_FILTER_TYPE_APF_MATCH=4, /* Android packet filter match */
+ WL_PKT_FILTER_TYPE_PATTERN_MATCH_TIMEOUT=5, /* Pattern matching filter with timeout event */
+} wl_pkt_filter_type_t;
+
+static s32 brcmf_add_enable_pktfilter(struct brcmf_if *ifp,
+ wl_pkt_filter_t * pkt_filterp, u32 pkt_filter_size,
+ u32 id, u32 type, u32 negate_match, u32 offset, u8 *mask, u8 *pattern, u32 pattern_size)
+{
+ wl_pkt_filter_t *local_pkt_filterp = NULL;
+ u32 local_pkt_filter_size = WL_PKT_FILTER_FIXED_LEN +
+ WL_PKT_FILTER_PATTERN_FIXED_LEN + (2 * pattern_size);
+ wl_pkt_filter_enable_t pkt_filter_enable;
+ s32 err = 0;
+
+ if (!pkt_filterp) {
+ local_pkt_filterp = (wl_pkt_filter_t *) kzalloc(local_pkt_filter_size, GFP_KERNEL);
+ if (local_pkt_filterp == NULL) {
+ brcmf_err("Error allocating buffer for pkt filters\n");
+ return -ENOMEM;
+ }
+
+ local_pkt_filterp->id = cpu_to_le32(id);
+ local_pkt_filterp->type = cpu_to_le32(type);
+ local_pkt_filterp->negate_match = cpu_to_le32(negate_match);
+
+ local_pkt_filterp->u.pattern.offset = cpu_to_le32(offset);
+ local_pkt_filterp->u.pattern.size_bytes = cpu_to_le32(pattern_size);
+
+ memcpy(&local_pkt_filterp->u.pattern.mask_and_pattern[0], mask, pattern_size);
+ memcpy(&local_pkt_filterp->u.pattern.mask_and_pattern[pattern_size], pattern, pattern_size);
+ pkt_filterp = local_pkt_filterp;
+ pkt_filter_size = local_pkt_filter_size;
+ }
+
+ /* add pkt filter */
+ err = brcmf_fil_iovar_data_set(ifp, "pkt_filter_add", pkt_filterp, pkt_filter_size);
+ if (err) {
+ brcmf_err("pkt_filter_add failed, id=%d, err=%d\n",
+ pkt_filterp->id, err);
+ goto exit;
+ }
+
+ /* enable pkt filter id */
+ pkt_filter_enable.id = cpu_to_le32(pkt_filterp->id);
+ pkt_filter_enable.enable = cpu_to_le32(true);
+ err = brcmf_fil_iovar_data_set(ifp, "pkt_filter_enable", &pkt_filter_enable, sizeof(pkt_filter_enable));
+ if (err) {
+ brcmf_err("pkt_filter_enable failed, id=%d, err=%d\n",
+ pkt_filterp->id, err);
+ goto exit;
+ }
+
+exit:
+ if (local_pkt_filterp)
+ kfree(local_pkt_filterp);
+
+ return err;
+}
+
+static s32 brcmf_configure_pktfilter(struct brcmf_if *ifp, struct cfg80211_wowlan *wow)
+{
+ s32 err = 0;
+ u32 i = 0, j = 0;
+ u32 buf_len = 0, pattern_size = 0;
+ wl_pkt_filter_t *pkt_filterp = NULL;
+ u8 mask_bytes_len = 0, mask_byte_idx = 0, mask_bit_idx = 0;
+ const u32 max_buf_size = WL_PKT_FILTER_FIXED_LEN +
+ WL_PKT_FILTER_PATTERN_TIMEOUT_FIXED_LEN + (2 * WL_WOWLAN_MAX_PATTERN_LEN);
+
+ /* configure wowlan pattern filters */
+ if (0 < wow->n_patterns) {
+
+ pkt_filterp = (wl_pkt_filter_t *) kzalloc(max_buf_size, GFP_KERNEL);
+ if (pkt_filterp == NULL) {
+ brcmf_err("Error allocating buffer for pkt filters\n");
+ return -ENOMEM;
+ }
+
+ if (unlikely(g_keepalive_debug)) brcmf_info("wow %p, pattern count %d->\n", wow, wow->n_patterns);
+
+ while (i < wow->n_patterns) {
+ u32 shouldTimeout = wow->patterns[i].action == NL80211_WOWLAN_ACTION_DROP;
+
+ if (unlikely(g_keepalive_debug)) brcmf_info("wow intr=%d to=%d\n", tcpka_intr_enabled, shouldTimeout);
+
+ /* reset buffers */
+ buf_len = 0;
+ memset(pkt_filterp, 0, max_buf_size);
+
+ /* copy filter id */
+ pkt_filterp->id = cpu_to_le32(WL_WOWLAN_PKT_FILTER_ID_FIRST + i);
+
+ /* copy filter type */
+ if (shouldTimeout)
+ pkt_filterp->type = cpu_to_le32(WL_PKT_FILTER_TYPE_PATTERN_MATCH_TIMEOUT);
+ else
+ pkt_filterp->type = cpu_to_le32(WL_PKT_FILTER_TYPE_PATTERN_MATCH);
+
+ /* copy negate_match */
+ if (shouldTimeout)
+ pkt_filterp->negate_match = cpu_to_le32(1);
+
+ /* copy size */
+ pattern_size = wow->patterns[i].pattern_len;
+ if (shouldTimeout)
+ pkt_filterp->u.pattern_timeout.size_bytes = cpu_to_le32(pattern_size);
+ else
+ pkt_filterp->u.pattern.size_bytes = cpu_to_le32(pattern_size);
+
+ if (shouldTimeout) {
+ if (tcpka_intr_enabled == true)
+ pkt_filterp->u.pattern_timeout.timeout = cpu_to_le32(g_remote_ka_timeout);
+ else
+ pkt_filterp->u.pattern_timeout.timeout = cpu_to_le32(0);
+ }
+
+ /* copy offset */
+ if (shouldTimeout)
+ pkt_filterp->u.pattern_timeout.offset = cpu_to_le32(wow->patterns[i].pkt_offset);
+ else
+ pkt_filterp->u.pattern.offset = cpu_to_le32(wow->patterns[i].pkt_offset);
+
+ /* convert mask from bit to byte format */
+ j = 0;
+ mask_bit_idx = 0;
+ mask_byte_idx = 0;
+ mask_bytes_len = DIV_ROUND_UP(pattern_size, 8);
+ while ((mask_byte_idx < mask_bytes_len) &&
+ (mask_bit_idx < pattern_size)) {
+
+ if (wow->patterns[i].mask[mask_byte_idx] & BIT(mask_bit_idx++)) {
+ if (shouldTimeout)
+ pkt_filterp->u.pattern_timeout.mask_and_pattern[j] = 0xFF;
+ else
+ pkt_filterp->u.pattern.mask_and_pattern[j] = 0xFF;
+ }
+ j++;
+ if (mask_bit_idx >= 8) {
+ /* move to next mask byte */
+ mask_bit_idx = 0;
+ mask_byte_idx++;
+ }
+ }
+
+ if (unlikely(g_keepalive_debug)) {
+ brcmf_dbg_hex_dump(true, wow->patterns[i].pattern, pattern_size, "pattern data %d:\n", i);
+ if (shouldTimeout)
+ brcmf_dbg_hex_dump(true, pkt_filterp->u.pattern_timeout.mask_and_pattern, pattern_size, "pattern mask %d:\n", i);
+ else
+ brcmf_dbg_hex_dump(true, pkt_filterp->u.pattern.mask_and_pattern, pattern_size, "pattern mask %d:\n", i);
+ }
+
+ /* copy pattern to be matched */
+ if (shouldTimeout)
+ memcpy(&pkt_filterp->u.pattern_timeout.mask_and_pattern[pattern_size],
+ wow->patterns[i].pattern, pattern_size);
+ else
+ memcpy(&pkt_filterp->u.pattern.mask_and_pattern[pattern_size],
+ wow->patterns[i].pattern, pattern_size);
+
+ /* calculate filter buffer len */
+ buf_len += WL_PKT_FILTER_FIXED_LEN;
+ if (shouldTimeout)
+ buf_len += (WL_PKT_FILTER_PATTERN_TIMEOUT_FIXED_LEN + (2 * pattern_size));
+ else
+ buf_len += (WL_PKT_FILTER_PATTERN_FIXED_LEN + (2 * pattern_size));
+
+ err = brcmf_add_enable_pktfilter(ifp, pkt_filterp, buf_len, 0, 0, 0, 0, NULL, NULL, 0);
+ if (err) {
+ brcmf_err("pkt_filter_add_enable failed, id=%d, err=%d\n",
+ pkt_filterp->id, err);
+ goto exit;
+ }
+ i++; /* move to next pattern */
+ }
+ } else
+ brcmf_dbg(INFO, "wowlan filters not found\n");
+
+exit:
+ if (pkt_filterp)
+ kfree(pkt_filterp);
+
+ return err;
+}
+
+#define BCME_BADARG -2 /* Bad Argument */
+
+static void brcmf_remove_pktfilter(struct brcmf_if *ifp)
+{
+ int pkt_filter_id = WL_WOWLAN_PKT_FILTER_ID_FIRST;
+ s32 err = 0;
+
+ while (pkt_filter_id <= WL_WOWLAN_PKT_FILTER_ID_LAST) {
+ if (unlikely(g_keepalive_debug)) {
+ wl_pkt_filter_stats_t pkt_filter_stats;
+
+ /* wowlan pkt filter stats if any */
+ memset(&pkt_filter_stats, 0, sizeof(pkt_filter_stats));
+ memcpy(&pkt_filter_stats, &pkt_filter_id, sizeof(pkt_filter_id));
+ err = brcmf_fil_iovar_data_get(ifp, "pkt_filter_stats", &pkt_filter_stats, sizeof(pkt_filter_stats));
+ if (0 == err && pkt_filter_stats.num_pkts_matched) {
+ brcmf_info("ndx%d_hits=%d\n", pkt_filter_id, le32_to_cpu(pkt_filter_stats.num_pkts_matched));
+ }
+ }
+
+ /* delete wowlan pkt filter if any */
+ err = brcmf_fil_iovar_int_set(ifp, "pkt_filter_delete", pkt_filter_id);
+ /* pkt_filter_delete would return BCME_BADARG when pkt filter id
+ * does not exist in filter list of firmware, ignore it.
+ */
+ if (BCME_BADARG == err)
+ err = 0;
+
+ if (err) {
+ brcmf_err("pkt_filter_delete failed, id=%d, err=%d\n",
+ pkt_filter_id, err);
+ }
+ pkt_filter_id++;
+ }
+
+}
+
+extern int g_4334x_pkt_filter;
+static s32 brcmf_dongle_pktfilter(struct brcmf_if *ifp)
+{
+ u8 gwifi_mask[] ={
+ 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
+ 0xFF, 0xFF
+ };
+ u8 gwifi_pattern[] = {
+ 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x09
+ };
+ s32 err = 0;
+
+ if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PKT_FILTER))
+ return 0;
+
+ if (!g_4334x_pkt_filter) {
+ /* put gwifi filter at id #106 */
+ err = brcmf_add_enable_pktfilter(ifp, NULL, 0,
+ 106, WL_PKT_FILTER_TYPE_PATTERN_MATCH, 1, 12,
+ gwifi_mask, gwifi_pattern, sizeof(gwifi_mask)/sizeof(u8));
+ }
+
+ return err;
+}
+
+#ifdef CONFIG_PM
+
+static void brcmf_report_wowl_wakeind(struct wiphy *wiphy, struct brcmf_if *ifp)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct brcmf_wowl_wakeind_le wake_ind_le;
+ struct cfg80211_wowlan_wakeup wakeup_data;
+ struct cfg80211_wowlan_wakeup *wakeup;
+ u32 wakeind;
+ s32 err;
+ int timeout;
+
+ err = brcmf_fil_iovar_data_get(ifp, "wowl_wakeind", &wake_ind_le,
+ sizeof(wake_ind_le));
+ if (err) {
+ brcmf_err("Get wowl_wakeind failed, err = %d\n", err);
+ return;
+ }
+
+ wakeind = le32_to_cpu(wake_ind_le.ucode_wakeind);
+ if (wakeind & (BRCMF_WOWL_MAGIC | BRCMF_WOWL_DIS | BRCMF_WOWL_BCN |
+ BRCMF_WOWL_RETR | BRCMF_WOWL_NET |
+ BRCMF_WOWL_PFN_FOUND)) {
+ wakeup = &wakeup_data;
+ memset(&wakeup_data, 0, sizeof(wakeup_data));
+ wakeup_data.pattern_idx = -1;
+
+ if (wakeind & BRCMF_WOWL_MAGIC) {
+ brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_MAGIC\n");
+ wakeup_data.magic_pkt = true;
+ }
+ if (wakeind & BRCMF_WOWL_DIS) {
+ brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_DIS\n");
+ wakeup_data.disconnect = true;
+ }
+ if (wakeind & BRCMF_WOWL_BCN) {
+ brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_BCN\n");
+ wakeup_data.disconnect = true;
+ }
+ if (wakeind & BRCMF_WOWL_RETR) {
+ brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_RETR\n");
+ wakeup_data.disconnect = true;
+ }
+ if (wakeind & BRCMF_WOWL_NET) {
+ brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_NET\n");
+ /* For now always map to pattern 0, no API to get
+ * correct information available at the moment.
+ */
+ wakeup_data.pattern_idx = 0;
+ }
+ if (wakeind & BRCMF_WOWL_PFN_FOUND) {
+ brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_PFN_FOUND\n");
+ timeout = wait_event_timeout(cfg->wowl.nd_data_wait,
+ cfg->wowl.nd_data_completed,
+ BRCMF_ND_INFO_TIMEOUT);
+ if (!timeout)
+ brcmf_err("No result for wowl net detect\n");
+ else
+ wakeup_data.net_detect = cfg->wowl.nd_info;
+ }
+ if (wakeind & BRCMF_WOWL_GTK_FAILURE) {
+ brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_GTK_FAILURE\n");
+ wakeup_data.gtk_rekey_failure = true;
+ }
+ } else {
+ wakeup = NULL;
+ }
+ cfg80211_report_wowlan_wakeup(&ifp->vif->wdev, wakeup, GFP_KERNEL);
+}
+
+#else
+
+static void brcmf_report_wowl_wakeind(struct wiphy *wiphy, struct brcmf_if *ifp)
+{
+}
+
+#endif /* CONFIG_PM */
+
+static s32 brcmf_cfg80211_resume(struct wiphy *wiphy)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct net_device *ndev = cfg_to_ndev(cfg);
+ struct brcmf_if *ifp = netdev_priv(ndev);
+
+ brcmf_info("Enter\n");
+
+ g_wakeup_irq = 0;
+
+ if (cfg->wowl.active) {
+ s32 ret = 0;
+ u32 pm = 0;
+
+ ret = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_PM, &pm);
+ if (ret || (pm != dhd_power_mode))
+ brcmf_dbg(INFO,"Not desired power mode, ret=%d, PM=%d\n", ret, pm);
+
+ if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PKT_FILTER)) {
+ u16 events[] = {BRCMF_E_P2P_PROBEREQ_MSG, BRCMF_E_P2P_DISC_LISTEN_COMPLETE};
+
+ brcmf_fweh_add_remove_events(ifp, events, sizeof(events)/sizeof(events[0]), true);
+ brcmf_remove_pktfilter(ifp);
+ } else {
+ brcmf_report_wowl_wakeind(wiphy, ifp);
+ brcmf_fil_iovar_int_set(ifp, "wowl_clear", 0);
+ brcmf_config_wowl_pattern(ifp, "clr", NULL, 0, NULL, 0);
+ if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_ARP_ND))
+ brcmf_configure_arp_nd_offload(ifp, true);
+ brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM,
+ cfg->wowl.pre_pmmode);
+ }
+ cfg->wowl.active = false;
+ if (cfg->wowl.nd_enabled) {
+ brcmf_cfg80211_sched_scan_stop(cfg->wiphy, ifp->ndev, 0);
+ brcmf_fweh_unregister(cfg->pub, BRCMF_E_PFN_NET_FOUND);
+ brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND,
+ brcmf_notify_sched_scan_results);
+ cfg->wowl.nd_enabled = false;
+ }
+ }
+ return 0;
+}
+
+
+static void brcmf_configure_wowl(struct brcmf_cfg80211_info *cfg,
+ struct brcmf_if *ifp,
+ struct cfg80211_wowlan *wowl)
+{
+ u32 wowl_config;
+ struct brcmf_wowl_wakeind_le wowl_wakeind;
+ u32 i;
+ s32 ret = 0;
+
+ brcmf_dbg(TRACE, "Suspend, wowl config.\n");
+
+ ret = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_PM, &cfg->wowl.pre_pmmode);
+ if (ret || (cfg->wowl.pre_pmmode != dhd_power_mode))
+ brcmf_dbg(INFO,"Not desired power mode, ret=%d, PM=%d\n", ret, cfg->wowl.pre_pmmode);
+
+ brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, dhd_power_mode);
+
+ if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PKT_FILTER)) {
+ u16 events[] = {BRCMF_E_P2P_PROBEREQ_MSG, BRCMF_E_P2P_DISC_LISTEN_COMPLETE};
+
+ brcmf_fweh_add_remove_events(ifp, events, sizeof(events)/sizeof(events[0]), false);
+ brcmf_configure_pktfilter(ifp, wowl);
+ /* skip the original 'wowl' iovar process */
+ goto exit;
+ }
+
+ if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_ARP_ND))
+ brcmf_configure_arp_nd_offload(ifp, false);
+
+ wowl_config = 0;
+ if (wowl->disconnect)
+ wowl_config = BRCMF_WOWL_DIS | BRCMF_WOWL_BCN | BRCMF_WOWL_RETR;
+ if (wowl->magic_pkt)
+ wowl_config |= BRCMF_WOWL_MAGIC;
+ if ((wowl->patterns) && (wowl->n_patterns)) {
+ wowl_config |= BRCMF_WOWL_NET;
+ for (i = 0; i < wowl->n_patterns; i++) {
+ brcmf_config_wowl_pattern(ifp, "add",
+ (u8 *)wowl->patterns[i].pattern,
+ wowl->patterns[i].pattern_len,
+ (u8 *)wowl->patterns[i].mask,
+ wowl->patterns[i].pkt_offset);
+ }
+ }
+ if (wowl->nd_config) {
+ brcmf_cfg80211_sched_scan_start(cfg->wiphy, ifp->ndev,
+ wowl->nd_config);
+ wowl_config |= BRCMF_WOWL_PFN_FOUND;
+
+ cfg->wowl.nd_data_completed = false;
+ cfg->wowl.nd_enabled = true;
+ /* Now reroute the event for PFN to the wowl function. */
+ brcmf_fweh_unregister(cfg->pub, BRCMF_E_PFN_NET_FOUND);
+ brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND,
+ brcmf_wowl_nd_results);
+ }
+ if (wowl->gtk_rekey_failure)
+ wowl_config |= BRCMF_WOWL_GTK_FAILURE;
+ if (!test_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state))
+ wowl_config |= BRCMF_WOWL_UNASSOC;
+
+ memcpy(&wowl_wakeind, "clear", 6);
+ brcmf_fil_iovar_data_set(ifp, "wowl_wakeind", &wowl_wakeind,
+ sizeof(wowl_wakeind));
+ brcmf_fil_iovar_int_set(ifp, "wowl", wowl_config);
+ brcmf_fil_iovar_int_set(ifp, "wowl_activate", 1);
+
+exit:
+ brcmf_bus_wowl_config(cfg->pub->bus_if, true);
+ cfg->wowl.active = true;
+}
+
+static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy,
+ struct cfg80211_wowlan *wowl)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct net_device *ndev = cfg_to_ndev(cfg);
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ struct brcmf_cfg80211_vif *vif;
+
+ brcmf_info("Enter\n");
+
+ /* if the primary net_device is not READY there is nothing
+ * we can do but pray resume goes smoothly.
+ */
+ if (!check_vif_up(ifp->vif))
+ goto exit;
+
+ /* Stop scheduled scan */
+ if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO))
+ brcmf_cfg80211_sched_scan_stop(wiphy, ndev, 0);
+
+ /* end any scanning */
+ if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status))
+ brcmf_abort_scanning(cfg);
+
+ if (wowl == NULL) {
+ brcmf_bus_wowl_config(cfg->pub->bus_if, false);
+ list_for_each_entry(vif, &cfg->vif_list, list) {
+ if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state))
+ continue;
+ /* While going to suspend if associated with AP
+ * disassociate from AP to save power while system is
+ * in suspended state
+ */
+ brcmf_link_down(vif, WLAN_REASON_UNSPECIFIED);
+ /* Make sure WPA_Supplicant receives all the event
+ * generated due to DISASSOC call to the fw to keep
+ * the state fw and WPA_Supplicant state consistent
+ */
+ brcmf_delay(500);
+ }
+ /* Configure MPC */
+ brcmf_set_mpc(ifp, 1);
+
+ } else {
+ /* Configure WOWL paramaters */
+ brcmf_configure_wowl(cfg, ifp, wowl);
+ }
+
+exit:
+ brcmf_dbg(TRACE, "Exit\n");
+ /* clear any scanning activity */
+ cfg->scan_status = 0;
+
+ g_wakeup_irq = 1;
+ return 0;
+}
+
+static __used s32
+brcmf_update_pmklist(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp)
+{
+ struct brcmf_pmk_list_le *pmk_list;
+ int i;
+ u32 npmk;
+ s32 err;
+
+ pmk_list = &cfg->pmk_list;
+ npmk = le32_to_cpu(pmk_list->npmk);
+
+ brcmf_dbg(CONN, "No of elements %d\n", npmk);
+ for (i = 0; i < npmk; i++)
+ brcmf_dbg(CONN, "PMK[%d]: %pM\n", i, &pmk_list->pmk[i].bssid);
+
+ err = brcmf_fil_iovar_data_set(ifp, "pmkid_info", pmk_list,
+ sizeof(*pmk_list));
+
+ return err;
+}
+
+static s32
+brcmf_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *ndev,
+ struct cfg80211_pmksa *pmksa)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ struct brcmf_pmksa *pmk = &cfg->pmk_list.pmk[0];
+ s32 err;
+ u32 npmk, i;
+
+ brcmf_dbg(TRACE, "Enter\n");
+ if (!check_vif_up(ifp->vif))
+ return -EIO;
+
+ npmk = le32_to_cpu(cfg->pmk_list.npmk);
+ for (i = 0; i < npmk; i++)
+ if (!memcmp(pmksa->bssid, pmk[i].bssid, ETH_ALEN))
+ break;
+ if (i < BRCMF_MAXPMKID) {
+ memcpy(pmk[i].bssid, pmksa->bssid, ETH_ALEN);
+ memcpy(pmk[i].pmkid, pmksa->pmkid, WLAN_PMKID_LEN);
+ if (i == npmk) {
+ npmk++;
+ cfg->pmk_list.npmk = cpu_to_le32(npmk);
+ }
+ } else {
+ brcmf_err("Too many PMKSA entries cached %d\n", npmk);
+ return -EINVAL;
+ }
+
+ brcmf_dbg(CONN, "set_pmksa - PMK bssid: %pM =\n", pmk[npmk].bssid);
+ for (i = 0; i < WLAN_PMKID_LEN; i += 4)
+ brcmf_dbg(CONN, "%02x %02x %02x %02x\n", pmk[npmk].pmkid[i],
+ pmk[npmk].pmkid[i + 1], pmk[npmk].pmkid[i + 2],
+ pmk[npmk].pmkid[i + 3]);
+
+ err = brcmf_update_pmklist(cfg, ifp);
+
+ brcmf_dbg(TRACE, "Exit\n");
+ return err;
+}
+
+static s32
+brcmf_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *ndev,
+ struct cfg80211_pmksa *pmksa)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ struct brcmf_pmksa *pmk = &cfg->pmk_list.pmk[0];
+ s32 err;
+ u32 npmk, i;
+
+ brcmf_dbg(TRACE, "Enter\n");
+ if (!check_vif_up(ifp->vif))
+ return -EIO;
+
+ brcmf_dbg(CONN, "del_pmksa - PMK bssid = %pM\n", pmksa->bssid);
+
+ npmk = le32_to_cpu(cfg->pmk_list.npmk);
+ for (i = 0; i < npmk; i++)
+ if (!memcmp(pmksa->bssid, pmk[i].bssid, ETH_ALEN))
+ break;
+
+ if ((npmk > 0) && (i < npmk)) {
+ for (; i < (npmk - 1); i++) {
+ memcpy(&pmk[i].bssid, &pmk[i + 1].bssid, ETH_ALEN);
+ memcpy(&pmk[i].pmkid, &pmk[i + 1].pmkid,
+ WLAN_PMKID_LEN);
+ }
+ memset(&pmk[i], 0, sizeof(*pmk));
+ cfg->pmk_list.npmk = cpu_to_le32(npmk - 1);
+ } else {
+ brcmf_err("Cache entry not found\n");
+ return -EINVAL;
+ }
+
+ err = brcmf_update_pmklist(cfg, ifp);
+
+ brcmf_dbg(TRACE, "Exit\n");
+ return err;
+
+}
+
+static s32
+brcmf_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *ndev)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ s32 err;
+
+ brcmf_dbg(TRACE, "Enter\n");
+ if (!check_vif_up(ifp->vif))
+ return -EIO;
+
+ memset(&cfg->pmk_list, 0, sizeof(cfg->pmk_list));
+ err = brcmf_update_pmklist(cfg, ifp);
+
+ brcmf_dbg(TRACE, "Exit\n");
+ return err;
+
+}
+
+static s32 brcmf_configure_opensecurity(struct brcmf_if *ifp)
+{
+ s32 err;
+ s32 wpa_val;
+
+ /* set auth */
+ err = brcmf_fil_bsscfg_int_set(ifp, "auth", 0);
+ if (err < 0) {
+ brcmf_err("auth error %d\n", err);
+ return err;
+ }
+ /* set wsec */
+ err = brcmf_fil_bsscfg_int_set(ifp, "wsec", 0);
+ if (err < 0) {
+ brcmf_err("wsec error %d\n", err);
+ return err;
+ }
+ /* set upper-layer auth */
+ if (brcmf_is_ibssmode(ifp->vif))
+ wpa_val = WPA_AUTH_NONE;
+ else
+ wpa_val = WPA_AUTH_DISABLED;
+ err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", wpa_val);
+ if (err < 0) {
+ brcmf_err("wpa_auth error %d\n", err);
+ return err;
+ }
+
+ return 0;
+}
+
+static bool brcmf_valid_wpa_oui(u8 *oui, bool is_rsn_ie)
+{
+ if (is_rsn_ie)
+ return (memcmp(oui, RSN_OUI, TLV_OUI_LEN) == 0);
+
+ return (memcmp(oui, WPA_OUI, TLV_OUI_LEN) == 0);
+}
+
+static s32
+brcmf_configure_wpaie(struct brcmf_if *ifp,
+ const struct brcmf_vs_tlv *wpa_ie,
+ bool is_rsn_ie)
+{
+ u32 auth = 0; /* d11 open authentication */
+ u16 count;
+ s32 err = 0;
+ s32 len;
+ u32 i;
+ u32 wsec;
+ u32 pval = 0;
+ u32 gval = 0;
+ u32 wpa_auth = 0;
+ u32 offset;
+ u8 *data;
+ u16 rsn_cap;
+ u32 wme_bss_disable;
+ u32 mfp;
+
+ brcmf_dbg(TRACE, "Enter\n");
+ if (wpa_ie == NULL)
+ goto exit;
+
+ len = wpa_ie->len + TLV_HDR_LEN;
+ data = (u8 *)wpa_ie;
+ offset = TLV_HDR_LEN;
+ if (!is_rsn_ie)
+ offset += VS_IE_FIXED_HDR_LEN;
+ else
+ offset += WPA_IE_VERSION_LEN;
+
+ /* check for multicast cipher suite */
+ if (offset + WPA_IE_MIN_OUI_LEN > len) {
+ err = -EINVAL;
+ brcmf_err("no multicast cipher suite\n");
+ goto exit;
+ }
+
+ if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
+ err = -EINVAL;
+ brcmf_err("ivalid OUI\n");
+ goto exit;
+ }
+ offset += TLV_OUI_LEN;
+
+ /* pick up multicast cipher */
+ switch (data[offset]) {
+ case WPA_CIPHER_NONE:
+ gval = 0;
+ break;
+ case WPA_CIPHER_WEP_40:
+ case WPA_CIPHER_WEP_104:
+ gval = WEP_ENABLED;
+ break;
+ case WPA_CIPHER_TKIP:
+ gval = TKIP_ENABLED;
+ break;
+ case WPA_CIPHER_AES_CCM:
+ gval = AES_ENABLED;
+ break;
+ default:
+ err = -EINVAL;
+ brcmf_err("Invalid multi cast cipher info\n");
+ goto exit;
+ }
+
+ offset++;
+ /* walk thru unicast cipher list and pick up what we recognize */
+ count = data[offset] + (data[offset + 1] << 8);
+ offset += WPA_IE_SUITE_COUNT_LEN;
+ /* Check for unicast suite(s) */
+ if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {
+ err = -EINVAL;
+ brcmf_err("no unicast cipher suite\n");
+ goto exit;
+ }
+ for (i = 0; i < count; i++) {
+ if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
+ err = -EINVAL;
+ brcmf_err("ivalid OUI\n");
+ goto exit;
+ }
+ offset += TLV_OUI_LEN;
+ switch (data[offset]) {
+ case WPA_CIPHER_NONE:
+ break;
+ case WPA_CIPHER_WEP_40:
+ case WPA_CIPHER_WEP_104:
+ pval |= WEP_ENABLED;
+ break;
+ case WPA_CIPHER_TKIP:
+ pval |= TKIP_ENABLED;
+ break;
+ case WPA_CIPHER_AES_CCM:
+ pval |= AES_ENABLED;
+ break;
+ default:
+ brcmf_err("Invalid unicast security info\n");
+ }
+ offset++;
+ }
+ /* walk thru auth management suite list and pick up what we recognize */
+ count = data[offset] + (data[offset + 1] << 8);
+ offset += WPA_IE_SUITE_COUNT_LEN;
+ /* Check for auth key management suite(s) */
+ if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {
+ err = -EINVAL;
+ brcmf_err("no auth key mgmt suite\n");
+ goto exit;
+ }
+ for (i = 0; i < count; i++) {
+ if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
+ err = -EINVAL;
+ brcmf_err("ivalid OUI\n");
+ goto exit;
+ }
+ offset += TLV_OUI_LEN;
+ switch (data[offset]) {
+ case RSN_AKM_NONE:
+ brcmf_dbg(TRACE, "RSN_AKM_NONE\n");
+ wpa_auth |= WPA_AUTH_NONE;
+ break;
+ case RSN_AKM_UNSPECIFIED:
+ brcmf_dbg(TRACE, "RSN_AKM_UNSPECIFIED\n");
+ is_rsn_ie ? (wpa_auth |= WPA2_AUTH_UNSPECIFIED) :
+ (wpa_auth |= WPA_AUTH_UNSPECIFIED);
+ break;
+ case RSN_AKM_PSK:
+ brcmf_dbg(TRACE, "RSN_AKM_PSK\n");
+ is_rsn_ie ? (wpa_auth |= WPA2_AUTH_PSK) :
+ (wpa_auth |= WPA_AUTH_PSK);
+ break;
+ case RSN_AKM_SHA256_PSK:
+ brcmf_dbg(TRACE, "RSN_AKM_MFP_PSK\n");
+ wpa_auth |= WPA2_AUTH_PSK_SHA256;
+ break;
+ case RSN_AKM_SHA256_1X:
+ brcmf_dbg(TRACE, "RSN_AKM_MFP_1X\n");
+ wpa_auth |= WPA2_AUTH_1X_SHA256;
+ break;
+ default:
+ brcmf_err("Invalid key mgmt info\n");
+ }
+ offset++;
+ }
+
+ mfp = BRCMF_MFP_NONE;
+ if (is_rsn_ie) {
+ wme_bss_disable = 1;
+ if ((offset + RSN_CAP_LEN) <= len) {
+ rsn_cap = data[offset] + (data[offset + 1] << 8);
+ if (rsn_cap & RSN_CAP_PTK_REPLAY_CNTR_MASK)
+ wme_bss_disable = 0;
+ if (rsn_cap & RSN_CAP_MFPR_MASK) {
+ brcmf_dbg(TRACE, "MFP Required\n");
+ mfp = BRCMF_MFP_REQUIRED;
+ /* Firmware only supports mfp required in
+ * combination with WPA2_AUTH_PSK_SHA256 or
+ * WPA2_AUTH_1X_SHA256.
+ */
+ if (!(wpa_auth & (WPA2_AUTH_PSK_SHA256 |
+ WPA2_AUTH_1X_SHA256))) {
+ err = -EINVAL;
+ goto exit;
+ }
+ /* Firmware has requirement that WPA2_AUTH_PSK/
+ * WPA2_AUTH_UNSPECIFIED be set, if SHA256 OUI
+ * is to be included in the rsn ie.
+ */
+ if (wpa_auth & WPA2_AUTH_PSK_SHA256)
+ wpa_auth |= WPA2_AUTH_PSK;
+ else if (wpa_auth & WPA2_AUTH_1X_SHA256)
+ wpa_auth |= WPA2_AUTH_UNSPECIFIED;
+ } else if (rsn_cap & RSN_CAP_MFPC_MASK) {
+ brcmf_dbg(TRACE, "MFP Capable\n");
+ mfp = BRCMF_MFP_CAPABLE;
+ }
+ }
+ offset += RSN_CAP_LEN;
+ /* set wme_bss_disable to sync RSN Capabilities */
+ err = brcmf_fil_bsscfg_int_set(ifp, "wme_bss_disable",
+ wme_bss_disable);
+ if (err < 0) {
+ brcmf_err("wme_bss_disable error %d\n", err);
+ goto exit;
+ }
+
+ /* Skip PMKID cnt as it is know to be 0 for AP. */
+ offset += RSN_PMKID_COUNT_LEN;
+
+ /* See if there is BIP wpa suite left for MFP */
+ if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP) &&
+ ((offset + WPA_IE_MIN_OUI_LEN) <= len)) {
+ err = brcmf_fil_bsscfg_data_set(ifp, "bip",
+ &data[offset],
+ WPA_IE_MIN_OUI_LEN);
+ if (err < 0) {
+ brcmf_err("bip error %d\n", err);
+ goto exit;
+ }
+ }
+ }
+ /* FOR WPS , set SES_OW_ENABLED */
+ wsec = (pval | gval | SES_OW_ENABLED);
+
+ /* set auth */
+ err = brcmf_fil_bsscfg_int_set(ifp, "auth", auth);
+ if (err < 0) {
+ brcmf_err("auth error %d\n", err);
+ goto exit;
+ }
+ /* set wsec */
+ err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
+ if (err < 0) {
+ brcmf_err("wsec error %d\n", err);
+ goto exit;
+ }
+ /* Configure MFP, this needs to go after wsec otherwise the wsec command
+ * will overwrite the values set by MFP
+ */
+ if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP)) {
+ err = brcmf_fil_bsscfg_int_set(ifp, "mfp", mfp);
+ if (err < 0) {
+ brcmf_err("mfp error %d\n", err);
+ goto exit;
+ }
+ }
+ /* set upper-layer auth */
+ err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", wpa_auth);
+ if (err < 0) {
+ brcmf_err("wpa_auth error %d\n", err);
+ goto exit;
+ }
+
+exit:
+ return err;
+}
+
+static s32
+brcmf_parse_vndr_ies(const u8 *vndr_ie_buf, u32 vndr_ie_len,
+ struct parsed_vndr_ies *vndr_ies)
+{
+ struct brcmf_vs_tlv *vndrie;
+ struct brcmf_tlv *ie;
+ struct parsed_vndr_ie_info *parsed_info;
+ s32 remaining_len;
+
+ remaining_len = (s32)vndr_ie_len;
+ memset(vndr_ies, 0, sizeof(*vndr_ies));
+
+ ie = (struct brcmf_tlv *)vndr_ie_buf;
+ while (ie) {
+ if (ie->id != WLAN_EID_VENDOR_SPECIFIC)
+ goto next;
+ vndrie = (struct brcmf_vs_tlv *)ie;
+ /* len should be bigger than OUI length + one */
+ if (vndrie->len < (VS_IE_FIXED_HDR_LEN - TLV_HDR_LEN + 1)) {
+ brcmf_err("invalid vndr ie. length is too small %d\n",
+ vndrie->len);
+ goto next;
+ }
+ /* if wpa or wme ie, do not add ie */
+ if (!memcmp(vndrie->oui, (u8 *)WPA_OUI, TLV_OUI_LEN) &&
+ ((vndrie->oui_type == WPA_OUI_TYPE) ||
+ (vndrie->oui_type == WME_OUI_TYPE))) {
+ brcmf_dbg(TRACE, "Found WPA/WME oui. Do not add it\n");
+ goto next;
+ }
+
+ parsed_info = &vndr_ies->ie_info[vndr_ies->count];
+
+ /* save vndr ie information */
+ parsed_info->ie_ptr = (char *)vndrie;
+ parsed_info->ie_len = vndrie->len + TLV_HDR_LEN;
+ memcpy(&parsed_info->vndrie, vndrie, sizeof(*vndrie));
+
+ vndr_ies->count++;
+
+ brcmf_dbg(TRACE, "** OUI %02x %02x %02x, type 0x%02x\n",
+ parsed_info->vndrie.oui[0],
+ parsed_info->vndrie.oui[1],
+ parsed_info->vndrie.oui[2],
+ parsed_info->vndrie.oui_type);
+
+ if (vndr_ies->count >= VNDR_IE_PARSE_LIMIT)
+ break;
+next:
+ remaining_len -= (ie->len + TLV_HDR_LEN);
+ if (remaining_len <= TLV_HDR_LEN)
+ ie = NULL;
+ else
+ ie = (struct brcmf_tlv *)(((u8 *)ie) + ie->len +
+ TLV_HDR_LEN);
+ }
+ return 0;
+}
+
+static u32
+brcmf_vndr_ie(u8 *iebuf, s32 pktflag, u8 *ie_ptr, u32 ie_len, s8 *add_del_cmd)
+{
+
+ strncpy(iebuf, add_del_cmd, VNDR_IE_CMD_LEN - 1);
+ iebuf[VNDR_IE_CMD_LEN - 1] = '\0';
+
+ put_unaligned_le32(1, &iebuf[VNDR_IE_COUNT_OFFSET]);
+
+ put_unaligned_le32(pktflag, &iebuf[VNDR_IE_PKTFLAG_OFFSET]);
+
+ memcpy(&iebuf[VNDR_IE_VSIE_OFFSET], ie_ptr, ie_len);
+
+ return ie_len + VNDR_IE_HDR_SIZE;
+}
+
+s32 brcmf_vif_set_mgmt_ie(struct brcmf_cfg80211_vif *vif, s32 pktflag,
+ const u8 *vndr_ie_buf, u32 vndr_ie_len)
+{
+ struct brcmf_if *ifp;
+ struct vif_saved_ie *saved_ie;
+ s32 err = 0;
+ u8 *iovar_ie_buf;
+ u8 *curr_ie_buf;
+ u8 *mgmt_ie_buf = NULL;
+ int mgmt_ie_buf_len;
+ u32 *mgmt_ie_len;
+ u32 del_add_ie_buf_len = 0;
+ u32 total_ie_buf_len = 0;
+ u32 parsed_ie_buf_len = 0;
+ struct parsed_vndr_ies old_vndr_ies;
+ struct parsed_vndr_ies new_vndr_ies;
+ struct parsed_vndr_ie_info *vndrie_info;
+ s32 i;
+ u8 *ptr;
+ int remained_buf_len;
+
+ if (!vif)
+ return -ENODEV;
+ ifp = vif->ifp;
+ saved_ie = &vif->saved_ie;
+
+ brcmf_dbg(TRACE, "bsscfgidx %d, pktflag : 0x%02X\n", ifp->bsscfgidx,
+ pktflag);
+ iovar_ie_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
+ if (!iovar_ie_buf)
+ return -ENOMEM;
+ curr_ie_buf = iovar_ie_buf;
+ switch (pktflag) {
+ case BRCMF_VNDR_IE_PRBREQ_FLAG:
+ mgmt_ie_buf = saved_ie->probe_req_ie;
+ mgmt_ie_len = &saved_ie->probe_req_ie_len;
+ mgmt_ie_buf_len = sizeof(saved_ie->probe_req_ie);
+ break;
+ case BRCMF_VNDR_IE_PRBRSP_FLAG:
+ mgmt_ie_buf = saved_ie->probe_res_ie;
+ mgmt_ie_len = &saved_ie->probe_res_ie_len;
+ mgmt_ie_buf_len = sizeof(saved_ie->probe_res_ie);
+ break;
+ case BRCMF_VNDR_IE_BEACON_FLAG:
+ mgmt_ie_buf = saved_ie->beacon_ie;
+ mgmt_ie_len = &saved_ie->beacon_ie_len;
+ mgmt_ie_buf_len = sizeof(saved_ie->beacon_ie);
+ break;
+ case BRCMF_VNDR_IE_ASSOCREQ_FLAG:
+ mgmt_ie_buf = saved_ie->assoc_req_ie;
+ mgmt_ie_len = &saved_ie->assoc_req_ie_len;
+ mgmt_ie_buf_len = sizeof(saved_ie->assoc_req_ie);
+ break;
+ default:
+ err = -EPERM;
+ brcmf_err("not suitable type\n");
+ goto exit;
+ }
+
+ if (vndr_ie_len > mgmt_ie_buf_len) {
+ err = -ENOMEM;
+ brcmf_err("extra IE size too big\n");
+ goto exit;
+ }
+
+ /* parse and save new vndr_ie in curr_ie_buff before comparing it */
+ if (vndr_ie_buf && vndr_ie_len && curr_ie_buf) {
+ ptr = curr_ie_buf;
+ brcmf_parse_vndr_ies(vndr_ie_buf, vndr_ie_len, &new_vndr_ies);
+ for (i = 0; i < new_vndr_ies.count; i++) {
+ vndrie_info = &new_vndr_ies.ie_info[i];
+ memcpy(ptr + parsed_ie_buf_len, vndrie_info->ie_ptr,
+ vndrie_info->ie_len);
+ parsed_ie_buf_len += vndrie_info->ie_len;
+ }
+ }
+
+ if (mgmt_ie_buf && *mgmt_ie_len) {
+ if (parsed_ie_buf_len && (parsed_ie_buf_len == *mgmt_ie_len) &&
+ (memcmp(mgmt_ie_buf, curr_ie_buf,
+ parsed_ie_buf_len) == 0)) {
+ brcmf_dbg(TRACE, "Previous mgmt IE equals to current IE\n");
+ goto exit;
+ }
+
+ /* parse old vndr_ie */
+ brcmf_parse_vndr_ies(mgmt_ie_buf, *mgmt_ie_len, &old_vndr_ies);
+
+ /* make a command to delete old ie */
+ for (i = 0; i < old_vndr_ies.count; i++) {
+ vndrie_info = &old_vndr_ies.ie_info[i];
+
+ brcmf_dbg(TRACE, "DEL ID : %d, Len: %d , OUI:%02x:%02x:%02x\n",
+ vndrie_info->vndrie.id,
+ vndrie_info->vndrie.len,
+ vndrie_info->vndrie.oui[0],
+ vndrie_info->vndrie.oui[1],
+ vndrie_info->vndrie.oui[2]);
+
+ del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag,
+ vndrie_info->ie_ptr,
+ vndrie_info->ie_len,
+ "del");
+ curr_ie_buf += del_add_ie_buf_len;
+ total_ie_buf_len += del_add_ie_buf_len;
+ }
+ }
+
+ *mgmt_ie_len = 0;
+ /* Add if there is any extra IE */
+ if (mgmt_ie_buf && parsed_ie_buf_len) {
+ ptr = mgmt_ie_buf;
+
+ remained_buf_len = mgmt_ie_buf_len;
+
+ /* make a command to add new ie */
+ for (i = 0; i < new_vndr_ies.count; i++) {
+ vndrie_info = &new_vndr_ies.ie_info[i];
+
+ /* verify remained buf size before copy data */
+ if (remained_buf_len < (vndrie_info->vndrie.len +
+ VNDR_IE_VSIE_OFFSET)) {
+ brcmf_err("no space in mgmt_ie_buf: len left %d",
+ remained_buf_len);
+ break;
+ }
+ remained_buf_len -= (vndrie_info->ie_len +
+ VNDR_IE_VSIE_OFFSET);
+
+ brcmf_dbg(TRACE, "ADDED ID : %d, Len: %d, OUI:%02x:%02x:%02x\n",
+ vndrie_info->vndrie.id,
+ vndrie_info->vndrie.len,
+ vndrie_info->vndrie.oui[0],
+ vndrie_info->vndrie.oui[1],
+ vndrie_info->vndrie.oui[2]);
+
+ del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag,
+ vndrie_info->ie_ptr,
+ vndrie_info->ie_len,
+ "add");
+
+ /* save the parsed IE in wl struct */
+ memcpy(ptr + (*mgmt_ie_len), vndrie_info->ie_ptr,
+ vndrie_info->ie_len);
+ *mgmt_ie_len += vndrie_info->ie_len;
+
+ curr_ie_buf += del_add_ie_buf_len;
+ total_ie_buf_len += del_add_ie_buf_len;
+ }
+ }
+ if (total_ie_buf_len) {
+ err = brcmf_fil_bsscfg_data_set(ifp, "vndr_ie", iovar_ie_buf,
+ total_ie_buf_len);
+ if (err)
+ brcmf_err("vndr ie set error : %d\n", err);
+ }
+
+exit:
+ kfree(iovar_ie_buf);
+ return err;
+}
+
+s32 brcmf_vif_clear_mgmt_ies(struct brcmf_cfg80211_vif *vif)
+{
+ s32 pktflags[] = {
+ BRCMF_VNDR_IE_PRBREQ_FLAG,
+ BRCMF_VNDR_IE_PRBRSP_FLAG,
+ BRCMF_VNDR_IE_BEACON_FLAG
+ };
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(pktflags); i++)
+ brcmf_vif_set_mgmt_ie(vif, pktflags[i], NULL, 0);
+
+ memset(&vif->saved_ie, 0, sizeof(vif->saved_ie));
+ return 0;
+}
+
+static s32
+brcmf_config_ap_mgmt_ie(struct brcmf_cfg80211_vif *vif,
+ struct cfg80211_beacon_data *beacon)
+{
+ s32 err;
+
+ /* Set Beacon IEs to FW */
+ err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_BEACON_FLAG,
+ beacon->tail, beacon->tail_len);
+ if (err) {
+ brcmf_err("Set Beacon IE Failed\n");
+ return err;
+ }
+ brcmf_dbg(TRACE, "Applied Vndr IEs for Beacon\n");
+
+ /* Set Probe Response IEs to FW */
+ err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_PRBRSP_FLAG,
+ beacon->proberesp_ies,
+ beacon->proberesp_ies_len);
+ if (err)
+ brcmf_err("Set Probe Resp IE Failed\n");
+ else
+ brcmf_dbg(TRACE, "Applied Vndr IEs for Probe Resp\n");
+
+ return err;
+}
+
+static s32
+brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
+ struct cfg80211_ap_settings *settings)
+{
+ s32 ie_offset;
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ const struct brcmf_tlv *ssid_ie;
+ const struct brcmf_tlv *country_ie;
+ struct brcmf_ssid_le ssid_le;
+ s32 err = -EPERM;
+ const struct brcmf_tlv *rsn_ie;
+ const struct brcmf_vs_tlv *wpa_ie;
+ struct brcmf_join_params join_params;
+ enum nl80211_iftype dev_role;
+ struct brcmf_fil_bss_enable_le bss_enable;
+ u16 chanspec = chandef_to_chanspec(&cfg->d11inf, &settings->chandef);
+ bool mbss;
+ int is_11d;
+ bool supports_11d;
+
+ brcmf_dbg(TRACE, "ctrlchn=%d, center=%d, bw=%d, beacon_interval=%d, dtim_period=%d,\n",
+ settings->chandef.chan->hw_value,
+ settings->chandef.center_freq1, settings->chandef.width,
+ settings->beacon_interval, settings->dtim_period);
+ brcmf_dbg(TRACE, "ssid=%s(%zu), auth_type=%d, inactivity_timeout=%d\n",
+ settings->ssid, settings->ssid_len, settings->auth_type,
+ settings->inactivity_timeout);
+ dev_role = ifp->vif->wdev.iftype;
+ mbss = ifp->vif->mbss;
+
+ /* store current 11d setting */
+ if (brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_REGULATORY,
+ &ifp->vif->is_11d)) {
+ is_11d = supports_11d = false;
+ } else {
+ country_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail,
+ settings->beacon.tail_len,
+ WLAN_EID_COUNTRY);
+ is_11d = country_ie ? 1 : 0;
+ supports_11d = true;
+ }
+
+ memset(&ssid_le, 0, sizeof(ssid_le));
+ if (settings->ssid == NULL || settings->ssid_len == 0) {
+ ie_offset = DOT11_MGMT_HDR_LEN + DOT11_BCN_PRB_FIXED_LEN;
+ ssid_ie = brcmf_parse_tlvs(
+ (u8 *)&settings->beacon.head[ie_offset],
+ settings->beacon.head_len - ie_offset,
+ WLAN_EID_SSID);
+ if (!ssid_ie || ssid_ie->len > IEEE80211_MAX_SSID_LEN)
+ return -EINVAL;
+
+ memcpy(ssid_le.SSID, ssid_ie->data, ssid_ie->len);
+ ssid_le.SSID_len = cpu_to_le32(ssid_ie->len);
+ brcmf_dbg(TRACE, "SSID is (%s) in Head\n", ssid_le.SSID);
+ } else {
+ memcpy(ssid_le.SSID, settings->ssid, settings->ssid_len);
+ ssid_le.SSID_len = cpu_to_le32((u32)settings->ssid_len);
+ }
+
+ if (!mbss) {
+ brcmf_set_mpc(ifp, 0);
+ brcmf_configure_arp_nd_offload(ifp, false);
+ }
+
+ /* find the RSN_IE */
+ rsn_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail,
+ settings->beacon.tail_len, WLAN_EID_RSN);
+
+ /* find the WPA_IE */
+ wpa_ie = brcmf_find_wpaie((u8 *)settings->beacon.tail,
+ settings->beacon.tail_len);
+
+ if ((wpa_ie != NULL || rsn_ie != NULL)) {
+ brcmf_dbg(TRACE, "WPA(2) IE is found\n");
+ if (wpa_ie != NULL) {
+ /* WPA IE */
+ err = brcmf_configure_wpaie(ifp, wpa_ie, false);
+ if (err < 0)
+ goto exit;
+ } else {
+ struct brcmf_vs_tlv *tmp_ie;
+
+ tmp_ie = (struct brcmf_vs_tlv *)rsn_ie;
+
+ /* RSN IE */
+ err = brcmf_configure_wpaie(ifp, tmp_ie, true);
+ if (err < 0)
+ goto exit;
+ }
+ } else {
+ brcmf_dbg(TRACE, "No WPA(2) IEs found\n");
+ brcmf_configure_opensecurity(ifp);
+ }
+
+ /* Parameters shared by all radio interfaces */
+ if (!mbss) {
+ if ((supports_11d) && (is_11d != ifp->vif->is_11d)) {
+ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_REGULATORY,
+ is_11d);
+ if (err < 0) {
+ brcmf_err("Regulatory Set Error, %d\n", err);
+ goto exit;
+ }
+ }
+ if (settings->beacon_interval) {
+ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD,
+ settings->beacon_interval);
+ if (err < 0) {
+ brcmf_err("Beacon Interval Set Error, %d\n",
+ err);
+ goto exit;
+ }
+ }
+ if (settings->dtim_period) {
+ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_DTIMPRD,
+ settings->dtim_period);
+ if (err < 0) {
+ brcmf_err("DTIM Interval Set Error, %d\n", err);
+ goto exit;
+ }
+ }
+
+ if ((dev_role == NL80211_IFTYPE_AP) && (ifp->ifidx == 0)) {
+ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
+ if (err < 0) {
+ brcmf_err("BRCMF_C_DOWN error %d\n", err);
+ goto exit;
+ }
+ brcmf_fil_iovar_int_set(ifp, "apsta", 0);
+ }
+
+ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 1);
+ if (err < 0) {
+ brcmf_err("SET INFRA error %d\n", err);
+ goto exit;
+ }
+ } else if (WARN_ON(supports_11d && (is_11d != ifp->vif->is_11d))) {
+ /* Multiple-BSS should use same 11d configuration */
+ err = -EINVAL;
+ goto exit;
+ }
+
+ /* Interface specific setup */
+ if (dev_role == NL80211_IFTYPE_AP) {
+ if ((brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) && (!mbss))
+ brcmf_fil_iovar_int_set(ifp, "mbss", 1);
+
+ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 1);
+ if (err < 0) {
+ brcmf_err("setting AP mode failed %d\n", err);
+ goto exit;
+ }
+ if (!mbss) {
+ /* Firmware 10.x requires setting channel after enabling
+ * AP and before bringing interface up.
+ */
+ err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec);
+ if (err < 0) {
+ brcmf_err("Set Channel failed: chspec=%d, %d\n",
+ chanspec, err);
+ goto exit;
+ }
+ }
+ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1);
+ if (err < 0) {
+ brcmf_err("BRCMF_C_UP error (%d)\n", err);
+ goto exit;
+ }
+ /* On DOWN the firmware removes the WEP keys, reconfigure
+ * them if they were set.
+ */
+ brcmf_cfg80211_reconfigure_wep(ifp);
+
+ memset(&join_params, 0, sizeof(join_params));
+ /* join parameters starts with ssid */
+ memcpy(&join_params.ssid_le, &ssid_le, sizeof(ssid_le));
+ /* create softap */
+ err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
+ &join_params, sizeof(join_params));
+ if (err < 0) {
+ brcmf_err("SET SSID error (%d)\n", err);
+ goto exit;
+ }
+
+ if (settings->hidden_ssid) {
+ err = brcmf_fil_iovar_int_set(ifp, "closednet", 1);
+ if (err) {
+ brcmf_err("closednet error (%d)\n", err);
+ goto exit;
+ }
+ }
+
+ brcmf_dbg(TRACE, "AP mode configuration complete\n");
+ } else if (dev_role == NL80211_IFTYPE_P2P_GO) {
+ err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec);
+ if (err < 0) {
+ brcmf_err("Set Channel failed: chspec=%d, %d\n",
+ chanspec, err);
+ goto exit;
+ }
+ err = brcmf_fil_bsscfg_data_set(ifp, "ssid", &ssid_le,
+ sizeof(ssid_le));
+ if (err < 0) {
+ brcmf_err("setting ssid failed %d\n", err);
+ goto exit;
+ }
+ bss_enable.bsscfgidx = cpu_to_le32(ifp->bsscfgidx);
+ bss_enable.enable = cpu_to_le32(1);
+ err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable,
+ sizeof(bss_enable));
+ if (err < 0) {
+ brcmf_err("bss_enable config failed %d\n", err);
+ goto exit;
+ }
+
+ brcmf_dbg(TRACE, "GO mode configuration complete\n");
+ } else {
+ WARN_ON(1);
+ }
+
+ brcmf_config_ap_mgmt_ie(ifp->vif, &settings->beacon);
+ set_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);
+ brcmf_net_setcarrier(ifp, true);
+
+exit:
+ if ((err) && (!mbss)) {
+ brcmf_set_mpc(ifp, 1);
+ brcmf_configure_arp_nd_offload(ifp, true);
+ }
+ return err;
+}
+
+static int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev)
+{
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ s32 err;
+ struct brcmf_fil_bss_enable_le bss_enable;
+ struct brcmf_join_params join_params;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ if (ifp->vif->wdev.iftype == NL80211_IFTYPE_AP) {
+ /* Due to most likely deauths outstanding we sleep */
+ /* first to make sure they get processed by fw. */
+ msleep(400);
+
+ if (ifp->vif->mbss) {
+ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
+ return err;
+ }
+
+ /* First BSS doesn't get a full reset */
+ if (ifp->bsscfgidx == 0)
+ brcmf_fil_iovar_int_set(ifp, "closednet", 0);
+
+ memset(&join_params, 0, sizeof(join_params));
+ err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
+ &join_params, sizeof(join_params));
+ if (err < 0)
+ brcmf_err("SET SSID error (%d)\n", err);
+ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
+ if (err < 0)
+ brcmf_err("BRCMF_C_DOWN error %d\n", err);
+ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 0);
+ if (err < 0)
+ brcmf_err("setting AP mode failed %d\n", err);
+ if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS))
+ brcmf_fil_iovar_int_set(ifp, "mbss", 0);
+ brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_REGULATORY,
+ ifp->vif->is_11d);
+ /* Bring device back up so it can be used again */
+ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1);
+ if (err < 0)
+ brcmf_err("BRCMF_C_UP error %d\n", err);
+
+ brcmf_vif_clear_mgmt_ies(ifp->vif);
+ } else {
+ bss_enable.bsscfgidx = cpu_to_le32(ifp->bsscfgidx);
+ bss_enable.enable = cpu_to_le32(0);
+ err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable,
+ sizeof(bss_enable));
+ if (err < 0)
+ brcmf_err("bss_enable config failed %d\n", err);
+ }
+ brcmf_set_mpc(ifp, 1);
+ brcmf_configure_arp_nd_offload(ifp, true);
+ clear_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);
+ brcmf_net_setcarrier(ifp, false);
+
+ return err;
+}
+
+static s32
+brcmf_cfg80211_change_beacon(struct wiphy *wiphy, struct net_device *ndev,
+ struct cfg80211_beacon_data *info)
+{
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ s32 err;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ err = brcmf_config_ap_mgmt_ie(ifp->vif, info);
+
+ return err;
+}
+
+static int
+brcmf_cfg80211_del_station(struct wiphy *wiphy, struct net_device *ndev,
+ struct station_del_parameters *params)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct brcmf_scb_val_le scbval;
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ s32 err;
+
+ if (!params->mac)
+ return -EFAULT;
+
+ brcmf_dbg(TRACE, "Enter %pM\n", params->mac);
+
+ if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif)
+ ifp = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp;
+ if (!check_vif_up(ifp->vif))
+ return -EIO;
+
+ memcpy(&scbval.ea, params->mac, ETH_ALEN);
+ scbval.val = cpu_to_le32(params->reason_code);
+ err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCB_DEAUTHENTICATE_FOR_REASON,
+ &scbval, sizeof(scbval));
+ if (err)
+ brcmf_err("SCB_DEAUTHENTICATE_FOR_REASON failed %d\n", err);
+
+ brcmf_dbg(TRACE, "Exit\n");
+ return err;
+}
+
+static int
+brcmf_cfg80211_change_station(struct wiphy *wiphy, struct net_device *ndev,
+ const u8 *mac, struct station_parameters *params)
+{
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ s32 err;
+
+ brcmf_dbg(TRACE, "Enter, MAC %pM, mask 0x%04x set 0x%04x\n", mac,
+ params->sta_flags_mask, params->sta_flags_set);
+
+ /* Ignore all 00 MAC */
+ if (is_zero_ether_addr(mac))
+ return 0;
+
+ if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))
+ return 0;
+
+ if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED))
+ err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SCB_AUTHORIZE,
+ (void *)mac, ETH_ALEN);
+ else
+ err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SCB_DEAUTHORIZE,
+ (void *)mac, ETH_ALEN);
+ if (err < 0)
+ brcmf_err("Setting SCB (de-)authorize failed, %d\n", err);
+
+ return err;
+}
+
+static void
+brcmf_cfg80211_mgmt_frame_register(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ u16 frame_type, bool reg)
+{
+ struct brcmf_cfg80211_vif *vif;
+ u16 mgmt_type;
+
+ brcmf_dbg(TRACE, "Enter, frame_type %04x, reg=%d\n", frame_type, reg);
+
+ mgmt_type = (frame_type & IEEE80211_FCTL_STYPE) >> 4;
+ vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
+ if (reg)
+ vif->mgmt_rx_reg |= BIT(mgmt_type);
+ else
+ vif->mgmt_rx_reg &= ~BIT(mgmt_type);
+}
+
+
+static int
+brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
+ struct cfg80211_mgmt_tx_params *params, u64 *cookie)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct ieee80211_channel *chan = params->chan;
+ const u8 *buf = params->buf;
+ size_t len = params->len;
+ const struct ieee80211_mgmt *mgmt;
+ struct brcmf_cfg80211_vif *vif;
+ s32 err = 0;
+ s32 ie_offset;
+ s32 ie_len;
+ struct brcmf_fil_action_frame_le *action_frame;
+ struct brcmf_fil_af_params_le *af_params;
+ bool ack;
+ s32 chan_nr;
+ u32 freq;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ *cookie = 0;
+
+ mgmt = (const struct ieee80211_mgmt *)buf;
+
+ if (!ieee80211_is_mgmt(mgmt->frame_control)) {
+ brcmf_err("Driver only allows MGMT packet type\n");
+ return -EPERM;
+ }
+
+ vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
+
+ if (ieee80211_is_probe_resp(mgmt->frame_control)) {
+ /* Right now the only reason to get a probe response */
+ /* is for p2p listen response or for p2p GO from */
+ /* wpa_supplicant. Unfortunately the probe is send */
+ /* on primary ndev, while dongle wants it on the p2p */
+ /* vif. Since this is only reason for a probe */
+ /* response to be sent, the vif is taken from cfg. */
+ /* If ever desired to send proberesp for non p2p */
+ /* response then data should be checked for */
+ /* "DIRECT-". Note in future supplicant will take */
+ /* dedicated p2p wdev to do this and then this 'hack'*/
+ /* is not needed anymore. */
+ ie_offset = DOT11_MGMT_HDR_LEN +
+ DOT11_BCN_PRB_FIXED_LEN;
+ ie_len = len - ie_offset;
+ if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif)
+ vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
+ err = brcmf_vif_set_mgmt_ie(vif,
+ BRCMF_VNDR_IE_PRBRSP_FLAG,
+ &buf[ie_offset],
+ ie_len);
+ cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, true,
+ GFP_KERNEL);
+ } else if (ieee80211_is_action(mgmt->frame_control)) {
+ af_params = kzalloc(sizeof(*af_params), GFP_KERNEL);
+ if (af_params == NULL) {
+ brcmf_err("unable to allocate frame\n");
+ err = -ENOMEM;
+ goto exit;
+ }
+ action_frame = &af_params->action_frame;
+ /* Add the packet Id */
+ action_frame->packet_id = cpu_to_le32(*cookie);
+ /* Add BSSID */
+ memcpy(&action_frame->da[0], &mgmt->da[0], ETH_ALEN);
+ memcpy(&af_params->bssid[0], &mgmt->bssid[0], ETH_ALEN);
+ /* Add the length exepted for 802.11 header */
+ action_frame->len = cpu_to_le16(len - DOT11_MGMT_HDR_LEN);
+ /* Add the channel. Use the one specified as parameter if any or
+ * the current one (got from the firmware) otherwise
+ */
+ if (chan)
+ freq = chan->center_freq;
+ else
+ brcmf_fil_cmd_int_get(vif->ifp, BRCMF_C_GET_CHANNEL,
+ &freq);
+ chan_nr = ieee80211_frequency_to_channel(freq);
+ af_params->channel = cpu_to_le32(chan_nr);
+
+ memcpy(action_frame->data, &buf[DOT11_MGMT_HDR_LEN],
+ le16_to_cpu(action_frame->len));
+
+ brcmf_dbg(TRACE, "Action frame, cookie=%lld, len=%d, freq=%d\n",
+ *cookie, le16_to_cpu(action_frame->len), freq);
+
+ ack = brcmf_p2p_send_action_frame(cfg, cfg_to_ndev(cfg),
+ af_params);
+
+ cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, ack,
+ GFP_KERNEL);
+ kfree(af_params);
+ } else {
+ brcmf_dbg(TRACE, "Unhandled, fc=%04x!!\n", mgmt->frame_control);
+ brcmf_dbg_hex_dump(true, buf, len, "payload, len=%zu\n", len);
+ }
+
+exit:
+ return err;
+}
+
+
+static int
+brcmf_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ u64 cookie)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct brcmf_cfg80211_vif *vif;
+ int err = 0;
+
+ brcmf_dbg(TRACE, "Enter p2p listen cancel\n");
+
+ vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
+ if (vif == NULL) {
+ brcmf_err("No p2p device available for probe response\n");
+ err = -ENODEV;
+ goto exit;
+ }
+ brcmf_p2p_cancel_remain_on_channel(vif->ifp);
+exit:
+ return err;
+}
+
+static int brcmf_cfg80211_get_channel(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ struct cfg80211_chan_def *chandef)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct net_device *ndev = wdev->netdev;
+ struct brcmf_if *ifp;
+ struct brcmu_chan ch;
+ enum nl80211_band band = 0;
+ enum nl80211_chan_width width = 0;
+ u32 chanspec;
+ int freq, err;
+
+ if (!ndev)
+ return -ENODEV;
+ ifp = netdev_priv(ndev);
+
+ err = brcmf_fil_iovar_int_get(ifp, "chanspec", &chanspec);
+ if (err) {
+ brcmf_err("chanspec failed (%d)\n", err);
+ return err;
+ }
+
+ ch.chspec = chanspec;
+ cfg->d11inf.decchspec(&ch);
+
+ switch (ch.band) {
+ case BRCMU_CHAN_BAND_2G:
+ band = NL80211_BAND_2GHZ;
+ break;
+ case BRCMU_CHAN_BAND_5G:
+ band = NL80211_BAND_5GHZ;
+ break;
+ }
+
+ switch (ch.bw) {
+ case BRCMU_CHAN_BW_80:
+ width = NL80211_CHAN_WIDTH_80;
+ break;
+ case BRCMU_CHAN_BW_40:
+ width = NL80211_CHAN_WIDTH_40;
+ break;
+ case BRCMU_CHAN_BW_20:
+ width = NL80211_CHAN_WIDTH_20;
+ break;
+ case BRCMU_CHAN_BW_80P80:
+ width = NL80211_CHAN_WIDTH_80P80;
+ break;
+ case BRCMU_CHAN_BW_160:
+ width = NL80211_CHAN_WIDTH_160;
+ break;
+ }
+
+ freq = ieee80211_channel_to_frequency(ch.control_ch_num, band);
+ chandef->chan = ieee80211_get_channel(wiphy, freq);
+ chandef->width = width;
+ chandef->center_freq1 = ieee80211_channel_to_frequency(ch.chnum, band);
+ chandef->center_freq2 = 0;
+
+ return 0;
+}
+
+static int brcmf_cfg80211_crit_proto_start(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ enum nl80211_crit_proto_id proto,
+ u16 duration)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct brcmf_cfg80211_vif *vif;
+
+ vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
+
+ /* only DHCP support for now */
+ if (proto != NL80211_CRIT_PROTO_DHCP)
+ return -EINVAL;
+
+ /* suppress and abort scanning */
+ set_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
+ brcmf_abort_scanning(cfg);
+
+ return brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_DISABLED, duration);
+}
+
+static void brcmf_cfg80211_crit_proto_stop(struct wiphy *wiphy,
+ struct wireless_dev *wdev)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct brcmf_cfg80211_vif *vif;
+
+ vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
+
+ brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0);
+ clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
+}
+
+static s32
+brcmf_notify_tdls_peer_event(struct brcmf_if *ifp,
+ const struct brcmf_event_msg *e, void *data)
+{
+ switch (e->reason) {
+ case BRCMF_E_REASON_TDLS_PEER_DISCOVERED:
+ brcmf_dbg(TRACE, "TDLS Peer Discovered\n");
+ break;
+ case BRCMF_E_REASON_TDLS_PEER_CONNECTED:
+ brcmf_dbg(TRACE, "TDLS Peer Connected\n");
+ brcmf_proto_add_tdls_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
+ break;
+ case BRCMF_E_REASON_TDLS_PEER_DISCONNECTED:
+ brcmf_dbg(TRACE, "TDLS Peer Disconnected\n");
+ brcmf_proto_delete_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
+ break;
+ }
+
+ return 0;
+}
+
+static int brcmf_convert_nl80211_tdls_oper(enum nl80211_tdls_operation oper)
+{
+ int ret;
+
+ switch (oper) {
+ case NL80211_TDLS_DISCOVERY_REQ:
+ ret = BRCMF_TDLS_MANUAL_EP_DISCOVERY;
+ break;
+ case NL80211_TDLS_SETUP:
+ ret = BRCMF_TDLS_MANUAL_EP_CREATE;
+ break;
+ case NL80211_TDLS_TEARDOWN:
+ ret = BRCMF_TDLS_MANUAL_EP_DELETE;
+ break;
+ default:
+ brcmf_err("unsupported operation: %d\n", oper);
+ ret = -EOPNOTSUPP;
+ }
+ return ret;
+}
+
+static int brcmf_cfg80211_tdls_oper(struct wiphy *wiphy,
+ struct net_device *ndev, const u8 *peer,
+ enum nl80211_tdls_operation oper)
+{
+ struct brcmf_if *ifp;
+ struct brcmf_tdls_iovar_le info;
+ int ret = 0;
+
+ ret = brcmf_convert_nl80211_tdls_oper(oper);
+ if (ret < 0)
+ return ret;
+
+ ifp = netdev_priv(ndev);
+ memset(&info, 0, sizeof(info));
+ info.mode = (u8)ret;
+ if (peer)
+ memcpy(info.ea, peer, ETH_ALEN);
+
+ ret = brcmf_fil_iovar_data_set(ifp, "tdls_endpoint",
+ &info, sizeof(info));
+ if (ret < 0)
+ brcmf_err("tdls_endpoint iovar failed: ret=%d\n", ret);
+
+ return ret;
+}
+
+static int
+brcmf_cfg80211_update_conn_params(struct wiphy *wiphy,
+ struct net_device *ndev,
+ struct cfg80211_connect_params *sme,
+ u32 changed)
+{
+ struct brcmf_if *ifp;
+ int err;
+
+ if (!(changed & UPDATE_ASSOC_IES))
+ return 0;
+
+ ifp = netdev_priv(ndev);
+ err = brcmf_vif_set_mgmt_ie(ifp->vif, BRCMF_VNDR_IE_ASSOCREQ_FLAG,
+ sme->ie, sme->ie_len);
+ if (err)
+ brcmf_err("Set Assoc REQ IE Failed\n");
+ else
+ brcmf_dbg(TRACE, "Applied Vndr IEs for Assoc request\n");
+
+ return err;
+}
+
+#ifdef CONFIG_PM
+static int
+brcmf_cfg80211_set_rekey_data(struct wiphy *wiphy, struct net_device *ndev,
+ struct cfg80211_gtk_rekey_data *gtk)
+{
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ struct brcmf_gtk_keyinfo_le gtk_le;
+ int ret;
+
+ brcmf_dbg(TRACE, "Enter, bssidx=%d\n", ifp->bsscfgidx);
+
+ memcpy(gtk_le.kck, gtk->kck, sizeof(gtk_le.kck));
+ memcpy(gtk_le.kek, gtk->kek, sizeof(gtk_le.kek));
+ memcpy(gtk_le.replay_counter, gtk->replay_ctr,
+ sizeof(gtk_le.replay_counter));
+
+ ret = brcmf_fil_iovar_data_set(ifp, "gtk_key_info", >k_le,
+ sizeof(gtk_le));
+ if (ret < 0)
+ brcmf_err("gtk_key_info iovar failed: ret=%d\n", ret);
+
+ return ret;
+}
+#endif
+
+static int brcmf_cfg80211_set_pmk(struct wiphy *wiphy, struct net_device *dev,
+ const struct cfg80211_pmk_conf *conf)
+{
+ struct brcmf_if *ifp;
+
+ brcmf_dbg(TRACE, "enter\n");
+
+ /* expect using firmware supplicant for 1X */
+ ifp = netdev_priv(dev);
+ if (WARN_ON(ifp->vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_1X))
+ return -EINVAL;
+
+ return brcmf_set_pmk(ifp, conf->pmk, conf->pmk_len);
+}
+
+static int brcmf_cfg80211_del_pmk(struct wiphy *wiphy, struct net_device *dev,
+ const u8 *aa)
+{
+ struct brcmf_if *ifp;
+
+ brcmf_dbg(TRACE, "enter\n");
+ ifp = netdev_priv(dev);
+ if (WARN_ON(ifp->vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_1X))
+ return -EINVAL;
+
+ return brcmf_set_pmk(ifp, NULL, 0);
+}
+
+#define BCMHD_KLV_MAX_LEN 256
+static s32 brcmf_cfg80211_change_keepalive (struct wiphy *wiphy,
+ struct net_device *dev,
+ struct cfg80211_keepalive_request *cfg80211_klv,
+ bool show)
+{
+ struct brcmf_if *ifp;
+ wl_mkeep_alive_pkt_t klv = {0};
+ wl_mkeep_alive_pkt_t *pklv = NULL;
+ char *klv_buf = NULL;
+ char *pdata = NULL;
+ int klv_buf_len = 0;
+ s32 err = 0;
+ u16 protocol;
+
+ brcmf_dbg(TRACE, "enter\n");
+ ifp = netdev_priv(dev);
+
+ if (!cfg80211_klv) {
+ brcmf_err("klv not found\n");
+ err = -EINVAL;
+ goto exit;
+ }
+
+ brcmf_info("cmd %d interval %d payload_len %d\n",
+ cfg80211_klv->cmd, cfg80211_klv->interval,
+ cfg80211_klv->payload_len);
+
+ if (cfg80211_klv->cmd != 0 && cfg80211_klv->cmd != 1) {
+ /* we only support add, del */
+ brcmf_err("klv cmd %d UNKNOWN\n", cfg80211_klv->cmd);
+ err = -EINVAL;
+ goto exit;
+ }
+
+ if (cfg80211_klv->cmd == 0 && cfg80211_klv->payload_len == 0) {
+ /* an add with no payload */
+ brcmf_err("klv cmd %d with no payload\n", cfg80211_klv->cmd);
+ err = -EINVAL;
+ goto exit;
+ }
+
+ if (cfg80211_klv->cmd == 0 && cfg80211_klv->interval == 0) {
+ /* an add with no interval */
+ brcmf_err("klv cmd %d with no interval\n", cfg80211_klv->cmd);
+ err = -EINVAL;
+ goto exit;
+ }
+
+ klv_buf = kzalloc(BCMHD_KLV_MAX_LEN, GFP_KERNEL);
+ if (!klv_buf) {
+ brcmf_err("klv kzalloc failed\n");
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ /* Keepalive attributes are set in local variable (klv),
+ * and then memcpy'ed into buffer (pklv) since there is
+ * no guarantee that the buffer is properly aligned.
+ */
+ klv.version = cpu_to_le16(WL_MKEEP_ALIVE_VERSION);
+ klv.length = cpu_to_le16(WL_MKEEP_ALIVE_FIXED_LEN);
+ klv.period_msec = cpu_to_le32(cfg80211_klv->interval);
+ if (cfg80211_klv->cmd == 0) {
+ /* payload length is DA+SA+type(0x800)+cfg80211_payload */
+ klv.len_bytes =
+ cpu_to_le16(ETH_ALEN * 2 + 2 + cfg80211_klv->payload_len);
+ }
+ klv.keep_alive_id = cfg80211_klv->index;
+
+ pklv = (wl_mkeep_alive_pkt_t *) (klv_buf + klv_buf_len);
+ memcpy((char *)pklv, &klv, WL_MKEEP_ALIVE_FIXED_LEN);
+ klv_buf_len += WL_MKEEP_ALIVE_FIXED_LEN;
+
+ if (cfg80211_klv->cmd == 0) {
+ /* We are adding a KLV, copy over the payload of the packet
+ * DA SA TYPE PAYLOAD
+ */
+ pdata = pklv->data;
+ memcpy((char *)pdata, cfg80211_klv->dst_macaddr, ETH_ALEN);
+ klv_buf_len += ETH_ALEN;
+ pdata += ETH_ALEN;
+
+ memcpy((char *)pdata, ifp->mac_addr, ETH_ALEN);
+ klv_buf_len += ETH_ALEN;
+ pdata += ETH_ALEN;
+
+ protocol = cpu_to_be16(0x800);
+ memcpy((char *)pdata, (void *) &protocol, sizeof(protocol));
+ klv_buf_len += 2;
+ pdata += 2;
+
+ memcpy((char *)pdata, cfg80211_klv->payload,
+ cfg80211_klv->payload_len);
+ klv_buf_len += cfg80211_klv->payload_len;
+ }
+
+ /* all filled in, issue the command */
+ err = brcmf_fil_iovar_data_set(ifp, "mkeep_alive", klv_buf, klv_buf_len);
+
+ /* set in /etc/modprobe.d/wlan_bcm.conf */
+ if (g_keepalive_debug) {
+ brcmf_info("klv.version %d\n", klv.version);
+ brcmf_info("klv.length %d\n", klv.length);
+ brcmf_info("klv.period_msec %d\n", cpu_to_le32(klv.period_msec));
+ brcmf_info("klv.len_bytes %d\n", klv.len_bytes);
+ brcmf_info("klv.keep_alive_id %d\n", klv.keep_alive_id);
+ brcmf_info("klv_buf @ %p = %d bytes\n", klv_buf, BCMHD_KLV_MAX_LEN);
+ brcmf_info("klv_buf_len %d\n", klv_buf_len);
+ brcmf_dbg_hex_dump(true, klv_buf, klv_buf_len, "klv:\n");
+ }
+
+exit:
+ if (klv_buf) kfree(klv_buf);
+ return err;
+}
+#undef BCMHD_KLV_MAX_LEN
+
+static struct cfg80211_ops brcmf_cfg80211_ops = {
+ .add_virtual_intf = brcmf_cfg80211_add_iface,
+ .del_virtual_intf = brcmf_cfg80211_del_iface,
+ .change_virtual_intf = brcmf_cfg80211_change_iface,
+ .scan = brcmf_cfg80211_scan,
+ .set_wiphy_params = brcmf_cfg80211_set_wiphy_params,
+ .join_ibss = brcmf_cfg80211_join_ibss,
+ .leave_ibss = brcmf_cfg80211_leave_ibss,
+ .get_station = brcmf_cfg80211_get_station,
+ .dump_station = brcmf_cfg80211_dump_station,
+ .set_tx_power = brcmf_cfg80211_set_tx_power,
+ .get_tx_power = brcmf_cfg80211_get_tx_power,
+ .add_key = brcmf_cfg80211_add_key,
+ .del_key = brcmf_cfg80211_del_key,
+ .get_key = brcmf_cfg80211_get_key,
+ .set_default_key = brcmf_cfg80211_config_default_key,
+ .set_default_mgmt_key = brcmf_cfg80211_config_default_mgmt_key,
+ .set_power_mgmt = brcmf_cfg80211_set_power_mgmt,
+ .connect = brcmf_cfg80211_connect,
+ .disconnect = brcmf_cfg80211_disconnect,
+ .suspend = brcmf_cfg80211_suspend,
+ .resume = brcmf_cfg80211_resume,
+ .set_pmksa = brcmf_cfg80211_set_pmksa,
+ .del_pmksa = brcmf_cfg80211_del_pmksa,
+ .flush_pmksa = brcmf_cfg80211_flush_pmksa,
+ .start_ap = brcmf_cfg80211_start_ap,
+ .stop_ap = brcmf_cfg80211_stop_ap,
+ .change_beacon = brcmf_cfg80211_change_beacon,
+ .del_station = brcmf_cfg80211_del_station,
+ .change_station = brcmf_cfg80211_change_station,
+ .sched_scan_start = brcmf_cfg80211_sched_scan_start,
+ .sched_scan_stop = brcmf_cfg80211_sched_scan_stop,
+ .mgmt_frame_register = brcmf_cfg80211_mgmt_frame_register,
+ .mgmt_tx = brcmf_cfg80211_mgmt_tx,
+ .remain_on_channel = brcmf_p2p_remain_on_channel,
+ .cancel_remain_on_channel = brcmf_cfg80211_cancel_remain_on_channel,
+ .get_channel = brcmf_cfg80211_get_channel,
+ .start_p2p_device = brcmf_p2p_start_device,
+ .stop_p2p_device = brcmf_p2p_stop_device,
+ .crit_proto_start = brcmf_cfg80211_crit_proto_start,
+ .crit_proto_stop = brcmf_cfg80211_crit_proto_stop,
+ .tdls_oper = brcmf_cfg80211_tdls_oper,
+ .update_connect_params = brcmf_cfg80211_update_conn_params,
+ .set_pmk = brcmf_cfg80211_set_pmk,
+ .del_pmk = brcmf_cfg80211_del_pmk,
+ .change_keepalive = brcmf_cfg80211_change_keepalive,
+};
+
+struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg,
+ enum nl80211_iftype type)
+{
+ struct brcmf_cfg80211_vif *vif_walk;
+ struct brcmf_cfg80211_vif *vif;
+ bool mbss;
+
+ brcmf_dbg(TRACE, "allocating virtual interface (size=%zu)\n",
+ sizeof(*vif));
+ vif = kzalloc(sizeof(*vif), GFP_KERNEL);
+ if (!vif)
+ return ERR_PTR(-ENOMEM);
+
+ vif->wdev.wiphy = cfg->wiphy;
+ vif->wdev.iftype = type;
+
+ brcmf_init_prof(&vif->profile);
+
+ if (type == NL80211_IFTYPE_AP) {
+ mbss = false;
+ list_for_each_entry(vif_walk, &cfg->vif_list, list) {
+ if (vif_walk->wdev.iftype == NL80211_IFTYPE_AP) {
+ mbss = true;
+ break;
+ }
+ }
+ vif->mbss = mbss;
+ }
+
+ list_add_tail(&vif->list, &cfg->vif_list);
+ return vif;
+}
+
+void brcmf_free_vif(struct brcmf_cfg80211_vif *vif)
+{
+ list_del(&vif->list);
+ kfree(vif);
+}
+
+void brcmf_cfg80211_free_netdev(struct net_device *ndev)
+{
+ struct brcmf_cfg80211_vif *vif;
+ struct brcmf_if *ifp;
+
+ ifp = netdev_priv(ndev);
+ vif = ifp->vif;
+
+ if (vif)
+ brcmf_free_vif(vif);
+}
+
+static bool brcmf_is_linkup(struct brcmf_cfg80211_vif *vif,
+ const struct brcmf_event_msg *e)
+{
+ u32 event = e->event_code;
+ u32 status = e->status;
+
+ if (vif->profile.use_fwsup == BRCMF_PROFILE_FWSUP_PSK &&
+ event == BRCMF_E_PSK_SUP &&
+ status == BRCMF_E_STATUS_FWSUP_COMPLETED)
+ set_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, &vif->sme_state);
+ if (event == BRCMF_E_SET_SSID && status == BRCMF_E_STATUS_SUCCESS) {
+ brcmf_dbg(CONN, "Processing set ssid\n");
+ memcpy(vif->profile.bssid, e->addr, ETH_ALEN);
+ if (vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_PSK)
+ return true;
+
+ set_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, &vif->sme_state);
+ }
+
+ if (test_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, &vif->sme_state) &&
+ test_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, &vif->sme_state)) {
+ clear_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, &vif->sme_state);
+ clear_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, &vif->sme_state);
+ return true;
+ }
+ return false;
+}
+
+static bool brcmf_is_linkdown(const struct brcmf_event_msg *e)
+{
+ u32 event = e->event_code;
+ u16 flags = e->flags;
+
+ if ((event == BRCMF_E_DEAUTH) || (event == BRCMF_E_DEAUTH_IND) ||
+ (event == BRCMF_E_DISASSOC_IND) ||
+ ((event == BRCMF_E_LINK) && (!(flags & BRCMF_EVENT_MSG_LINK)))) {
+ brcmf_dbg(CONN, "Processing link down\n");
+ return true;
+ }
+ return false;
+}
+
+static bool brcmf_is_nonetwork(struct brcmf_cfg80211_info *cfg,
+ const struct brcmf_event_msg *e)
+{
+ u32 event = e->event_code;
+ u32 status = e->status;
+
+ if (event == BRCMF_E_LINK && status == BRCMF_E_STATUS_NO_NETWORKS) {
+ brcmf_dbg(CONN, "Processing Link %s & no network found\n",
+ e->flags & BRCMF_EVENT_MSG_LINK ? "up" : "down");
+ return true;
+ }
+
+ if (event == BRCMF_E_SET_SSID && status != BRCMF_E_STATUS_SUCCESS) {
+ brcmf_dbg(CONN, "Processing connecting & no network found\n");
+ return true;
+ }
+
+ if (event == BRCMF_E_PSK_SUP &&
+ status != BRCMF_E_STATUS_FWSUP_COMPLETED) {
+ brcmf_dbg(CONN, "Processing failed supplicant state: %u\n",
+ status);
+ return true;
+ }
+
+ return false;
+}
+
+static void brcmf_clear_assoc_ies(struct brcmf_cfg80211_info *cfg)
+{
+ struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
+
+ kfree(conn_info->req_ie);
+ conn_info->req_ie = NULL;
+ conn_info->req_ie_len = 0;
+ kfree(conn_info->resp_ie);
+ conn_info->resp_ie = NULL;
+ conn_info->resp_ie_len = 0;
+}
+
+static s32 brcmf_get_assoc_ies(struct brcmf_cfg80211_info *cfg,
+ struct brcmf_if *ifp)
+{
+ struct brcmf_cfg80211_assoc_ielen_le *assoc_info;
+ struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
+ u32 req_len;
+ u32 resp_len;
+ s32 err = 0;
+
+ brcmf_clear_assoc_ies(cfg);
+
+ err = brcmf_fil_iovar_data_get(ifp, "assoc_info",
+ cfg->extra_buf, WL_ASSOC_INFO_MAX);
+ if (err) {
+ brcmf_err("could not get assoc info (%d)\n", err);
+ return err;
+ }
+ assoc_info =
+ (struct brcmf_cfg80211_assoc_ielen_le *)cfg->extra_buf;
+ req_len = le32_to_cpu(assoc_info->req_len);
+ resp_len = le32_to_cpu(assoc_info->resp_len);
+ if (req_len) {
+ err = brcmf_fil_iovar_data_get(ifp, "assoc_req_ies",
+ cfg->extra_buf,
+ WL_ASSOC_INFO_MAX);
+ if (err) {
+ brcmf_err("could not get assoc req (%d)\n", err);
+ return err;
+ }
+ conn_info->req_ie_len = req_len;
+ conn_info->req_ie =
+ kmemdup(cfg->extra_buf, conn_info->req_ie_len,
+ GFP_KERNEL);
+ } else {
+ conn_info->req_ie_len = 0;
+ conn_info->req_ie = NULL;
+ }
+ if (resp_len) {
+ err = brcmf_fil_iovar_data_get(ifp, "assoc_resp_ies",
+ cfg->extra_buf,
+ WL_ASSOC_INFO_MAX);
+ if (err) {
+ brcmf_err("could not get assoc resp (%d)\n", err);
+ return err;
+ }
+ conn_info->resp_ie_len = resp_len;
+ conn_info->resp_ie =
+ kmemdup(cfg->extra_buf, conn_info->resp_ie_len,
+ GFP_KERNEL);
+ } else {
+ conn_info->resp_ie_len = 0;
+ conn_info->resp_ie = NULL;
+ }
+ brcmf_dbg(CONN, "req len (%d) resp len (%d)\n",
+ conn_info->req_ie_len, conn_info->resp_ie_len);
+
+ return err;
+}
+
+static s32
+brcmf_bss_roaming_done(struct brcmf_cfg80211_info *cfg,
+ struct net_device *ndev,
+ const struct brcmf_event_msg *e)
+{
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
+ struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
+ struct wiphy *wiphy = cfg_to_wiphy(cfg);
+ struct ieee80211_channel *notify_channel = NULL;
+ struct ieee80211_supported_band *band;
+ struct brcmf_bss_info_le *bi;
+ struct brcmu_chan ch;
+ struct cfg80211_roam_info roam_info = {};
+ u32 freq;
+ s32 err = 0;
+ u8 *buf;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ brcmf_get_assoc_ies(cfg, ifp);
+ memcpy(profile->bssid, e->addr, ETH_ALEN);
+ brcmf_update_bss_info(cfg, ifp);
+
+ buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
+ if (buf == NULL) {
+ err = -ENOMEM;
+ goto done;
+ }
+
+ /* data sent to dongle has to be little endian */
+ *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);
+ err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO,
+ buf, WL_BSS_INFO_MAX);
+
+ if (err)
+ goto done;
+
+ bi = (struct brcmf_bss_info_le *)(buf + 4);
+ ch.chspec = le16_to_cpu(bi->chanspec);
+ cfg->d11inf.decchspec(&ch);
+
+ if (ch.band == BRCMU_CHAN_BAND_2G)
+ band = wiphy->bands[NL80211_BAND_2GHZ];
+ else
+ band = wiphy->bands[NL80211_BAND_5GHZ];
+
+ freq = ieee80211_channel_to_frequency(ch.control_ch_num, band->band);
+ notify_channel = ieee80211_get_channel(wiphy, freq);
+
+done:
+ kfree(buf);
+
+ roam_info.channel = notify_channel;
+ roam_info.bssid = profile->bssid;
+ roam_info.req_ie = conn_info->req_ie;
+ roam_info.req_ie_len = conn_info->req_ie_len;
+ roam_info.resp_ie = conn_info->resp_ie;
+ roam_info.resp_ie_len = conn_info->resp_ie_len;
+
+ cfg80211_roamed(ndev, &roam_info, GFP_KERNEL);
+ brcmf_dbg(CONN, "Report roaming result\n");
+
+ set_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state);
+ brcmf_dbg(TRACE, "Exit\n");
+ return err;
+}
+
+static s32
+brcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg,
+ struct net_device *ndev, const struct brcmf_event_msg *e,
+ bool completed)
+{
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
+ struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTING,
+ &ifp->vif->sme_state)) {
+ if (completed) {
+ brcmf_get_assoc_ies(cfg, ifp);
+ brcmf_update_bss_info(cfg, ifp);
+ set_bit(BRCMF_VIF_STATUS_CONNECTED,
+ &ifp->vif->sme_state);
+ }
+ cfg80211_connect_result(ndev,
+ (u8 *)profile->bssid,
+ conn_info->req_ie,
+ conn_info->req_ie_len,
+ conn_info->resp_ie,
+ conn_info->resp_ie_len,
+ completed ? WLAN_STATUS_SUCCESS :
+ WLAN_STATUS_AUTH_TIMEOUT,
+ GFP_KERNEL);
+ brcmf_dbg(CONN, "Report connect result - connection %s\n",
+ completed ? "succeeded" : "failed");
+ }
+ brcmf_dbg(TRACE, "Exit\n");
+ return 0;
+}
+
+static s32
+brcmf_notify_connect_status_ap(struct brcmf_cfg80211_info *cfg,
+ struct net_device *ndev,
+ const struct brcmf_event_msg *e, void *data)
+{
+ static int generation;
+ u32 event = e->event_code;
+ u32 reason = e->reason;
+ struct station_info sinfo;
+
+ brcmf_dbg(CONN, "event %s (%u), reason %d\n",
+ brcmf_fweh_event_name(event), event, reason);
+ if (event == BRCMF_E_LINK && reason == BRCMF_E_REASON_LINK_BSSCFG_DIS &&
+ ndev != cfg_to_ndev(cfg)) {
+ brcmf_dbg(CONN, "AP mode link down\n");
+ complete(&cfg->vif_disabled);
+ return 0;
+ }
+
+ if (((event == BRCMF_E_ASSOC_IND) || (event == BRCMF_E_REASSOC_IND)) &&
+ (reason == BRCMF_E_STATUS_SUCCESS)) {
+ memset(&sinfo, 0, sizeof(sinfo));
+ if (!data) {
+ brcmf_err("No IEs present in ASSOC/REASSOC_IND");
+ return -EINVAL;
+ }
+ sinfo.assoc_req_ies = data;
+ sinfo.assoc_req_ies_len = e->datalen;
+ generation++;
+ sinfo.generation = generation;
+ cfg80211_new_sta(ndev, e->addr, &sinfo, GFP_KERNEL);
+ } else if ((event == BRCMF_E_DISASSOC_IND) ||
+ (event == BRCMF_E_DEAUTH_IND) ||
+ (event == BRCMF_E_DEAUTH)) {
+ cfg80211_del_sta(ndev, e->addr, GFP_KERNEL);
+ }
+ return 0;
+}
+
+static s32
+brcmf_notify_connect_status(struct brcmf_if *ifp,
+ const struct brcmf_event_msg *e, void *data)
+{
+ struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
+ struct net_device *ndev = ifp->ndev;
+ struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
+ struct ieee80211_channel *chan;
+ s32 err = 0;
+
+ if ((e->event_code == BRCMF_E_DEAUTH) ||
+ (e->event_code == BRCMF_E_DEAUTH_IND) ||
+ (e->event_code == BRCMF_E_DISASSOC_IND) ||
+ ((e->event_code == BRCMF_E_LINK) && (!e->flags))) {
+ brcmf_proto_delete_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
+ }
+
+ if (brcmf_is_apmode(ifp->vif)) {
+ err = brcmf_notify_connect_status_ap(cfg, ndev, e, data);
+ } else if (brcmf_is_linkup(ifp->vif, e)) {
+ brcmf_dbg(CONN, "Linkup\n");
+ if (brcmf_is_ibssmode(ifp->vif)) {
+ brcmf_inform_ibss(cfg, ndev, e->addr);
+ chan = ieee80211_get_channel(cfg->wiphy, cfg->channel);
+ memcpy(profile->bssid, e->addr, ETH_ALEN);
+ cfg80211_ibss_joined(ndev, e->addr, chan, GFP_KERNEL);
+ clear_bit(BRCMF_VIF_STATUS_CONNECTING,
+ &ifp->vif->sme_state);
+ set_bit(BRCMF_VIF_STATUS_CONNECTED,
+ &ifp->vif->sme_state);
+ } else
+ brcmf_bss_connect_done(cfg, ndev, e, true);
+ brcmf_net_setcarrier(ifp, true);
+ } else if (brcmf_is_linkdown(e)) {
+ brcmf_dbg(CONN, "Linkdown\n");
+ if (!brcmf_is_ibssmode(ifp->vif)) {
+ pm_wakeup_event(ifp->drvr->bus_if->dev, 0);
+ brcmf_bss_connect_done(cfg, ndev, e, false);
+ brcmf_link_down(ifp->vif,
+ brcmf_map_fw_linkdown_reason(e));
+ brcmf_init_prof(ndev_to_prof(ndev));
+ if (ndev != cfg_to_ndev(cfg))
+ complete(&cfg->vif_disabled);
+ brcmf_net_setcarrier(ifp, false);
+ }
+ } else if (brcmf_is_nonetwork(cfg, e)) {
+ if (brcmf_is_ibssmode(ifp->vif))
+ clear_bit(BRCMF_VIF_STATUS_CONNECTING,
+ &ifp->vif->sme_state);
+ else
+ brcmf_bss_connect_done(cfg, ndev, e, false);
+ }
+
+ return err;
+}
+
+static s32
+brcmf_notify_roaming_status(struct brcmf_if *ifp,
+ const struct brcmf_event_msg *e, void *data)
+{
+ struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
+ u32 event = e->event_code;
+ u32 status = e->status;
+
+ if (event == BRCMF_E_ROAM && status == BRCMF_E_STATUS_SUCCESS) {
+ if (test_bit(BRCMF_VIF_STATUS_CONNECTED,
+ &ifp->vif->sme_state)) {
+ brcmf_bss_roaming_done(cfg, ifp->ndev, e);
+ } else {
+ brcmf_bss_connect_done(cfg, ifp->ndev, e, true);
+ brcmf_net_setcarrier(ifp, true);
+ }
+ }
+
+ return 0;
+}
+
+static s32
+brcmf_notify_mic_status(struct brcmf_if *ifp,
+ const struct brcmf_event_msg *e, void *data)
+{
+ u16 flags = e->flags;
+ enum nl80211_key_type key_type;
+
+ if (flags & BRCMF_EVENT_MSG_GROUP)
+ key_type = NL80211_KEYTYPE_GROUP;
+ else
+ key_type = NL80211_KEYTYPE_PAIRWISE;
+
+ cfg80211_michael_mic_failure(ifp->ndev, (u8 *)&e->addr, key_type, -1,
+ NULL, GFP_KERNEL);
+
+ return 0;
+}
+
+static s32 brcmf_notify_vif_event(struct brcmf_if *ifp,
+ const struct brcmf_event_msg *e, void *data)
+{
+ struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
+ struct brcmf_if_event *ifevent = (struct brcmf_if_event *)data;
+ struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
+ struct brcmf_cfg80211_vif *vif;
+
+ brcmf_dbg(TRACE, "Enter: action %u flags %u ifidx %u bsscfgidx %u\n",
+ ifevent->action, ifevent->flags, ifevent->ifidx,
+ ifevent->bsscfgidx);
+
+ spin_lock(&event->vif_event_lock);
+ event->action = ifevent->action;
+ vif = event->vif;
+
+ switch (ifevent->action) {
+ case BRCMF_E_IF_ADD:
+ /* waiting process may have timed out */
+ if (!cfg->vif_event.vif) {
+ spin_unlock(&event->vif_event_lock);
+ return -EBADF;
+ }
+
+ ifp->vif = vif;
+ vif->ifp = ifp;
+ if (ifp->ndev) {
+ vif->wdev.netdev = ifp->ndev;
+ ifp->ndev->ieee80211_ptr = &vif->wdev;
+ SET_NETDEV_DEV(ifp->ndev, wiphy_dev(cfg->wiphy));
+ }
+ spin_unlock(&event->vif_event_lock);
+ wake_up(&event->vif_wq);
+ return 0;
+
+ case BRCMF_E_IF_DEL:
+ spin_unlock(&event->vif_event_lock);
+ /* event may not be upon user request */
+ if (brcmf_cfg80211_vif_event_armed(cfg))
+ wake_up(&event->vif_wq);
+ return 0;
+
+ case BRCMF_E_IF_CHANGE:
+ spin_unlock(&event->vif_event_lock);
+ wake_up(&event->vif_wq);
+ return 0;
+
+ default:
+ spin_unlock(&event->vif_event_lock);
+ break;
+ }
+ return -EINVAL;
+}
+
+static s32
+brcmf_notify_pkt_filter_event(struct brcmf_if *ifp,
+ const struct brcmf_event_msg *e, void *data)
+{
+ u32 status = le32_to_cpu(e->status);
+
+ if (status == BRCMF_E_PKT_FILTER_TIMEOUT) {
+ brcmf_info("MACEVENT: Remote TCPKA Timed Out\n");
+ g_TCPKATimeout = true;
+ }
+
+ return 0;
+}
+
+static void brcmf_init_conf(struct brcmf_cfg80211_conf *conf)
+{
+ conf->frag_threshold = (u32)-1;
+ conf->rts_threshold = (u32)-1;
+ conf->retry_short = (u32)-1;
+ conf->retry_long = (u32)-1;
+}
+
+static void brcmf_register_event_handlers(struct brcmf_cfg80211_info *cfg)
+{
+ brcmf_fweh_register(cfg->pub, BRCMF_E_LINK,
+ brcmf_notify_connect_status);
+ brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH_IND,
+ brcmf_notify_connect_status);
+ brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH,
+ brcmf_notify_connect_status);
+ brcmf_fweh_register(cfg->pub, BRCMF_E_DISASSOC_IND,
+ brcmf_notify_connect_status);
+ brcmf_fweh_register(cfg->pub, BRCMF_E_ASSOC_IND,
+ brcmf_notify_connect_status);
+ brcmf_fweh_register(cfg->pub, BRCMF_E_REASSOC_IND,
+ brcmf_notify_connect_status);
+ brcmf_fweh_register(cfg->pub, BRCMF_E_ROAM,
+ brcmf_notify_roaming_status);
+ brcmf_fweh_register(cfg->pub, BRCMF_E_MIC_ERROR,
+ brcmf_notify_mic_status);
+ brcmf_fweh_register(cfg->pub, BRCMF_E_SET_SSID,
+ brcmf_notify_connect_status);
+ brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND,
+ brcmf_notify_sched_scan_results);
+ brcmf_fweh_register(cfg->pub, BRCMF_E_IF,
+ brcmf_notify_vif_event);
+ brcmf_fweh_register(cfg->pub, BRCMF_E_P2P_PROBEREQ_MSG,
+ brcmf_p2p_notify_rx_mgmt_p2p_probereq);
+ brcmf_fweh_register(cfg->pub, BRCMF_E_P2P_DISC_LISTEN_COMPLETE,
+ brcmf_p2p_notify_listen_complete);
+ brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_RX,
+ brcmf_p2p_notify_action_frame_rx);
+ brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_COMPLETE,
+ brcmf_p2p_notify_action_tx_complete);
+ brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_OFF_CHAN_COMPLETE,
+ brcmf_p2p_notify_action_tx_complete);
+ brcmf_fweh_register(cfg->pub, BRCMF_E_PSK_SUP,
+ brcmf_notify_connect_status);
+ brcmf_fweh_register(cfg->pub, BRCMF_E_PKT_FILTER,
+ brcmf_notify_pkt_filter_event);
+}
+
+static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_info *cfg)
+{
+ kfree(cfg->conf);
+ cfg->conf = NULL;
+ kfree(cfg->extra_buf);
+ cfg->extra_buf = NULL;
+ kfree(cfg->wowl.nd);
+ cfg->wowl.nd = NULL;
+ kfree(cfg->wowl.nd_info);
+ cfg->wowl.nd_info = NULL;
+ kfree(cfg->escan_info.escan_buf);
+ cfg->escan_info.escan_buf = NULL;
+}
+
+static s32 brcmf_init_priv_mem(struct brcmf_cfg80211_info *cfg)
+{
+ cfg->conf = kzalloc(sizeof(*cfg->conf), GFP_KERNEL);
+ if (!cfg->conf)
+ goto init_priv_mem_out;
+ cfg->extra_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
+ if (!cfg->extra_buf)
+ goto init_priv_mem_out;
+ cfg->wowl.nd = kzalloc(sizeof(*cfg->wowl.nd) + sizeof(u32), GFP_KERNEL);
+ if (!cfg->wowl.nd)
+ goto init_priv_mem_out;
+ cfg->wowl.nd_info = kzalloc(sizeof(*cfg->wowl.nd_info) +
+ sizeof(struct cfg80211_wowlan_nd_match *),
+ GFP_KERNEL);
+ if (!cfg->wowl.nd_info)
+ goto init_priv_mem_out;
+ cfg->escan_info.escan_buf = kzalloc(BRCMF_ESCAN_BUF_SIZE, GFP_KERNEL);
+ if (!cfg->escan_info.escan_buf)
+ goto init_priv_mem_out;
+
+ return 0;
+
+init_priv_mem_out:
+ brcmf_deinit_priv_mem(cfg);
+
+ return -ENOMEM;
+}
+
+static s32 wl_init_priv(struct brcmf_cfg80211_info *cfg)
+{
+ s32 err = 0;
+
+ cfg->scan_request = NULL;
+ cfg->pwr_save = true;
+ cfg->active_scan = true; /* we do active scan per default */
+ cfg->dongle_up = false; /* dongle is not up yet */
+ err = brcmf_init_priv_mem(cfg);
+ if (err)
+ return err;
+ brcmf_register_event_handlers(cfg);
+ mutex_init(&cfg->usr_sync);
+ brcmf_init_escan(cfg);
+ brcmf_init_conf(cfg->conf);
+ init_completion(&cfg->vif_disabled);
+ return err;
+}
+
+static void wl_deinit_priv(struct brcmf_cfg80211_info *cfg)
+{
+ cfg->dongle_up = false; /* dongle down */
+ brcmf_abort_scanning(cfg);
+ brcmf_deinit_priv_mem(cfg);
+}
+
+static void init_vif_event(struct brcmf_cfg80211_vif_event *event)
+{
+ init_waitqueue_head(&event->vif_wq);
+ spin_lock_init(&event->vif_event_lock);
+}
+
+extern int g_beacon_loss_timeout;
+static s32 brcmf_dongle_roam(struct brcmf_if *ifp)
+{
+ s32 err;
+ u32 bcn_timeout;
+ __le32 roamtrigger[2];
+ __le32 roam_delta[2];
+
+ /* Configure beacon timeout value based upon roaming setting */
+ if (g_beacon_loss_timeout > 0)
+ bcn_timeout = g_beacon_loss_timeout;
+ else if (ifp->drvr->settings->roamoff)
+ bcn_timeout = BRCMF_DEFAULT_BCN_TIMEOUT_ROAM_OFF;
+ else
+ bcn_timeout = BRCMF_DEFAULT_BCN_TIMEOUT_ROAM_ON;
+ err = brcmf_fil_iovar_int_set(ifp, "bcn_timeout", bcn_timeout);
+ if (err) {
+ brcmf_err("bcn_timeout error (%d)\n", err);
+ goto roam_setup_done;
+ }
+
+ /* Enable/Disable built-in roaming to allow supplicant to take care of
+ * roaming.
+ */
+ brcmf_dbg(INFO, "Internal Roaming = %s\n",
+ ifp->drvr->settings->roamoff ? "Off" : "On");
+ err = brcmf_fil_iovar_int_set(ifp, "roam_off",
+ ifp->drvr->settings->roamoff);
+ if (err) {
+ brcmf_err("roam_off error (%d)\n", err);
+ goto roam_setup_done;
+ }
+
+ roamtrigger[0] = cpu_to_le32(WL_ROAM_TRIGGER_LEVEL);
+ roamtrigger[1] = cpu_to_le32(BRCM_BAND_ALL);
+ err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_TRIGGER,
+ (void *)roamtrigger, sizeof(roamtrigger));
+ if (err) {
+ brcmf_err("WLC_SET_ROAM_TRIGGER error (%d)\n", err);
+ goto roam_setup_done;
+ }
+
+ roam_delta[0] = cpu_to_le32(WL_ROAM_DELTA);
+ roam_delta[1] = cpu_to_le32(BRCM_BAND_ALL);
+ err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_DELTA,
+ (void *)roam_delta, sizeof(roam_delta));
+ if (err) {
+ brcmf_err("WLC_SET_ROAM_DELTA error (%d)\n", err);
+ goto roam_setup_done;
+ }
+
+roam_setup_done:
+ return err;
+}
+
+static s32
+brcmf_dongle_scantime(struct brcmf_if *ifp)
+{
+ s32 err = 0;
+
+ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_CHANNEL_TIME,
+ BRCMF_SCAN_CHANNEL_TIME);
+ if (err) {
+ brcmf_err("Scan assoc time error (%d)\n", err);
+ goto dongle_scantime_out;
+ }
+ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_UNASSOC_TIME,
+ BRCMF_SCAN_UNASSOC_TIME);
+ if (err) {
+ brcmf_err("Scan unassoc time error (%d)\n", err);
+ goto dongle_scantime_out;
+ }
+
+ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_PASSIVE_TIME,
+ BRCMF_SCAN_PASSIVE_TIME);
+ if (err) {
+ brcmf_err("Scan passive time error (%d)\n", err);
+ goto dongle_scantime_out;
+ }
+
+dongle_scantime_out:
+ return err;
+}
+
+static void brcmf_update_bw40_channel_flag(struct ieee80211_channel *channel,
+ struct brcmu_chan *ch)
+{
+ u32 ht40_flag;
+
+ ht40_flag = channel->flags & IEEE80211_CHAN_NO_HT40;
+ if (ch->sb == BRCMU_CHAN_SB_U) {
+ if (ht40_flag == IEEE80211_CHAN_NO_HT40)
+ channel->flags &= ~IEEE80211_CHAN_NO_HT40;
+ channel->flags |= IEEE80211_CHAN_NO_HT40PLUS;
+ } else {
+ /* It should be one of
+ * IEEE80211_CHAN_NO_HT40 or
+ * IEEE80211_CHAN_NO_HT40PLUS
+ */
+ channel->flags &= ~IEEE80211_CHAN_NO_HT40;
+ if (ht40_flag == IEEE80211_CHAN_NO_HT40)
+ channel->flags |= IEEE80211_CHAN_NO_HT40MINUS;
+ }
+}
+
+static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg,
+ u32 bw_cap[])
+{
+ struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
+ struct ieee80211_supported_band *band;
+ struct ieee80211_channel *channel;
+ struct wiphy *wiphy;
+ struct brcmf_chanspec_list *list;
+ struct brcmu_chan ch;
+ int err;
+ u8 *pbuf;
+ u32 i, j;
+ u32 total;
+ u32 chaninfo;
+
+ pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
+
+ if (pbuf == NULL)
+ return -ENOMEM;
+
+ list = (struct brcmf_chanspec_list *)pbuf;
+
+ err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf,
+ BRCMF_DCMD_MEDLEN);
+ if (err) {
+ brcmf_err("get chanspecs error (%d)\n", err);
+ goto fail_pbuf;
+ }
+
+ wiphy = cfg_to_wiphy(cfg);
+ band = wiphy->bands[NL80211_BAND_2GHZ];
+ if (band)
+ for (i = 0; i < band->n_channels; i++)
+ band->channels[i].flags = IEEE80211_CHAN_DISABLED;
+ band = wiphy->bands[NL80211_BAND_5GHZ];
+ if (band)
+ for (i = 0; i < band->n_channels; i++)
+ band->channels[i].flags = IEEE80211_CHAN_DISABLED;
+
+ total = le32_to_cpu(list->count);
+ for (i = 0; i < total; i++) {
+ ch.chspec = (u16)le32_to_cpu(list->element[i]);
+ cfg->d11inf.decchspec(&ch);
+
+ if (ch.band == BRCMU_CHAN_BAND_2G) {
+ band = wiphy->bands[NL80211_BAND_2GHZ];
+ } else if (ch.band == BRCMU_CHAN_BAND_5G) {
+ band = wiphy->bands[NL80211_BAND_5GHZ];
+ } else {
+ brcmf_err("Invalid channel Spec. 0x%x.\n", ch.chspec);
+ continue;
+ }
+ if (!band)
+ continue;
+ if (!(bw_cap[band->band] & WLC_BW_40MHZ_BIT) &&
+ ch.bw == BRCMU_CHAN_BW_40)
+ continue;
+ if (!(bw_cap[band->band] & WLC_BW_80MHZ_BIT) &&
+ ch.bw == BRCMU_CHAN_BW_80)
+ continue;
+
+ channel = NULL;
+ for (j = 0; j < band->n_channels; j++) {
+ if (band->channels[j].hw_value == ch.control_ch_num) {
+ channel = &band->channels[j];
+ break;
+ }
+ }
+ if (!channel) {
+ /* It seems firmware supports some channel we never
+ * considered. Something new in IEEE standard?
+ */
+ brcmf_err("Ignoring unexpected firmware channel %d\n",
+ ch.control_ch_num);
+ continue;
+ }
+
+ if (channel->orig_flags & IEEE80211_CHAN_DISABLED)
+ continue;
+
+ /* assuming the chanspecs order is HT20,
+ * HT40 upper, HT40 lower, and VHT80.
+ */
+ if (ch.bw == BRCMU_CHAN_BW_80) {
+ channel->flags &= ~IEEE80211_CHAN_NO_80MHZ;
+ } else if (ch.bw == BRCMU_CHAN_BW_40) {
+ brcmf_update_bw40_channel_flag(channel, &ch);
+ } else {
+ /* enable the channel and disable other bandwidths
+ * for now as mentioned order assure they are enabled
+ * for subsequent chanspecs.
+ */
+ channel->flags = IEEE80211_CHAN_NO_HT40 |
+ IEEE80211_CHAN_NO_80MHZ;
+ ch.bw = BRCMU_CHAN_BW_20;
+ cfg->d11inf.encchspec(&ch);
+ chaninfo = ch.chspec;
+ err = brcmf_fil_bsscfg_int_get(ifp, "per_chan_info",
+ &chaninfo);
+ if (!err) {
+ if (chaninfo & WL_CHAN_RADAR)
+ channel->flags |=
+ (IEEE80211_CHAN_RADAR |
+ IEEE80211_CHAN_NO_IR);
+ if (chaninfo & WL_CHAN_PASSIVE)
+ channel->flags |=
+ IEEE80211_CHAN_NO_IR;
+ }
+ }
+ }
+
+fail_pbuf:
+ kfree(pbuf);
+ return err;
+}
+
+static int brcmf_enable_bw40_2g(struct brcmf_cfg80211_info *cfg)
+{
+ struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
+ struct ieee80211_supported_band *band;
+ struct brcmf_fil_bwcap_le band_bwcap;
+ struct brcmf_chanspec_list *list;
+ u8 *pbuf;
+ u32 val;
+ int err;
+ struct brcmu_chan ch;
+ u32 num_chan;
+ int i, j;
+
+ /* verify support for bw_cap command */
+ val = WLC_BAND_5G;
+ err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &val);
+
+ if (!err) {
+ /* only set 2G bandwidth using bw_cap command */
+ band_bwcap.band = cpu_to_le32(WLC_BAND_2G);
+ band_bwcap.bw_cap = cpu_to_le32(WLC_BW_CAP_40MHZ);
+ err = brcmf_fil_iovar_data_set(ifp, "bw_cap", &band_bwcap,
+ sizeof(band_bwcap));
+ } else {
+ brcmf_dbg(INFO, "fallback to mimo_bw_cap\n");
+ val = WLC_N_BW_40ALL;
+ err = brcmf_fil_iovar_int_set(ifp, "mimo_bw_cap", val);
+ }
+
+ if (!err) {
+ /* update channel info in 2G band */
+ pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
+
+ if (pbuf == NULL)
+ return -ENOMEM;
+
+ ch.band = BRCMU_CHAN_BAND_2G;
+ ch.bw = BRCMU_CHAN_BW_40;
+ ch.sb = BRCMU_CHAN_SB_NONE;
+ ch.chnum = 0;
+ cfg->d11inf.encchspec(&ch);
+
+ /* pass encoded chanspec in query */
+ *(__le16 *)pbuf = cpu_to_le16(ch.chspec);
+
+ err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf,
+ BRCMF_DCMD_MEDLEN);
+ if (err) {
+ brcmf_err("get chanspecs error (%d)\n", err);
+ kfree(pbuf);
+ return err;
+ }
+
+ band = cfg_to_wiphy(cfg)->bands[NL80211_BAND_2GHZ];
+ list = (struct brcmf_chanspec_list *)pbuf;
+ num_chan = le32_to_cpu(list->count);
+ for (i = 0; i < num_chan; i++) {
+ ch.chspec = (u16)le32_to_cpu(list->element[i]);
+ cfg->d11inf.decchspec(&ch);
+ if (WARN_ON(ch.band != BRCMU_CHAN_BAND_2G))
+ continue;
+ if (WARN_ON(ch.bw != BRCMU_CHAN_BW_40))
+ continue;
+ for (j = 0; j < band->n_channels; j++) {
+ if (band->channels[j].hw_value == ch.control_ch_num)
+ break;
+ }
+ if (WARN_ON(j == band->n_channels))
+ continue;
+
+ brcmf_update_bw40_channel_flag(&band->channels[j], &ch);
+ }
+ kfree(pbuf);
+ }
+ return err;
+}
+
+static void brcmf_get_bwcap(struct brcmf_if *ifp, u32 bw_cap[])
+{
+ u32 band, mimo_bwcap;
+ int err;
+
+ band = WLC_BAND_2G;
+ err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &band);
+ if (!err) {
+ bw_cap[NL80211_BAND_2GHZ] = band;
+ band = WLC_BAND_5G;
+ err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &band);
+ if (!err) {
+ bw_cap[NL80211_BAND_5GHZ] = band;
+ return;
+ }
+ WARN_ON(1);
+ return;
+ }
+ brcmf_dbg(INFO, "fallback to mimo_bw_cap info\n");
+ mimo_bwcap = 0;
+ err = brcmf_fil_iovar_int_get(ifp, "mimo_bw_cap", &mimo_bwcap);
+ if (err)
+ /* assume 20MHz if firmware does not give a clue */
+ mimo_bwcap = WLC_N_BW_20ALL;
+
+ switch (mimo_bwcap) {
+ case WLC_N_BW_40ALL:
+ bw_cap[NL80211_BAND_2GHZ] |= WLC_BW_40MHZ_BIT;
+ /* fall-thru */
+ case WLC_N_BW_20IN2G_40IN5G:
+ bw_cap[NL80211_BAND_5GHZ] |= WLC_BW_40MHZ_BIT;
+ /* fall-thru */
+ case WLC_N_BW_20ALL:
+ bw_cap[NL80211_BAND_2GHZ] |= WLC_BW_20MHZ_BIT;
+ bw_cap[NL80211_BAND_5GHZ] |= WLC_BW_20MHZ_BIT;
+ break;
+ default:
+ brcmf_err("invalid mimo_bw_cap value\n");
+ }
+}
+
+static void brcmf_update_ht_cap(struct ieee80211_supported_band *band,
+ u32 bw_cap[2], u32 nchain)
+{
+ band->ht_cap.ht_supported = true;
+ if (bw_cap[band->band] & WLC_BW_40MHZ_BIT) {
+ band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40;
+ band->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+ }
+ band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20;
+ band->ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40;
+ band->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
+ band->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16;
+ memset(band->ht_cap.mcs.rx_mask, 0xff, nchain);
+ band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
+}
+
+static __le16 brcmf_get_mcs_map(u32 nchain, enum ieee80211_vht_mcs_support supp)
+{
+ u16 mcs_map;
+ int i;
+
+ for (i = 0, mcs_map = 0xFFFF; i < nchain; i++)
+ mcs_map = (mcs_map << 2) | supp;
+
+ return cpu_to_le16(mcs_map);
+}
+
+static void brcmf_update_vht_cap(struct ieee80211_supported_band *band,
+ u32 bw_cap[2], u32 nchain, u32 txstreams,
+ u32 txbf_bfe_cap, u32 txbf_bfr_cap)
+{
+ __le16 mcs_map;
+
+ /* not allowed in 2.4G band */
+ if (band->band == NL80211_BAND_2GHZ)
+ return;
+
+ band->vht_cap.vht_supported = true;
+ /* 80MHz is mandatory */
+ band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_80;
+ if (bw_cap[band->band] & WLC_BW_160MHZ_BIT) {
+ band->vht_cap.cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
+ band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_160;
+ }
+ /* all support 256-QAM */
+ mcs_map = brcmf_get_mcs_map(nchain, IEEE80211_VHT_MCS_SUPPORT_0_9);
+ band->vht_cap.vht_mcs.rx_mcs_map = mcs_map;
+ band->vht_cap.vht_mcs.tx_mcs_map = mcs_map;
+
+ /* Beamforming support information */
+ if (txbf_bfe_cap & BRCMF_TXBF_SU_BFE_CAP)
+ band->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
+ if (txbf_bfe_cap & BRCMF_TXBF_MU_BFE_CAP)
+ band->vht_cap.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
+ if (txbf_bfr_cap & BRCMF_TXBF_SU_BFR_CAP)
+ band->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE;
+ if (txbf_bfr_cap & BRCMF_TXBF_MU_BFR_CAP)
+ band->vht_cap.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE;
+
+ if ((txbf_bfe_cap || txbf_bfr_cap) && (txstreams > 1)) {
+ band->vht_cap.cap |=
+ (2 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT);
+ band->vht_cap.cap |= ((txstreams - 1) <<
+ IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT);
+ band->vht_cap.cap |=
+ IEEE80211_VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB;
+ }
+}
+
+static int brcmf_setup_wiphybands(struct wiphy *wiphy)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
+ struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
+ u32 nmode = 0;
+ u32 vhtmode = 0;
+ u32 bw_cap[2] = { WLC_BW_20MHZ_BIT, WLC_BW_20MHZ_BIT };
+ u32 rxchain;
+ u32 nchain;
+ int err;
+ s32 i;
+ struct ieee80211_supported_band *band;
+ u32 txstreams = 0;
+ u32 txbf_bfe_cap = 0;
+ u32 txbf_bfr_cap = 0;
+
+ (void)brcmf_fil_iovar_int_get(ifp, "vhtmode", &vhtmode);
+ err = brcmf_fil_iovar_int_get(ifp, "nmode", &nmode);
+ if (err) {
+ brcmf_err("nmode error (%d)\n", err);
+ } else {
+ brcmf_get_bwcap(ifp, bw_cap);
+ }
+ brcmf_dbg(INFO, "nmode=%d, vhtmode=%d, bw_cap=(%d, %d)\n",
+ nmode, vhtmode, bw_cap[NL80211_BAND_2GHZ],
+ bw_cap[NL80211_BAND_5GHZ]);
+
+ err = brcmf_fil_iovar_int_get(ifp, "rxchain", &rxchain);
+ if (err) {
+ brcmf_err("rxchain error (%d)\n", err);
+ nchain = 1;
+ } else {
+ for (nchain = 0; rxchain; nchain++)
+ rxchain = rxchain & (rxchain - 1);
+ }
+ brcmf_dbg(INFO, "nchain=%d\n", nchain);
+
+ err = brcmf_construct_chaninfo(cfg, bw_cap);
+ if (err) {
+ brcmf_err("brcmf_construct_chaninfo failed (%d)\n", err);
+ return err;
+ }
+
+ if (vhtmode) {
+ (void)brcmf_fil_iovar_int_get(ifp, "txstreams", &txstreams);
+ (void)brcmf_fil_iovar_int_get(ifp, "txbf_bfe_cap",
+ &txbf_bfe_cap);
+ (void)brcmf_fil_iovar_int_get(ifp, "txbf_bfr_cap",
+ &txbf_bfr_cap);
+ }
+
+ wiphy = cfg_to_wiphy(cfg);
+ for (i = 0; i < ARRAY_SIZE(wiphy->bands); i++) {
+ band = wiphy->bands[i];
+ if (band == NULL)
+ continue;
+
+ if (nmode)
+ brcmf_update_ht_cap(band, bw_cap, nchain);
+ if (vhtmode)
+ brcmf_update_vht_cap(band, bw_cap, nchain, txstreams,
+ txbf_bfe_cap, txbf_bfr_cap);
+ }
+
+ return 0;
+}
+
+static const struct ieee80211_txrx_stypes
+brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = {
+ [NL80211_IFTYPE_STATION] = {
+ .tx = 0xffff,
+ .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
+ },
+ [NL80211_IFTYPE_P2P_CLIENT] = {
+ .tx = 0xffff,
+ .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
+ },
+ [NL80211_IFTYPE_P2P_GO] = {
+ .tx = 0xffff,
+ .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
+ BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
+ BIT(IEEE80211_STYPE_DISASSOC >> 4) |
+ BIT(IEEE80211_STYPE_AUTH >> 4) |
+ BIT(IEEE80211_STYPE_DEAUTH >> 4) |
+ BIT(IEEE80211_STYPE_ACTION >> 4)
+ },
+ [NL80211_IFTYPE_P2P_DEVICE] = {
+ .tx = 0xffff,
+ .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
+ }
+};
+
+/**
+ * brcmf_setup_ifmodes() - determine interface modes and combinations.
+ *
+ * @wiphy: wiphy object.
+ * @ifp: interface object needed for feat module api.
+ *
+ * The interface modes and combinations are determined dynamically here
+ * based on firmware functionality.
+ *
+ * no p2p and no mbss:
+ *
+ * #STA <= 1, #AP <= 1, channels = 1, 2 total
+ *
+ * no p2p and mbss:
+ *
+ * #STA <= 1, #AP <= 1, channels = 1, 2 total
+ * #AP <= 4, matching BI, channels = 1, 4 total
+ *
+ * p2p, no mchan, and mbss:
+ *
+ * #STA <= 1, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 1, channels = 1, 3 total
+ * #STA <= 1, #P2P-DEV <= 1, #AP <= 1, #P2P-CL <= 1, channels = 1, 4 total
+ * #AP <= 4, matching BI, channels = 1, 4 total
+ *
+ * p2p, mchan, and mbss:
+ *
+ * #STA <= 1, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 1, channels = 2, 3 total
+ * #STA <= 1, #P2P-DEV <= 1, #AP <= 1, #P2P-CL <= 1, channels = 1, 4 total
+ * #AP <= 4, matching BI, channels = 1, 4 total
+ */
+static int brcmf_setup_ifmodes(struct wiphy *wiphy, struct brcmf_if *ifp)
+{
+ struct ieee80211_iface_combination *combo = NULL;
+ struct ieee80211_iface_limit *c0_limits = NULL;
+ struct ieee80211_iface_limit *p2p_limits = NULL;
+ struct ieee80211_iface_limit *mbss_limits = NULL;
+ bool mbss, p2p;
+ int i, c, n_combos;
+
+ mbss = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS);
+ p2p = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_P2P);
+
+ n_combos = 1 + !!p2p + !!mbss;
+ combo = kcalloc(n_combos, sizeof(*combo), GFP_KERNEL);
+ if (!combo)
+ goto err;
+
+ wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
+ BIT(NL80211_IFTYPE_ADHOC) |
+ BIT(NL80211_IFTYPE_AP);
+
+ c = 0;
+ i = 0;
+ c0_limits = kcalloc(p2p ? 3 : 2, sizeof(*c0_limits), GFP_KERNEL);
+ if (!c0_limits)
+ goto err;
+ c0_limits[i].max = 1;
+ c0_limits[i++].types = BIT(NL80211_IFTYPE_STATION);
+ if (p2p) {
+ if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN))
+ combo[c].num_different_channels = 2;
+ else
+ combo[c].num_different_channels = 1;
+ wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_CLIENT) |
+ BIT(NL80211_IFTYPE_P2P_GO) |
+ BIT(NL80211_IFTYPE_P2P_DEVICE);
+ c0_limits[i].max = 1;
+ c0_limits[i++].types = BIT(NL80211_IFTYPE_P2P_DEVICE);
+ c0_limits[i].max = 1;
+ c0_limits[i++].types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
+ BIT(NL80211_IFTYPE_P2P_GO);
+ } else {
+ combo[c].num_different_channels = 1;
+ c0_limits[i].max = 1;
+ c0_limits[i++].types = BIT(NL80211_IFTYPE_AP);
+ }
+ combo[c].max_interfaces = i;
+ combo[c].n_limits = i;
+ combo[c].limits = c0_limits;
+
+ if (p2p) {
+ c++;
+ i = 0;
+ p2p_limits = kcalloc(4, sizeof(*p2p_limits), GFP_KERNEL);
+ if (!p2p_limits)
+ goto err;
+ p2p_limits[i].max = 1;
+ p2p_limits[i++].types = BIT(NL80211_IFTYPE_STATION);
+ p2p_limits[i].max = 1;
+ p2p_limits[i++].types = BIT(NL80211_IFTYPE_AP);
+ p2p_limits[i].max = 1;
+ p2p_limits[i++].types = BIT(NL80211_IFTYPE_P2P_CLIENT);
+ p2p_limits[i].max = 1;
+ p2p_limits[i++].types = BIT(NL80211_IFTYPE_P2P_DEVICE);
+ combo[c].num_different_channels = 1;
+ combo[c].max_interfaces = i;
+ combo[c].n_limits = i;
+ combo[c].limits = p2p_limits;
+ }
+
+ if (mbss) {
+ c++;
+ i = 0;
+ mbss_limits = kcalloc(1, sizeof(*mbss_limits), GFP_KERNEL);
+ if (!mbss_limits)
+ goto err;
+ mbss_limits[i].max = 4;
+ mbss_limits[i++].types = BIT(NL80211_IFTYPE_AP);
+ combo[c].beacon_int_infra_match = true;
+ combo[c].num_different_channels = 1;
+ combo[c].max_interfaces = 4;
+ combo[c].n_limits = i;
+ combo[c].limits = mbss_limits;
+ }
+
+ wiphy->n_iface_combinations = n_combos;
+ wiphy->iface_combinations = combo;
+ return 0;
+
+err:
+ kfree(c0_limits);
+ kfree(p2p_limits);
+ kfree(mbss_limits);
+ kfree(combo);
+ return -ENOMEM;
+}
+
+static void brcmf_wiphy_pno_params(struct wiphy *wiphy)
+{
+ /* scheduled scan settings */
+ wiphy->max_sched_scan_reqs = 1;
+ wiphy->max_sched_scan_ssids = BRCMF_PNO_MAX_PFN_COUNT;
+ wiphy->max_match_sets = BRCMF_PNO_MAX_PFN_COUNT;
+ wiphy->max_sched_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
+ wiphy->max_sched_scan_plan_interval = BRCMF_PNO_SCHED_SCAN_MAX_PERIOD;
+}
+
+#ifdef CONFIG_PM
+static const struct wiphy_wowlan_support brcmf_wowlan_support = {
+ .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT,
+ .n_patterns = BRCMF_WOWL_MAXPATTERNS,
+ .pattern_max_len = BRCMF_WOWL_MAXPATTERNSIZE,
+ .pattern_min_len = 1,
+ .max_pkt_offset = 1500,
+};
+#endif
+
+static void brcmf_wiphy_wowl_params(struct wiphy *wiphy, struct brcmf_if *ifp)
+{
+#ifdef CONFIG_PM
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct wiphy_wowlan_support *wowl;
+ struct cfg80211_wowlan *brcm_wowlan_config = NULL;
+
+ wowl = kmemdup(&brcmf_wowlan_support, sizeof(brcmf_wowlan_support),
+ GFP_KERNEL);
+ if (!wowl) {
+ brcmf_err("only support basic wowlan features\n");
+ wiphy->wowlan = &brcmf_wowlan_support;
+ return;
+ }
+
+ if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO)) {
+ if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_ND)) {
+ wowl->flags |= WIPHY_WOWLAN_NET_DETECT;
+ wowl->max_nd_match_sets = BRCMF_PNO_MAX_PFN_COUNT;
+ init_waitqueue_head(&cfg->wowl.nd_data_wait);
+ }
+ }
+ if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_GTK)) {
+ wowl->flags |= WIPHY_WOWLAN_SUPPORTS_GTK_REKEY;
+ wowl->flags |= WIPHY_WOWLAN_GTK_REKEY_FAILURE;
+ }
+
+ wiphy->wowlan = wowl;
+
+ /* If this is not provided cfg stack will get disconnect
+ * during suspend.
+ */
+ brcm_wowlan_config = kzalloc(sizeof(struct cfg80211_wowlan), GFP_KERNEL);
+ if (brcm_wowlan_config) {
+ brcm_wowlan_config->disconnect = true;
+ brcm_wowlan_config->gtk_rekey_failure = true;
+ brcm_wowlan_config->eap_identity_req = true;
+ brcm_wowlan_config->four_way_handshake = true;
+ brcm_wowlan_config->patterns = NULL;
+ brcm_wowlan_config->n_patterns = 0;
+ brcm_wowlan_config->tcp = NULL;
+ } else {
+ brcmf_err("Can not allocate memory for brcm_wowlan_config,"
+ " So wiphy->wowlan_config is set to NULL\n");
+ }
+ wiphy->wowlan_config = brcm_wowlan_config;
+
+#endif
+}
+
+static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp)
+{
+ struct brcmf_pub *drvr = ifp->drvr;
+ const struct ieee80211_iface_combination *combo;
+ struct ieee80211_supported_band *band;
+ u16 max_interfaces = 0;
+ __le32 bandlist[3];
+ u32 n_bands;
+ int err, i;
+
+ wiphy->max_scan_ssids = WL_NUM_SCAN_MAX;
+ wiphy->max_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
+ wiphy->max_num_pmkids = BRCMF_MAXPMKID;
+
+ err = brcmf_setup_ifmodes(wiphy, ifp);
+ if (err)
+ return err;
+
+ for (i = 0, combo = wiphy->iface_combinations;
+ i < wiphy->n_iface_combinations; i++, combo++) {
+ max_interfaces = max(max_interfaces, combo->max_interfaces);
+ }
+
+ for (i = 0; i < max_interfaces && i < ARRAY_SIZE(drvr->addresses);
+ i++) {
+ u8 *addr = drvr->addresses[i].addr;
+
+ memcpy(addr, drvr->mac, ETH_ALEN);
+ if (i) {
+ addr[0] |= BIT(1);
+ addr[ETH_ALEN - 1] ^= i;
+ }
+ }
+ wiphy->addresses = drvr->addresses;
+ wiphy->n_addresses = i;
+
+ wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
+ wiphy->cipher_suites = brcmf_cipher_suites;
+ wiphy->n_cipher_suites = ARRAY_SIZE(brcmf_cipher_suites);
+ if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP))
+ wiphy->n_cipher_suites--;
+ wiphy->bss_select_support = BIT(NL80211_BSS_SELECT_ATTR_RSSI) |
+ BIT(NL80211_BSS_SELECT_ATTR_BAND_PREF) |
+ BIT(NL80211_BSS_SELECT_ATTR_RSSI_ADJUST);
+
+ wiphy->flags |= WIPHY_FLAG_NETNS_OK |
+ WIPHY_FLAG_PS_ON_BY_DEFAULT |
+ WIPHY_FLAG_OFFCHAN_TX |
+ WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
+ if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_TDLS))
+ wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS;
+ if (!ifp->drvr->settings->roamoff)
+ wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM;
+ if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_FWSUP)) {
+ wiphy_ext_feature_set(wiphy,
+ NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK);
+ wiphy_ext_feature_set(wiphy,
+ NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X);
+ }
+ wiphy->mgmt_stypes = brcmf_txrx_stypes;
+ wiphy->max_remain_on_channel_duration = 5000;
+ if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO))
+ brcmf_wiphy_pno_params(wiphy);
+
+ /* vendor commands/events support */
+ wiphy->vendor_commands = brcmf_vendor_cmds;
+ wiphy->n_vendor_commands = BRCMF_VNDR_CMDS_LAST - 1;
+
+ if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL))
+ brcmf_wiphy_wowl_params(wiphy, ifp);
+ err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BANDLIST, &bandlist,
+ sizeof(bandlist));
+ if (err) {
+ brcmf_err("could not obtain band info: err=%d\n", err);
+ return err;
+ }
+ /* first entry in bandlist is number of bands */
+ n_bands = le32_to_cpu(bandlist[0]);
+ for (i = 1; i <= n_bands && i < ARRAY_SIZE(bandlist); i++) {
+ if (bandlist[i] == cpu_to_le32(WLC_BAND_2G)) {
+ band = kmemdup(&__wl_band_2ghz, sizeof(__wl_band_2ghz),
+ GFP_KERNEL);
+ if (!band)
+ return -ENOMEM;
+
+ band->channels = kmemdup(&__wl_2ghz_channels,
+ sizeof(__wl_2ghz_channels),
+ GFP_KERNEL);
+ if (!band->channels) {
+ kfree(band);
+ return -ENOMEM;
+ }
+
+ band->n_channels = ARRAY_SIZE(__wl_2ghz_channels);
+ wiphy->bands[NL80211_BAND_2GHZ] = band;
+ }
+ if (bandlist[i] == cpu_to_le32(WLC_BAND_5G)) {
+ band = kmemdup(&__wl_band_5ghz, sizeof(__wl_band_5ghz),
+ GFP_KERNEL);
+ if (!band)
+ return -ENOMEM;
+
+ band->channels = kmemdup(&__wl_5ghz_channels,
+ sizeof(__wl_5ghz_channels),
+ GFP_KERNEL);
+ if (!band->channels) {
+ kfree(band);
+ return -ENOMEM;
+ }
+
+ band->n_channels = ARRAY_SIZE(__wl_5ghz_channels);
+ wiphy->bands[NL80211_BAND_5GHZ] = band;
+ }
+ }
+
+ wiphy_read_of_freq_limits(wiphy);
+
+ return 0;
+}
+
+static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg)
+{
+ struct net_device *ndev;
+ struct wireless_dev *wdev;
+ struct brcmf_if *ifp;
+ s32 power_mode;
+ s32 err = 0;
+
+ if (cfg->dongle_up)
+ return err;
+
+ ndev = cfg_to_ndev(cfg);
+ wdev = ndev->ieee80211_ptr;
+ ifp = netdev_priv(ndev);
+
+ /* make sure RF is ready for work */
+ brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 0);
+
+ brcmf_dongle_scantime(ifp);
+
+ power_mode = cfg->pwr_save ? dhd_power_mode : PM_OFF;
+ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, power_mode);
+ if (err)
+ goto default_conf_out;
+ brcmf_dbg(INFO, "power save set to %s\n",
+ (power_mode ? "enabled" : "disabled"));
+
+ err = brcmf_dongle_roam(ifp);
+ if (err)
+ goto default_conf_out;
+ err = brcmf_cfg80211_change_iface(wdev->wiphy, ndev, wdev->iftype,
+ NULL);
+ if (err)
+ goto default_conf_out;
+
+ brcmf_configure_arp_nd_offload(ifp, true);
+
+ brcmf_dongle_pktfilter(ifp);
+
+ cfg->dongle_up = true;
+default_conf_out:
+
+ return err;
+
+}
+
+static s32 __brcmf_cfg80211_up(struct brcmf_if *ifp)
+{
+ set_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state);
+
+ return brcmf_config_dongle(ifp->drvr->config);
+}
+
+static s32 __brcmf_cfg80211_down(struct brcmf_if *ifp)
+{
+ struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
+
+ /*
+ * While going down, if associated with AP disassociate
+ * from AP to save power
+ */
+ if (check_vif_up(ifp->vif)) {
+ brcmf_link_down(ifp->vif, WLAN_REASON_UNSPECIFIED);
+
+ /* Make sure WPA_Supplicant receives all the event
+ generated due to DISASSOC call to the fw to keep
+ the state fw and WPA_Supplicant state consistent
+ */
+ brcmf_delay(500);
+ }
+
+ brcmf_abort_scanning(cfg);
+ clear_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state);
+
+ return 0;
+}
+
+s32 brcmf_cfg80211_up(struct net_device *ndev)
+{
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
+ s32 err = 0;
+
+ mutex_lock(&cfg->usr_sync);
+ err = __brcmf_cfg80211_up(ifp);
+ mutex_unlock(&cfg->usr_sync);
+
+ return err;
+}
+
+s32 brcmf_cfg80211_down(struct net_device *ndev)
+{
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
+ s32 err = 0;
+
+ mutex_lock(&cfg->usr_sync);
+ err = __brcmf_cfg80211_down(ifp);
+ mutex_unlock(&cfg->usr_sync);
+
+ return err;
+}
+
+enum nl80211_iftype brcmf_cfg80211_get_iftype(struct brcmf_if *ifp)
+{
+ struct wireless_dev *wdev = &ifp->vif->wdev;
+
+ return wdev->iftype;
+}
+
+bool brcmf_get_vif_state_any(struct brcmf_cfg80211_info *cfg,
+ unsigned long state)
+{
+ struct brcmf_cfg80211_vif *vif;
+
+ list_for_each_entry(vif, &cfg->vif_list, list) {
+ if (test_bit(state, &vif->sme_state))
+ return true;
+ }
+ return false;
+}
+
+static inline bool vif_event_equals(struct brcmf_cfg80211_vif_event *event,
+ u8 action)
+{
+ u8 evt_action;
+
+ spin_lock(&event->vif_event_lock);
+ evt_action = event->action;
+ spin_unlock(&event->vif_event_lock);
+ return evt_action == action;
+}
+
+void brcmf_cfg80211_arm_vif_event(struct brcmf_cfg80211_info *cfg,
+ struct brcmf_cfg80211_vif *vif)
+{
+ struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
+
+ spin_lock(&event->vif_event_lock);
+ event->vif = vif;
+ event->action = 0;
+ spin_unlock(&event->vif_event_lock);
+}
+
+bool brcmf_cfg80211_vif_event_armed(struct brcmf_cfg80211_info *cfg)
+{
+ struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
+ bool armed;
+
+ spin_lock(&event->vif_event_lock);
+ armed = event->vif != NULL;
+ spin_unlock(&event->vif_event_lock);
+
+ return armed;
+}
+
+int brcmf_cfg80211_wait_vif_event(struct brcmf_cfg80211_info *cfg,
+ u8 action, ulong timeout)
+{
+ struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
+
+ return wait_event_timeout(event->vif_wq,
+ vif_event_equals(event, action), timeout);
+}
+
+
+struct brcmfmac_pd_cc_entry brcm_wlan_translate_nodfs_table[] = {
+ {"00", "XZ", 981}, /* Universal if Country code is unknown or empty */
+ {"US", "US", 901},
+ {"AT", "AT", 289},
+ {"AU", "AU", 41},
+ {"BR", "BR", 18},
+ {"CA", "CA", 939},
+ {"CH", "E0", 33},
+ {"CY", "E0", 33},
+ {"CZ", "E0", 33},
+ {"DE", "E0", 33},
+ {"DK", "E0", 33},
+ {"EE", "E0", 33},
+ {"ES", "E0", 33},
+ {"EU", "E0", 33},
+ {"FI", "E0", 33},
+ {"FR", "E0", 33},
+ {"GB", "E0", 33},
+ {"GR", "E0", 33},
+ {"HK", "SG", 20},
+ {"HR", "E0", 33},
+ {"HU", "E0", 33},
+ {"IE", "E0", 33},
+ {"IN", "IN", 29},
+ {"ID", "ID", 5},
+ {"IS", "E0", 33},
+ {"IT", "E0", 33},
+ {"JP", "JP", 87},
+ {"KR", "KR", 79},
+ {"KW", "KW", 5},
+ {"LI", "E0", 33},
+ {"LT", "E0", 33},
+ {"LU", "E0", 33},
+ {"LV", "LV", 4},
+ {"MA", "MA", 2},
+ {"MT", "E0", 33},
+ {"MY", "MY", 17},
+ {"MX", "US", 177},
+ {"NL", "E0", 33},
+ {"NO", "E0", 33},
+ {"PL", "E0", 33},
+ {"PT", "E0", 33},
+ {"RO", "E0", 33},
+ {"SE", "E0", 33},
+ {"SG", "SG", 20},
+ {"SI", "E0", 33},
+ {"SK", "E0", 33},
+ {"SZ", "E0", 33},
+ {"TH", "TH", 9},
+ {"TW", "TW", 60},
+};
+
+/* Customized Locale table : for each of the products */
+/* Note:
+ * FW version 6.30.190.31 don't have Europe country locale defined for F1 & O1,
+ * thus map AT to ROW (XT/71) for F1 & O1
+ */
+const struct brcmfmac_pd_cc_entry O1_translate_custom_table[] = {
+ {"00", "XT", 71}, /* WW locale "00" */
+ {"AT", "XT", 995},
+ {"CA", "CA", 935},
+ {"US", "US", 893}
+};
+const struct brcmfmac_pd_cc_entry D3_translate_custom_table[] = {
+ {"00", "XT", 71}, /* WW locale "00" */
+ {"AT", "AT", 295},
+ {"CA", "CA", 937},
+ {"US", "US", 895}
+};
+const struct brcmfmac_pd_cc_entry F1_translate_custom_table[] = {
+ {"00", "XT", 71}, /* WW locale "00" */
+ {"AT", "XT", 71},
+ {"CA", "CA", 936},
+ {"US", "US", 894}
+};
+
+const struct brcmfmac_pd_cc_entry RQ1_translate_custom_table[] = {
+ {"00", "AT", 38}, /* WW locale "00" */
+ {"AT", "AT", 38},
+ {"CA", "CA", 930},
+ {"US", "US", 877}
+};
+
+const struct brcmfmac_pd_cc_entry BQ1_translate_custom_table[] = {
+ {"00", "AT", 22}, /* WW locale "00" */
+ {"AT", "AT", 22},
+ {"CA", "CA", 933},
+ {"US", "US", 889}
+};
+
+s32 brcmf_translate_country_code(struct brcmf_pub *drvr, char alpha2[2],
+ struct brcmf_fil_country_le *ccreq)
+{
+#ifdef PD_COUNTRY_CODES
+ struct brcmfmac_pd_cc *country_codes;
+#endif
+ struct brcmfmac_pd_cc_entry *cc, *cntry_tbl;
+ s32 found_index;
+ int i, size;
+
+#ifdef PD_COUNTRY_CODES
+ country_codes = drvr->settings->country_codes;
+ if (!country_codes) {
+ brcmf_dbg(TRACE, "No country codes configured for device\n");
+ return -EINVAL;
+ }
+ cntry_tbl = country_codes->table;
+ size = country_codes->table_size;
+#else
+ switch (g_devtype) {
+ case 1: /* D3 - Diamond3 */
+ case 8: /* A2 - Amber */
+ cntry_tbl = (struct brcmfmac_pd_cc_entry *)D3_translate_custom_table;
+ size = ARRAY_SIZE(D3_translate_custom_table);
+ break;
+ case 2: /* O1 - Onyx */
+ case 9: /* AG1 - Agate */
+ cntry_tbl = (struct brcmfmac_pd_cc_entry *)O1_translate_custom_table;
+ size = ARRAY_SIZE(O1_translate_custom_table);
+ break;
+ case 3: /* F1 - Flintstone */
+ cntry_tbl = (struct brcmfmac_pd_cc_entry *)F1_translate_custom_table;
+ size = ARRAY_SIZE(F1_translate_custom_table);
+ break;
+ case 4: /* QV2 */
+ cntry_tbl = (struct brcmfmac_pd_cc_entry *)brcm_wlan_translate_nodfs_table;
+ size = ARRAY_SIZE(brcm_wlan_translate_nodfs_table);
+ break;
+ case 5: /* RQ1 */
+ cntry_tbl = (struct brcmfmac_pd_cc_entry *)RQ1_translate_custom_table;
+ size = ARRAY_SIZE(RQ1_translate_custom_table);
+ break;
+ case 6: /* BQ1 */
+ cntry_tbl = (struct brcmfmac_pd_cc_entry *)BQ1_translate_custom_table;
+ size = ARRAY_SIZE(BQ1_translate_custom_table);
+ break;
+ default:
+ brcmf_err("%s: INVALID Device (%d)\n", __FUNCTION__, g_devtype);
+ return -EINVAL;
+ }
+#endif
+
+ if ((alpha2[0] == ccreq->country_abbrev[0]) &&
+ (alpha2[1] == ccreq->country_abbrev[1])) {
+ brcmf_dbg(TRACE, "Country code already set\n");
+ return -EAGAIN;
+ }
+
+ found_index = -1;
+ for (i = 0; i < size; i++) {
+ cc = &cntry_tbl[i];
+ if ((cc->iso3166[0] == '\0') && (found_index == -1))
+ found_index = i;
+ if ((cc->iso3166[0] == alpha2[0]) &&
+ (cc->iso3166[1] == alpha2[1])) {
+ found_index = i;
+ break;
+ }
+ }
+ if (found_index == -1) {
+ brcmf_dbg(TRACE, "No country code match found\n");
+ return -EINVAL;
+ }
+ memset(ccreq, 0, sizeof(*ccreq));
+ ccreq->rev = cpu_to_le32(cntry_tbl[found_index].rev);
+ memcpy(ccreq->ccode, cntry_tbl[found_index].cc,
+ BRCMF_COUNTRY_BUF_SZ);
+ ccreq->country_abbrev[0] = alpha2[0];
+ ccreq->country_abbrev[1] = alpha2[1];
+ ccreq->country_abbrev[2] = 0;
+
+ return 0;
+}
+
+s32 brcmf_set_restricted_band(struct brcmf_if *ifp, struct brcmf_fil_country_le *ccreq)
+{
+ if (!ifp || !ccreq) {
+ brcmf_err("%s: null parameter: ifp %p ccreq %p\n", __FUNCTION__, ifp, ccreq);
+ return 0;
+ }
+
+ if (g_devtype == 5 || g_devtype == 6) {
+ u32 to_band = WLC_BAND_INVALID, cur_band = WLC_BAND_INVALID;
+ s32 ret = 0;
+
+ /* RQ, BQ, required bandlock on 2g for all countries except US */
+ if (ccreq->country_abbrev[0] == 'U' && ccreq->country_abbrev[1] =='S')
+ to_band = WLC_BAND_AUTO;
+ else
+ to_band = WLC_BAND_2G;
+
+ ret = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BAND, to_band);
+ if (ret < 0) {
+ brcmf_err("%s: set band to %d failed with error %d\n", __FUNCTION__, to_band, ret);
+ return ret;
+ }
+
+ ret = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_BAND, &cur_band);
+ if (ret < 0) {
+ brcmf_err("%s: get band failed with error %d\n", __FUNCTION__, ret);
+ return ret;
+ }
+
+ if (cur_band != to_band) {
+ brcmf_err("%s: tried to set band %d, but read back different band %d\n", __FUNCTION__, to_band, cur_band);
+ return -1;
+ }
+
+ brcmf_info("%s: set band to %d succeed\n", __FUNCTION__, to_band);
+ }
+
+ return 0;
+}
+
+extern char g_country_abbrev[BRCMF_COUNTRY_BUF_SZ];
+static void brcmf_cfg80211_reg_notifier(struct wiphy *wiphy,
+ struct regulatory_request *req)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
+ struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
+ struct brcmf_fil_country_le ccreq;
+ s32 err;
+ int i;
+
+ /* The country code gets set to "00" by default at boot, ignore */
+ if (req->alpha2[0] == '0' && req->alpha2[1] == '0')
+ return;
+
+ /* reject country set if already set during driver load */
+ if (g_country_abbrev[0]) {
+ return;
+ }
+
+ /* ignore non-ISO3166 country codes */
+ for (i = 0; i < sizeof(req->alpha2); i++)
+ if (req->alpha2[i] < 'A' || req->alpha2[i] > 'Z') {
+ brcmf_err("not a ISO3166 code (0x%02x 0x%02x)\n",
+ req->alpha2[0], req->alpha2[1]);
+ return;
+ }
+
+ brcmf_dbg(TRACE, "Enter: initiator=%d, alpha=%c%c\n", req->initiator,
+ req->alpha2[0], req->alpha2[1]);
+
+ err = brcmf_fil_iovar_data_get(ifp, "country", &ccreq, sizeof(ccreq));
+ if (err) {
+ brcmf_err("Country code iovar returned err = %d\n", err);
+ return;
+ }
+
+ err = brcmf_translate_country_code(ifp->drvr, req->alpha2, &ccreq);
+ if (err)
+ return;
+
+ err = brcmf_fil_iovar_data_set(ifp, "country", &ccreq, sizeof(ccreq));
+ if (err) {
+ brcmf_err("Firmware rejected country setting\n");
+ return;
+ }
+
+ brcmf_set_restricted_band(ifp, &ccreq);
+
+ brcmf_setup_wiphybands(wiphy);
+}
+
+static void brcmf_free_wiphy(struct wiphy *wiphy)
+{
+ int i;
+
+ if (!wiphy)
+ return;
+
+ if (wiphy->iface_combinations) {
+ for (i = 0; i < wiphy->n_iface_combinations; i++)
+ kfree(wiphy->iface_combinations[i].limits);
+ }
+ kfree(wiphy->iface_combinations);
+ if (wiphy->bands[NL80211_BAND_2GHZ]) {
+ kfree(wiphy->bands[NL80211_BAND_2GHZ]->channels);
+ kfree(wiphy->bands[NL80211_BAND_2GHZ]);
+ }
+ if (wiphy->bands[NL80211_BAND_5GHZ]) {
+ kfree(wiphy->bands[NL80211_BAND_5GHZ]->channels);
+ kfree(wiphy->bands[NL80211_BAND_5GHZ]);
+ }
+#if IS_ENABLED(CONFIG_PM)
+ if (wiphy->wowlan != &brcmf_wowlan_support)
+ kfree(wiphy->wowlan);
+#endif
+ wiphy_free(wiphy);
+}
+
+struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
+ struct device *busdev,
+ bool p2pdev_forced)
+{
+ struct net_device *ndev = brcmf_get_ifp(drvr, 0)->ndev;
+ struct brcmf_cfg80211_info *cfg;
+ struct wiphy *wiphy;
+ struct cfg80211_ops *ops;
+ struct brcmf_cfg80211_vif *vif;
+ struct brcmf_if *ifp;
+ s32 err = 0;
+ s32 io_type;
+ u16 *cap = NULL;
+
+ if (!ndev) {
+ brcmf_err("ndev is invalid\n");
+ return NULL;
+ }
+
+ ops = kmemdup(&brcmf_cfg80211_ops, sizeof(*ops), GFP_KERNEL);
+ if (!ops)
+ return NULL;
+
+ ifp = netdev_priv(ndev);
+#ifdef CONFIG_PM
+ if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_GTK))
+ ops->set_rekey_data = brcmf_cfg80211_set_rekey_data;
+#endif
+ wiphy = wiphy_new(ops, sizeof(struct brcmf_cfg80211_info));
+ if (!wiphy) {
+ brcmf_err("Could not allocate wiphy device\n");
+ return NULL;
+ }
+ device_disable_async_suspend(&wiphy->dev);
+ memcpy(wiphy->perm_addr, drvr->mac, ETH_ALEN);
+ set_wiphy_dev(wiphy, busdev);
+
+ cfg = wiphy_priv(wiphy);
+ cfg->wiphy = wiphy;
+ cfg->ops = ops;
+ cfg->pub = drvr;
+ init_vif_event(&cfg->vif_event);
+ INIT_LIST_HEAD(&cfg->vif_list);
+
+ vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_STATION);
+ if (IS_ERR(vif))
+ goto wiphy_out;
+
+ vif->ifp = ifp;
+ vif->wdev.netdev = ndev;
+ ndev->ieee80211_ptr = &vif->wdev;
+ SET_NETDEV_DEV(ndev, wiphy_dev(cfg->wiphy));
+
+ err = wl_init_priv(cfg);
+ if (err) {
+ brcmf_err("Failed to init iwm_priv (%d)\n", err);
+ brcmf_free_vif(vif);
+ goto wiphy_out;
+ }
+ ifp->vif = vif;
+
+ /* determine d11 io type before wiphy setup */
+ err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_VERSION, &io_type);
+ if (err) {
+ brcmf_err("Failed to get D11 version (%d)\n", err);
+ goto priv_out;
+ }
+ cfg->d11inf.io_type = (u8)io_type;
+ brcmu_d11_attach(&cfg->d11inf);
+
+ err = brcmf_setup_wiphy(wiphy, ifp);
+ if (err < 0)
+ goto priv_out;
+
+ brcmf_dbg(INFO, "Registering custom regulatory\n");
+ wiphy->reg_notifier = brcmf_cfg80211_reg_notifier;
+ wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG | REGULATORY_COUNTRY_IE_IGNORE;
+ wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom);
+
+ /* firmware defaults to 40MHz disabled in 2G band. We signal
+ * cfg80211 here that we do and have it decide we can enable
+ * it. But first check if device does support 2G operation.
+ */
+ if (wiphy->bands[NL80211_BAND_2GHZ]) {
+ cap = &wiphy->bands[NL80211_BAND_2GHZ]->ht_cap.cap;
+ *cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+ }
+ err = wiphy_register(wiphy);
+ if (err < 0) {
+ brcmf_err("Could not register wiphy device (%d)\n", err);
+ goto priv_out;
+ }
+
+ err = brcmf_setup_wiphybands(wiphy);
+ if (err) {
+ brcmf_err("Setting wiphy bands failed (%d)\n", err);
+ goto wiphy_unreg_out;
+ }
+
+ /* If cfg80211 didn't disable 40MHz HT CAP in wiphy_register(),
+ * setup 40MHz in 2GHz band and enable OBSS scanning.
+ */
+ if (cap && (*cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) {
+ err = brcmf_enable_bw40_2g(cfg);
+ if (!err)
+ err = brcmf_fil_iovar_int_set(ifp, "obss_coex",
+ BRCMF_OBSS_COEX_AUTO);
+ else
+ *cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+ }
+ /* p2p might require that "if-events" get processed by fweh. So
+ * activate the already registered event handlers now and activate
+ * the rest when initialization has completed. drvr->config needs to
+ * be assigned before activating events.
+ */
+ drvr->config = cfg;
+ err = brcmf_fweh_activate_events(ifp);
+ if (err) {
+ brcmf_err("FWEH activation failed (%d)\n", err);
+ goto wiphy_unreg_out;
+ }
+
+ err = brcmf_p2p_attach(cfg, p2pdev_forced);
+ if (err) {
+ brcmf_err("P2P initialisation failed (%d)\n", err);
+ goto wiphy_unreg_out;
+ }
+ err = brcmf_btcoex_attach(cfg);
+ if (err) {
+ brcmf_err("BT-coex initialisation failed (%d)\n", err);
+ brcmf_p2p_detach(&cfg->p2p);
+ goto wiphy_unreg_out;
+ }
+
+ if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_TDLS)) {
+ err = brcmf_fil_iovar_int_set(ifp, "tdls_enable", 1);
+ if (err) {
+ brcmf_dbg(INFO, "TDLS not enabled (%d)\n", err);
+ wiphy->flags &= ~WIPHY_FLAG_SUPPORTS_TDLS;
+ } else {
+ brcmf_fweh_register(cfg->pub, BRCMF_E_TDLS_PEER_EVENT,
+ brcmf_notify_tdls_peer_event);
+ }
+ }
+
+ /* (re-) activate FWEH event handling */
+ err = brcmf_fweh_activate_events(ifp);
+ if (err) {
+ brcmf_err("FWEH activation failed (%d)\n", err);
+ goto detach;
+ }
+
+ /* Fill in some of the advertised nl80211 supported features */
+ if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SCAN_RANDOM_MAC)) {
+ wiphy->features |= NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR;
+#ifdef CONFIG_PM
+ if (wiphy->wowlan &&
+ wiphy->wowlan->flags & WIPHY_WOWLAN_NET_DETECT)
+ wiphy->features |= NL80211_FEATURE_ND_RANDOM_MAC_ADDR;
+#endif
+ }
+
+ return cfg;
+
+detach:
+ brcmf_btcoex_detach(cfg);
+ brcmf_p2p_detach(&cfg->p2p);
+wiphy_unreg_out:
+ wiphy_unregister(cfg->wiphy);
+priv_out:
+ wl_deinit_priv(cfg);
+ brcmf_free_vif(vif);
+ ifp->vif = NULL;
+wiphy_out:
+ brcmf_free_wiphy(wiphy);
+ kfree(ops);
+ return NULL;
+}
+
+void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg)
+{
+ if (!cfg)
+ return;
+
+ brcmf_btcoex_detach(cfg);
+ wiphy_unregister(cfg->wiphy);
+ kfree(cfg->ops);
+ wl_deinit_priv(cfg);
+ brcmf_free_wiphy(cfg->wiphy);
+}
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h
new file mode 100644
index 0000000..1bd1a9f
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h
@@ -0,0 +1,429 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef BRCMFMAC_CFG80211_H
+#define BRCMFMAC_CFG80211_H
+
+/* for brcmu_d11inf */
+#include <brcmu_d11.h>
+
+#include "core.h"
+#include "fwil_types.h"
+#include "p2p.h"
+
+#define WL_NUM_SCAN_MAX 1
+#define WL_TLV_INFO_MAX 1024
+#define WL_BSS_INFO_MAX 2048
+#define WL_ASSOC_INFO_MAX 512 /* assoc related fil max buf */
+#define WL_EXTRA_BUF_MAX 2048
+#define WL_ROAM_TRIGGER_LEVEL -75
+#define WL_ROAM_DELTA 20
+
+/* Keep BRCMF_ESCAN_BUF_SIZE below 64K (65536). Allocing over 64K can be
+ * problematic on some systems and should be avoided.
+ */
+#define BRCMF_ESCAN_BUF_SIZE 65000
+#define BRCMF_ESCAN_TIMER_INTERVAL_MS 10000 /* E-Scan timeout */
+
+#define WL_ESCAN_ACTION_START 1
+#define WL_ESCAN_ACTION_CONTINUE 2
+#define WL_ESCAN_ACTION_ABORT 3
+
+#define WL_AUTH_SHARED_KEY 1 /* d11 shared authentication */
+#define IE_MAX_LEN 512
+
+/* IE TLV processing */
+#define TLV_LEN_OFF 1 /* length offset */
+#define TLV_HDR_LEN 2 /* header length */
+#define TLV_BODY_OFF 2 /* body offset */
+#define TLV_OUI_LEN 3 /* oui id length */
+
+/* 802.11 Mgmt Packet flags */
+#define BRCMF_VNDR_IE_BEACON_FLAG 0x1
+#define BRCMF_VNDR_IE_PRBRSP_FLAG 0x2
+#define BRCMF_VNDR_IE_ASSOCRSP_FLAG 0x4
+#define BRCMF_VNDR_IE_AUTHRSP_FLAG 0x8
+#define BRCMF_VNDR_IE_PRBREQ_FLAG 0x10
+#define BRCMF_VNDR_IE_ASSOCREQ_FLAG 0x20
+/* vendor IE in IW advertisement protocol ID field */
+#define BRCMF_VNDR_IE_IWAPID_FLAG 0x40
+/* allow custom IE id */
+#define BRCMF_VNDR_IE_CUSTOM_FLAG 0x100
+
+/* P2P Action Frames flags (spec ordered) */
+#define BRCMF_VNDR_IE_GONREQ_FLAG 0x001000
+#define BRCMF_VNDR_IE_GONRSP_FLAG 0x002000
+#define BRCMF_VNDR_IE_GONCFM_FLAG 0x004000
+#define BRCMF_VNDR_IE_INVREQ_FLAG 0x008000
+#define BRCMF_VNDR_IE_INVRSP_FLAG 0x010000
+#define BRCMF_VNDR_IE_DISREQ_FLAG 0x020000
+#define BRCMF_VNDR_IE_DISRSP_FLAG 0x040000
+#define BRCMF_VNDR_IE_PRDREQ_FLAG 0x080000
+#define BRCMF_VNDR_IE_PRDRSP_FLAG 0x100000
+
+#define BRCMF_VNDR_IE_P2PAF_SHIFT 12
+
+#define BRCMF_MAX_DEFAULT_KEYS 6
+
+/* beacon loss timeout defaults */
+#define BRCMF_DEFAULT_BCN_TIMEOUT_ROAM_ON 2
+#define BRCMF_DEFAULT_BCN_TIMEOUT_ROAM_OFF 4
+
+#define BRCMF_VIF_EVENT_TIMEOUT msecs_to_jiffies(1500)
+
+#define WLAN_CIPHER_SUITE_PMK 0x00904C00
+
+/**
+ * enum brcmf_scan_status - scan engine status
+ *
+ * @BRCMF_SCAN_STATUS_BUSY: scanning in progress on dongle.
+ * @BRCMF_SCAN_STATUS_ABORT: scan being aborted on dongle.
+ * @BRCMF_SCAN_STATUS_SUPPRESS: scanning is suppressed in driver.
+ */
+enum brcmf_scan_status {
+ BRCMF_SCAN_STATUS_BUSY,
+ BRCMF_SCAN_STATUS_ABORT,
+ BRCMF_SCAN_STATUS_SUPPRESS,
+};
+
+/* dongle configuration */
+struct brcmf_cfg80211_conf {
+ u32 frag_threshold;
+ u32 rts_threshold;
+ u32 retry_short;
+ u32 retry_long;
+};
+
+/* security information with currently associated ap */
+struct brcmf_cfg80211_security {
+ u32 wpa_versions;
+ u32 auth_type;
+ u32 cipher_pairwise;
+ u32 cipher_group;
+};
+
+enum brcmf_profile_fwsup {
+ BRCMF_PROFILE_FWSUP_NONE,
+ BRCMF_PROFILE_FWSUP_PSK,
+ BRCMF_PROFILE_FWSUP_1X
+};
+
+/**
+ * struct brcmf_cfg80211_profile - profile information.
+ *
+ * @bssid: bssid of joined/joining ibss.
+ * @sec: security information.
+ * @key: key information
+ */
+struct brcmf_cfg80211_profile {
+ u8 bssid[ETH_ALEN];
+ struct brcmf_cfg80211_security sec;
+ struct brcmf_wsec_key key[BRCMF_MAX_DEFAULT_KEYS];
+ enum brcmf_profile_fwsup use_fwsup;
+};
+
+/**
+ * enum brcmf_vif_status - bit indices for vif status.
+ *
+ * @BRCMF_VIF_STATUS_READY: ready for operation.
+ * @BRCMF_VIF_STATUS_CONNECTING: connect/join in progress.
+ * @BRCMF_VIF_STATUS_CONNECTED: connected/joined successfully.
+ * @BRCMF_VIF_STATUS_DISCONNECTING: disconnect/disable in progress.
+ * @BRCMF_VIF_STATUS_AP_CREATED: AP operation started.
+ * @BRCMF_VIF_STATUS_EAP_SUCCUSS: EAPOL handshake successful.
+ * @BRCMF_VIF_STATUS_ASSOC_SUCCESS: successful SET_SSID received.
+ */
+enum brcmf_vif_status {
+ BRCMF_VIF_STATUS_READY,
+ BRCMF_VIF_STATUS_CONNECTING,
+ BRCMF_VIF_STATUS_CONNECTED,
+ BRCMF_VIF_STATUS_DISCONNECTING,
+ BRCMF_VIF_STATUS_AP_CREATED,
+ BRCMF_VIF_STATUS_EAP_SUCCESS,
+ BRCMF_VIF_STATUS_ASSOC_SUCCESS,
+};
+
+/**
+ * struct vif_saved_ie - holds saved IEs for a virtual interface.
+ *
+ * @probe_req_ie: IE info for probe request.
+ * @probe_res_ie: IE info for probe response.
+ * @beacon_ie: IE info for beacon frame.
+ * @probe_req_ie_len: IE info length for probe request.
+ * @probe_res_ie_len: IE info length for probe response.
+ * @beacon_ie_len: IE info length for beacon frame.
+ */
+struct vif_saved_ie {
+ u8 probe_req_ie[IE_MAX_LEN];
+ u8 probe_res_ie[IE_MAX_LEN];
+ u8 beacon_ie[IE_MAX_LEN];
+ u8 assoc_req_ie[IE_MAX_LEN];
+ u32 probe_req_ie_len;
+ u32 probe_res_ie_len;
+ u32 beacon_ie_len;
+ u32 assoc_req_ie_len;
+};
+
+/**
+ * struct brcmf_cfg80211_vif - virtual interface specific information.
+ *
+ * @ifp: lower layer interface pointer
+ * @wdev: wireless device.
+ * @profile: profile information.
+ * @sme_state: SME state using enum brcmf_vif_status bits.
+ * @list: linked list.
+ * @mgmt_rx_reg: registered rx mgmt frame types.
+ * @mbss: Multiple BSS type, set if not first AP (not relevant for P2P).
+ */
+struct brcmf_cfg80211_vif {
+ struct brcmf_if *ifp;
+ struct wireless_dev wdev;
+ struct brcmf_cfg80211_profile profile;
+ unsigned long sme_state;
+ struct vif_saved_ie saved_ie;
+ struct list_head list;
+ u16 mgmt_rx_reg;
+ bool mbss;
+ int is_11d;
+};
+
+/* association inform */
+struct brcmf_cfg80211_connect_info {
+ u8 *req_ie;
+ s32 req_ie_len;
+ u8 *resp_ie;
+ s32 resp_ie_len;
+};
+
+/* assoc ie length */
+struct brcmf_cfg80211_assoc_ielen_le {
+ __le32 req_len;
+ __le32 resp_len;
+};
+
+/* dongle escan state */
+enum wl_escan_state {
+ WL_ESCAN_STATE_IDLE,
+ WL_ESCAN_STATE_SCANNING
+};
+
+struct escan_info {
+ u32 escan_state;
+ u8 *escan_buf;
+ struct wiphy *wiphy;
+ struct brcmf_if *ifp;
+ s32 (*run)(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp,
+ struct cfg80211_scan_request *request);
+};
+
+/**
+ * struct brcmf_cfg80211_vif_event - virtual interface event information.
+ *
+ * @vif_wq: waitqueue awaiting interface event from firmware.
+ * @vif_event_lock: protects other members in this structure.
+ * @vif_complete: completion for net attach.
+ * @action: either add, change, or delete.
+ * @vif: virtual interface object related to the event.
+ */
+struct brcmf_cfg80211_vif_event {
+ wait_queue_head_t vif_wq;
+ spinlock_t vif_event_lock;
+ u8 action;
+ struct brcmf_cfg80211_vif *vif;
+};
+
+/**
+ * struct brcmf_cfg80211_wowl - wowl related information.
+ *
+ * @active: set on suspend, cleared on resume.
+ * @pre_pmmode: firmware PM mode at entering suspend.
+ * @nd: net dectect data.
+ * @nd_info: helper struct to pass to cfg80211.
+ * @nd_data_wait: wait queue to sync net detect data.
+ * @nd_data_completed: completion for net detect data.
+ * @nd_enabled: net detect enabled.
+ */
+struct brcmf_cfg80211_wowl {
+ bool active;
+ u32 pre_pmmode;
+ struct cfg80211_wowlan_nd_match *nd;
+ struct cfg80211_wowlan_nd_info *nd_info;
+ wait_queue_head_t nd_data_wait;
+ bool nd_data_completed;
+ bool nd_enabled;
+};
+
+/**
+ * struct brcmf_cfg80211_info - dongle private data of cfg80211 interface
+ *
+ * @wiphy: wiphy object for cfg80211 interface.
+ * @ops: pointer to copy of ops as registered with wiphy object.
+ * @conf: dongle configuration.
+ * @p2p: peer-to-peer specific information.
+ * @btcoex: Bluetooth coexistence information.
+ * @scan_request: cfg80211 scan request object.
+ * @usr_sync: mainly for dongle up/down synchronization.
+ * @bss_list: bss_list holding scanned ap information.
+ * @bss_info: bss information for cfg80211 layer.
+ * @conn_info: association info.
+ * @pmk_list: wpa2 pmk list.
+ * @scan_status: scan activity on the dongle.
+ * @pub: common driver information.
+ * @channel: current channel.
+ * @active_scan: current scan mode.
+ * @internal_escan: indicates internally initiated e-scan is running.
+ * @ibss_starter: indicates this sta is ibss starter.
+ * @pwr_save: indicate whether dongle to support power save mode.
+ * @dongle_up: indicate whether dongle up or not.
+ * @roam_on: on/off switch for dongle self-roaming.
+ * @scan_tried: indicates if first scan attempted.
+ * @dcmd_buf: dcmd buffer.
+ * @extra_buf: mainly to grab assoc information.
+ * @debugfsdir: debugfs folder for this device.
+ * @escan_info: escan information.
+ * @escan_timeout: Timer for catch scan timeout.
+ * @escan_timeout_work: scan timeout worker.
+ * @vif_list: linked list of vif instances.
+ * @vif_cnt: number of vif instances.
+ * @vif_event: vif event signalling.
+ * @wowl: wowl related information.
+ */
+struct brcmf_cfg80211_info {
+ struct wiphy *wiphy;
+ struct cfg80211_ops *ops;
+ struct brcmf_cfg80211_conf *conf;
+ struct brcmf_p2p_info p2p;
+ struct brcmf_btcoex_info *btcoex;
+ struct cfg80211_scan_request *scan_request;
+ int scan_retry_cnt; /* scan retry count */
+ struct mutex usr_sync;
+ struct wl_cfg80211_bss_info *bss_info;
+ struct brcmf_cfg80211_connect_info conn_info;
+ struct brcmf_pmk_list_le pmk_list;
+ unsigned long scan_status;
+ struct brcmf_pub *pub;
+ u32 channel;
+ bool active_scan;
+ bool internal_escan;
+ bool ibss_starter;
+ bool pwr_save;
+ bool dongle_up;
+ bool scan_tried;
+ u8 *dcmd_buf;
+ u8 *extra_buf;
+ struct dentry *debugfsdir;
+ struct escan_info escan_info;
+ struct timer_list escan_timeout;
+ struct work_struct escan_timeout_work;
+ struct list_head vif_list;
+ struct brcmf_cfg80211_vif_event vif_event;
+ struct completion vif_disabled;
+ struct brcmu_d11inf d11inf;
+ struct brcmf_assoclist_le assoclist;
+ struct brcmf_cfg80211_wowl wowl;
+};
+
+/**
+ * struct brcmf_tlv - tag_ID/length/value_buffer tuple.
+ *
+ * @id: tag identifier.
+ * @len: number of bytes in value buffer.
+ * @data: value buffer.
+ */
+struct brcmf_tlv {
+ u8 id;
+ u8 len;
+ u8 data[1];
+};
+
+static inline struct wiphy *cfg_to_wiphy(struct brcmf_cfg80211_info *cfg)
+{
+ return cfg->wiphy;
+}
+
+static inline struct brcmf_cfg80211_info *wiphy_to_cfg(struct wiphy *w)
+{
+ return (struct brcmf_cfg80211_info *)(wiphy_priv(w));
+}
+
+static inline struct brcmf_cfg80211_info *wdev_to_cfg(struct wireless_dev *wd)
+{
+ return (struct brcmf_cfg80211_info *)(wdev_priv(wd));
+}
+
+static inline
+struct net_device *cfg_to_ndev(struct brcmf_cfg80211_info *cfg)
+{
+ struct brcmf_cfg80211_vif *vif;
+ vif = list_first_entry(&cfg->vif_list, struct brcmf_cfg80211_vif, list);
+ return vif->wdev.netdev;
+}
+
+static inline struct brcmf_cfg80211_info *ndev_to_cfg(struct net_device *ndev)
+{
+ return wdev_to_cfg(ndev->ieee80211_ptr);
+}
+
+static inline struct brcmf_cfg80211_profile *ndev_to_prof(struct net_device *nd)
+{
+ struct brcmf_if *ifp = netdev_priv(nd);
+ return &ifp->vif->profile;
+}
+
+static inline struct brcmf_cfg80211_vif *ndev_to_vif(struct net_device *ndev)
+{
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ return ifp->vif;
+}
+
+static inline struct
+brcmf_cfg80211_connect_info *cfg_to_conn(struct brcmf_cfg80211_info *cfg)
+{
+ return &cfg->conn_info;
+}
+
+struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
+ struct device *busdev,
+ bool p2pdev_forced);
+void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg);
+s32 brcmf_cfg80211_up(struct net_device *ndev);
+s32 brcmf_cfg80211_down(struct net_device *ndev);
+enum nl80211_iftype brcmf_cfg80211_get_iftype(struct brcmf_if *ifp);
+
+struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg,
+ enum nl80211_iftype type);
+void brcmf_free_vif(struct brcmf_cfg80211_vif *vif);
+
+s32 brcmf_vif_set_mgmt_ie(struct brcmf_cfg80211_vif *vif, s32 pktflag,
+ const u8 *vndr_ie_buf, u32 vndr_ie_len);
+s32 brcmf_vif_clear_mgmt_ies(struct brcmf_cfg80211_vif *vif);
+u16 channel_to_chanspec(struct brcmu_d11inf *d11inf,
+ struct ieee80211_channel *ch);
+bool brcmf_get_vif_state_any(struct brcmf_cfg80211_info *cfg,
+ unsigned long state);
+void brcmf_cfg80211_arm_vif_event(struct brcmf_cfg80211_info *cfg,
+ struct brcmf_cfg80211_vif *vif);
+bool brcmf_cfg80211_vif_event_armed(struct brcmf_cfg80211_info *cfg);
+int brcmf_cfg80211_wait_vif_event(struct brcmf_cfg80211_info *cfg,
+ u8 action, ulong timeout);
+s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg,
+ struct brcmf_if *ifp, bool aborted,
+ bool fw_abort);
+void brcmf_set_mpc(struct brcmf_if *ndev, int mpc);
+void brcmf_abort_scanning(struct brcmf_cfg80211_info *cfg);
+void brcmf_cfg80211_free_netdev(struct net_device *ndev);
+
+#endif /* BRCMFMAC_CFG80211_H */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c
new file mode 100644
index 0000000..ad54409
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c
@@ -0,0 +1,1380 @@
+/*
+ * Copyright (c) 2014 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/list.h>
+#include <linux/ssb/ssb_regs.h>
+#include <linux/bcma/bcma.h>
+#include <linux/bcma/bcma_regs.h>
+
+#include <defs.h>
+#include <soc.h>
+#include <brcm_hw_ids.h>
+#include <brcmu_utils.h>
+#include <chipcommon.h>
+#include "debug.h"
+#include "chip.h"
+
+/* SOC Interconnect types (aka chip types) */
+#define SOCI_SB 0
+#define SOCI_AI 1
+
+/* PL-368 DMP definitions */
+#define DMP_DESC_TYPE_MSK 0x0000000F
+#define DMP_DESC_EMPTY 0x00000000
+#define DMP_DESC_VALID 0x00000001
+#define DMP_DESC_COMPONENT 0x00000001
+#define DMP_DESC_MASTER_PORT 0x00000003
+#define DMP_DESC_ADDRESS 0x00000005
+#define DMP_DESC_ADDRSIZE_GT32 0x00000008
+#define DMP_DESC_EOT 0x0000000F
+
+#define DMP_COMP_DESIGNER 0xFFF00000
+#define DMP_COMP_DESIGNER_S 20
+#define DMP_COMP_PARTNUM 0x000FFF00
+#define DMP_COMP_PARTNUM_S 8
+#define DMP_COMP_CLASS 0x000000F0
+#define DMP_COMP_CLASS_S 4
+#define DMP_COMP_REVISION 0xFF000000
+#define DMP_COMP_REVISION_S 24
+#define DMP_COMP_NUM_SWRAP 0x00F80000
+#define DMP_COMP_NUM_SWRAP_S 19
+#define DMP_COMP_NUM_MWRAP 0x0007C000
+#define DMP_COMP_NUM_MWRAP_S 14
+#define DMP_COMP_NUM_SPORT 0x00003E00
+#define DMP_COMP_NUM_SPORT_S 9
+#define DMP_COMP_NUM_MPORT 0x000001F0
+#define DMP_COMP_NUM_MPORT_S 4
+
+#define DMP_MASTER_PORT_UID 0x0000FF00
+#define DMP_MASTER_PORT_UID_S 8
+#define DMP_MASTER_PORT_NUM 0x000000F0
+#define DMP_MASTER_PORT_NUM_S 4
+
+#define DMP_SLAVE_ADDR_BASE 0xFFFFF000
+#define DMP_SLAVE_ADDR_BASE_S 12
+#define DMP_SLAVE_PORT_NUM 0x00000F00
+#define DMP_SLAVE_PORT_NUM_S 8
+#define DMP_SLAVE_TYPE 0x000000C0
+#define DMP_SLAVE_TYPE_S 6
+#define DMP_SLAVE_TYPE_SLAVE 0
+#define DMP_SLAVE_TYPE_BRIDGE 1
+#define DMP_SLAVE_TYPE_SWRAP 2
+#define DMP_SLAVE_TYPE_MWRAP 3
+#define DMP_SLAVE_SIZE_TYPE 0x00000030
+#define DMP_SLAVE_SIZE_TYPE_S 4
+#define DMP_SLAVE_SIZE_4K 0
+#define DMP_SLAVE_SIZE_8K 1
+#define DMP_SLAVE_SIZE_16K 2
+#define DMP_SLAVE_SIZE_DESC 3
+
+/* EROM CompIdentB */
+#define CIB_REV_MASK 0xff000000
+#define CIB_REV_SHIFT 24
+
+/* ARM CR4 core specific control flag bits */
+#define ARMCR4_BCMA_IOCTL_CPUHALT 0x0020
+
+/* D11 core specific control flag bits */
+#define D11_BCMA_IOCTL_PHYCLOCKEN 0x0004
+#define D11_BCMA_IOCTL_PHYRESET 0x0008
+
+/* chip core base & ramsize */
+/* bcm4329 */
+/* SDIO device core, ID 0x829 */
+#define BCM4329_CORE_BUS_BASE 0x18011000
+/* internal memory core, ID 0x80e */
+#define BCM4329_CORE_SOCRAM_BASE 0x18003000
+/* ARM Cortex M3 core, ID 0x82a */
+#define BCM4329_CORE_ARM_BASE 0x18002000
+
+/* Max possibly supported memory size (limited by IO mapped memory) */
+#define BRCMF_CHIP_MAX_MEMSIZE (4 * 1024 * 1024)
+
+#define CORE_SB(base, field) \
+ (base + SBCONFIGOFF + offsetof(struct sbconfig, field))
+#define SBCOREREV(sbidh) \
+ ((((sbidh) & SSB_IDHIGH_RCHI) >> SSB_IDHIGH_RCHI_SHIFT) | \
+ ((sbidh) & SSB_IDHIGH_RCLO))
+
+struct sbconfig {
+ u32 PAD[2];
+ u32 sbipsflag; /* initiator port ocp slave flag */
+ u32 PAD[3];
+ u32 sbtpsflag; /* target port ocp slave flag */
+ u32 PAD[11];
+ u32 sbtmerrloga; /* (sonics >= 2.3) */
+ u32 PAD;
+ u32 sbtmerrlog; /* (sonics >= 2.3) */
+ u32 PAD[3];
+ u32 sbadmatch3; /* address match3 */
+ u32 PAD;
+ u32 sbadmatch2; /* address match2 */
+ u32 PAD;
+ u32 sbadmatch1; /* address match1 */
+ u32 PAD[7];
+ u32 sbimstate; /* initiator agent state */
+ u32 sbintvec; /* interrupt mask */
+ u32 sbtmstatelow; /* target state */
+ u32 sbtmstatehigh; /* target state */
+ u32 sbbwa0; /* bandwidth allocation table0 */
+ u32 PAD;
+ u32 sbimconfiglow; /* initiator configuration */
+ u32 sbimconfighigh; /* initiator configuration */
+ u32 sbadmatch0; /* address match0 */
+ u32 PAD;
+ u32 sbtmconfiglow; /* target configuration */
+ u32 sbtmconfighigh; /* target configuration */
+ u32 sbbconfig; /* broadcast configuration */
+ u32 PAD;
+ u32 sbbstate; /* broadcast state */
+ u32 PAD[3];
+ u32 sbactcnfg; /* activate configuration */
+ u32 PAD[3];
+ u32 sbflagst; /* current sbflags */
+ u32 PAD[3];
+ u32 sbidlow; /* identification */
+ u32 sbidhigh; /* identification */
+};
+
+/* bankidx and bankinfo reg defines corerev >= 8 */
+#define SOCRAM_BANKINFO_RETNTRAM_MASK 0x00010000
+#define SOCRAM_BANKINFO_SZMASK 0x0000007f
+#define SOCRAM_BANKIDX_ROM_MASK 0x00000100
+
+#define SOCRAM_BANKIDX_MEMTYPE_SHIFT 8
+/* socram bankinfo memtype */
+#define SOCRAM_MEMTYPE_RAM 0
+#define SOCRAM_MEMTYPE_R0M 1
+#define SOCRAM_MEMTYPE_DEVRAM 2
+
+#define SOCRAM_BANKINFO_SZBASE 8192
+#define SRCI_LSS_MASK 0x00f00000
+#define SRCI_LSS_SHIFT 20
+#define SRCI_SRNB_MASK 0xf0
+#define SRCI_SRNB_MASK_EXT 0x100
+#define SRCI_SRNB_SHIFT 4
+#define SRCI_SRBSZ_MASK 0xf
+#define SRCI_SRBSZ_SHIFT 0
+#define SR_BSZ_BASE 14
+
+struct sbsocramregs {
+ u32 coreinfo;
+ u32 bwalloc;
+ u32 extracoreinfo;
+ u32 biststat;
+ u32 bankidx;
+ u32 standbyctrl;
+
+ u32 errlogstatus; /* rev 6 */
+ u32 errlogaddr; /* rev 6 */
+ /* used for patching rev 3 & 5 */
+ u32 cambankidx;
+ u32 cambankstandbyctrl;
+ u32 cambankpatchctrl;
+ u32 cambankpatchtblbaseaddr;
+ u32 cambankcmdreg;
+ u32 cambankdatareg;
+ u32 cambankmaskreg;
+ u32 PAD[1];
+ u32 bankinfo; /* corev 8 */
+ u32 bankpda;
+ u32 PAD[14];
+ u32 extmemconfig;
+ u32 extmemparitycsr;
+ u32 extmemparityerrdata;
+ u32 extmemparityerrcnt;
+ u32 extmemwrctrlandsize;
+ u32 PAD[84];
+ u32 workaround;
+ u32 pwrctl; /* corerev >= 2 */
+ u32 PAD[133];
+ u32 sr_control; /* corerev >= 15 */
+ u32 sr_status; /* corerev >= 15 */
+ u32 sr_address; /* corerev >= 15 */
+ u32 sr_data; /* corerev >= 15 */
+};
+
+#define SOCRAMREGOFFS(_f) offsetof(struct sbsocramregs, _f)
+#define SYSMEMREGOFFS(_f) offsetof(struct sbsocramregs, _f)
+
+#define ARMCR4_CAP (0x04)
+#define ARMCR4_BANKIDX (0x40)
+#define ARMCR4_BANKINFO (0x44)
+#define ARMCR4_BANKPDA (0x4C)
+
+#define ARMCR4_TCBBNB_MASK 0xf0
+#define ARMCR4_TCBBNB_SHIFT 4
+#define ARMCR4_TCBANB_MASK 0xf
+#define ARMCR4_TCBANB_SHIFT 0
+
+#define ARMCR4_BSZ_MASK 0x3f
+#define ARMCR4_BSZ_MULT 8192
+
+struct brcmf_core_priv {
+ struct brcmf_core pub;
+ u32 wrapbase;
+ struct list_head list;
+ struct brcmf_chip_priv *chip;
+};
+
+struct brcmf_chip_priv {
+ struct brcmf_chip pub;
+ const struct brcmf_buscore_ops *ops;
+ void *ctx;
+ /* assured first core is chipcommon, second core is buscore */
+ struct list_head cores;
+ u16 num_cores;
+
+ bool (*iscoreup)(struct brcmf_core_priv *core);
+ void (*coredisable)(struct brcmf_core_priv *core, u32 prereset,
+ u32 reset);
+ void (*resetcore)(struct brcmf_core_priv *core, u32 prereset, u32 reset,
+ u32 postreset);
+};
+
+static void brcmf_chip_sb_corerev(struct brcmf_chip_priv *ci,
+ struct brcmf_core *core)
+{
+ u32 regdata;
+
+ regdata = ci->ops->read32(ci->ctx, CORE_SB(core->base, sbidhigh));
+ core->rev = SBCOREREV(regdata);
+}
+
+static bool brcmf_chip_sb_iscoreup(struct brcmf_core_priv *core)
+{
+ struct brcmf_chip_priv *ci;
+ u32 regdata;
+ u32 address;
+
+ ci = core->chip;
+ address = CORE_SB(core->pub.base, sbtmstatelow);
+ regdata = ci->ops->read32(ci->ctx, address);
+ regdata &= (SSB_TMSLOW_RESET | SSB_TMSLOW_REJECT |
+ SSB_IMSTATE_REJECT | SSB_TMSLOW_CLOCK);
+ return SSB_TMSLOW_CLOCK == regdata;
+}
+
+static bool brcmf_chip_ai_iscoreup(struct brcmf_core_priv *core)
+{
+ struct brcmf_chip_priv *ci;
+ u32 regdata;
+ bool ret;
+
+ ci = core->chip;
+ regdata = ci->ops->read32(ci->ctx, core->wrapbase + BCMA_IOCTL);
+ ret = (regdata & (BCMA_IOCTL_FGC | BCMA_IOCTL_CLK)) == BCMA_IOCTL_CLK;
+
+ regdata = ci->ops->read32(ci->ctx, core->wrapbase + BCMA_RESET_CTL);
+ ret = ret && ((regdata & BCMA_RESET_CTL_RESET) == 0);
+
+ return ret;
+}
+
+static void brcmf_chip_sb_coredisable(struct brcmf_core_priv *core,
+ u32 prereset, u32 reset)
+{
+ struct brcmf_chip_priv *ci;
+ u32 val, base;
+
+ ci = core->chip;
+ base = core->pub.base;
+ val = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow));
+ if (val & SSB_TMSLOW_RESET)
+ return;
+
+ val = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow));
+ if ((val & SSB_TMSLOW_CLOCK) != 0) {
+ /*
+ * set target reject and spin until busy is clear
+ * (preserve core-specific bits)
+ */
+ val = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow));
+ ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatelow),
+ val | SSB_TMSLOW_REJECT);
+
+ val = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow));
+ udelay(1);
+ SPINWAIT((ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatehigh))
+ & SSB_TMSHIGH_BUSY), 100000);
+
+ val = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatehigh));
+ if (val & SSB_TMSHIGH_BUSY)
+ brcmf_err("core state still busy\n");
+
+ val = ci->ops->read32(ci->ctx, CORE_SB(base, sbidlow));
+ if (val & SSB_IDLOW_INITIATOR) {
+ val = ci->ops->read32(ci->ctx,
+ CORE_SB(base, sbimstate));
+ val |= SSB_IMSTATE_REJECT;
+ ci->ops->write32(ci->ctx,
+ CORE_SB(base, sbimstate), val);
+ val = ci->ops->read32(ci->ctx,
+ CORE_SB(base, sbimstate));
+ udelay(1);
+ SPINWAIT((ci->ops->read32(ci->ctx,
+ CORE_SB(base, sbimstate)) &
+ SSB_IMSTATE_BUSY), 100000);
+ }
+
+ /* set reset and reject while enabling the clocks */
+ val = SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK |
+ SSB_TMSLOW_REJECT | SSB_TMSLOW_RESET;
+ ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatelow), val);
+ val = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow));
+ udelay(10);
+
+ /* clear the initiator reject bit */
+ val = ci->ops->read32(ci->ctx, CORE_SB(base, sbidlow));
+ if (val & SSB_IDLOW_INITIATOR) {
+ val = ci->ops->read32(ci->ctx,
+ CORE_SB(base, sbimstate));
+ val &= ~SSB_IMSTATE_REJECT;
+ ci->ops->write32(ci->ctx,
+ CORE_SB(base, sbimstate), val);
+ }
+ }
+
+ /* leave reset and reject asserted */
+ ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatelow),
+ (SSB_TMSLOW_REJECT | SSB_TMSLOW_RESET));
+ udelay(1);
+}
+
+static void brcmf_chip_ai_coredisable(struct brcmf_core_priv *core,
+ u32 prereset, u32 reset)
+{
+ struct brcmf_chip_priv *ci;
+ u32 regdata;
+
+ ci = core->chip;
+
+ /* if core is already in reset, skip reset */
+ regdata = ci->ops->read32(ci->ctx, core->wrapbase + BCMA_RESET_CTL);
+ if ((regdata & BCMA_RESET_CTL_RESET) != 0)
+ goto in_reset_configure;
+
+ /* configure reset */
+ ci->ops->write32(ci->ctx, core->wrapbase + BCMA_IOCTL,
+ prereset | BCMA_IOCTL_FGC | BCMA_IOCTL_CLK);
+ ci->ops->read32(ci->ctx, core->wrapbase + BCMA_IOCTL);
+
+ /* put in reset */
+ ci->ops->write32(ci->ctx, core->wrapbase + BCMA_RESET_CTL,
+ BCMA_RESET_CTL_RESET);
+ usleep_range(10, 20);
+
+ /* wait till reset is 1 */
+ SPINWAIT(ci->ops->read32(ci->ctx, core->wrapbase + BCMA_RESET_CTL) !=
+ BCMA_RESET_CTL_RESET, 300);
+
+in_reset_configure:
+ /* in-reset configure */
+ ci->ops->write32(ci->ctx, core->wrapbase + BCMA_IOCTL,
+ reset | BCMA_IOCTL_FGC | BCMA_IOCTL_CLK);
+ ci->ops->read32(ci->ctx, core->wrapbase + BCMA_IOCTL);
+}
+
+static void brcmf_chip_sb_resetcore(struct brcmf_core_priv *core, u32 prereset,
+ u32 reset, u32 postreset)
+{
+ struct brcmf_chip_priv *ci;
+ u32 regdata;
+ u32 base;
+
+ ci = core->chip;
+ base = core->pub.base;
+ /*
+ * Must do the disable sequence first to work for
+ * arbitrary current core state.
+ */
+ brcmf_chip_sb_coredisable(core, 0, 0);
+
+ /*
+ * Now do the initialization sequence.
+ * set reset while enabling the clock and
+ * forcing them on throughout the core
+ */
+ ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatelow),
+ SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK |
+ SSB_TMSLOW_RESET);
+ regdata = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow));
+ udelay(1);
+
+ /* clear any serror */
+ regdata = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatehigh));
+ if (regdata & SSB_TMSHIGH_SERR)
+ ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatehigh), 0);
+
+ regdata = ci->ops->read32(ci->ctx, CORE_SB(base, sbimstate));
+ if (regdata & (SSB_IMSTATE_IBE | SSB_IMSTATE_TO)) {
+ regdata &= ~(SSB_IMSTATE_IBE | SSB_IMSTATE_TO);
+ ci->ops->write32(ci->ctx, CORE_SB(base, sbimstate), regdata);
+ }
+
+ /* clear reset and allow it to propagate throughout the core */
+ ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatelow),
+ SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK);
+ regdata = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow));
+ udelay(1);
+
+ /* leave clock enabled */
+ ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatelow),
+ SSB_TMSLOW_CLOCK);
+ regdata = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow));
+ udelay(1);
+}
+
+static void brcmf_chip_ai_resetcore(struct brcmf_core_priv *core, u32 prereset,
+ u32 reset, u32 postreset)
+{
+ struct brcmf_chip_priv *ci;
+ int count;
+
+ ci = core->chip;
+
+ /* must disable first to work for arbitrary current core state */
+ brcmf_chip_ai_coredisable(core, prereset, reset);
+
+ count = 0;
+ while (ci->ops->read32(ci->ctx, core->wrapbase + BCMA_RESET_CTL) &
+ BCMA_RESET_CTL_RESET) {
+ ci->ops->write32(ci->ctx, core->wrapbase + BCMA_RESET_CTL, 0);
+ count++;
+ if (count > 50)
+ break;
+ usleep_range(40, 60);
+ }
+
+ ci->ops->write32(ci->ctx, core->wrapbase + BCMA_IOCTL,
+ postreset | BCMA_IOCTL_CLK);
+ ci->ops->read32(ci->ctx, core->wrapbase + BCMA_IOCTL);
+}
+
+static char *brcmf_chip_name(uint chipid, char *buf, uint len)
+{
+ const char *fmt;
+
+ fmt = ((chipid > 0xa000) || (chipid < 0x4000)) ? "%d" : "%x";
+ snprintf(buf, len, fmt, chipid);
+ return buf;
+}
+
+static struct brcmf_core *brcmf_chip_add_core(struct brcmf_chip_priv *ci,
+ u16 coreid, u32 base,
+ u32 wrapbase)
+{
+ struct brcmf_core_priv *core;
+
+ core = kzalloc(sizeof(*core), GFP_KERNEL);
+ if (!core)
+ return ERR_PTR(-ENOMEM);
+
+ core->pub.id = coreid;
+ core->pub.base = base;
+ core->chip = ci;
+ core->wrapbase = wrapbase;
+
+ list_add_tail(&core->list, &ci->cores);
+ return &core->pub;
+}
+
+/* safety check for chipinfo */
+static int brcmf_chip_cores_check(struct brcmf_chip_priv *ci)
+{
+ struct brcmf_core_priv *core;
+ bool need_socram = false;
+ bool has_socram = false;
+ bool cpu_found = false;
+ int idx = 1;
+
+ list_for_each_entry(core, &ci->cores, list) {
+ brcmf_dbg(INFO, " [%-2d] core 0x%x:%-2d base 0x%08x wrap 0x%08x\n",
+ idx++, core->pub.id, core->pub.rev, core->pub.base,
+ core->wrapbase);
+
+ switch (core->pub.id) {
+ case BCMA_CORE_ARM_CM3:
+ cpu_found = true;
+ need_socram = true;
+ break;
+ case BCMA_CORE_INTERNAL_MEM:
+ has_socram = true;
+ break;
+ case BCMA_CORE_ARM_CR4:
+ cpu_found = true;
+ break;
+ case BCMA_CORE_ARM_CA7:
+ cpu_found = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (!cpu_found) {
+ brcmf_err("CPU core not detected\n");
+ return -ENXIO;
+ }
+ /* check RAM core presence for ARM CM3 core */
+ if (need_socram && !has_socram) {
+ brcmf_err("RAM core not provided with ARM CM3 core\n");
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static u32 brcmf_chip_core_read32(struct brcmf_core_priv *core, u16 reg)
+{
+ return core->chip->ops->read32(core->chip->ctx, core->pub.base + reg);
+}
+
+static void brcmf_chip_core_write32(struct brcmf_core_priv *core,
+ u16 reg, u32 val)
+{
+ core->chip->ops->write32(core->chip->ctx, core->pub.base + reg, val);
+}
+
+static bool brcmf_chip_socram_banksize(struct brcmf_core_priv *core, u8 idx,
+ u32 *banksize)
+{
+ u32 bankinfo;
+ u32 bankidx = (SOCRAM_MEMTYPE_RAM << SOCRAM_BANKIDX_MEMTYPE_SHIFT);
+
+ bankidx |= idx;
+ brcmf_chip_core_write32(core, SOCRAMREGOFFS(bankidx), bankidx);
+ bankinfo = brcmf_chip_core_read32(core, SOCRAMREGOFFS(bankinfo));
+ *banksize = (bankinfo & SOCRAM_BANKINFO_SZMASK) + 1;
+ *banksize *= SOCRAM_BANKINFO_SZBASE;
+ return !!(bankinfo & SOCRAM_BANKINFO_RETNTRAM_MASK);
+}
+
+static void brcmf_chip_socram_ramsize(struct brcmf_core_priv *sr, u32 *ramsize,
+ u32 *srsize)
+{
+ u32 coreinfo;
+ uint nb, banksize, lss;
+ bool retent;
+ int i;
+
+ *ramsize = 0;
+ *srsize = 0;
+
+ if (WARN_ON(sr->pub.rev < 4))
+ return;
+
+ if (!brcmf_chip_iscoreup(&sr->pub))
+ brcmf_chip_resetcore(&sr->pub, 0, 0, 0);
+
+ /* Get info for determining size */
+ coreinfo = brcmf_chip_core_read32(sr, SOCRAMREGOFFS(coreinfo));
+ nb = (coreinfo & SRCI_SRNB_MASK) >> SRCI_SRNB_SHIFT;
+
+ if ((sr->pub.rev <= 7) || (sr->pub.rev == 12)) {
+ banksize = (coreinfo & SRCI_SRBSZ_MASK);
+ lss = (coreinfo & SRCI_LSS_MASK) >> SRCI_LSS_SHIFT;
+ if (lss != 0)
+ nb--;
+ *ramsize = nb * (1 << (banksize + SR_BSZ_BASE));
+ if (lss != 0)
+ *ramsize += (1 << ((lss - 1) + SR_BSZ_BASE));
+ } else {
+ /* length of SRAM Banks increased for corerev greater than 23 */
+ if (sr->pub.rev >= 23) {
+ nb = (coreinfo & (SRCI_SRNB_MASK | SRCI_SRNB_MASK_EXT))
+ >> SRCI_SRNB_SHIFT;
+ } else {
+ nb = (coreinfo & SRCI_SRNB_MASK) >> SRCI_SRNB_SHIFT;
+ }
+ for (i = 0; i < nb; i++) {
+ retent = brcmf_chip_socram_banksize(sr, i, &banksize);
+ *ramsize += banksize;
+ if (retent)
+ *srsize += banksize;
+ }
+ }
+
+ /* hardcoded save&restore memory sizes */
+ switch (sr->chip->pub.chip) {
+ case BRCM_CC_4334_CHIP_ID:
+ if (sr->chip->pub.chiprev < 2)
+ *srsize = (32 * 1024);
+ break;
+ case BRCM_CC_43430_CHIP_ID:
+ /* assume sr for now as we can not check
+ * firmware sr capability at this point.
+ */
+ *srsize = (64 * 1024);
+ break;
+ default:
+ break;
+ }
+}
+
+/** Return the SYS MEM size */
+static u32 brcmf_chip_sysmem_ramsize(struct brcmf_core_priv *sysmem)
+{
+ u32 memsize = 0;
+ u32 coreinfo;
+ u32 idx;
+ u32 nb;
+ u32 banksize;
+
+ if (!brcmf_chip_iscoreup(&sysmem->pub))
+ brcmf_chip_resetcore(&sysmem->pub, 0, 0, 0);
+
+ coreinfo = brcmf_chip_core_read32(sysmem, SYSMEMREGOFFS(coreinfo));
+ nb = (coreinfo & SRCI_SRNB_MASK) >> SRCI_SRNB_SHIFT;
+
+ for (idx = 0; idx < nb; idx++) {
+ brcmf_chip_socram_banksize(sysmem, idx, &banksize);
+ memsize += banksize;
+ }
+
+ return memsize;
+}
+
+/** Return the TCM-RAM size of the ARMCR4 core. */
+static u32 brcmf_chip_tcm_ramsize(struct brcmf_core_priv *cr4)
+{
+ u32 corecap;
+ u32 memsize = 0;
+ u32 nab;
+ u32 nbb;
+ u32 totb;
+ u32 bxinfo;
+ u32 idx;
+
+ corecap = brcmf_chip_core_read32(cr4, ARMCR4_CAP);
+
+ nab = (corecap & ARMCR4_TCBANB_MASK) >> ARMCR4_TCBANB_SHIFT;
+ nbb = (corecap & ARMCR4_TCBBNB_MASK) >> ARMCR4_TCBBNB_SHIFT;
+ totb = nab + nbb;
+
+ for (idx = 0; idx < totb; idx++) {
+ brcmf_chip_core_write32(cr4, ARMCR4_BANKIDX, idx);
+ bxinfo = brcmf_chip_core_read32(cr4, ARMCR4_BANKINFO);
+ memsize += ((bxinfo & ARMCR4_BSZ_MASK) + 1) * ARMCR4_BSZ_MULT;
+ }
+
+ return memsize;
+}
+
+static u32 brcmf_chip_tcm_rambase(struct brcmf_chip_priv *ci)
+{
+ switch (ci->pub.chip) {
+ case BRCM_CC_4345_CHIP_ID:
+ return 0x198000;
+ case BRCM_CC_4335_CHIP_ID:
+ case BRCM_CC_4339_CHIP_ID:
+ case BRCM_CC_4350_CHIP_ID:
+ case BRCM_CC_4354_CHIP_ID:
+ case BRCM_CC_4356_CHIP_ID:
+ case BRCM_CC_43567_CHIP_ID:
+ case BRCM_CC_43569_CHIP_ID:
+ case BRCM_CC_43570_CHIP_ID:
+ case BRCM_CC_4358_CHIP_ID:
+ case BRCM_CC_4359_CHIP_ID:
+ case BRCM_CC_43602_CHIP_ID:
+ case BRCM_CC_4371_CHIP_ID:
+ return 0x180000;
+ case BRCM_CC_43465_CHIP_ID:
+ case BRCM_CC_43525_CHIP_ID:
+ case BRCM_CC_4365_CHIP_ID:
+ case BRCM_CC_4366_CHIP_ID:
+ return 0x200000;
+ case CY_CC_4373_CHIP_ID:
+ return 0x160000;
+ default:
+ brcmf_err("unknown chip: %s\n", ci->pub.name);
+ break;
+ }
+ return 0;
+}
+
+static int brcmf_chip_get_raminfo(struct brcmf_chip_priv *ci)
+{
+ struct brcmf_core_priv *mem_core;
+ struct brcmf_core *mem;
+
+ mem = brcmf_chip_get_core(&ci->pub, BCMA_CORE_ARM_CR4);
+ if (mem) {
+ mem_core = container_of(mem, struct brcmf_core_priv, pub);
+ ci->pub.ramsize = brcmf_chip_tcm_ramsize(mem_core);
+ ci->pub.rambase = brcmf_chip_tcm_rambase(ci);
+ if (!ci->pub.rambase) {
+ brcmf_err("RAM base not provided with ARM CR4 core\n");
+ return -EINVAL;
+ }
+ } else {
+ mem = brcmf_chip_get_core(&ci->pub, BCMA_CORE_SYS_MEM);
+ if (mem) {
+ mem_core = container_of(mem, struct brcmf_core_priv,
+ pub);
+ ci->pub.ramsize = brcmf_chip_sysmem_ramsize(mem_core);
+ ci->pub.rambase = brcmf_chip_tcm_rambase(ci);
+ if (!ci->pub.rambase) {
+ brcmf_err("RAM base not provided with ARM CA7 core\n");
+ return -EINVAL;
+ }
+ } else {
+ mem = brcmf_chip_get_core(&ci->pub,
+ BCMA_CORE_INTERNAL_MEM);
+ if (!mem) {
+ brcmf_err("No memory cores found\n");
+ return -ENOMEM;
+ }
+ mem_core = container_of(mem, struct brcmf_core_priv,
+ pub);
+ brcmf_chip_socram_ramsize(mem_core, &ci->pub.ramsize,
+ &ci->pub.srsize);
+ }
+ }
+ brcmf_dbg(INFO, "RAM: base=0x%x size=%d (0x%x) sr=%d (0x%x)\n",
+ ci->pub.rambase, ci->pub.ramsize, ci->pub.ramsize,
+ ci->pub.srsize, ci->pub.srsize);
+
+ if (!ci->pub.ramsize) {
+ brcmf_err("RAM size is undetermined\n");
+ return -ENOMEM;
+ }
+
+ if (ci->pub.ramsize > BRCMF_CHIP_MAX_MEMSIZE) {
+ brcmf_err("RAM size is incorrect\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static u32 brcmf_chip_dmp_get_desc(struct brcmf_chip_priv *ci, u32 *eromaddr,
+ u8 *type)
+{
+ u32 val;
+
+ /* read next descriptor */
+ val = ci->ops->read32(ci->ctx, *eromaddr);
+ *eromaddr += 4;
+
+ if (!type)
+ return val;
+
+ /* determine descriptor type */
+ *type = (val & DMP_DESC_TYPE_MSK);
+ if ((*type & ~DMP_DESC_ADDRSIZE_GT32) == DMP_DESC_ADDRESS)
+ *type = DMP_DESC_ADDRESS;
+
+ return val;
+}
+
+static int brcmf_chip_dmp_get_regaddr(struct brcmf_chip_priv *ci, u32 *eromaddr,
+ u32 *regbase, u32 *wrapbase)
+{
+ u8 desc;
+ u32 val;
+ u8 mpnum = 0;
+ u8 stype, sztype, wraptype;
+
+ *regbase = 0;
+ *wrapbase = 0;
+
+ val = brcmf_chip_dmp_get_desc(ci, eromaddr, &desc);
+ if (desc == DMP_DESC_MASTER_PORT) {
+ mpnum = (val & DMP_MASTER_PORT_NUM) >> DMP_MASTER_PORT_NUM_S;
+ wraptype = DMP_SLAVE_TYPE_MWRAP;
+ } else if (desc == DMP_DESC_ADDRESS) {
+ /* revert erom address */
+ *eromaddr -= 4;
+ wraptype = DMP_SLAVE_TYPE_SWRAP;
+ } else {
+ *eromaddr -= 4;
+ return -EILSEQ;
+ }
+
+ do {
+ /* locate address descriptor */
+ do {
+ val = brcmf_chip_dmp_get_desc(ci, eromaddr, &desc);
+ /* unexpected table end */
+ if (desc == DMP_DESC_EOT) {
+ *eromaddr -= 4;
+ return -EFAULT;
+ }
+ } while (desc != DMP_DESC_ADDRESS &&
+ desc != DMP_DESC_COMPONENT);
+
+ /* stop if we crossed current component border */
+ if (desc == DMP_DESC_COMPONENT) {
+ *eromaddr -= 4;
+ return 0;
+ }
+
+ /* skip upper 32-bit address descriptor */
+ if (val & DMP_DESC_ADDRSIZE_GT32)
+ brcmf_chip_dmp_get_desc(ci, eromaddr, NULL);
+
+ sztype = (val & DMP_SLAVE_SIZE_TYPE) >> DMP_SLAVE_SIZE_TYPE_S;
+
+ /* next size descriptor can be skipped */
+ if (sztype == DMP_SLAVE_SIZE_DESC) {
+ val = brcmf_chip_dmp_get_desc(ci, eromaddr, NULL);
+ /* skip upper size descriptor if present */
+ if (val & DMP_DESC_ADDRSIZE_GT32)
+ brcmf_chip_dmp_get_desc(ci, eromaddr, NULL);
+ }
+
+ /* only look for 4K register regions */
+ if (sztype != DMP_SLAVE_SIZE_4K)
+ continue;
+
+ stype = (val & DMP_SLAVE_TYPE) >> DMP_SLAVE_TYPE_S;
+
+ /* only regular slave and wrapper */
+ if (*regbase == 0 && stype == DMP_SLAVE_TYPE_SLAVE)
+ *regbase = val & DMP_SLAVE_ADDR_BASE;
+ if (*wrapbase == 0 && stype == wraptype)
+ *wrapbase = val & DMP_SLAVE_ADDR_BASE;
+ } while (*regbase == 0 || *wrapbase == 0);
+
+ return 0;
+}
+
+static
+int brcmf_chip_dmp_erom_scan(struct brcmf_chip_priv *ci)
+{
+ struct brcmf_core *core;
+ u32 eromaddr;
+ u8 desc_type = 0;
+ u32 val;
+ u16 id;
+ u8 nmp, nsp, nmw, nsw, rev;
+ u32 base, wrap;
+ int err;
+
+ eromaddr = ci->ops->read32(ci->ctx, CORE_CC_REG(SI_ENUM_BASE, eromptr));
+
+ while (desc_type != DMP_DESC_EOT) {
+ val = brcmf_chip_dmp_get_desc(ci, &eromaddr, &desc_type);
+ if (!(val & DMP_DESC_VALID))
+ continue;
+
+ if (desc_type == DMP_DESC_EMPTY)
+ continue;
+
+ /* need a component descriptor */
+ if (desc_type != DMP_DESC_COMPONENT)
+ continue;
+
+ id = (val & DMP_COMP_PARTNUM) >> DMP_COMP_PARTNUM_S;
+
+ /* next descriptor must be component as well */
+ val = brcmf_chip_dmp_get_desc(ci, &eromaddr, &desc_type);
+ if (WARN_ON((val & DMP_DESC_TYPE_MSK) != DMP_DESC_COMPONENT))
+ return -EFAULT;
+
+ /* only look at cores with master port(s) */
+ nmp = (val & DMP_COMP_NUM_MPORT) >> DMP_COMP_NUM_MPORT_S;
+ nsp = (val & DMP_COMP_NUM_SPORT) >> DMP_COMP_NUM_SPORT_S;
+ nmw = (val & DMP_COMP_NUM_MWRAP) >> DMP_COMP_NUM_MWRAP_S;
+ nsw = (val & DMP_COMP_NUM_SWRAP) >> DMP_COMP_NUM_SWRAP_S;
+ rev = (val & DMP_COMP_REVISION) >> DMP_COMP_REVISION_S;
+
+ /* need core with ports */
+ if (nmw + nsw == 0 &&
+ id != BCMA_CORE_PMU)
+ continue;
+
+ /* try to obtain register address info */
+ err = brcmf_chip_dmp_get_regaddr(ci, &eromaddr, &base, &wrap);
+ if (err)
+ continue;
+
+ /* finally a core to be added */
+ core = brcmf_chip_add_core(ci, id, base, wrap);
+ if (IS_ERR(core))
+ return PTR_ERR(core);
+
+ core->rev = rev;
+ }
+
+ return 0;
+}
+
+static int brcmf_chip_recognition(struct brcmf_chip_priv *ci)
+{
+ struct brcmf_core *core;
+ u32 regdata;
+ u32 socitype;
+ int ret;
+
+ /* Get CC core rev
+ * Chipid is assume to be at offset 0 from SI_ENUM_BASE
+ * For different chiptypes or old sdio hosts w/o chipcommon,
+ * other ways of recognition should be added here.
+ */
+ regdata = ci->ops->read32(ci->ctx, CORE_CC_REG(SI_ENUM_BASE, chipid));
+ ci->pub.chip = regdata & CID_ID_MASK;
+ ci->pub.chiprev = (regdata & CID_REV_MASK) >> CID_REV_SHIFT;
+ socitype = (regdata & CID_TYPE_MASK) >> CID_TYPE_SHIFT;
+
+ brcmf_chip_name(ci->pub.chip, ci->pub.name, sizeof(ci->pub.name));
+ brcmf_dbg(INFO, "found %s chip: BCM%s, rev=%d\n",
+ socitype == SOCI_SB ? "SB" : "AXI", ci->pub.name,
+ ci->pub.chiprev);
+
+ if (socitype == SOCI_SB) {
+ if (ci->pub.chip != BRCM_CC_4329_CHIP_ID) {
+ brcmf_err("SB chip is not supported\n");
+ return -ENODEV;
+ }
+ ci->iscoreup = brcmf_chip_sb_iscoreup;
+ ci->coredisable = brcmf_chip_sb_coredisable;
+ ci->resetcore = brcmf_chip_sb_resetcore;
+
+ core = brcmf_chip_add_core(ci, BCMA_CORE_CHIPCOMMON,
+ SI_ENUM_BASE, 0);
+ brcmf_chip_sb_corerev(ci, core);
+ core = brcmf_chip_add_core(ci, BCMA_CORE_SDIO_DEV,
+ BCM4329_CORE_BUS_BASE, 0);
+ brcmf_chip_sb_corerev(ci, core);
+ core = brcmf_chip_add_core(ci, BCMA_CORE_INTERNAL_MEM,
+ BCM4329_CORE_SOCRAM_BASE, 0);
+ brcmf_chip_sb_corerev(ci, core);
+ core = brcmf_chip_add_core(ci, BCMA_CORE_ARM_CM3,
+ BCM4329_CORE_ARM_BASE, 0);
+ brcmf_chip_sb_corerev(ci, core);
+
+ core = brcmf_chip_add_core(ci, BCMA_CORE_80211, 0x18001000, 0);
+ brcmf_chip_sb_corerev(ci, core);
+ } else if (socitype == SOCI_AI) {
+ ci->iscoreup = brcmf_chip_ai_iscoreup;
+ ci->coredisable = brcmf_chip_ai_coredisable;
+ ci->resetcore = brcmf_chip_ai_resetcore;
+
+ brcmf_chip_dmp_erom_scan(ci);
+ } else {
+ brcmf_err("chip backplane type %u is not supported\n",
+ socitype);
+ return -ENODEV;
+ }
+
+ ret = brcmf_chip_cores_check(ci);
+ if (ret)
+ return ret;
+
+ /* assure chip is passive for core access */
+ brcmf_chip_set_passive(&ci->pub);
+
+ /* Call bus specific reset function now. Cores have been determined
+ * but further access may require a chip specific reset at this point.
+ */
+ if (ci->ops->reset) {
+ ci->ops->reset(ci->ctx, &ci->pub);
+ brcmf_chip_set_passive(&ci->pub);
+ }
+
+ return brcmf_chip_get_raminfo(ci);
+}
+
+static void brcmf_chip_disable_arm(struct brcmf_chip_priv *chip, u16 id)
+{
+ struct brcmf_core *core;
+ struct brcmf_core_priv *cpu;
+ u32 val;
+
+
+ core = brcmf_chip_get_core(&chip->pub, id);
+ if (!core)
+ return;
+
+ switch (id) {
+ case BCMA_CORE_ARM_CM3:
+ brcmf_chip_coredisable(core, 0, 0);
+ break;
+ case BCMA_CORE_ARM_CR4:
+ case BCMA_CORE_ARM_CA7:
+ cpu = container_of(core, struct brcmf_core_priv, pub);
+
+ /* clear all IOCTL bits except HALT bit */
+ val = chip->ops->read32(chip->ctx, cpu->wrapbase + BCMA_IOCTL);
+ val &= ARMCR4_BCMA_IOCTL_CPUHALT;
+ brcmf_chip_resetcore(core, val, ARMCR4_BCMA_IOCTL_CPUHALT,
+ ARMCR4_BCMA_IOCTL_CPUHALT);
+ break;
+ default:
+ brcmf_err("unknown id: %u\n", id);
+ break;
+ }
+}
+
+static int brcmf_chip_setup(struct brcmf_chip_priv *chip)
+{
+ struct brcmf_chip *pub;
+ struct brcmf_core_priv *cc;
+ struct brcmf_core *pmu;
+ u32 base;
+ u32 val;
+ int ret = 0;
+
+ pub = &chip->pub;
+ cc = list_first_entry(&chip->cores, struct brcmf_core_priv, list);
+ base = cc->pub.base;
+
+ /* get chipcommon capabilites */
+ pub->cc_caps = chip->ops->read32(chip->ctx,
+ CORE_CC_REG(base, capabilities));
+ pub->cc_caps_ext = chip->ops->read32(chip->ctx,
+ CORE_CC_REG(base,
+ capabilities_ext));
+
+ /* get pmu caps & rev */
+ pmu = brcmf_chip_get_pmu(pub); /* after reading cc_caps_ext */
+ if (pub->cc_caps & CC_CAP_PMU) {
+ val = chip->ops->read32(chip->ctx,
+ CORE_CC_REG(pmu->base, pmucapabilities));
+ pub->pmurev = val & PCAP_REV_MASK;
+ pub->pmucaps = val;
+ }
+
+ brcmf_dbg(INFO, "ccrev=%d, pmurev=%d, pmucaps=0x%x\n",
+ cc->pub.rev, pub->pmurev, pub->pmucaps);
+
+ /* execute bus core specific setup */
+ if (chip->ops->setup)
+ ret = chip->ops->setup(chip->ctx, pub);
+
+ return ret;
+}
+
+struct brcmf_chip *brcmf_chip_attach(void *ctx,
+ const struct brcmf_buscore_ops *ops)
+{
+ struct brcmf_chip_priv *chip;
+ int err = 0;
+
+ if (WARN_ON(!ops->read32))
+ err = -EINVAL;
+ if (WARN_ON(!ops->write32))
+ err = -EINVAL;
+ if (WARN_ON(!ops->prepare))
+ err = -EINVAL;
+ if (WARN_ON(!ops->activate))
+ err = -EINVAL;
+ if (err < 0)
+ return ERR_PTR(-EINVAL);
+
+ chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return ERR_PTR(-ENOMEM);
+
+ INIT_LIST_HEAD(&chip->cores);
+ chip->num_cores = 0;
+ chip->ops = ops;
+ chip->ctx = ctx;
+
+ err = ops->prepare(ctx);
+ if (err < 0)
+ goto fail;
+
+ err = brcmf_chip_recognition(chip);
+ if (err < 0)
+ goto fail;
+
+ err = brcmf_chip_setup(chip);
+ if (err < 0)
+ goto fail;
+
+ return &chip->pub;
+
+fail:
+ brcmf_chip_detach(&chip->pub);
+ return ERR_PTR(err);
+}
+
+void brcmf_chip_detach(struct brcmf_chip *pub)
+{
+ struct brcmf_chip_priv *chip;
+ struct brcmf_core_priv *core;
+ struct brcmf_core_priv *tmp;
+
+ chip = container_of(pub, struct brcmf_chip_priv, pub);
+ list_for_each_entry_safe(core, tmp, &chip->cores, list) {
+ list_del(&core->list);
+ kfree(core);
+ }
+ kfree(chip);
+}
+
+struct brcmf_core *brcmf_chip_get_core(struct brcmf_chip *pub, u16 coreid)
+{
+ struct brcmf_chip_priv *chip;
+ struct brcmf_core_priv *core;
+
+ chip = container_of(pub, struct brcmf_chip_priv, pub);
+ list_for_each_entry(core, &chip->cores, list)
+ if (core->pub.id == coreid)
+ return &core->pub;
+
+ return NULL;
+}
+
+struct brcmf_core *brcmf_chip_get_chipcommon(struct brcmf_chip *pub)
+{
+ struct brcmf_chip_priv *chip;
+ struct brcmf_core_priv *cc;
+
+ chip = container_of(pub, struct brcmf_chip_priv, pub);
+ cc = list_first_entry(&chip->cores, struct brcmf_core_priv, list);
+ if (WARN_ON(!cc || cc->pub.id != BCMA_CORE_CHIPCOMMON))
+ return brcmf_chip_get_core(pub, BCMA_CORE_CHIPCOMMON);
+ return &cc->pub;
+}
+
+struct brcmf_core *brcmf_chip_get_pmu(struct brcmf_chip *pub)
+{
+ struct brcmf_core *cc = brcmf_chip_get_chipcommon(pub);
+ struct brcmf_core *pmu;
+
+ /* See if there is separated PMU core available */
+ if (cc->rev >= 35 &&
+ pub->cc_caps_ext & BCMA_CC_CAP_EXT_AOB_PRESENT) {
+ pmu = brcmf_chip_get_core(pub, BCMA_CORE_PMU);
+ if (pmu)
+ return pmu;
+ }
+
+ /* Fallback to ChipCommon core for older hardware */
+ return cc;
+}
+
+bool brcmf_chip_iscoreup(struct brcmf_core *pub)
+{
+ struct brcmf_core_priv *core;
+
+ core = container_of(pub, struct brcmf_core_priv, pub);
+ return core->chip->iscoreup(core);
+}
+
+void brcmf_chip_coredisable(struct brcmf_core *pub, u32 prereset, u32 reset)
+{
+ struct brcmf_core_priv *core;
+
+ core = container_of(pub, struct brcmf_core_priv, pub);
+ core->chip->coredisable(core, prereset, reset);
+}
+
+void brcmf_chip_resetcore(struct brcmf_core *pub, u32 prereset, u32 reset,
+ u32 postreset)
+{
+ struct brcmf_core_priv *core;
+
+ core = container_of(pub, struct brcmf_core_priv, pub);
+ core->chip->resetcore(core, prereset, reset, postreset);
+}
+
+static void
+brcmf_chip_cm3_set_passive(struct brcmf_chip_priv *chip)
+{
+ struct brcmf_core *core;
+ struct brcmf_core_priv *sr;
+
+ brcmf_chip_disable_arm(chip, BCMA_CORE_ARM_CM3);
+ core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_80211);
+ brcmf_chip_resetcore(core, D11_BCMA_IOCTL_PHYRESET |
+ D11_BCMA_IOCTL_PHYCLOCKEN,
+ D11_BCMA_IOCTL_PHYCLOCKEN,
+ D11_BCMA_IOCTL_PHYCLOCKEN);
+ core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_INTERNAL_MEM);
+ brcmf_chip_resetcore(core, 0, 0, 0);
+
+ /* disable bank #3 remap for this device */
+ if (chip->pub.chip == BRCM_CC_43430_CHIP_ID) {
+ sr = container_of(core, struct brcmf_core_priv, pub);
+ brcmf_chip_core_write32(sr, SOCRAMREGOFFS(bankidx), 3);
+ brcmf_chip_core_write32(sr, SOCRAMREGOFFS(bankpda), 0);
+ }
+}
+
+static bool brcmf_chip_cm3_set_active(struct brcmf_chip_priv *chip)
+{
+ struct brcmf_core *core;
+
+ core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_INTERNAL_MEM);
+ if (!brcmf_chip_iscoreup(core)) {
+ brcmf_err("SOCRAM core is down after reset?\n");
+ return false;
+ }
+
+ chip->ops->activate(chip->ctx, &chip->pub, 0);
+
+ core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_ARM_CM3);
+ brcmf_chip_resetcore(core, 0, 0, 0);
+
+ return true;
+}
+
+static inline void
+brcmf_chip_cr4_set_passive(struct brcmf_chip_priv *chip)
+{
+ struct brcmf_core *core;
+
+ brcmf_chip_disable_arm(chip, BCMA_CORE_ARM_CR4);
+
+ core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_80211);
+ brcmf_chip_resetcore(core, D11_BCMA_IOCTL_PHYRESET |
+ D11_BCMA_IOCTL_PHYCLOCKEN,
+ D11_BCMA_IOCTL_PHYCLOCKEN,
+ D11_BCMA_IOCTL_PHYCLOCKEN);
+}
+
+static bool brcmf_chip_cr4_set_active(struct brcmf_chip_priv *chip, u32 rstvec)
+{
+ struct brcmf_core *core;
+
+ chip->ops->activate(chip->ctx, &chip->pub, rstvec);
+
+ /* restore ARM */
+ core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_ARM_CR4);
+ brcmf_chip_resetcore(core, ARMCR4_BCMA_IOCTL_CPUHALT, 0, 0);
+
+ return true;
+}
+
+static inline void
+brcmf_chip_ca7_set_passive(struct brcmf_chip_priv *chip)
+{
+ struct brcmf_core *core;
+
+ brcmf_chip_disable_arm(chip, BCMA_CORE_ARM_CA7);
+
+ core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_80211);
+ brcmf_chip_resetcore(core, D11_BCMA_IOCTL_PHYRESET |
+ D11_BCMA_IOCTL_PHYCLOCKEN,
+ D11_BCMA_IOCTL_PHYCLOCKEN,
+ D11_BCMA_IOCTL_PHYCLOCKEN);
+}
+
+static bool brcmf_chip_ca7_set_active(struct brcmf_chip_priv *chip, u32 rstvec)
+{
+ struct brcmf_core *core;
+
+ chip->ops->activate(chip->ctx, &chip->pub, rstvec);
+
+ /* restore ARM */
+ core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_ARM_CA7);
+ brcmf_chip_resetcore(core, ARMCR4_BCMA_IOCTL_CPUHALT, 0, 0);
+
+ return true;
+}
+
+void brcmf_chip_set_passive(struct brcmf_chip *pub)
+{
+ struct brcmf_chip_priv *chip;
+ struct brcmf_core *arm;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ chip = container_of(pub, struct brcmf_chip_priv, pub);
+ arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CR4);
+ if (arm) {
+ brcmf_chip_cr4_set_passive(chip);
+ return;
+ }
+ arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CA7);
+ if (arm) {
+ brcmf_chip_ca7_set_passive(chip);
+ return;
+ }
+ arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CM3);
+ if (arm) {
+ brcmf_chip_cm3_set_passive(chip);
+ return;
+ }
+}
+
+bool brcmf_chip_set_active(struct brcmf_chip *pub, u32 rstvec)
+{
+ struct brcmf_chip_priv *chip;
+ struct brcmf_core *arm;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ chip = container_of(pub, struct brcmf_chip_priv, pub);
+ arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CR4);
+ if (arm)
+ return brcmf_chip_cr4_set_active(chip, rstvec);
+ arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CA7);
+ if (arm)
+ return brcmf_chip_ca7_set_active(chip, rstvec);
+ arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CM3);
+ if (arm)
+ return brcmf_chip_cm3_set_active(chip);
+
+ return false;
+}
+
+bool brcmf_chip_sr_capable(struct brcmf_chip *pub)
+{
+ u32 base, addr, reg, pmu_cc3_mask = ~0;
+ struct brcmf_chip_priv *chip;
+ struct brcmf_core *pmu = brcmf_chip_get_pmu(pub);
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ /* old chips with PMU version less than 17 don't support save restore */
+ if (pub->pmurev < 17)
+ return false;
+
+ base = brcmf_chip_get_chipcommon(pub)->base;
+ chip = container_of(pub, struct brcmf_chip_priv, pub);
+
+ switch (pub->chip) {
+ case BRCM_CC_4354_CHIP_ID:
+ case BRCM_CC_4356_CHIP_ID:
+ /* explicitly check SR engine enable bit */
+ pmu_cc3_mask = BIT(2);
+ /* fall-through */
+ case BRCM_CC_43241_CHIP_ID:
+ case BRCM_CC_4335_CHIP_ID:
+ case BRCM_CC_4339_CHIP_ID:
+ /* read PMU chipcontrol register 3 */
+ addr = CORE_CC_REG(pmu->base, chipcontrol_addr);
+ chip->ops->write32(chip->ctx, addr, 3);
+ addr = CORE_CC_REG(pmu->base, chipcontrol_data);
+ reg = chip->ops->read32(chip->ctx, addr);
+ return (reg & pmu_cc3_mask) != 0;
+ case BRCM_CC_43430_CHIP_ID:
+ addr = CORE_CC_REG(base, sr_control1);
+ reg = chip->ops->read32(chip->ctx, addr);
+ return reg != 0;
+ case CY_CC_43012_CHIP_ID:
+ addr = CORE_CC_REG(pmu->base, retention_ctl);
+ reg = chip->ops->read32(chip->ctx, addr);
+ return (reg & (PMU_RCTL_MACPHY_DISABLE_MASK |
+ PMU_RCTL_LOGIC_DISABLE_MASK)) == 0;
+ default:
+ addr = CORE_CC_REG(pmu->base, pmucapabilities_ext);
+ reg = chip->ops->read32(chip->ctx, addr);
+ if ((reg & PCAPEXT_SR_SUPPORTED_MASK) == 0)
+ return false;
+
+ addr = CORE_CC_REG(pmu->base, retention_ctl);
+ reg = chip->ops->read32(chip->ctx, addr);
+ return (reg & (PMU_RCTL_MACPHY_DISABLE_MASK |
+ PMU_RCTL_LOGIC_DISABLE_MASK)) == 0;
+ }
+}
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h
new file mode 100644
index 0000000..dd0ec3e
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2014 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#ifndef BRCMF_CHIP_H
+#define BRCMF_CHIP_H
+
+#include <linux/types.h>
+
+#define CORE_CC_REG(base, field) \
+ (base + offsetof(struct chipcregs, field))
+
+/**
+ * struct brcmf_chip - chip level information.
+ *
+ * @chip: chip identifier.
+ * @chiprev: chip revision.
+ * @cc_caps: chipcommon core capabilities.
+ * @cc_caps_ext: chipcommon core extended capabilities.
+ * @pmucaps: PMU capabilities.
+ * @pmurev: PMU revision.
+ * @rambase: RAM base address (only applicable for ARM CR4 chips).
+ * @ramsize: amount of RAM on chip including retention.
+ * @srsize: amount of retention RAM on chip.
+ * @name: string representation of the chip identifier.
+ */
+struct brcmf_chip {
+ u32 chip;
+ u32 chiprev;
+ u32 cc_caps;
+ u32 cc_caps_ext;
+ u32 pmucaps;
+ u32 pmurev;
+ u32 rambase;
+ u32 ramsize;
+ u32 srsize;
+ char name[8];
+};
+
+/**
+ * struct brcmf_core - core related information.
+ *
+ * @id: core identifier.
+ * @rev: core revision.
+ * @base: base address of core register space.
+ */
+struct brcmf_core {
+ u16 id;
+ u16 rev;
+ u32 base;
+};
+
+/**
+ * struct brcmf_buscore_ops - buscore specific callbacks.
+ *
+ * @read32: read 32-bit value over bus.
+ * @write32: write 32-bit value over bus.
+ * @prepare: prepare bus for core configuration.
+ * @setup: bus-specific core setup.
+ * @active: chip becomes active.
+ * The callback should use the provided @rstvec when non-zero.
+ */
+struct brcmf_buscore_ops {
+ u32 (*read32)(void *ctx, u32 addr);
+ void (*write32)(void *ctx, u32 addr, u32 value);
+ int (*prepare)(void *ctx);
+ int (*reset)(void *ctx, struct brcmf_chip *chip);
+ int (*setup)(void *ctx, struct brcmf_chip *chip);
+ void (*activate)(void *ctx, struct brcmf_chip *chip, u32 rstvec);
+};
+
+struct brcmf_chip *brcmf_chip_attach(void *ctx,
+ const struct brcmf_buscore_ops *ops);
+void brcmf_chip_detach(struct brcmf_chip *chip);
+struct brcmf_core *brcmf_chip_get_core(struct brcmf_chip *chip, u16 coreid);
+struct brcmf_core *brcmf_chip_get_chipcommon(struct brcmf_chip *chip);
+struct brcmf_core *brcmf_chip_get_pmu(struct brcmf_chip *pub);
+bool brcmf_chip_iscoreup(struct brcmf_core *core);
+void brcmf_chip_coredisable(struct brcmf_core *core, u32 prereset, u32 reset);
+void brcmf_chip_resetcore(struct brcmf_core *core, u32 prereset, u32 reset,
+ u32 postreset);
+void brcmf_chip_set_passive(struct brcmf_chip *ci);
+bool brcmf_chip_set_active(struct brcmf_chip *ci, u32 rstvec);
+bool brcmf_chip_sr_capable(struct brcmf_chip *pub);
+
+#endif /* BRCMF_AXIDMP_H */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
new file mode 100644
index 0000000..1a5095e
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
@@ -0,0 +1,779 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/netdevice.h>
+#include <linux/module.h>
+#include <linux/firmware.h>
+#include <brcmu_wifi.h>
+#include <brcmu_utils.h>
+#include <defs.h>
+#include "core.h"
+#include "bus.h"
+#include "debug.h"
+#include "fwil.h"
+#include "fwil_types.h"
+#include "tracepoint.h"
+#include "common.h"
+#include "of.h"
+#include "firmware.h"
+
+MODULE_AUTHOR("Broadcom Corporation");
+MODULE_DESCRIPTION("Broadcom 802.11 wireless LAN fullmac driver.");
+MODULE_LICENSE("Dual BSD/GPL");
+
+const u8 ALLFFMAC[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+#define BRCMF_DEFAULT_SCAN_CHANNEL_TIME 40
+#define BRCMF_DEFAULT_SCAN_UNASSOC_TIME 40
+
+/* default boost value for RSSI_DELTA in preferred join selection */
+#define BRCMF_JOIN_PREF_RSSI_BOOST 8
+
+#define BRCMF_DEFAULT_TXGLOM_SIZE 32 /* max tx frames in glom chain */
+
+static int brcmf_sdiod_txglomsz = BRCMF_DEFAULT_TXGLOM_SIZE;
+module_param_named(txglomsz, brcmf_sdiod_txglomsz, int, 0);
+MODULE_PARM_DESC(txglomsz, "Maximum tx packet chain size [SDIO]");
+
+/* Debug level configuration. See debug.h for bits, sysfs modifiable */
+int brcmf_msg_level;
+module_param_named(debug, brcmf_msg_level, int, S_IRUSR | S_IWUSR);
+MODULE_PARM_DESC(debug, "Level of debug output");
+
+static int brcmf_p2p_enable;
+module_param_named(p2pon, brcmf_p2p_enable, int, 0);
+MODULE_PARM_DESC(p2pon, "Enable legacy p2p management functionality");
+
+static int brcmf_feature_disable;
+module_param_named(feature_disable, brcmf_feature_disable, int, 0);
+MODULE_PARM_DESC(feature_disable, "Disable features");
+
+static char brcmf_firmware_path[BRCMF_FW_ALTPATH_LEN];
+module_param_string(alternative_fw_path, brcmf_firmware_path,
+ BRCMF_FW_ALTPATH_LEN, S_IRUSR);
+MODULE_PARM_DESC(alternative_fw_path, "Alternative firmware path");
+
+static int brcmf_fcmode;
+module_param_named(fcmode, brcmf_fcmode, int, 0);
+MODULE_PARM_DESC(fcmode, "Mode of firmware signalled flow control");
+
+static int brcmf_roamoff;
+module_param_named(roamoff, brcmf_roamoff, int, S_IRUSR);
+MODULE_PARM_DESC(roamoff, "Do not use internal roaming engine");
+
+#ifdef DEBUG
+/* always succeed brcmf_bus_started() */
+static int brcmf_ignore_probe_fail;
+module_param_named(ignore_probe_fail, brcmf_ignore_probe_fail, int, 0);
+MODULE_PARM_DESC(ignore_probe_fail, "always succeed probe for debugging");
+#endif
+
+/* Nest Sapphire-9229 mac addr */
+char nest_etheraddr[32] = {'\0'};
+module_param_string(nest_etheraddr, nest_etheraddr, 32, 0);
+static int nest_set_macaddr(struct brcmf_if *ifp, char *value);
+
+static int nest_keepalive_interval = 0;
+module_param(nest_keepalive_interval, int, 0);
+static int nest_set_keepalive(struct brcmf_if *ifp, int value);
+
+/* NEST: enable keepalive debugging */
+int g_keepalive_debug = 0;
+module_param(g_keepalive_debug, int, 0644);
+
+/* NEST: seconds of no beacons before declaring loss */
+int g_beacon_loss_timeout = 3;
+module_param(g_beacon_loss_timeout, int, 0644);
+
+/* NEST: number of times for 43340 to SW retry assoc */
+int g_assoc_retry_max = 0;
+module_param(g_assoc_retry_max, int, 0644);
+
+/* NEST: inactivity timer in milliseconds before device starts 802.11 PS */
+int g_pm2_sleep_ret = 200;
+module_param(g_pm2_sleep_ret, int, 0644);
+
+int g_remote_ka_timeout = 390; /* number of seconds without seeing a TCPKLV before waking */
+module_param(g_remote_ka_timeout, int, 0644);
+
+int g_ampdu_enable = 1; /* if enable ampdu */
+module_param(g_ampdu_enable, int, 0644);
+
+int g_ampdu_mpdu = -1; /* Default power mode */
+module_param(g_ampdu_mpdu, int, 0644);
+
+int g_poll_sdio_bus = 0;
+module_param(g_poll_sdio_bus, int, 0644);
+
+int dhd_power_mode = PM_FAST; /* Default power mode */
+module_param(dhd_power_mode, int, 0644);
+
+int g_4334x_pkt_filter = 1; /* Default 4334x pkt filter */
+module_param(g_4334x_pkt_filter, int, 0644);
+
+int g_buf_key_b4_m4 = 0;
+module_param(g_buf_key_b4_m4, int, 0644);
+
+#define MAX_DEV_TYPE 10
+static const char nest_devtype[MAX_DEV_TYPE][4] = {
+ "NA", "D3", "O1", "F1", "QV2", "RQ1", "BQ1", "TQ1", "A2", "AG1"
+};
+
+int g_devtype = 0; /* device type defined by nest_devtype[] */
+module_param(g_devtype, int, 0644);
+
+char g_country_abbrev[BRCMF_COUNTRY_BUF_SZ] = {'\0'};
+module_param_string(g_country_abbrev, g_country_abbrev, BRCMF_COUNTRY_BUF_SZ, 0);
+
+int g_pfn_enable = 0; /* if enable pfn */
+module_param(g_pfn_enable, int, 0644);
+
+static struct brcmfmac_platform_data *brcmfmac_pdata;
+struct brcmf_mp_global_t brcmf_mp_global;
+
+void brcmf_c_set_joinpref_default(struct brcmf_if *ifp)
+{
+ struct brcmf_join_pref_params join_pref_params[2];
+ int err;
+
+ /* Setup join_pref to select target by RSSI (boost on 5GHz) */
+ join_pref_params[0].type = BRCMF_JOIN_PREF_RSSI_DELTA;
+ join_pref_params[0].len = 2;
+ join_pref_params[0].rssi_gain = BRCMF_JOIN_PREF_RSSI_BOOST;
+ join_pref_params[0].band = WLC_BAND_5G;
+
+ join_pref_params[1].type = BRCMF_JOIN_PREF_RSSI;
+ join_pref_params[1].len = 2;
+ join_pref_params[1].rssi_gain = 0;
+ join_pref_params[1].band = 0;
+ err = brcmf_fil_iovar_data_set(ifp, "join_pref", join_pref_params,
+ sizeof(join_pref_params));
+ if (err)
+ brcmf_err("Set join_pref error (%d)\n", err);
+}
+
+int brcmf_c_download_2_dongle(struct brcmf_if *ifp, char *dcmd, u16 flag,
+ u16 dload_type, char *dload_buf, u32 len)
+{
+ struct brcmf_dload_data_le *dload_ptr;
+ u32 dload_data_offset;
+ u16 flags;
+ s32 err;
+
+ dload_ptr = (struct brcmf_dload_data_le *)dload_buf;
+ dload_data_offset = offsetof(struct brcmf_dload_data_le, data);
+ flags = flag | (DLOAD_HANDLER_VER << DLOAD_FLAG_VER_SHIFT);
+
+ dload_ptr->flag = cpu_to_le16(flags);
+ dload_ptr->dload_type = cpu_to_le16(dload_type);
+ dload_ptr->len = cpu_to_le32(len - dload_data_offset);
+ dload_ptr->crc = cpu_to_le32(0);
+ len = len + 8 - (len % 8);
+
+ err = brcmf_fil_iovar_data_set(ifp, dcmd, (void *)dload_buf, len);
+
+ return err;
+}
+
+int brcmf_c_get_clm_name(struct brcmf_if *ifp, u8 *clm_name)
+{
+ struct brcmf_rev_info_le revinfo;
+ struct brcmf_bus *bus = ifp->drvr->bus_if;
+ u8 fw_name[BRCMF_FW_NAME_LEN];
+ u8 *ptr;
+ size_t len;
+ u32 chipnum;
+ u32 chiprev;
+ s32 err;
+
+ err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_REVINFO, &revinfo,
+ sizeof(revinfo));
+ if (err < 0) {
+ brcmf_err("retrieving revision info failed (%d)\n", err);
+ goto done;
+ }
+
+ chipnum = le32_to_cpu(revinfo.chipnum);
+ chiprev = le32_to_cpu(revinfo.chiprev);
+
+ memset(fw_name, 0, BRCMF_FW_NAME_LEN);
+ err = brcmf_bus_get_fwname(bus, chipnum, chiprev, fw_name);
+ if (err) {
+ brcmf_err("get firmware name failed (%d)\n", err);
+ goto done;
+ }
+
+ /* generate CLM blob file name */
+ ptr = strrchr(fw_name, '.');
+ len = ptr - fw_name + 1;
+ if (len + strlen(".clm_blob") > BRCMF_FW_NAME_LEN) {
+ err = -E2BIG;
+ } else {
+ strlcpy(clm_name, fw_name, len);
+ strlcat(clm_name, ".clm_blob", BRCMF_FW_NAME_LEN);
+ }
+done:
+ return err;
+}
+
+int brcmf_c_process_clm_blob(struct brcmf_if *ifp)
+{
+ struct device *dev = ifp->drvr->bus_if->dev;
+ const struct firmware *clm = NULL;
+ u8 buf[BRCMF_DCMD_SMLEN];
+ u8 clm_name[BRCMF_FW_NAME_LEN];
+ u32 data_offset;
+ u32 size2alloc;
+ u8 *chunk_buf;
+ u32 chunk_len;
+ u32 datalen;
+ u32 cumulative_len = 0;
+ u16 dl_flag = DL_BEGIN;
+ s32 err;
+
+ brcmf_dbg(INFO, "Enter\n");
+
+ memset(clm_name, 0, BRCMF_FW_NAME_LEN);
+ err = brcmf_c_get_clm_name(ifp, clm_name);
+ if (err) {
+ brcmf_err("get CLM blob file name failed (%d)\n", err);
+ return err;
+ }
+
+ err = request_firmware_direct(&clm, clm_name, dev);
+ if (err) {
+ if (err == -ENOENT || err == -ENOMEM)
+ return 0;
+ brcmf_err("request CLM blob file failed (%d)\n", err);
+ return err;
+ }
+
+ datalen = clm->size;
+ data_offset = offsetof(struct brcmf_dload_data_le, data);
+ size2alloc = data_offset + MAX_CHUNK_LEN;
+
+ chunk_buf = kzalloc(size2alloc, GFP_KERNEL);
+ if (!chunk_buf) {
+ err = -ENOMEM;
+ goto done;
+ }
+
+ do {
+ if (datalen > MAX_CHUNK_LEN) {
+ chunk_len = MAX_CHUNK_LEN;
+ } else {
+ chunk_len = datalen;
+ dl_flag |= DL_END;
+ }
+
+ memcpy(chunk_buf + data_offset, clm->data + cumulative_len,
+ chunk_len);
+
+ err = brcmf_c_download_2_dongle(ifp, "clmload", dl_flag,
+ DL_TYPE_CLM, chunk_buf,
+ data_offset + chunk_len);
+
+ dl_flag &= ~DL_BEGIN;
+
+ cumulative_len += chunk_len;
+ datalen -= chunk_len;
+ } while ((datalen > 0) && (err == 0));
+
+ if (err) {
+ brcmf_err("clmload (%d byte file) failed (%d); ",
+ (u32)clm->size, err);
+ /* Retrieve clmload_status and print */
+ memset(buf, 0, BRCMF_DCMD_SMLEN);
+ err = brcmf_fil_iovar_data_get(ifp, "clmload_status", buf,
+ BRCMF_DCMD_SMLEN);
+ if (err)
+ brcmf_err("get clmload_status failed (%d)\n", err);
+ else
+ brcmf_err("clmload_status=%d\n", *((int *)buf));
+ err = -EIO;
+ }
+
+ kfree(chunk_buf);
+done:
+ release_firmware(clm);
+ return err;
+}
+
+extern s32 brcmf_translate_country_code(struct brcmf_pub *drvr, char alpha2[2], struct brcmf_fil_country_le *ccreq);
+extern s32 brcmf_set_restricted_band(struct brcmf_if *ifp, struct brcmf_fil_country_le *ccreq);
+
+int brcmf_c_preinit_dcmds(struct brcmf_if *ifp)
+{
+ s8 eventmask[BRCMF_EVENTING_MASK_LEN];
+ u8 buf[BRCMF_DCMD_SMLEN];
+ struct brcmf_rev_info_le revinfo;
+ struct brcmf_rev_info *ri;
+ char *clmver;
+ char *ptr;
+ s32 err;
+
+ /* Do any CLM downloading */
+ err = brcmf_c_process_clm_blob(ifp);
+ if (err < 0) {
+ brcmf_err("download CLM blob file failed, %d\n", err);
+ goto done;
+ }
+
+ /* Nest SAPPHIRE-9229 */
+ if (nest_etheraddr[0] && ((err = nest_set_macaddr(ifp, nest_etheraddr))) < 0) {
+ brcmf_err("error=%d, nest_etheraddr=%s\n", err, nest_etheraddr);
+ }
+
+ /* retreive mac address */
+ err = brcmf_fil_iovar_data_get(ifp, "cur_etheraddr", ifp->mac_addr,
+ sizeof(ifp->mac_addr));
+ if (err < 0) {
+ brcmf_err("Retreiving cur_etheraddr failed, %d\n", err);
+ goto done;
+ }
+ memcpy(ifp->drvr->mac, ifp->mac_addr, sizeof(ifp->drvr->mac));
+
+ err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_REVINFO,
+ &revinfo, sizeof(revinfo));
+ ri = &ifp->drvr->revinfo;
+ if (err < 0) {
+ brcmf_err("retrieving revision info failed, %d\n", err);
+ } else {
+ ri->vendorid = le32_to_cpu(revinfo.vendorid);
+ ri->deviceid = le32_to_cpu(revinfo.deviceid);
+ ri->radiorev = le32_to_cpu(revinfo.radiorev);
+ ri->chiprev = le32_to_cpu(revinfo.chiprev);
+ ri->corerev = le32_to_cpu(revinfo.corerev);
+ ri->boardid = le32_to_cpu(revinfo.boardid);
+ ri->boardvendor = le32_to_cpu(revinfo.boardvendor);
+ ri->boardrev = le32_to_cpu(revinfo.boardrev);
+ ri->driverrev = le32_to_cpu(revinfo.driverrev);
+ ri->ucoderev = le32_to_cpu(revinfo.ucoderev);
+ ri->bus = le32_to_cpu(revinfo.bus);
+ ri->chipnum = le32_to_cpu(revinfo.chipnum);
+ ri->phytype = le32_to_cpu(revinfo.phytype);
+ ri->phyrev = le32_to_cpu(revinfo.phyrev);
+ ri->anarev = le32_to_cpu(revinfo.anarev);
+ ri->chippkg = le32_to_cpu(revinfo.chippkg);
+ ri->nvramrev = le32_to_cpu(revinfo.nvramrev);
+ }
+ ri->result = err;
+
+ /* query for 'ver' to get version info from firmware */
+ memset(buf, 0, sizeof(buf));
+ strcpy(buf, "ver");
+ err = brcmf_fil_iovar_data_get(ifp, "ver", buf, sizeof(buf));
+ if (err < 0) {
+ brcmf_err("Retreiving version information failed, %d\n",
+ err);
+ goto done;
+ }
+ ptr = (char *)buf;
+ strsep(&ptr, "\n");
+
+ /* Print fw version info */
+ brcmf_info("Firmware version = %s\n", buf);
+
+ /* locate firmware version number for ethtool */
+ ptr = strrchr(buf, ' ') + 1;
+ strlcpy(ifp->drvr->fwver, ptr, sizeof(ifp->drvr->fwver));
+
+ /* Query for 'clmver' to get CLM version info from firmware */
+ memset(buf, 0, sizeof(buf));
+ err = brcmf_fil_iovar_data_get(ifp, "clmver", buf, sizeof(buf));
+ if (err) {
+ if (err == -23) {
+ brcmf_dbg(INFO, "clmver iovar unsupported, %d\n", err);
+ } else {
+ brcmf_err("retrieving clmver failed, %d\n", err);
+ goto done;
+ }
+ } else {
+ clmver = (char *)buf;
+ /* Replace all newline/linefeed characters with space
+ * character
+ */
+ ptr = clmver;
+ while ((ptr = strchr(ptr, '\n')) != NULL)
+ *ptr = ' ';
+
+ brcmf_err("CLM version = %s\n", clmver);
+ }
+
+ /* set mpc */
+ err = brcmf_fil_iovar_int_set(ifp, "mpc", 1);
+ if (err) {
+ brcmf_err("failed setting mpc\n");
+ goto done;
+ }
+
+ brcmf_c_set_joinpref_default(ifp);
+
+ /* Setup event_msgs, enable E_IF */
+ err = brcmf_fil_iovar_data_get(ifp, "event_msgs", eventmask,
+ BRCMF_EVENTING_MASK_LEN);
+ if (err) {
+ brcmf_err("Get event_msgs error (%d)\n", err);
+ goto done;
+ }
+ setbit(eventmask, BRCMF_E_IF);
+ err = brcmf_fil_iovar_data_set(ifp, "event_msgs", eventmask,
+ BRCMF_EVENTING_MASK_LEN);
+ if (err) {
+ brcmf_err("Set event_msgs error (%d)\n", err);
+ goto done;
+ }
+
+ /* Setup default scan channel time */
+ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_CHANNEL_TIME,
+ BRCMF_DEFAULT_SCAN_CHANNEL_TIME);
+ if (err) {
+ brcmf_err("BRCMF_C_SET_SCAN_CHANNEL_TIME error (%d)\n",
+ err);
+ goto done;
+ }
+
+ /* Setup default scan unassoc time */
+ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_UNASSOC_TIME,
+ BRCMF_DEFAULT_SCAN_UNASSOC_TIME);
+ if (err) {
+ brcmf_err("BRCMF_C_SET_SCAN_UNASSOC_TIME error (%d)\n",
+ err);
+ goto done;
+ }
+
+ /* Enable tx beamforming, errors can be ignored (not supported) */
+ (void)brcmf_fil_iovar_int_set(ifp, "txbf", 1);
+
+ if (nest_keepalive_interval && ((err = nest_set_keepalive(ifp, nest_keepalive_interval))) < 0) {
+ brcmf_err("error=%d, nest_keepalive_interval=%d\n", err, nest_keepalive_interval);
+ goto done;
+ }
+
+ /* All Nest-added configurable parameters and their values */
+ brcmf_info("g_keepalive_debug %d\n", g_keepalive_debug);
+ brcmf_info("g_beacon_loss_timeout %d\n", g_beacon_loss_timeout);
+ brcmf_info("g_assoc_retry_max %d\n", g_assoc_retry_max);
+ brcmf_info("g_pm2_sleep_ret %d\n", g_pm2_sleep_ret);
+ brcmf_info("g_remote_ka_timeout = %d\n", g_remote_ka_timeout);
+ brcmf_info("nest_etheraddr[] = %s\n", nest_etheraddr);
+ if (g_devtype < 0 || g_devtype >= MAX_DEV_TYPE) {
+ brcmf_err("UNKNOWN DEVICE TYPE [%d] (set to default type = 0)!!!\n", g_devtype);
+ g_devtype = 0;
+ }
+ brcmf_info("g_devtype = %s\n", nest_devtype[g_devtype]);
+ brcmf_info("g_ampdu_enable = %d\n", g_ampdu_enable);
+ brcmf_info("g_ampdu_mpdu = %d\n", g_ampdu_mpdu);
+ brcmf_info("g_poll_sdio_bus = %d\n", g_poll_sdio_bus);
+ brcmf_info("dhd_power_mode = %d\n", dhd_power_mode);
+ brcmf_info("g_buf_key_b4_m4 = %d\n", g_buf_key_b4_m4);
+ brcmf_info("g_country_abbrev = %s\n", g_country_abbrev);
+ brcmf_info("g_4334x_pkt_filter = %d\n", g_4334x_pkt_filter);
+ brcmf_info("g_pfn_enable = %d\n", g_pfn_enable);
+
+ err = brcmf_fil_iovar_int_set(ifp, "assoc_retry_max", g_assoc_retry_max);
+ if (err < 0)
+ brcmf_err("Set assoc_retry_max failed %d\n", err);
+
+ err = brcmf_fil_iovar_int_set(ifp, "pm2_sleep_ret", g_pm2_sleep_ret);
+ if (err < 0)
+ brcmf_err("Set pm2_sleep_ret failed %d\n", err);
+
+ if (g_ampdu_enable == 0) {
+ /* only send iovar when disable */
+ err = brcmf_fil_iovar_int_set(ifp, "ampdu", g_ampdu_enable);
+ if (err < 0)
+ brcmf_err("Set g_ampdu_enable failed %d\n", err);
+ }
+
+ if (g_ampdu_mpdu != -1) {
+ err = brcmf_fil_iovar_int_set(ifp, "ampdu_mpdu", g_ampdu_mpdu);
+ if (err < 0)
+ brcmf_err("Set g_ampdu_mpdu failed %d\n", err);
+ }
+
+ if (g_buf_key_b4_m4 != 0) {
+ err = brcmf_fil_iovar_int_set(ifp, "buf_key_b4_m4", g_buf_key_b4_m4);
+ if (err < 0)
+ brcmf_err("Set g_buf_key_b4_m4 failed %d\n", err);
+ }
+
+ if (g_country_abbrev[0] != 0) {
+ struct brcmf_fil_country_le ccreq;
+
+ memset(&ccreq, 0, sizeof(ccreq));
+
+ err = brcmf_translate_country_code(ifp->drvr, g_country_abbrev, &ccreq);
+ if (err)
+ brcmf_err("Translate country %s failed %d\n", g_country_abbrev, err);
+ else {
+ err = brcmf_fil_iovar_data_set(ifp, "country", &ccreq, sizeof(ccreq));
+ if (err)
+ brcmf_err("Firmware rejected country setting\n");
+ else
+ brcmf_info("Set country to abbrev %s ccode %s success\n", ccreq.country_abbrev, ccreq.ccode);
+ }
+
+ brcmf_set_restricted_band(ifp, &ccreq);
+ }
+
+ /* do bus specific preinit here */
+ err = brcmf_bus_preinit(ifp->drvr->bus_if);
+done:
+ return err;
+}
+
+/* Nest SAPPHIRE-9229 */
+static int nest_set_macaddr(struct brcmf_if *ifp, char *value)
+{
+ char buf[ETH_ALEN], *a = value, *c = NULL;
+ int i = 0, err;
+
+ memset(buf, 0, ETH_ALEN);
+ for (;;) {
+ buf[i++] = (char)simple_strtoul(a, &c, 16);
+ if (!*c++ || i == ETH_ALEN)
+ break;
+ a = c;
+ }
+
+ if (i != ETH_ALEN) {
+ brcmf_err("Wrong Macaddr %s\n", value);
+ err = -1;
+ goto exit;
+ }
+
+ err = memcmp(buf, ifp->drvr->mac, ETH_ALEN);
+ if (err == 0) {
+ brcmf_err("Same Macaddr\n");
+ goto exit;
+ }
+
+ brcmf_info("Change Macaddr = %02X:%02X:%02X:%02X:%02X:%02X\n",
+ buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]);
+
+ err = brcmf_fil_iovar_data_set(ifp, "cur_etheraddr", buf, ETH_ALEN);
+ if (err < 0) {
+ brcmf_err("Setting cur_etheraddr failed, %d\n", err);
+ } else {
+ memcpy(ifp->mac_addr, buf, ETH_ALEN);
+ memcpy(ifp->drvr->mac, buf, ETH_ALEN);
+ }
+
+exit:
+ return err;
+}
+
+static int nest_set_keepalive(struct brcmf_if *ifp, int value)
+{
+ wl_mkeep_alive_pkt_t mkeep_alive_pkt = {0};
+ int res = -1;
+
+ brcmf_dbg(TRACE, ("execution\n"));
+
+ mkeep_alive_pkt.period_msec = cpu_to_le32(value);
+ mkeep_alive_pkt.version = cpu_to_le16(WL_MKEEP_ALIVE_VERSION);
+ mkeep_alive_pkt.length = cpu_to_le16(WL_MKEEP_ALIVE_FIXED_LEN);
+ /* Setup keep alive zero for null packet generation */
+ mkeep_alive_pkt.keep_alive_id = 0;
+ mkeep_alive_pkt.len_bytes = 0;
+ memset(mkeep_alive_pkt.data, 0, sizeof(mkeep_alive_pkt.data));
+
+ res = brcmf_fil_iovar_data_set(ifp, "mkeep_alive", &mkeep_alive_pkt, sizeof(mkeep_alive_pkt));
+ if (res < 0) {
+ brcmf_err("Setting mkeep_alive failed, %d\n", res);
+ }
+
+ return res;
+}
+
+#ifndef CPTCFG_BRCM_TRACING
+void __brcmf_err(const char *func, const char *fmt, ...)
+{
+ struct va_format vaf;
+ va_list args;
+
+ va_start(args, fmt);
+
+ vaf.fmt = fmt;
+ vaf.va = &args;
+ pr_err("%s: %pV", func, &vaf);
+
+ va_end(args);
+}
+#endif
+
+#if defined(CPTCFG_BRCM_TRACING) || defined(CPTCFG_BRCMDBG)
+void __brcmf_dbg(u32 level, const char *func, const char *fmt, ...)
+{
+ struct va_format vaf = {
+ .fmt = fmt,
+ };
+ va_list args;
+
+ va_start(args, fmt);
+ vaf.va = &args;
+ if (brcmf_msg_level & level)
+ pr_info("%s %pV", func, &vaf);
+ trace_brcmf_dbg(level, func, &vaf);
+ va_end(args);
+}
+#endif
+
+static void brcmf_mp_attach(void)
+{
+ /* If module param firmware path is set then this will always be used,
+ * if not set then if available use the platform data version. To make
+ * sure it gets initialized at all, always copy the module param version
+ */
+ strlcpy(brcmf_mp_global.firmware_path, brcmf_firmware_path,
+ BRCMF_FW_ALTPATH_LEN);
+ if ((brcmfmac_pdata) && (brcmfmac_pdata->fw_alternative_path) &&
+ (brcmf_mp_global.firmware_path[0] == '\0')) {
+ strlcpy(brcmf_mp_global.firmware_path,
+ brcmfmac_pdata->fw_alternative_path,
+ BRCMF_FW_ALTPATH_LEN);
+ }
+}
+
+struct brcmf_mp_device *brcmf_get_module_param(struct device *dev,
+ enum brcmf_bus_type bus_type,
+ u32 chip, u32 chiprev)
+{
+ struct brcmf_mp_device *settings;
+ struct brcmfmac_pd_device *device_pd;
+ bool found;
+ int i;
+
+ brcmf_dbg(INFO, "Enter, bus=%d, chip=%d, rev=%d\n", bus_type, chip,
+ chiprev);
+ settings = kzalloc(sizeof(*settings), GFP_ATOMIC);
+ if (!settings)
+ return NULL;
+
+ /* start by using the module paramaters */
+ settings->p2p_enable = !!brcmf_p2p_enable;
+ settings->feature_disable = brcmf_feature_disable;
+ settings->fcmode = brcmf_fcmode;
+ settings->roamoff = !!brcmf_roamoff;
+#ifdef DEBUG
+ settings->ignore_probe_fail = !!brcmf_ignore_probe_fail;
+#endif
+
+ if (bus_type == BRCMF_BUSTYPE_SDIO)
+ settings->bus.sdio.txglomsz = brcmf_sdiod_txglomsz;
+
+ /* See if there is any device specific platform data configured */
+ found = false;
+ if (brcmfmac_pdata) {
+ for (i = 0; i < brcmfmac_pdata->device_count; i++) {
+ device_pd = &brcmfmac_pdata->devices[i];
+ if ((device_pd->bus_type == bus_type) &&
+ (device_pd->id == chip) &&
+ ((device_pd->rev == chiprev) ||
+ (device_pd->rev == -1))) {
+ brcmf_dbg(INFO, "Platform data for device found\n");
+ settings->country_codes =
+ device_pd->country_codes;
+ if (device_pd->bus_type == BRCMF_BUSTYPE_SDIO)
+ memcpy(&settings->bus.sdio,
+ &device_pd->bus.sdio,
+ sizeof(settings->bus.sdio));
+ found = true;
+ break;
+ }
+ }
+ }
+ if (!found) {
+ /* No platform data for this device, try OF (Open Firwmare) */
+ brcmf_of_probe(dev, bus_type, settings);
+ }
+ return settings;
+}
+
+void brcmf_release_module_param(struct brcmf_mp_device *module_param)
+{
+ kfree(module_param);
+}
+
+static int __init brcmf_common_pd_probe(struct platform_device *pdev)
+{
+ brcmf_dbg(INFO, "Enter\n");
+
+ brcmfmac_pdata = dev_get_platdata(&pdev->dev);
+
+ if (brcmfmac_pdata->power_on)
+ brcmfmac_pdata->power_on();
+
+ return 0;
+}
+
+static int brcmf_common_pd_remove(struct platform_device *pdev)
+{
+ brcmf_dbg(INFO, "Enter\n");
+
+ if (brcmfmac_pdata->power_off)
+ brcmfmac_pdata->power_off();
+
+ return 0;
+}
+
+static struct platform_driver brcmf_pd = {
+ .remove = brcmf_common_pd_remove,
+ .driver = {
+ .name = BRCMFMAC_PDATA_NAME,
+ }
+};
+
+static int __init brcmfmac_module_init(void)
+{
+ int err;
+
+ /* Initialize debug system first */
+ brcmf_debugfs_init();
+
+ /* Get the platform data (if available) for our devices */
+ err = platform_driver_probe(&brcmf_pd, brcmf_common_pd_probe);
+ if (err == -ENODEV)
+ brcmf_dbg(INFO, "No platform data available.\n");
+
+ /* Initialize global module paramaters */
+ brcmf_mp_attach();
+
+ /* Continue the initialization by registering the different busses */
+ err = brcmf_core_init();
+ if (err) {
+ brcmf_debugfs_exit();
+ if (brcmfmac_pdata)
+ platform_driver_unregister(&brcmf_pd);
+ }
+
+ return err;
+}
+
+static void __exit brcmfmac_module_exit(void)
+{
+ brcmf_core_exit();
+ if (brcmfmac_pdata)
+ platform_driver_unregister(&brcmf_pd);
+ brcmf_debugfs_exit();
+}
+
+module_init(brcmfmac_module_init);
+module_exit(brcmfmac_module_exit);
+
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h
new file mode 100644
index 0000000..a62f8e7
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h
@@ -0,0 +1,78 @@
+/* Copyright (c) 2014 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#ifndef BRCMFMAC_COMMON_H
+#define BRCMFMAC_COMMON_H
+
+#include <linux/platform_device.h>
+#include <linux/platform_data/brcmfmac.h>
+#include "fwil_types.h"
+
+extern const u8 ALLFFMAC[ETH_ALEN];
+
+#define BRCMF_FW_ALTPATH_LEN 256
+
+/* Definitions for the module global and device specific settings are defined
+ * here. Two structs are used for them. brcmf_mp_global_t and brcmf_mp_device.
+ * The mp_global is instantiated once in a global struct and gets initialized
+ * by the common_attach function which should be called before any other
+ * (module) initiliazation takes place. The device specific settings is part
+ * of the drvr struct and should be initialized on every brcmf_attach.
+ */
+
+/**
+ * struct brcmf_mp_global_t - Global module paramaters.
+ *
+ * @firmware_path: Alternative firmware path.
+ */
+struct brcmf_mp_global_t {
+ char firmware_path[BRCMF_FW_ALTPATH_LEN];
+};
+
+extern struct brcmf_mp_global_t brcmf_mp_global;
+
+/**
+ * struct brcmf_mp_device - Device module paramaters.
+ *
+ * @p2p_enable: Legacy P2P0 enable (old wpa_supplicant).
+ * @feature_disable: Feature_disable bitmask.
+ * @fcmode: FWS flow control.
+ * @roamoff: Firmware roaming off?
+ * @ignore_probe_fail: Ignore probe failure.
+ * @country_codes: If available, pointer to struct for translating country codes
+ * @bus: Bus specific platform data. Only SDIO at the mmoment.
+ */
+struct brcmf_mp_device {
+ bool p2p_enable;
+ unsigned int feature_disable;
+ int fcmode;
+ bool roamoff;
+ bool ignore_probe_fail;
+ struct brcmfmac_pd_cc *country_codes;
+ union {
+ struct brcmfmac_sdio_pd sdio;
+ } bus;
+};
+
+void brcmf_c_set_joinpref_default(struct brcmf_if *ifp);
+
+struct brcmf_mp_device *brcmf_get_module_param(struct device *dev,
+ enum brcmf_bus_type bus_type,
+ u32 chip, u32 chiprev);
+void brcmf_release_module_param(struct brcmf_mp_device *module_param);
+
+/* Sets dongle media info (drv_version, mac address). */
+int brcmf_c_preinit_dcmds(struct brcmf_if *ifp);
+
+#endif /* BRCMFMAC_COMMON_H */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/commonring.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/commonring.c
new file mode 100644
index 0000000..7b0e521
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/commonring.c
@@ -0,0 +1,252 @@
+/* Copyright (c) 2014 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/types.h>
+#include <linux/netdevice.h>
+
+#include <brcmu_utils.h>
+#include <brcmu_wifi.h>
+
+#include "core.h"
+#include "commonring.h"
+
+void brcmf_commonring_register_cb(struct brcmf_commonring *commonring,
+ int (*cr_ring_bell)(void *ctx),
+ int (*cr_update_rptr)(void *ctx),
+ int (*cr_update_wptr)(void *ctx),
+ int (*cr_write_rptr)(void *ctx),
+ int (*cr_write_wptr)(void *ctx), void *ctx)
+{
+ commonring->cr_ring_bell = cr_ring_bell;
+ commonring->cr_update_rptr = cr_update_rptr;
+ commonring->cr_update_wptr = cr_update_wptr;
+ commonring->cr_write_rptr = cr_write_rptr;
+ commonring->cr_write_wptr = cr_write_wptr;
+ commonring->cr_ctx = ctx;
+}
+
+
+void brcmf_commonring_config(struct brcmf_commonring *commonring, u16 depth,
+ u16 item_len, void *buf_addr)
+{
+ commonring->depth = depth;
+ commonring->item_len = item_len;
+ commonring->buf_addr = buf_addr;
+ if (!commonring->inited) {
+ spin_lock_init(&commonring->lock);
+ commonring->inited = true;
+ }
+ commonring->r_ptr = 0;
+ if (commonring->cr_write_rptr)
+ commonring->cr_write_rptr(commonring->cr_ctx);
+ commonring->w_ptr = 0;
+ if (commonring->cr_write_wptr)
+ commonring->cr_write_wptr(commonring->cr_ctx);
+ commonring->f_ptr = 0;
+}
+
+
+void brcmf_commonring_lock(struct brcmf_commonring *commonring)
+ __acquires(&commonring->lock)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&commonring->lock, flags);
+ commonring->flags = flags;
+}
+
+
+void brcmf_commonring_unlock(struct brcmf_commonring *commonring)
+ __releases(&commonring->lock)
+{
+ spin_unlock_irqrestore(&commonring->lock, commonring->flags);
+}
+
+
+bool brcmf_commonring_write_available(struct brcmf_commonring *commonring)
+{
+ u16 available;
+ bool retry = true;
+
+again:
+ if (commonring->r_ptr <= commonring->w_ptr)
+ available = commonring->depth - commonring->w_ptr +
+ commonring->r_ptr;
+ else
+ available = commonring->r_ptr - commonring->w_ptr;
+
+ if (available > 1) {
+ if (!commonring->was_full)
+ return true;
+ if (available > commonring->depth / 8) {
+ commonring->was_full = false;
+ return true;
+ }
+ if (retry) {
+ if (commonring->cr_update_rptr)
+ commonring->cr_update_rptr(commonring->cr_ctx);
+ retry = false;
+ goto again;
+ }
+ return false;
+ }
+
+ if (retry) {
+ if (commonring->cr_update_rptr)
+ commonring->cr_update_rptr(commonring->cr_ctx);
+ retry = false;
+ goto again;
+ }
+
+ commonring->was_full = true;
+ return false;
+}
+
+
+void *brcmf_commonring_reserve_for_write(struct brcmf_commonring *commonring)
+{
+ void *ret_ptr;
+ u16 available;
+ bool retry = true;
+
+again:
+ if (commonring->r_ptr <= commonring->w_ptr)
+ available = commonring->depth - commonring->w_ptr +
+ commonring->r_ptr;
+ else
+ available = commonring->r_ptr - commonring->w_ptr;
+
+ if (available > 1) {
+ ret_ptr = commonring->buf_addr +
+ (commonring->w_ptr * commonring->item_len);
+ commonring->w_ptr++;
+ if (commonring->w_ptr == commonring->depth)
+ commonring->w_ptr = 0;
+ return ret_ptr;
+ }
+
+ if (retry) {
+ if (commonring->cr_update_rptr)
+ commonring->cr_update_rptr(commonring->cr_ctx);
+ retry = false;
+ goto again;
+ }
+
+ commonring->was_full = true;
+ return NULL;
+}
+
+
+void *
+brcmf_commonring_reserve_for_write_multiple(struct brcmf_commonring *commonring,
+ u16 n_items, u16 *alloced)
+{
+ void *ret_ptr;
+ u16 available;
+ bool retry = true;
+
+again:
+ if (commonring->r_ptr <= commonring->w_ptr)
+ available = commonring->depth - commonring->w_ptr +
+ commonring->r_ptr;
+ else
+ available = commonring->r_ptr - commonring->w_ptr;
+
+ if (available > 1) {
+ ret_ptr = commonring->buf_addr +
+ (commonring->w_ptr * commonring->item_len);
+ *alloced = min_t(u16, n_items, available - 1);
+ if (*alloced + commonring->w_ptr > commonring->depth)
+ *alloced = commonring->depth - commonring->w_ptr;
+ commonring->w_ptr += *alloced;
+ if (commonring->w_ptr == commonring->depth)
+ commonring->w_ptr = 0;
+ return ret_ptr;
+ }
+
+ if (retry) {
+ if (commonring->cr_update_rptr)
+ commonring->cr_update_rptr(commonring->cr_ctx);
+ retry = false;
+ goto again;
+ }
+
+ commonring->was_full = true;
+ return NULL;
+}
+
+
+int brcmf_commonring_write_complete(struct brcmf_commonring *commonring)
+{
+ void *address;
+
+ address = commonring->buf_addr;
+ address += (commonring->f_ptr * commonring->item_len);
+ if (commonring->f_ptr > commonring->w_ptr) {
+ address = commonring->buf_addr;
+ commonring->f_ptr = 0;
+ }
+
+ commonring->f_ptr = commonring->w_ptr;
+
+ if (commonring->cr_write_wptr)
+ commonring->cr_write_wptr(commonring->cr_ctx);
+ if (commonring->cr_ring_bell)
+ return commonring->cr_ring_bell(commonring->cr_ctx);
+
+ return -EIO;
+}
+
+
+void brcmf_commonring_write_cancel(struct brcmf_commonring *commonring,
+ u16 n_items)
+{
+ if (commonring->w_ptr == 0)
+ commonring->w_ptr = commonring->depth - n_items;
+ else
+ commonring->w_ptr -= n_items;
+}
+
+
+void *brcmf_commonring_get_read_ptr(struct brcmf_commonring *commonring,
+ u16 *n_items)
+{
+ if (commonring->cr_update_wptr)
+ commonring->cr_update_wptr(commonring->cr_ctx);
+
+ *n_items = (commonring->w_ptr >= commonring->r_ptr) ?
+ (commonring->w_ptr - commonring->r_ptr) :
+ (commonring->depth - commonring->r_ptr);
+
+ if (*n_items == 0)
+ return NULL;
+
+ return commonring->buf_addr +
+ (commonring->r_ptr * commonring->item_len);
+}
+
+
+int brcmf_commonring_read_complete(struct brcmf_commonring *commonring,
+ u16 n_items)
+{
+ commonring->r_ptr += n_items;
+ if (commonring->r_ptr == commonring->depth)
+ commonring->r_ptr = 0;
+
+ if (commonring->cr_write_rptr)
+ return commonring->cr_write_rptr(commonring->cr_ctx);
+
+ return -EIO;
+}
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/commonring.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/commonring.h
new file mode 100644
index 0000000..b850336
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/commonring.h
@@ -0,0 +1,72 @@
+/* Copyright (c) 2014 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#ifndef BRCMFMAC_COMMONRING_H
+#define BRCMFMAC_COMMONRING_H
+
+
+struct brcmf_commonring {
+ u16 r_ptr;
+ u16 w_ptr;
+ u16 f_ptr;
+ u16 depth;
+ u16 item_len;
+
+ void *buf_addr;
+
+ int (*cr_ring_bell)(void *ctx);
+ int (*cr_update_rptr)(void *ctx);
+ int (*cr_update_wptr)(void *ctx);
+ int (*cr_write_rptr)(void *ctx);
+ int (*cr_write_wptr)(void *ctx);
+
+ void *cr_ctx;
+
+ spinlock_t lock;
+ unsigned long flags;
+ bool inited;
+ bool was_full;
+
+ atomic_t outstanding_tx;
+};
+
+
+void brcmf_commonring_register_cb(struct brcmf_commonring *commonring,
+ int (*cr_ring_bell)(void *ctx),
+ int (*cr_update_rptr)(void *ctx),
+ int (*cr_update_wptr)(void *ctx),
+ int (*cr_write_rptr)(void *ctx),
+ int (*cr_write_wptr)(void *ctx), void *ctx);
+void brcmf_commonring_config(struct brcmf_commonring *commonring, u16 depth,
+ u16 item_len, void *buf_addr);
+void brcmf_commonring_lock(struct brcmf_commonring *commonring);
+void brcmf_commonring_unlock(struct brcmf_commonring *commonring);
+bool brcmf_commonring_write_available(struct brcmf_commonring *commonring);
+void *brcmf_commonring_reserve_for_write(struct brcmf_commonring *commonring);
+void *
+brcmf_commonring_reserve_for_write_multiple(struct brcmf_commonring *commonring,
+ u16 n_items, u16 *alloced);
+int brcmf_commonring_write_complete(struct brcmf_commonring *commonring);
+void brcmf_commonring_write_cancel(struct brcmf_commonring *commonring,
+ u16 n_items);
+void *brcmf_commonring_get_read_ptr(struct brcmf_commonring *commonring,
+ u16 *n_items);
+int brcmf_commonring_read_complete(struct brcmf_commonring *commonring,
+ u16 n_items);
+
+#define brcmf_commonring_n_items(commonring) (commonring->depth)
+#define brcmf_commonring_len_item(commonring) (commonring->item_len)
+
+
+#endif /* BRCMFMAC_COMMONRING_H */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
new file mode 100644
index 0000000..ee2e77a
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
@@ -0,0 +1,1429 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/etherdevice.h>
+#include <linux/ip.h>
+#include <linux/module.h>
+#include <linux/inetdevice.h>
+#include <linux/wireless.h>
+#include <net/cfg80211.h>
+#include <net/rtnetlink.h>
+#include <net/addrconf.h>
+#include <net/ipv6.h>
+#include <brcmu_utils.h>
+#include <brcmu_wifi.h>
+#include <defs.h>
+
+#include "core.h"
+#include "bus.h"
+#include "debug.h"
+#include "tracepoint.h"
+#include "fwil_types.h"
+#include "p2p.h"
+#include "cfg80211.h"
+#include "fwil.h"
+#include "feature.h"
+#include "proto.h"
+#include "pcie.h"
+#include "common.h"
+
+#define MAX_WAIT_FOR_8021X_TX msecs_to_jiffies(950)
+
+#define BRCMF_BSSIDX_INVALID -1
+
+#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
+#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x"
+
+#define DECODE_MIN 46
+#define DUMP_MAX 80
+#define DUMP_FORMAT "%02x %02x %02x %02x %02x %02x %02x %02x"
+
+extern u8 g_dump_packet;
+
+void brcmf_dump_packet(const char *data, int len)
+{
+ int pos;
+
+ if (data == NULL) {
+ brcmf_err("null packet data pointer\n");
+ return;
+ }
+
+ brcmf_dbg(INFO, "rx packet length: %d\n", len);
+
+ if (len > DECODE_MIN) {
+ struct ethhdr *eh = (struct ethhdr *)data;
+ u16 ether_type = ntohs(eh->h_proto);
+
+ brcmf_dbg(INFO, "dst: " MACSTR " src: " MACSTR " type: %04x\n",
+ MAC2STR(eh->h_dest), MAC2STR(eh->h_source), ether_type);
+
+ if (ether_type == ETH_P_IP) {
+ struct iphdr *ip = (struct iphdr *)(eh + 1);
+ const u8 *srcip = (const u8 *)&ip->saddr;
+ const u8 *dstip = (const u8 *)&ip->daddr;
+
+ brcmf_dbg(INFO, "dstip: %d.%d.%d.%d srcip: %d.%d.%d.%d\n",
+ dstip[0], dstip[1], dstip[2], dstip[3],
+ srcip[0], srcip[1], srcip[2], srcip[3]);
+
+ if (ip->protocol == IPPROTO_TCP) {
+ struct tcphdr *tcp = (struct tcphdr *)(ip + 1);
+
+ brcmf_dbg(INFO, "dport: %d sport: %d flags: %c%c%c%c%c%c%c%c\n",
+ ntohs(tcp->dest),
+ ntohs(tcp->source),
+ tcp->cwr ? 'C' : '-',
+ tcp->ece ? 'E' : '-',
+ tcp->urg ? 'U' : '-',
+ tcp->ack ? 'A' : '-',
+ tcp->psh ? 'P' : '-',
+ tcp->rst ? 'R' : '-',
+ tcp->syn ? 'S' : '-',
+ tcp->fin ? 'F' : '-');
+ }
+ }
+ }
+
+ for (pos = 0; pos < DUMP_MAX && (len - pos) >= 16; pos += 16, data += 16)
+ brcmf_dbg(INFO, DUMP_FORMAT " " DUMP_FORMAT "\n",
+ data[0], data[1], data[2], data[3],
+ data[4], data[5], data[6], data[7],
+ data[8], data[9], data[10], data[11],
+ data[12], data[13], data[14], data[15]);
+}
+
+char *brcmf_ifname(struct brcmf_if *ifp)
+{
+ if (!ifp)
+ return "<if_null>";
+
+ if (ifp->ndev)
+ return ifp->ndev->name;
+
+ return "<if_none>";
+}
+
+struct brcmf_if *brcmf_get_ifp(struct brcmf_pub *drvr, int ifidx)
+{
+ struct brcmf_if *ifp;
+ s32 bsscfgidx;
+
+ if (ifidx < 0 || ifidx >= BRCMF_MAX_IFS) {
+ brcmf_err("ifidx %d out of range\n", ifidx);
+ return NULL;
+ }
+
+ ifp = NULL;
+ bsscfgidx = drvr->if2bss[ifidx];
+ if (bsscfgidx >= 0)
+ ifp = drvr->iflist[bsscfgidx];
+
+ return ifp;
+}
+
+static void _brcmf_set_multicast_list(struct work_struct *work)
+{
+ struct brcmf_if *ifp;
+ struct net_device *ndev;
+ struct netdev_hw_addr *ha;
+ u32 cmd_value, cnt;
+ __le32 cnt_le;
+ char *buf, *bufp;
+ u32 buflen;
+ s32 err;
+
+ ifp = container_of(work, struct brcmf_if, multicast_work);
+
+ brcmf_dbg(TRACE, "Enter, bsscfgidx=%d\n", ifp->bsscfgidx);
+
+ ndev = ifp->ndev;
+
+ /* Determine initial value of allmulti flag */
+ cmd_value = (ndev->flags & IFF_ALLMULTI) ? true : false;
+
+ /* Send down the multicast list first. */
+ cnt = netdev_mc_count(ndev);
+ buflen = sizeof(cnt) + (cnt * ETH_ALEN);
+ buf = kmalloc(buflen, GFP_ATOMIC);
+ if (!buf)
+ return;
+ bufp = buf;
+
+ cnt_le = cpu_to_le32(cnt);
+ memcpy(bufp, &cnt_le, sizeof(cnt_le));
+ bufp += sizeof(cnt_le);
+
+ netdev_for_each_mc_addr(ha, ndev) {
+ if (!cnt)
+ break;
+ memcpy(bufp, ha->addr, ETH_ALEN);
+ bufp += ETH_ALEN;
+ cnt--;
+ }
+
+ err = brcmf_fil_iovar_data_set(ifp, "mcast_list", buf, buflen);
+ if (err < 0) {
+ brcmf_err("Setting mcast_list failed, %d\n", err);
+ cmd_value = cnt ? true : cmd_value;
+ }
+
+ kfree(buf);
+
+ /*
+ * Now send the allmulti setting. This is based on the setting in the
+ * net_device flags, but might be modified above to be turned on if we
+ * were trying to set some addresses and dongle rejected it...
+ */
+ err = brcmf_fil_iovar_int_set(ifp, "allmulti", cmd_value);
+ if (err < 0)
+ brcmf_err("Setting allmulti failed, %d\n", err);
+
+ /*Finally, pick up the PROMISC flag */
+ cmd_value = (ndev->flags & IFF_PROMISC) ? true : false;
+ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PROMISC, cmd_value);
+ if (err < 0)
+ brcmf_err("Setting BRCMF_C_SET_PROMISC failed, %d\n",
+ err);
+}
+
+#if IS_ENABLED(CONFIG_IPV6)
+static void _brcmf_update_ndtable(struct work_struct *work)
+{
+ struct brcmf_if *ifp;
+ int i, ret;
+
+ ifp = container_of(work, struct brcmf_if, ndoffload_work);
+
+ /* clear the table in firmware */
+ ret = brcmf_fil_iovar_data_set(ifp, "nd_hostip_clear", NULL, 0);
+ if (ret) {
+ brcmf_dbg(TRACE, "fail to clear nd ip table err:%d\n", ret);
+ return;
+ }
+
+ for (i = 0; i < ifp->ipv6addr_idx; i++) {
+ ret = brcmf_fil_iovar_data_set(ifp, "nd_hostip",
+ &ifp->ipv6_addr_tbl[i],
+ sizeof(struct in6_addr));
+ if (ret)
+ brcmf_err("add nd ip err %d\n", ret);
+ }
+}
+#else
+static void _brcmf_update_ndtable(struct work_struct *work)
+{
+}
+#endif
+
+static int brcmf_netdev_set_mac_address(struct net_device *ndev, void *addr)
+{
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ struct sockaddr *sa = (struct sockaddr *)addr;
+ int err;
+
+ brcmf_dbg(TRACE, "Enter, bsscfgidx=%d\n", ifp->bsscfgidx);
+
+ err = brcmf_fil_iovar_data_set(ifp, "cur_etheraddr", sa->sa_data,
+ ETH_ALEN);
+ if (err < 0) {
+ brcmf_err("Setting cur_etheraddr failed, %d\n", err);
+ } else {
+ brcmf_dbg(TRACE, "updated to %pM\n", sa->sa_data);
+ memcpy(ifp->mac_addr, sa->sa_data, ETH_ALEN);
+ memcpy(ifp->ndev->dev_addr, ifp->mac_addr, ETH_ALEN);
+ }
+ return err;
+}
+
+static void brcmf_netdev_set_multicast_list(struct net_device *ndev)
+{
+ struct brcmf_if *ifp = netdev_priv(ndev);
+
+ schedule_work(&ifp->multicast_work);
+}
+
+static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb,
+ struct net_device *ndev)
+{
+ int ret;
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ struct brcmf_pub *drvr = ifp->drvr;
+ struct ethhdr *eh;
+
+ brcmf_dbg(DATA, "Enter, bsscfgidx=%d\n", ifp->bsscfgidx);
+
+ /* Can the device send data? */
+ if (drvr->bus_if->state != BRCMF_BUS_UP) {
+ brcmf_err("xmit rejected state=%d\n", drvr->bus_if->state);
+ netif_stop_queue(ndev);
+ dev_kfree_skb(skb);
+ ret = -ENODEV;
+ goto done;
+ }
+
+ /* Make sure there's enough writable headroom*/
+ ret = skb_cow_head(skb, drvr->hdrlen);
+ if (ret < 0) {
+ brcmf_err("%s: skb_cow_head failed\n",
+ brcmf_ifname(ifp));
+ dev_kfree_skb(skb);
+ goto done;
+ }
+
+ /* validate length for ether packet */
+ if (skb->len < sizeof(*eh)) {
+ ret = -EINVAL;
+ dev_kfree_skb(skb);
+ goto done;
+ }
+
+ eh = (struct ethhdr *)(skb->data);
+
+ if (eh->h_proto == htons(ETH_P_PAE))
+ atomic_inc(&ifp->pend_8021x_cnt);
+
+ /* determine the priority */
+ if ((skb->priority == 0) || (skb->priority > 7))
+ skb->priority = cfg80211_classify8021d(skb, NULL);
+
+ ret = brcmf_proto_tx_queue_data(drvr, ifp->ifidx, skb);
+ if (ret < 0)
+ brcmf_txfinalize(ifp, skb, false);
+
+done:
+ if (ret) {
+ ndev->stats.tx_dropped++;
+ } else {
+ ndev->stats.tx_packets++;
+ ndev->stats.tx_bytes += skb->len;
+ }
+
+ /* Return ok: we always eat the packet */
+ return NETDEV_TX_OK;
+}
+
+void brcmf_txflowblock_if(struct brcmf_if *ifp,
+ enum brcmf_netif_stop_reason reason, bool state)
+{
+ unsigned long flags;
+
+ if (!ifp || !ifp->ndev)
+ return;
+
+ brcmf_dbg(TRACE, "enter: bsscfgidx=%d stop=0x%X reason=%d state=%d\n",
+ ifp->bsscfgidx, ifp->netif_stop, reason, state);
+
+ spin_lock_irqsave(&ifp->netif_stop_lock, flags);
+ if (state) {
+ if (!ifp->netif_stop)
+ netif_stop_queue(ifp->ndev);
+ ifp->netif_stop |= reason;
+ } else {
+ ifp->netif_stop &= ~reason;
+ if (!ifp->netif_stop)
+ netif_wake_queue(ifp->ndev);
+ }
+ spin_unlock_irqrestore(&ifp->netif_stop_lock, flags);
+}
+
+void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb)
+{
+ if (skb->pkt_type == PACKET_MULTICAST)
+ ifp->ndev->stats.multicast++;
+
+ if (!(ifp->ndev->flags & IFF_UP)) {
+ brcmu_pkt_buf_free_skb(skb);
+ return;
+ }
+
+ ifp->ndev->stats.rx_bytes += skb->len;
+ ifp->ndev->stats.rx_packets++;
+
+ brcmf_dbg(DATA, "rx proto=0x%X\n", ntohs(skb->protocol));
+ if (in_interrupt())
+ netif_rx(skb);
+ else
+ /* If the receive is not processed inside an ISR,
+ * the softirqd must be woken explicitly to service
+ * the NET_RX_SOFTIRQ. This is handled by netif_rx_ni().
+ */
+ netif_rx_ni(skb);
+}
+
+static int brcmf_rx_hdrpull(struct brcmf_pub *drvr, struct sk_buff *skb,
+ struct brcmf_if **ifp)
+{
+ int ret;
+
+ /* process and remove protocol-specific header */
+ ret = brcmf_proto_hdrpull(drvr, true, skb, ifp);
+
+ if (ret || !(*ifp) || !(*ifp)->ndev) {
+ if (ret != -ENODATA && *ifp)
+ (*ifp)->ndev->stats.rx_errors++;
+ brcmu_pkt_buf_free_skb(skb);
+ return -ENODATA;
+ }
+
+ /* NESTLABS: dump packet that woke us. */
+ if (g_dump_packet) {
+ g_dump_packet = 0;
+ brcmf_dump_packet(skb->data, skb->len);
+ }
+
+ skb->protocol = eth_type_trans(skb, (*ifp)->ndev);
+ return 0;
+}
+
+void brcmf_rx_frame(struct device *dev, struct sk_buff *skb, bool handle_event)
+{
+ struct brcmf_if *ifp;
+ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+ struct brcmf_pub *drvr = bus_if->drvr;
+
+ brcmf_dbg(DATA, "Enter: %s: rxp=%p\n", dev_name(dev), skb);
+
+ if (brcmf_rx_hdrpull(drvr, skb, &ifp))
+ return;
+
+ if (brcmf_proto_is_reorder_skb(skb)) {
+ brcmf_proto_rxreorder(ifp, skb);
+ } else {
+ /* Process special event packets */
+ if (handle_event)
+ brcmf_fweh_process_skb(ifp->drvr, skb);
+
+ brcmf_netif_rx(ifp, skb);
+ }
+}
+
+void brcmf_rx_event(struct device *dev, struct sk_buff *skb)
+{
+ struct brcmf_if *ifp;
+ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+ struct brcmf_pub *drvr = bus_if->drvr;
+
+ brcmf_dbg(EVENT, "Enter: %s: rxp=%p\n", dev_name(dev), skb);
+
+ if (brcmf_rx_hdrpull(drvr, skb, &ifp))
+ return;
+
+ brcmf_fweh_process_skb(ifp->drvr, skb);
+ brcmu_pkt_buf_free_skb(skb);
+}
+
+void brcmf_txfinalize(struct brcmf_if *ifp, struct sk_buff *txp, bool success)
+{
+ struct ethhdr *eh;
+ u16 type;
+
+ eh = (struct ethhdr *)(txp->data);
+ type = ntohs(eh->h_proto);
+
+ if (type == ETH_P_PAE) {
+ atomic_dec(&ifp->pend_8021x_cnt);
+ if (waitqueue_active(&ifp->pend_8021x_wait))
+ wake_up(&ifp->pend_8021x_wait);
+ }
+
+ if (!success)
+ ifp->ndev->stats.tx_errors++;
+
+ brcmu_pkt_buf_free_skb(txp);
+}
+
+static void brcmf_ethtool_get_drvinfo(struct net_device *ndev,
+ struct ethtool_drvinfo *info)
+{
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ struct brcmf_pub *drvr = ifp->drvr;
+ char drev[BRCMU_DOTREV_LEN] = "n/a";
+
+ if (drvr->revinfo.result == 0)
+ brcmu_dotrev_str(drvr->revinfo.driverrev, drev);
+ strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver));
+ strlcpy(info->version, drev, sizeof(info->version));
+ strlcpy(info->fw_version, drvr->fwver, sizeof(info->fw_version));
+ strlcpy(info->bus_info, dev_name(drvr->bus_if->dev),
+ sizeof(info->bus_info));
+}
+
+static const struct ethtool_ops brcmf_ethtool_ops = {
+ .get_drvinfo = brcmf_ethtool_get_drvinfo,
+};
+
+static int brcmf_netdev_stop(struct net_device *ndev)
+{
+ struct brcmf_if *ifp = netdev_priv(ndev);
+
+ brcmf_dbg(TRACE, "Enter, bsscfgidx=%d\n", ifp->bsscfgidx);
+
+ brcmf_cfg80211_down(ndev);
+
+ brcmf_fil_iovar_data_set(ifp, "arp_hostip_clear", NULL, 0);
+
+ brcmf_net_setcarrier(ifp, false);
+
+ return 0;
+}
+
+static int brcmf_netdev_open(struct net_device *ndev)
+{
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ struct brcmf_pub *drvr = ifp->drvr;
+ struct brcmf_bus *bus_if = drvr->bus_if;
+ u32 toe_ol;
+
+ brcmf_dbg(TRACE, "Enter, bsscfgidx=%d\n", ifp->bsscfgidx);
+
+ /* If bus is not ready, can't continue */
+ if (bus_if->state != BRCMF_BUS_UP) {
+ brcmf_err("failed bus is not ready\n");
+ return -EAGAIN;
+ }
+
+ atomic_set(&ifp->pend_8021x_cnt, 0);
+
+ /* Get current TOE mode from dongle */
+ if (brcmf_fil_iovar_int_get(ifp, "toe_ol", &toe_ol) >= 0
+ && (toe_ol & TOE_TX_CSUM_OL) != 0)
+ ndev->features |= NETIF_F_IP_CSUM;
+ else
+ ndev->features &= ~NETIF_F_IP_CSUM;
+
+ if (brcmf_cfg80211_up(ndev)) {
+ brcmf_err("failed to bring up cfg80211\n");
+ return -EIO;
+ }
+
+ /* Clear, carrier, set when connected or AP mode. */
+ netif_carrier_off(ndev);
+ return 0;
+}
+
+/* per-driver magic numbers */
+#define DHD_IOCTL_MAGIC 0x00444944
+#define DHD_IOCTL_MAXLEN (16384) /* max length ioctl buffer required */
+
+typedef struct dhd_ioctl {
+ u32 cmd; /* common ioctl definition */
+ void *buf; /* pointer to user buffer */
+ u32 len; /* length of user buffer */
+ u8 set; /* get or set request boolean (optional) */
+ u32 used; /* bytes read or written (optional) */
+ u32 needed; /* bytes needed (optional) */
+ u32 driver; /* to identify target driver */
+} dhd_ioctl_t;
+
+#ifdef CONFIG_COMPAT
+typedef struct compat_wl_ioctl {
+ u32 cmd; /**< common ioctl definition */
+ u32 buf; /**< pointer to user buffer */
+ u32 len; /**< length of user buffer */
+ u8 set; /**< 1=set IOCTL; 0=query IOCTL */
+ u32 used; /**< bytes read or written (optional) */
+ u32 needed; /**< bytes needed (optional) */
+} compat_wl_ioctl_t;
+#endif /* CONFIG_COMPAT */
+
+extern int g_TCPKATimeout;
+extern u32 tcpka_intr_enabled;
+extern int dhd_power_mode;
+
+static int
+brcmf_netdev_ioctl(struct net_device *ndev, struct ifreq *ifr, int cmd)
+{
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ dhd_ioctl_t ioc;
+ int ret;
+ void *local_buf = NULL;
+ u16 buflen = 0;
+
+ if (cmd != SIOCDEVPRIVATE) {
+ if (cmd != SIOCGIWRANGE)
+ brcmf_err("Invalid ioctl cmd: %d\n", cmd);
+ return -EOPNOTSUPP;
+ }
+
+ memset(&ioc, 0, sizeof(ioc));
+
+#ifdef CONFIG_COMPAT
+ if (is_compat_task()) {
+ compat_wl_ioctl_t compat_ioc;
+ if (copy_from_user(&compat_ioc, ifr->ifr_data, sizeof(compat_wl_ioctl_t))) {
+ brcmf_err("%d copy_from_user failed\n", __LINE__);
+ ret = -EFAULT;
+ goto done;
+ }
+ ioc.cmd = compat_ioc.cmd;
+ ioc.buf = compat_ptr(compat_ioc.buf);
+ ioc.len = compat_ioc.len;
+ ioc.set = compat_ioc.set;
+ ioc.used = compat_ioc.used;
+ ioc.needed = compat_ioc.needed;
+ /* To differentiate between wl and dhd read 4 more byes */
+ if ((copy_from_user(&ioc.driver, (char *)ifr->ifr_data + sizeof(compat_wl_ioctl_t), sizeof(uint)) != 0)) {
+ ret = -EFAULT;
+ brcmf_err("%d copy_from_user failed\n", __LINE__);
+ goto done;
+ }
+ } else
+#endif /* CONFIG_COMPAT */
+ {
+ /* Copy the ioc control structure part of ioctl request */
+ if (copy_from_user(&ioc, ifr->ifr_data, sizeof(dhd_ioctl_t))) {
+ ret = -EFAULT;
+ brcmf_err("%d copy_from_user failed\n", __LINE__);
+ goto done;
+ }
+ }
+
+ if (!capable(CAP_NET_ADMIN)) {
+ ret = -EPERM;
+ brcmf_err("capability check failed\n");
+ goto done;
+ }
+
+ if (ioc.driver == DHD_IOCTL_MAGIC) {
+ ret = -EOPNOTSUPP;
+ brcmf_err("Doesn't support dhd ioctl\n");
+ goto done;
+ }
+
+ if (ioc.len > 0) {
+ buflen = min_t(u32, ioc.len, DHD_IOCTL_MAXLEN);
+ if (!(local_buf = vzalloc(buflen+1))) {
+ ret = -ENOMEM;
+ brcmf_err("allocate buffer failed\n");
+ goto done;
+ }
+ if (copy_from_user(local_buf, ioc.buf, buflen)) {
+ ret = -EFAULT;
+ brcmf_err("%d copy_from_user failed\n", __LINE__);
+ goto done;
+ }
+ *(char *)(local_buf + buflen) = '\0';
+ }
+
+ if ((ioc.cmd == BRCMF_C_GET_VAR) && (strcmp("tcp_ka", local_buf) == 0)) {
+ *(int *)local_buf = g_TCPKATimeout;
+ ret = 0;
+ goto local;
+ }
+ if ((ioc.cmd == BRCMF_C_SET_VAR) && (strcmp("tcp_ka", local_buf) == 0)) {
+ memcpy(&g_TCPKATimeout, local_buf + 7, 4);
+ ret = 0;
+ goto local;
+ }
+ if ((ioc.cmd == BRCMF_C_SET_VAR) && (strcmp("intr_e", local_buf) == 0)) {
+ memcpy(&tcpka_intr_enabled, local_buf + 7, 4);
+ ret = 0;
+ goto local;
+ }
+
+ if ((ioc.cmd == BRCMF_C_GET_VAR) && (strcmp("dhd_pm", local_buf) == 0)) {
+ *(int *)local_buf = dhd_power_mode;
+ ret = 0;
+ goto local;
+ }
+
+ if ((ioc.cmd == BRCMF_C_SET_VAR) && (strcmp("dhd_pm", local_buf) == 0)) {
+ int pm = *(int *)((u8 *)local_buf + 7);
+
+ if (pm > PM_FAST || pm < PM_OFF) {
+ brcmf_err("Invalid dhd_pm %d set\n", pm);
+ ret = -EFAULT;
+ goto done;
+ }
+
+ ret = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, pm);
+ if (!ret)
+ dhd_power_mode = pm;
+
+ brcmf_info("set dhd_power_mode to %d, err %d\n", pm, ret);
+ goto local;
+ }
+
+ if (ioc.set)
+ ret = brcmf_fil_cmd_data_set(ifp, ioc.cmd, local_buf, buflen);
+ else
+ ret = brcmf_fil_cmd_data_get(ifp, ioc.cmd, local_buf, buflen);
+
+local:
+ if (!ret && buflen && local_buf && ioc.buf) {
+ if (copy_to_user(ioc.buf, local_buf, buflen))
+ ret = -EFAULT;
+ }
+
+done:
+ if (local_buf)
+ vfree(local_buf);
+
+ return ret;
+}
+
+static const struct net_device_ops brcmf_netdev_ops_pri = {
+ .ndo_open = brcmf_netdev_open,
+ .ndo_stop = brcmf_netdev_stop,
+ .ndo_do_ioctl = brcmf_netdev_ioctl,
+ .ndo_start_xmit = brcmf_netdev_start_xmit,
+ .ndo_set_mac_address = brcmf_netdev_set_mac_address,
+ .ndo_set_rx_mode = brcmf_netdev_set_multicast_list
+};
+
+int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked)
+{
+ struct brcmf_pub *drvr = ifp->drvr;
+ struct net_device *ndev;
+ s32 err;
+
+ brcmf_dbg(TRACE, "Enter, bsscfgidx=%d mac=%pM\n", ifp->bsscfgidx,
+ ifp->mac_addr);
+ ndev = ifp->ndev;
+
+ /* set appropriate operations */
+ ndev->netdev_ops = &brcmf_netdev_ops_pri;
+
+ ndev->needed_headroom += drvr->hdrlen;
+ ndev->ethtool_ops = &brcmf_ethtool_ops;
+
+ /* set the mac address & netns */
+ memcpy(ndev->dev_addr, ifp->mac_addr, ETH_ALEN);
+ dev_net_set(ndev, wiphy_net(cfg_to_wiphy(drvr->config)));
+
+ INIT_WORK(&ifp->multicast_work, _brcmf_set_multicast_list);
+ INIT_WORK(&ifp->ndoffload_work, _brcmf_update_ndtable);
+
+ if (rtnl_locked)
+ err = register_netdevice(ndev);
+ else
+ err = register_netdev(ndev);
+ if (err != 0) {
+ brcmf_err("couldn't register the net device\n");
+ goto fail;
+ }
+
+ brcmf_dbg(INFO, "%s: Broadcom Dongle Host Driver\n", ndev->name);
+ return 0;
+
+fail:
+ drvr->iflist[ifp->bsscfgidx] = NULL;
+ ndev->netdev_ops = NULL;
+ free_netdev(ndev);
+ return -EBADE;
+}
+
+static void brcmf_net_detach(struct net_device *ndev, bool rtnl_locked)
+{
+ if (ndev->reg_state == NETREG_REGISTERED) {
+ if (rtnl_locked)
+ unregister_netdevice(ndev);
+ else
+ unregister_netdev(ndev);
+ } else {
+ brcmf_cfg80211_free_netdev(ndev);
+ }
+}
+
+void brcmf_net_setcarrier(struct brcmf_if *ifp, bool on)
+{
+ struct net_device *ndev;
+
+ brcmf_dbg(TRACE, "Enter, bsscfgidx=%d carrier=%d\n", ifp->bsscfgidx,
+ on);
+
+ ndev = ifp->ndev;
+ brcmf_txflowblock_if(ifp, BRCMF_NETIF_STOP_REASON_DISCONNECTED, !on);
+ if (on) {
+ if (!netif_carrier_ok(ndev))
+ netif_carrier_on(ndev);
+
+ } else {
+ if (netif_carrier_ok(ndev))
+ netif_carrier_off(ndev);
+ }
+}
+
+static int brcmf_net_p2p_open(struct net_device *ndev)
+{
+ brcmf_dbg(TRACE, "Enter\n");
+
+ return brcmf_cfg80211_up(ndev);
+}
+
+static int brcmf_net_p2p_stop(struct net_device *ndev)
+{
+ brcmf_dbg(TRACE, "Enter\n");
+
+ return brcmf_cfg80211_down(ndev);
+}
+
+static netdev_tx_t brcmf_net_p2p_start_xmit(struct sk_buff *skb,
+ struct net_device *ndev)
+{
+ if (skb)
+ dev_kfree_skb_any(skb);
+
+ return NETDEV_TX_OK;
+}
+
+static const struct net_device_ops brcmf_netdev_ops_p2p = {
+ .ndo_open = brcmf_net_p2p_open,
+ .ndo_stop = brcmf_net_p2p_stop,
+ .ndo_start_xmit = brcmf_net_p2p_start_xmit
+};
+
+static int brcmf_net_p2p_attach(struct brcmf_if *ifp)
+{
+ struct net_device *ndev;
+
+ brcmf_dbg(TRACE, "Enter, bsscfgidx=%d mac=%pM\n", ifp->bsscfgidx,
+ ifp->mac_addr);
+ ndev = ifp->ndev;
+
+ ndev->netdev_ops = &brcmf_netdev_ops_p2p;
+
+ /* set the mac address */
+ memcpy(ndev->dev_addr, ifp->mac_addr, ETH_ALEN);
+
+ if (register_netdev(ndev) != 0) {
+ brcmf_err("couldn't register the p2p net device\n");
+ goto fail;
+ }
+
+ brcmf_dbg(INFO, "%s: Broadcom Dongle Host Driver\n", ndev->name);
+
+ return 0;
+
+fail:
+ ifp->drvr->iflist[ifp->bsscfgidx] = NULL;
+ ndev->netdev_ops = NULL;
+ free_netdev(ndev);
+ return -EBADE;
+}
+
+#if LINUX_VERSION_IS_LESS(4,12,0)
+static void __brcmf_cfg80211_free_netdev(struct net_device *ndev){
+ brcmf_cfg80211_free_netdev(ndev);
+ free_netdev(ndev);
+}
+#endif
+
+struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bsscfgidx, s32 ifidx,
+ bool is_p2pdev, const char *name, u8 *mac_addr)
+{
+ struct brcmf_if *ifp;
+ struct net_device *ndev;
+
+ brcmf_dbg(TRACE, "Enter, bsscfgidx=%d, ifidx=%d\n", bsscfgidx, ifidx);
+
+ ifp = drvr->iflist[bsscfgidx];
+ /*
+ * Delete the existing interface before overwriting it
+ * in case we missed the BRCMF_E_IF_DEL event.
+ */
+ if (ifp) {
+ if (ifidx) {
+ brcmf_err("ERROR: netdev:%s already exists\n",
+ ifp->ndev->name);
+ netif_stop_queue(ifp->ndev);
+ brcmf_net_detach(ifp->ndev, false);
+ drvr->iflist[bsscfgidx] = NULL;
+ } else {
+ brcmf_dbg(INFO, "netdev:%s ignore IF event\n",
+ ifp->ndev->name);
+ return ERR_PTR(-EINVAL);
+ }
+ }
+
+ if (!drvr->settings->p2p_enable && is_p2pdev) {
+ /* this is P2P_DEVICE interface */
+ brcmf_dbg(INFO, "allocate non-netdev interface\n");
+ ifp = kzalloc(sizeof(*ifp), GFP_KERNEL);
+ if (!ifp)
+ return ERR_PTR(-ENOMEM);
+ } else {
+ brcmf_dbg(INFO, "allocate netdev interface\n");
+ /* Allocate netdev, including space for private structure */
+ ndev = alloc_netdev(sizeof(*ifp), is_p2pdev ? "p2p%d" : name,
+ NET_NAME_UNKNOWN, ether_setup);
+ if (!ndev)
+ return ERR_PTR(-ENOMEM);
+
+ netdev_set_priv_destructor(ndev, brcmf_cfg80211_free_netdev);
+ ifp = netdev_priv(ndev);
+ ifp->ndev = ndev;
+ /* store mapping ifidx to bsscfgidx */
+ if (drvr->if2bss[ifidx] == BRCMF_BSSIDX_INVALID)
+ drvr->if2bss[ifidx] = bsscfgidx;
+ }
+
+ ifp->drvr = drvr;
+ drvr->iflist[bsscfgidx] = ifp;
+ ifp->ifidx = ifidx;
+ ifp->bsscfgidx = bsscfgidx;
+
+ init_waitqueue_head(&ifp->pend_8021x_wait);
+ spin_lock_init(&ifp->netif_stop_lock);
+
+ if (mac_addr != NULL)
+ memcpy(ifp->mac_addr, mac_addr, ETH_ALEN);
+
+ brcmf_dbg(TRACE, " ==== pid:%x, if:%s (%pM) created ===\n",
+ current->pid, name, ifp->mac_addr);
+
+ return ifp;
+}
+
+static void brcmf_del_if(struct brcmf_pub *drvr, s32 bsscfgidx,
+ bool rtnl_locked)
+{
+ struct brcmf_if *ifp;
+
+ ifp = drvr->iflist[bsscfgidx];
+ drvr->iflist[bsscfgidx] = NULL;
+ if (!ifp) {
+ brcmf_err("Null interface, bsscfgidx=%d\n", bsscfgidx);
+ return;
+ }
+ brcmf_dbg(TRACE, "Enter, bsscfgidx=%d, ifidx=%d\n", bsscfgidx,
+ ifp->ifidx);
+ if (drvr->if2bss[ifp->ifidx] == bsscfgidx)
+ drvr->if2bss[ifp->ifidx] = BRCMF_BSSIDX_INVALID;
+ if (ifp->ndev) {
+ if (bsscfgidx == 0) {
+ if (ifp->ndev->netdev_ops == &brcmf_netdev_ops_pri) {
+ rtnl_lock();
+ brcmf_netdev_stop(ifp->ndev);
+ rtnl_unlock();
+ }
+ } else {
+ netif_stop_queue(ifp->ndev);
+ }
+
+ if (ifp->ndev->netdev_ops == &brcmf_netdev_ops_pri) {
+ cancel_work_sync(&ifp->multicast_work);
+ cancel_work_sync(&ifp->ndoffload_work);
+ }
+ brcmf_net_detach(ifp->ndev, rtnl_locked);
+ } else {
+ /* Only p2p device interfaces which get dynamically created
+ * end up here. In this case the p2p module should be informed
+ * about the removal of the interface within the firmware. If
+ * not then p2p commands towards the firmware will cause some
+ * serious troublesome side effects. The p2p module will clean
+ * up the ifp if needed.
+ */
+ brcmf_p2p_ifp_removed(ifp, rtnl_locked);
+ kfree(ifp);
+ }
+}
+
+void brcmf_remove_interface(struct brcmf_if *ifp, bool rtnl_locked)
+{
+ if (!ifp || WARN_ON(ifp->drvr->iflist[ifp->bsscfgidx] != ifp))
+ return;
+ brcmf_dbg(TRACE, "Enter, bsscfgidx=%d, ifidx=%d\n", ifp->bsscfgidx,
+ ifp->ifidx);
+ brcmf_proto_del_if(ifp->drvr, ifp);
+ brcmf_del_if(ifp->drvr, ifp->bsscfgidx, rtnl_locked);
+}
+
+static int brcmf_psm_watchdog_notify(struct brcmf_if *ifp,
+ const struct brcmf_event_msg *evtmsg,
+ void *data)
+{
+ int err;
+
+ brcmf_dbg(TRACE, "enter: bsscfgidx=%d\n", ifp->bsscfgidx);
+
+ brcmf_err("PSM's watchdog has fired!\n");
+
+ err = brcmf_debug_create_memdump(ifp->drvr->bus_if, data,
+ evtmsg->datalen);
+ if (err)
+ brcmf_err("Failed to get memory dump, %d\n", err);
+
+ return err;
+}
+
+#ifdef CONFIG_INET
+#define ARPOL_MAX_ENTRIES 8
+static int brcmf_inetaddr_changed(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct brcmf_pub *drvr = container_of(nb, struct brcmf_pub,
+ inetaddr_notifier);
+ struct in_ifaddr *ifa = data;
+ struct net_device *ndev = ifa->ifa_dev->dev;
+ struct brcmf_if *ifp;
+ int idx, i, ret;
+ u32 val;
+ __be32 addr_table[ARPOL_MAX_ENTRIES] = {0};
+
+ /* Find out if the notification is meant for us */
+ for (idx = 0; idx < BRCMF_MAX_IFS; idx++) {
+ ifp = drvr->iflist[idx];
+ if (ifp && ifp->ndev == ndev)
+ break;
+ if (idx == BRCMF_MAX_IFS - 1)
+ return NOTIFY_DONE;
+ }
+
+ /* check if arp offload is supported */
+ ret = brcmf_fil_iovar_int_get(ifp, "arpoe", &val);
+ if (ret)
+ return NOTIFY_OK;
+
+ /* old version only support primary index */
+ ret = brcmf_fil_iovar_int_get(ifp, "arp_version", &val);
+ if (ret)
+ val = 1;
+ if (val == 1)
+ ifp = drvr->iflist[0];
+
+ /* retrieve the table from firmware */
+ ret = brcmf_fil_iovar_data_get(ifp, "arp_hostip", addr_table,
+ sizeof(addr_table));
+ if (ret) {
+ brcmf_err("fail to get arp ip table err:%d\n", ret);
+ return NOTIFY_OK;
+ }
+
+ for (i = 0; i < ARPOL_MAX_ENTRIES; i++)
+ if (ifa->ifa_address == addr_table[i])
+ break;
+
+ switch (action) {
+ case NETDEV_UP:
+ if (i == ARPOL_MAX_ENTRIES) {
+ brcmf_dbg(TRACE, "add %pI4 to arp table\n",
+ &ifa->ifa_address);
+ /* set it directly */
+ ret = brcmf_fil_iovar_data_set(ifp, "arp_hostip",
+ &ifa->ifa_address, sizeof(ifa->ifa_address));
+ if (ret)
+ brcmf_err("add arp ip err %d\n", ret);
+ }
+ break;
+ case NETDEV_DOWN:
+ if (i < ARPOL_MAX_ENTRIES) {
+ addr_table[i] = 0;
+ brcmf_dbg(TRACE, "remove %pI4 from arp table\n",
+ &ifa->ifa_address);
+ /* clear the table in firmware */
+ ret = brcmf_fil_iovar_data_set(ifp, "arp_hostip_clear",
+ NULL, 0);
+ if (ret) {
+ brcmf_err("fail to clear arp ip table err:%d\n",
+ ret);
+ return NOTIFY_OK;
+ }
+ for (i = 0; i < ARPOL_MAX_ENTRIES; i++) {
+ if (addr_table[i] == 0)
+ continue;
+ ret = brcmf_fil_iovar_data_set(ifp, "arp_hostip",
+ &addr_table[i],
+ sizeof(addr_table[i]));
+ if (ret)
+ brcmf_err("add arp ip err %d\n",
+ ret);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+#endif
+
+#if IS_ENABLED(CONFIG_IPV6)
+static int brcmf_inet6addr_changed(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct brcmf_pub *drvr = container_of(nb, struct brcmf_pub,
+ inet6addr_notifier);
+ struct inet6_ifaddr *ifa = data;
+ struct brcmf_if *ifp;
+ int i;
+ struct in6_addr *table;
+
+ /* Only handle primary interface */
+ ifp = drvr->iflist[0];
+ if (!ifp)
+ return NOTIFY_DONE;
+ if (ifp->ndev != ifa->idev->dev)
+ return NOTIFY_DONE;
+
+ table = ifp->ipv6_addr_tbl;
+ for (i = 0; i < NDOL_MAX_ENTRIES; i++)
+ if (ipv6_addr_equal(&ifa->addr, &table[i]))
+ break;
+
+ switch (action) {
+ case NETDEV_UP:
+ if (i == NDOL_MAX_ENTRIES) {
+ if (ifp->ipv6addr_idx < NDOL_MAX_ENTRIES) {
+ table[ifp->ipv6addr_idx++] = ifa->addr;
+ } else {
+ for (i = 0; i < NDOL_MAX_ENTRIES - 1; i++)
+ table[i] = table[i + 1];
+ table[NDOL_MAX_ENTRIES - 1] = ifa->addr;
+ }
+ }
+ break;
+ case NETDEV_DOWN:
+ if (i < NDOL_MAX_ENTRIES) {
+ for (; i < ifp->ipv6addr_idx - 1; i++)
+ table[i] = table[i + 1];
+ memset(&table[i], 0, sizeof(table[i]));
+ ifp->ipv6addr_idx--;
+ }
+ break;
+ default:
+ break;
+ }
+
+ schedule_work(&ifp->ndoffload_work);
+
+ return NOTIFY_OK;
+}
+#endif
+
+int brcmf_attach(struct device *dev, struct brcmf_mp_device *settings)
+{
+ struct brcmf_pub *drvr = NULL;
+ int ret = 0;
+ int i;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ /* Allocate primary brcmf_info */
+ drvr = kzalloc(sizeof(struct brcmf_pub), GFP_ATOMIC);
+ if (!drvr)
+ return -ENOMEM;
+
+ for (i = 0; i < ARRAY_SIZE(drvr->if2bss); i++)
+ drvr->if2bss[i] = BRCMF_BSSIDX_INVALID;
+
+ mutex_init(&drvr->proto_block);
+
+ /* Link to bus module */
+ drvr->hdrlen = 0;
+ drvr->bus_if = dev_get_drvdata(dev);
+ drvr->bus_if->drvr = drvr;
+ drvr->settings = settings;
+
+ /* attach debug facilities */
+ brcmf_debug_attach(drvr);
+
+ /* Attach and link in the protocol */
+ ret = brcmf_proto_attach(drvr);
+ if (ret != 0) {
+ brcmf_err("brcmf_prot_attach failed\n");
+ goto fail;
+ }
+
+ /* Attach to events important for core code */
+ brcmf_fweh_register(drvr, BRCMF_E_PSM_WATCHDOG,
+ brcmf_psm_watchdog_notify);
+
+ /* attach firmware event handler */
+ brcmf_fweh_attach(drvr);
+
+ return ret;
+
+fail:
+ brcmf_detach(dev);
+
+ return ret;
+}
+
+static int brcmf_revinfo_read(struct seq_file *s, void *data)
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(s->private);
+ struct brcmf_rev_info *ri = &bus_if->drvr->revinfo;
+ char drev[BRCMU_DOTREV_LEN];
+ char brev[BRCMU_BOARDREV_LEN];
+
+ seq_printf(s, "vendorid: 0x%04x\n", ri->vendorid);
+ seq_printf(s, "deviceid: 0x%04x\n", ri->deviceid);
+ seq_printf(s, "radiorev: %s\n", brcmu_dotrev_str(ri->radiorev, drev));
+ seq_printf(s, "chipnum: %u (%x)\n", ri->chipnum, ri->chipnum);
+ seq_printf(s, "chiprev: %u\n", ri->chiprev);
+ seq_printf(s, "chippkg: %u\n", ri->chippkg);
+ seq_printf(s, "corerev: %u\n", ri->corerev);
+ seq_printf(s, "boardid: 0x%04x\n", ri->boardid);
+ seq_printf(s, "boardvendor: 0x%04x\n", ri->boardvendor);
+ seq_printf(s, "boardrev: %s\n", brcmu_boardrev_str(ri->boardrev, brev));
+ seq_printf(s, "driverrev: %s\n", brcmu_dotrev_str(ri->driverrev, drev));
+ seq_printf(s, "ucoderev: %u\n", ri->ucoderev);
+ seq_printf(s, "bus: %u\n", ri->bus);
+ seq_printf(s, "phytype: %u\n", ri->phytype);
+ seq_printf(s, "phyrev: %u\n", ri->phyrev);
+ seq_printf(s, "anarev: %u\n", ri->anarev);
+ seq_printf(s, "nvramrev: %08x\n", ri->nvramrev);
+
+ return 0;
+}
+
+int brcmf_bus_started(struct device *dev)
+{
+ int ret = -1;
+ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+ struct brcmf_pub *drvr = bus_if->drvr;
+ struct brcmf_if *ifp;
+ struct brcmf_if *p2p_ifp;
+
+ brcmf_dbg(TRACE, "\n");
+
+ /* add primary networking interface */
+ ifp = brcmf_add_if(drvr, 0, 0, false, "wlan%d", NULL);
+ if (IS_ERR(ifp))
+ return PTR_ERR(ifp);
+
+ p2p_ifp = NULL;
+
+ /* signal bus ready */
+ brcmf_bus_change_state(bus_if, BRCMF_BUS_UP);
+
+ /* Bus is ready, do any initialization */
+ ret = brcmf_c_preinit_dcmds(ifp);
+ if (ret < 0)
+ goto fail;
+
+ brcmf_debugfs_add_entry(drvr, "revinfo", brcmf_revinfo_read);
+
+ /* assure we have chipid before feature attach */
+ if (!bus_if->chip) {
+ bus_if->chip = drvr->revinfo.chipnum;
+ bus_if->chiprev = drvr->revinfo.chiprev;
+ brcmf_dbg(INFO, "firmware revinfo: chip %x (%d) rev %d\n",
+ bus_if->chip, bus_if->chip, bus_if->chiprev);
+ }
+ brcmf_feat_attach(drvr);
+
+ ret = brcmf_proto_init_done(drvr);
+ if (ret < 0)
+ goto fail;
+
+ brcmf_proto_add_if(drvr, ifp);
+
+ drvr->config = brcmf_cfg80211_attach(drvr, bus_if->dev,
+ drvr->settings->p2p_enable);
+ if (drvr->config == NULL) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ ret = brcmf_net_attach(ifp, false);
+
+ if ((!ret) && (drvr->settings->p2p_enable)) {
+ p2p_ifp = drvr->iflist[1];
+ if (p2p_ifp)
+ ret = brcmf_net_p2p_attach(p2p_ifp);
+ }
+
+ if (ret)
+ goto fail;
+
+#ifdef CONFIG_INET
+ drvr->inetaddr_notifier.notifier_call = brcmf_inetaddr_changed;
+ ret = register_inetaddr_notifier(&drvr->inetaddr_notifier);
+ if (ret)
+ goto fail;
+
+#if IS_ENABLED(CONFIG_IPV6)
+ drvr->inet6addr_notifier.notifier_call = brcmf_inet6addr_changed;
+ ret = register_inet6addr_notifier(&drvr->inet6addr_notifier);
+ if (ret) {
+ unregister_inetaddr_notifier(&drvr->inetaddr_notifier);
+ goto fail;
+ }
+#endif
+#endif /* CONFIG_INET */
+
+ return 0;
+
+fail:
+ brcmf_err("failed: %d\n", ret);
+ if (drvr->config) {
+ brcmf_cfg80211_detach(drvr->config);
+ drvr->config = NULL;
+ }
+ brcmf_net_detach(ifp->ndev, false);
+ if (p2p_ifp)
+ brcmf_net_detach(p2p_ifp->ndev, false);
+ drvr->iflist[0] = NULL;
+ drvr->iflist[1] = NULL;
+ if (drvr->settings->ignore_probe_fail)
+ ret = 0;
+
+ return ret;
+}
+
+void brcmf_bus_add_txhdrlen(struct device *dev, uint len)
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+ struct brcmf_pub *drvr = bus_if->drvr;
+
+ if (drvr) {
+ drvr->hdrlen += len;
+ }
+}
+
+void brcmf_dev_reset(struct device *dev)
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+ struct brcmf_pub *drvr = bus_if->drvr;
+
+ if (drvr == NULL)
+ return;
+
+ if (drvr->iflist[0])
+ brcmf_fil_cmd_int_set(drvr->iflist[0], BRCMF_C_TERMINATED, 1);
+}
+
+void brcmf_detach(struct device *dev)
+{
+ s32 i;
+ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+ struct brcmf_pub *drvr = bus_if->drvr;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ if (drvr == NULL)
+ return;
+
+#ifdef CONFIG_INET
+ unregister_inetaddr_notifier(&drvr->inetaddr_notifier);
+#endif
+
+#if IS_ENABLED(CONFIG_IPV6)
+ unregister_inet6addr_notifier(&drvr->inet6addr_notifier);
+#endif
+
+ /* stop firmware event handling */
+ brcmf_fweh_detach(drvr);
+ if (drvr->config)
+ brcmf_p2p_detach(&drvr->config->p2p);
+
+ brcmf_bus_change_state(bus_if, BRCMF_BUS_DOWN);
+
+ /* make sure primary interface removed last */
+ for (i = BRCMF_MAX_IFS-1; i > -1; i--)
+ brcmf_remove_interface(drvr->iflist[i], false);
+
+ brcmf_cfg80211_detach(drvr->config);
+
+ brcmf_bus_stop(drvr->bus_if);
+
+ brcmf_proto_detach(drvr);
+
+ brcmf_debug_detach(drvr);
+ bus_if->drvr = NULL;
+ kfree(drvr);
+}
+
+s32 brcmf_iovar_data_set(struct device *dev, char *name, void *data, u32 len)
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+ struct brcmf_if *ifp = bus_if->drvr->iflist[0];
+
+ return brcmf_fil_iovar_data_set(ifp, name, data, len);
+}
+
+static int brcmf_get_pend_8021x_cnt(struct brcmf_if *ifp)
+{
+ return atomic_read(&ifp->pend_8021x_cnt);
+}
+
+int brcmf_netdev_wait_pend8021x(struct brcmf_if *ifp)
+{
+ int err;
+
+ err = wait_event_timeout(ifp->pend_8021x_wait,
+ !brcmf_get_pend_8021x_cnt(ifp),
+ MAX_WAIT_FOR_8021X_TX);
+
+ if (!err)
+ brcmf_err("Timed out waiting for no pending 802.1x packets\n");
+
+ return !err;
+}
+
+void brcmf_bus_change_state(struct brcmf_bus *bus, enum brcmf_bus_state state)
+{
+ struct brcmf_pub *drvr = bus->drvr;
+ struct net_device *ndev;
+ int ifidx;
+
+ brcmf_dbg(TRACE, "%d -> %d\n", bus->state, state);
+ bus->state = state;
+
+ if (state == BRCMF_BUS_UP) {
+ for (ifidx = 0; ifidx < BRCMF_MAX_IFS; ifidx++) {
+ if ((drvr->iflist[ifidx]) &&
+ (drvr->iflist[ifidx]->ndev)) {
+ ndev = drvr->iflist[ifidx]->ndev;
+ if (netif_queue_stopped(ndev))
+ netif_wake_queue(ndev);
+ }
+ }
+ }
+}
+
+static void brcmf_driver_register(struct work_struct *work)
+{
+#ifdef CPTCFG_BRCMFMAC_SDIO
+ brcmf_sdio_register();
+#endif
+#ifdef CPTCFG_BRCMFMAC_USB
+ brcmf_usb_register();
+#endif
+#ifdef CPTCFG_BRCMFMAC_PCIE
+ brcmf_pcie_register();
+#endif
+}
+static DECLARE_WORK(brcmf_driver_work, brcmf_driver_register);
+
+int __init brcmf_core_init(void)
+{
+ if (!schedule_work(&brcmf_driver_work))
+ return -EBUSY;
+
+ return 0;
+}
+
+void __exit brcmf_core_exit(void)
+{
+ cancel_work_sync(&brcmf_driver_work);
+
+#ifdef CPTCFG_BRCMFMAC_SDIO
+ brcmf_sdio_exit();
+#endif
+#ifdef CPTCFG_BRCMFMAC_USB
+ brcmf_usb_exit();
+#endif
+#ifdef CPTCFG_BRCMFMAC_PCIE
+ brcmf_pcie_exit();
+#endif
+}
+
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h
new file mode 100644
index 0000000..a4dd313
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/****************
+ * Common types *
+ */
+
+#ifndef BRCMFMAC_CORE_H
+#define BRCMFMAC_CORE_H
+
+#include <net/cfg80211.h>
+#include "fweh.h"
+
+#define TOE_TX_CSUM_OL 0x00000001
+#define TOE_RX_CSUM_OL 0x00000002
+
+/* For supporting multiple interfaces */
+#define BRCMF_MAX_IFS 16
+
+/* Small, medium and maximum buffer size for dcmd
+ */
+#define BRCMF_DCMD_SMLEN 256
+#define BRCMF_DCMD_MEDLEN 1536
+#define BRCMF_DCMD_MAXLEN 8192
+
+/* IOCTL from host to device are limited in lenght. A device can only handle
+ * ethernet frame size. This limitation is to be applied by protocol layer.
+ */
+#define BRCMF_TX_IOCTL_MAX_MSG_SIZE (ETH_FRAME_LEN+ETH_FCS_LEN)
+
+#define BRCMF_AMPDU_RX_REORDER_MAXFLOWS 256
+
+/* Length of firmware version string stored for
+ * ethtool driver info which uses 32 bytes as well.
+ */
+#define BRCMF_DRIVER_FIRMWARE_VERSION_LEN 32
+
+#define NDOL_MAX_ENTRIES 8
+
+/**
+ * struct brcmf_ampdu_rx_reorder - AMPDU receive reorder info
+ *
+ * @pktslots: dynamic allocated array for ordering AMPDU packets.
+ * @flow_id: AMPDU flow identifier.
+ * @cur_idx: last AMPDU index from firmware.
+ * @exp_idx: expected next AMPDU index.
+ * @max_idx: maximum amount of packets per AMPDU.
+ * @pend_pkts: number of packets currently in @pktslots.
+ */
+struct brcmf_ampdu_rx_reorder {
+ struct sk_buff **pktslots;
+ u8 flow_id;
+ u8 cur_idx;
+ u8 exp_idx;
+ u8 max_idx;
+ u8 pend_pkts;
+};
+
+/* Forward decls for struct brcmf_pub (see below) */
+struct brcmf_proto; /* device communication protocol info */
+struct brcmf_fws_info; /* firmware signalling info */
+struct brcmf_mp_device; /* module paramateres, device specific */
+
+/*
+ * struct brcmf_rev_info
+ *
+ * The result field stores the error code of the
+ * revision info request from firmware. For the
+ * other fields see struct brcmf_rev_info_le in
+ * fwil_types.h
+ */
+struct brcmf_rev_info {
+ int result;
+ u32 vendorid;
+ u32 deviceid;
+ u32 radiorev;
+ u32 chiprev;
+ u32 corerev;
+ u32 boardid;
+ u32 boardvendor;
+ u32 boardrev;
+ u32 driverrev;
+ u32 ucoderev;
+ u32 bus;
+ u32 chipnum;
+ u32 phytype;
+ u32 phyrev;
+ u32 anarev;
+ u32 chippkg;
+ u32 nvramrev;
+};
+
+/* Common structure for module and instance linkage */
+struct brcmf_pub {
+ /* Linkage ponters */
+ struct brcmf_bus *bus_if;
+ struct brcmf_proto *proto;
+ struct brcmf_cfg80211_info *config;
+
+ /* Internal brcmf items */
+ uint hdrlen; /* Total BRCMF header length (proto + bus) */
+
+ /* Dongle media info */
+ char fwver[BRCMF_DRIVER_FIRMWARE_VERSION_LEN];
+ u8 mac[ETH_ALEN]; /* MAC address obtained from dongle */
+
+ struct mac_address addresses[BRCMF_MAX_IFS];
+
+ struct brcmf_if *iflist[BRCMF_MAX_IFS];
+ s32 if2bss[BRCMF_MAX_IFS];
+
+ struct mutex proto_block;
+ unsigned char proto_buf[BRCMF_DCMD_MAXLEN];
+
+ struct brcmf_fweh_info fweh;
+
+ struct brcmf_ampdu_rx_reorder
+ *reorder_flows[BRCMF_AMPDU_RX_REORDER_MAXFLOWS];
+
+ u32 feat_flags;
+ u32 chip_quirks;
+
+ struct brcmf_rev_info revinfo;
+#ifdef DEBUG
+ struct dentry *dbgfs_dir;
+#endif
+
+ struct notifier_block inetaddr_notifier;
+ struct notifier_block inet6addr_notifier;
+ struct brcmf_mp_device *settings;
+};
+
+/* forward declarations */
+struct brcmf_cfg80211_vif;
+struct brcmf_fws_mac_descriptor;
+
+/**
+ * enum brcmf_netif_stop_reason - reason for stopping netif queue.
+ *
+ * @BRCMF_NETIF_STOP_REASON_FWS_FC:
+ * netif stopped due to firmware signalling flow control.
+ * @BRCMF_NETIF_STOP_REASON_FLOW:
+ * netif stopped due to flowring full.
+ * @BRCMF_NETIF_STOP_REASON_DISCONNECTED:
+ * netif stopped due to not being connected (STA mode).
+ */
+enum brcmf_netif_stop_reason {
+ BRCMF_NETIF_STOP_REASON_FWS_FC = BIT(0),
+ BRCMF_NETIF_STOP_REASON_FLOW = BIT(1),
+ BRCMF_NETIF_STOP_REASON_DISCONNECTED = BIT(2)
+};
+
+/**
+ * struct brcmf_if - interface control information.
+ *
+ * @drvr: points to device related information.
+ * @vif: points to cfg80211 specific interface information.
+ * @ndev: associated network device.
+ * @multicast_work: worker object for multicast provisioning.
+ * @ndoffload_work: worker object for neighbor discovery offload configuration.
+ * @fws_desc: interface specific firmware-signalling descriptor.
+ * @ifidx: interface index in device firmware.
+ * @bsscfgidx: index of bss associated with this interface.
+ * @mac_addr: assigned mac address.
+ * @netif_stop: bitmap indicates reason why netif queues are stopped.
+ * @netif_stop_lock: spinlock for update netif_stop from multiple sources.
+ * @pend_8021x_cnt: tracks outstanding number of 802.1x frames.
+ * @pend_8021x_wait: used for signalling change in count.
+ */
+struct brcmf_if {
+ struct brcmf_pub *drvr;
+ struct brcmf_cfg80211_vif *vif;
+ struct net_device *ndev;
+ struct work_struct multicast_work;
+ struct work_struct ndoffload_work;
+ struct brcmf_fws_mac_descriptor *fws_desc;
+ int ifidx;
+ s32 bsscfgidx;
+ u8 mac_addr[ETH_ALEN];
+ u8 netif_stop;
+ spinlock_t netif_stop_lock;
+ atomic_t pend_8021x_cnt;
+ wait_queue_head_t pend_8021x_wait;
+ struct in6_addr ipv6_addr_tbl[NDOL_MAX_ENTRIES];
+ u8 ipv6addr_idx;
+};
+
+int brcmf_netdev_wait_pend8021x(struct brcmf_if *ifp);
+
+/* Return pointer to interface name */
+char *brcmf_ifname(struct brcmf_if *ifp);
+struct brcmf_if *brcmf_get_ifp(struct brcmf_pub *drvr, int ifidx);
+int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked);
+struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bsscfgidx, s32 ifidx,
+ bool is_p2pdev, const char *name, u8 *mac_addr);
+void brcmf_remove_interface(struct brcmf_if *ifp, bool rtnl_locked);
+void brcmf_txflowblock_if(struct brcmf_if *ifp,
+ enum brcmf_netif_stop_reason reason, bool state);
+void brcmf_txfinalize(struct brcmf_if *ifp, struct sk_buff *txp, bool success);
+void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb);
+void brcmf_net_setcarrier(struct brcmf_if *ifp, bool on);
+int __init brcmf_core_init(void);
+void __exit brcmf_core_exit(void);
+
+#endif /* BRCMFMAC_CORE_H */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.c
new file mode 100644
index 0000000..1447a83
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.c
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2012 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <linux/debugfs.h>
+#include <linux/netdevice.h>
+#include <linux/module.h>
+#include <linux/devcoredump.h>
+
+#include <brcmu_wifi.h>
+#include <brcmu_utils.h>
+#include "core.h"
+#include "bus.h"
+#include "fweh.h"
+#include "debug.h"
+
+static struct dentry *root_folder;
+
+int brcmf_debug_create_memdump(struct brcmf_bus *bus, const void *data,
+ size_t len)
+{
+ void *dump;
+ size_t ramsize;
+ int err;
+
+ ramsize = brcmf_bus_get_ramsize(bus);
+ if (!ramsize)
+ return -ENOTSUPP;
+
+ dump = vzalloc(len + ramsize);
+ if (!dump)
+ return -ENOMEM;
+
+ memcpy(dump, data, len);
+ err = brcmf_bus_get_memdump(bus, dump + len, ramsize);
+ if (err) {
+ vfree(dump);
+ return err;
+ }
+
+ dev_coredumpv(bus->dev, dump, len + ramsize, GFP_KERNEL);
+
+ return 0;
+}
+
+void brcmf_debugfs_init(void)
+{
+ root_folder = debugfs_create_dir(KBUILD_MODNAME, NULL);
+ if (IS_ERR(root_folder))
+ root_folder = NULL;
+}
+
+void brcmf_debugfs_exit(void)
+{
+ if (!root_folder)
+ return;
+
+ debugfs_remove_recursive(root_folder);
+ root_folder = NULL;
+}
+
+int brcmf_debug_attach(struct brcmf_pub *drvr)
+{
+ struct device *dev = drvr->bus_if->dev;
+
+ if (!root_folder)
+ return -ENODEV;
+
+ drvr->dbgfs_dir = debugfs_create_dir(dev_name(dev), root_folder);
+ if (IS_ERR(drvr->dbgfs_dir))
+ return PTR_ERR(drvr->dbgfs_dir);
+
+ return 0;
+}
+
+void brcmf_debug_detach(struct brcmf_pub *drvr)
+{
+ brcmf_fweh_unregister(drvr, BRCMF_E_PSM_WATCHDOG);
+
+ if (!IS_ERR_OR_NULL(drvr->dbgfs_dir))
+ debugfs_remove_recursive(drvr->dbgfs_dir);
+}
+
+struct dentry *brcmf_debugfs_get_devdir(struct brcmf_pub *drvr)
+{
+ return drvr->dbgfs_dir;
+}
+
+int brcmf_debugfs_add_entry(struct brcmf_pub *drvr, const char *fn,
+ int (*read_fn)(struct seq_file *seq, void *data))
+{
+ struct dentry *e;
+
+ e = debugfs_create_devm_seqfile(drvr->bus_if->dev, fn,
+ drvr->dbgfs_dir, read_fn);
+ return PTR_ERR_OR_ZERO(e);
+}
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.h
new file mode 100644
index 0000000..86c931d
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef BRCMFMAC_DEBUG_H
+#define BRCMFMAC_DEBUG_H
+
+#include <linux/net.h> /* net_ratelimit() */
+
+/* message levels */
+#define BRCMF_TRACE_VAL 0x00000002
+#define BRCMF_INFO_VAL 0x00000004
+#define BRCMF_DATA_VAL 0x00000008
+#define BRCMF_CTL_VAL 0x00000010
+#define BRCMF_TIMER_VAL 0x00000020
+#define BRCMF_HDRS_VAL 0x00000040
+#define BRCMF_BYTES_VAL 0x00000080
+#define BRCMF_INTR_VAL 0x00000100
+#define BRCMF_GLOM_VAL 0x00000200
+#define BRCMF_EVENT_VAL 0x00000400
+#define BRCMF_BTA_VAL 0x00000800
+#define BRCMF_FIL_VAL 0x00001000
+#define BRCMF_USB_VAL 0x00002000
+#define BRCMF_SCAN_VAL 0x00004000
+#define BRCMF_CONN_VAL 0x00008000
+#define BRCMF_BCDC_VAL 0x00010000
+#define BRCMF_SDIO_VAL 0x00020000
+#define BRCMF_MSGBUF_VAL 0x00040000
+#define BRCMF_PCIE_VAL 0x00080000
+#define BRCMF_FWCON_VAL 0x00100000
+
+/* set default print format */
+#undef pr_fmt
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+__printf(2, 3)
+void __brcmf_err(const char *func, const char *fmt, ...);
+/* Macro for error messages. When debugging / tracing the driver all error
+ * messages are important to us.
+ */
+#define brcmf_err(fmt, ...) \
+ do { \
+ if (IS_ENABLED(CPTCFG_BRCMDBG) || \
+ IS_ENABLED(CPTCFG_BRCM_TRACING) || \
+ net_ratelimit()) \
+ __brcmf_err(__func__, fmt, ##__VA_ARGS__); \
+ } while (0)
+
+#if defined(DEBUG) || defined(CPTCFG_BRCM_TRACING)
+
+/* For debug/tracing purposes treat info messages as errors */
+#define brcmf_info brcmf_err
+
+__printf(3, 4)
+void __brcmf_dbg(u32 level, const char *func, const char *fmt, ...);
+#define brcmf_dbg(level, fmt, ...) \
+do { \
+ __brcmf_dbg(BRCMF_##level##_VAL, __func__, \
+ fmt, ##__VA_ARGS__); \
+} while (0)
+#define BRCMF_DATA_ON() (brcmf_msg_level & BRCMF_DATA_VAL)
+#define BRCMF_CTL_ON() (brcmf_msg_level & BRCMF_CTL_VAL)
+#define BRCMF_HDRS_ON() (brcmf_msg_level & BRCMF_HDRS_VAL)
+#define BRCMF_BYTES_ON() (brcmf_msg_level & BRCMF_BYTES_VAL)
+#define BRCMF_GLOM_ON() (brcmf_msg_level & BRCMF_GLOM_VAL)
+#define BRCMF_EVENT_ON() (brcmf_msg_level & BRCMF_EVENT_VAL)
+#define BRCMF_FIL_ON() (brcmf_msg_level & BRCMF_FIL_VAL)
+#define BRCMF_FWCON_ON() (brcmf_msg_level & BRCMF_FWCON_VAL)
+
+#else /* defined(DEBUG) || defined(CPTCFG_BRCM_TRACING) */
+
+#define brcmf_info(fmt, ...) \
+ do { \
+ pr_info("%s: " fmt, __func__, ##__VA_ARGS__); \
+ } while (0)
+
+#define brcmf_dbg(level, fmt, ...) no_printk(fmt, ##__VA_ARGS__)
+
+#define BRCMF_DATA_ON() 0
+#define BRCMF_CTL_ON() 0
+#define BRCMF_HDRS_ON() 0
+#define BRCMF_BYTES_ON() 0
+#define BRCMF_GLOM_ON() 0
+#define BRCMF_EVENT_ON() 0
+#define BRCMF_FIL_ON() 0
+#define BRCMF_FWCON_ON() 0
+
+#endif /* defined(DEBUG) || defined(CPTCFG_BRCM_TRACING) */
+
+#define brcmf_dbg_hex_dump(test, data, len, fmt, ...) \
+do { \
+ trace_brcmf_hexdump((void *)data, len); \
+ if (test) \
+ brcmu_dbg_hex_dump(data, len, fmt, ##__VA_ARGS__); \
+} while (0)
+
+extern int brcmf_msg_level;
+
+struct brcmf_bus;
+struct brcmf_pub;
+#ifdef DEBUG
+void brcmf_debugfs_init(void);
+void brcmf_debugfs_exit(void);
+int brcmf_debug_attach(struct brcmf_pub *drvr);
+void brcmf_debug_detach(struct brcmf_pub *drvr);
+struct dentry *brcmf_debugfs_get_devdir(struct brcmf_pub *drvr);
+int brcmf_debugfs_add_entry(struct brcmf_pub *drvr, const char *fn,
+ int (*read_fn)(struct seq_file *seq, void *data));
+int brcmf_debug_create_memdump(struct brcmf_bus *bus, const void *data,
+ size_t len);
+#else
+static inline void brcmf_debugfs_init(void)
+{
+}
+static inline void brcmf_debugfs_exit(void)
+{
+}
+static inline int brcmf_debug_attach(struct brcmf_pub *drvr)
+{
+ return 0;
+}
+static inline void brcmf_debug_detach(struct brcmf_pub *drvr)
+{
+}
+static inline
+int brcmf_debugfs_add_entry(struct brcmf_pub *drvr, const char *fn,
+ int (*read_fn)(struct seq_file *seq, void *data))
+{
+ return 0;
+}
+static inline
+int brcmf_debug_create_memdump(struct brcmf_bus *bus, const void *data,
+ size_t len)
+{
+ return 0;
+}
+#endif
+
+#endif /* BRCMFMAC_DEBUG_H */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c
new file mode 100644
index 0000000..386514c
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c
@@ -0,0 +1,216 @@
+/*
+ * Copyright (c) 2014 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/netdevice.h>
+#include <linux/module.h>
+
+#include <brcm_hw_ids.h>
+#include <brcmu_wifi.h>
+#include "core.h"
+#include "bus.h"
+#include "debug.h"
+#include "fwil.h"
+#include "fwil_types.h"
+#include "feature.h"
+#include "common.h"
+
+
+/*
+ * expand feature list to array of feature strings.
+ */
+#define BRCMF_FEAT_DEF(_f) \
+ #_f,
+static const char *brcmf_feat_names[] = {
+ BRCMF_FEAT_LIST
+};
+#undef BRCMF_FEAT_DEF
+
+struct brcmf_feat_fwcap {
+ enum brcmf_feat_id feature;
+ const char * const fwcap_id;
+};
+
+static const struct brcmf_feat_fwcap brcmf_fwcap_map[] = {
+ { BRCMF_FEAT_MBSS, "mbss" },
+ { BRCMF_FEAT_MCHAN, "mchan" },
+ { BRCMF_FEAT_P2P, "p2p" },
+};
+
+#ifdef DEBUG
+/*
+ * expand quirk list to array of quirk strings.
+ */
+#define BRCMF_QUIRK_DEF(_q) \
+ #_q,
+static const char * const brcmf_quirk_names[] = {
+ BRCMF_QUIRK_LIST
+};
+#undef BRCMF_QUIRK_DEF
+
+/**
+ * brcmf_feat_debugfs_read() - expose feature info to debugfs.
+ *
+ * @seq: sequence for debugfs entry.
+ * @data: raw data pointer.
+ */
+static int brcmf_feat_debugfs_read(struct seq_file *seq, void *data)
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(seq->private);
+ u32 feats = bus_if->drvr->feat_flags;
+ u32 quirks = bus_if->drvr->chip_quirks;
+ int id;
+
+ seq_printf(seq, "Features: %08x\n", feats);
+ for (id = 0; id < BRCMF_FEAT_LAST; id++)
+ if (feats & BIT(id))
+ seq_printf(seq, "\t%s\n", brcmf_feat_names[id]);
+ seq_printf(seq, "\nQuirks: %08x\n", quirks);
+ for (id = 0; id < BRCMF_FEAT_QUIRK_LAST; id++)
+ if (quirks & BIT(id))
+ seq_printf(seq, "\t%s\n", brcmf_quirk_names[id]);
+ return 0;
+}
+#else
+static int brcmf_feat_debugfs_read(struct seq_file *seq, void *data)
+{
+ return 0;
+}
+#endif /* DEBUG */
+
+/**
+ * brcmf_feat_iovar_int_get() - determine feature through iovar query.
+ *
+ * @ifp: interface to query.
+ * @id: feature id.
+ * @name: iovar name.
+ */
+static void brcmf_feat_iovar_int_get(struct brcmf_if *ifp,
+ enum brcmf_feat_id id, char *name)
+{
+ u32 data;
+ int err;
+
+ err = brcmf_fil_iovar_int_get(ifp, name, &data);
+ if (err == 0) {
+ brcmf_dbg(INFO, "enabling feature: %s\n", brcmf_feat_names[id]);
+ ifp->drvr->feat_flags |= BIT(id);
+ } else {
+ brcmf_dbg(TRACE, "%s feature check failed: %d\n",
+ brcmf_feat_names[id], err);
+ }
+}
+
+static void brcmf_feat_firmware_capabilities(struct brcmf_if *ifp)
+{
+ char caps[256];
+ enum brcmf_feat_id id;
+ int i;
+
+ brcmf_fil_iovar_data_get(ifp, "cap", caps, sizeof(caps));
+ brcmf_dbg(INFO, "[ %s]\n", caps);
+
+ for (i = 0; i < ARRAY_SIZE(brcmf_fwcap_map); i++) {
+ if (strnstr(caps, brcmf_fwcap_map[i].fwcap_id, sizeof(caps))) {
+ id = brcmf_fwcap_map[i].feature;
+ brcmf_dbg(INFO, "enabling feature: %s\n",
+ brcmf_feat_names[id]);
+ ifp->drvr->feat_flags |= BIT(id);
+ }
+ }
+}
+
+extern int g_pfn_enable;
+
+void brcmf_feat_attach(struct brcmf_pub *drvr)
+{
+ struct brcmf_if *ifp = brcmf_get_ifp(drvr, 0);
+ struct brcmf_pno_macaddr_le pfn_mac;
+ u32 wowl_cap;
+ s32 err;
+
+ brcmf_feat_firmware_capabilities(ifp);
+
+ if (g_pfn_enable)
+ brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_PNO, "pfn");
+
+ if (drvr->bus_if->wowl_supported)
+ brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_WOWL, "wowl");
+ if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL)) {
+ err = brcmf_fil_iovar_int_get(ifp, "wowl_cap", &wowl_cap);
+ if (!err) {
+ ifp->drvr->feat_flags |= BIT(BRCMF_FEAT_WOWL_ARP_ND);
+ if (wowl_cap & BRCMF_WOWL_PFN_FOUND)
+ ifp->drvr->feat_flags |=
+ BIT(BRCMF_FEAT_WOWL_ND);
+ if (wowl_cap & BRCMF_WOWL_GTK_FAILURE)
+ ifp->drvr->feat_flags |=
+ BIT(BRCMF_FEAT_WOWL_GTK);
+ }
+ } else if (drvr->bus_if->wowl_supported) {
+ brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_PKT_FILTER, "pkt_filter_mode");
+ if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PKT_FILTER)) {
+ ifp->drvr->feat_flags |= BIT(BRCMF_FEAT_WOWL);
+ brcmf_dbg(INFO, "enabling feature: %s\n", brcmf_feat_names[BRCMF_FEAT_WOWL]);
+ }
+ }
+
+ /* MBSS does not work for 43362 */
+ if (drvr->bus_if->chip == BRCM_CC_43362_CHIP_ID)
+ ifp->drvr->feat_flags &= ~BIT(BRCMF_FEAT_MBSS);
+ brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_RSDB, "rsdb_mode");
+ brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_TDLS, "tdls_enable");
+ brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_MFP, "mfp");
+
+ pfn_mac.version = BRCMF_PFN_MACADDR_CFG_VER;
+ err = brcmf_fil_iovar_data_get(ifp, "pfn_macaddr", &pfn_mac,
+ sizeof(pfn_mac));
+ if (!err)
+ ifp->drvr->feat_flags |= BIT(BRCMF_FEAT_SCAN_RANDOM_MAC);
+
+ if (drvr->settings->feature_disable) {
+ brcmf_dbg(INFO, "Features: 0x%02x, disable: 0x%02x\n",
+ ifp->drvr->feat_flags,
+ drvr->settings->feature_disable);
+ ifp->drvr->feat_flags &= ~drvr->settings->feature_disable;
+ }
+ brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_FWSUP, "sup_wpa");
+
+ /* set chip related quirks */
+ switch (drvr->bus_if->chip) {
+ case BRCM_CC_43236_CHIP_ID:
+ drvr->chip_quirks |= BIT(BRCMF_FEAT_QUIRK_AUTO_AUTH);
+ break;
+ case BRCM_CC_4329_CHIP_ID:
+ drvr->chip_quirks |= BIT(BRCMF_FEAT_QUIRK_NEED_MPC);
+ break;
+ default:
+ /* no quirks */
+ break;
+ }
+
+ brcmf_debugfs_add_entry(drvr, "features", brcmf_feat_debugfs_read);
+}
+
+bool brcmf_feat_is_enabled(struct brcmf_if *ifp, enum brcmf_feat_id id)
+{
+ return (ifp->drvr->feat_flags & BIT(id));
+}
+
+bool brcmf_feat_is_quirk_enabled(struct brcmf_if *ifp,
+ enum brcmf_feat_quirk quirk)
+{
+ return (ifp->drvr->chip_quirks & BIT(quirk));
+}
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h
new file mode 100644
index 0000000..85eaa2b
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2014 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#ifndef _BRCMF_FEATURE_H
+#define _BRCMF_FEATURE_H
+
+/*
+ * Features:
+ *
+ * MBSS: multiple BSSID support (eg. guest network in AP mode).
+ * MCHAN: multi-channel for concurrent P2P.
+ * PNO: preferred network offload.
+ * WOWL: Wake-On-WLAN.
+ * P2P: peer-to-peer
+ * RSDB: Real Simultaneous Dual Band
+ * TDLS: Tunneled Direct Link Setup
+ * SCAN_RANDOM_MAC: Random MAC during (net detect) scheduled scan.
+ * WOWL_ND: WOWL net detect (PNO)
+ * WOWL_GTK: (WOWL) GTK rekeying offload
+ * WOWL_ARP_ND: ARP and Neighbor Discovery offload support during WOWL.
+ * MFP: 802.11w Management Frame Protection.
+ * FWSUP: Firmware supplicant.
+ */
+#define BRCMF_FEAT_LIST \
+ BRCMF_FEAT_DEF(MBSS) \
+ BRCMF_FEAT_DEF(MCHAN) \
+ BRCMF_FEAT_DEF(PNO) \
+ BRCMF_FEAT_DEF(WOWL) \
+ BRCMF_FEAT_DEF(P2P) \
+ BRCMF_FEAT_DEF(RSDB) \
+ BRCMF_FEAT_DEF(TDLS) \
+ BRCMF_FEAT_DEF(SCAN_RANDOM_MAC) \
+ BRCMF_FEAT_DEF(WOWL_ND) \
+ BRCMF_FEAT_DEF(WOWL_GTK) \
+ BRCMF_FEAT_DEF(WOWL_ARP_ND) \
+ BRCMF_FEAT_DEF(MFP) \
+ BRCMF_FEAT_DEF(FWSUP) \
+ BRCMF_FEAT_DEF(PKT_FILTER)
+
+/*
+ * Quirks:
+ *
+ * AUTO_AUTH: workaround needed for automatic authentication type.
+ * NEED_MPC: driver needs to disable MPC during scanning operation.
+ */
+#define BRCMF_QUIRK_LIST \
+ BRCMF_QUIRK_DEF(AUTO_AUTH) \
+ BRCMF_QUIRK_DEF(NEED_MPC)
+
+#define BRCMF_FEAT_DEF(_f) \
+ BRCMF_FEAT_ ## _f,
+/*
+ * expand feature list to enumeration.
+ */
+enum brcmf_feat_id {
+ BRCMF_FEAT_LIST
+ BRCMF_FEAT_LAST
+};
+#undef BRCMF_FEAT_DEF
+
+#define BRCMF_QUIRK_DEF(_q) \
+ BRCMF_FEAT_QUIRK_ ## _q,
+/*
+ * expand quirk list to enumeration.
+ */
+enum brcmf_feat_quirk {
+ BRCMF_QUIRK_LIST
+ BRCMF_FEAT_QUIRK_LAST
+};
+#undef BRCMF_QUIRK_DEF
+
+/**
+ * brcmf_feat_attach() - determine features and quirks.
+ *
+ * @drvr: driver instance.
+ */
+void brcmf_feat_attach(struct brcmf_pub *drvr);
+
+/**
+ * brcmf_feat_is_enabled() - query feature.
+ *
+ * @ifp: interface instance.
+ * @id: feature id to check.
+ *
+ * Return: true is feature is enabled; otherwise false.
+ */
+bool brcmf_feat_is_enabled(struct brcmf_if *ifp, enum brcmf_feat_id id);
+
+/**
+ * brcmf_feat_is_quirk_enabled() - query chip quirk.
+ *
+ * @ifp: interface instance.
+ * @quirk: quirk id to check.
+ *
+ * Return: true is quirk is enabled; otherwise false.
+ */
+bool brcmf_feat_is_quirk_enabled(struct brcmf_if *ifp,
+ enum brcmf_feat_quirk quirk);
+
+#endif /* _BRCMF_FEATURE_H */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c
new file mode 100644
index 0000000..d231042
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c
@@ -0,0 +1,606 @@
+/*
+ * Copyright (c) 2013 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/module.h>
+#include <linux/bcm47xx_nvram.h>
+
+#include "debug.h"
+#include "firmware.h"
+#include "core.h"
+#include "common.h"
+
+#define BRCMF_FW_MAX_NVRAM_SIZE 64000
+#define BRCMF_FW_NVRAM_DEVPATH_LEN 19 /* devpath0=pcie/1/4/ */
+#define BRCMF_FW_NVRAM_PCIEDEV_LEN 10 /* pcie/1/4/ + \0 */
+#define BRCMF_FW_DEFAULT_BOARDREV "boardrev=0xff"
+
+enum nvram_parser_state {
+ IDLE,
+ KEY,
+ VALUE,
+ COMMENT,
+ END
+};
+
+/**
+ * struct nvram_parser - internal info for parser.
+ *
+ * @state: current parser state.
+ * @data: input buffer being parsed.
+ * @nvram: output buffer with parse result.
+ * @nvram_len: lenght of parse result.
+ * @line: current line.
+ * @column: current column in line.
+ * @pos: byte offset in input buffer.
+ * @entry: start position of key,value entry.
+ * @multi_dev_v1: detect pcie multi device v1 (compressed).
+ * @multi_dev_v2: detect pcie multi device v2.
+ * @boardrev_found: nvram contains boardrev information.
+ */
+struct nvram_parser {
+ enum nvram_parser_state state;
+ const u8 *data;
+ u8 *nvram;
+ u32 nvram_len;
+ u32 line;
+ u32 column;
+ u32 pos;
+ u32 entry;
+ bool multi_dev_v1;
+ bool multi_dev_v2;
+ bool boardrev_found;
+};
+
+/**
+ * is_nvram_char() - check if char is a valid one for NVRAM entry
+ *
+ * It accepts all printable ASCII chars except for '#' which opens a comment.
+ * Please note that ' ' (space) while accepted is not a valid key name char.
+ */
+static bool is_nvram_char(char c)
+{
+ /* comment marker excluded */
+ if (c == '#')
+ return false;
+
+ /* key and value may have any other readable character */
+ return (c >= 0x20 && c < 0x7f);
+}
+
+static bool is_whitespace(char c)
+{
+ return (c == ' ' || c == '\r' || c == '\n' || c == '\t');
+}
+
+static enum nvram_parser_state brcmf_nvram_handle_idle(struct nvram_parser *nvp)
+{
+ char c;
+
+ c = nvp->data[nvp->pos];
+ if (c == '\n')
+ return COMMENT;
+ if (is_whitespace(c) || c == '\0')
+ goto proceed;
+ if (c == '#')
+ return COMMENT;
+ if (is_nvram_char(c)) {
+ nvp->entry = nvp->pos;
+ return KEY;
+ }
+ brcmf_dbg(INFO, "warning: ln=%d:col=%d: ignoring invalid character\n",
+ nvp->line, nvp->column);
+proceed:
+ nvp->column++;
+ nvp->pos++;
+ return IDLE;
+}
+
+static enum nvram_parser_state brcmf_nvram_handle_key(struct nvram_parser *nvp)
+{
+ enum nvram_parser_state st = nvp->state;
+ char c;
+
+ c = nvp->data[nvp->pos];
+ if (c == '=') {
+ /* ignore RAW1 by treating as comment */
+ if (strncmp(&nvp->data[nvp->entry], "RAW1", 4) == 0)
+ st = COMMENT;
+ else
+ st = VALUE;
+ if (strncmp(&nvp->data[nvp->entry], "devpath", 7) == 0)
+ nvp->multi_dev_v1 = true;
+ if (strncmp(&nvp->data[nvp->entry], "pcie/", 5) == 0)
+ nvp->multi_dev_v2 = true;
+ if (strncmp(&nvp->data[nvp->entry], "boardrev", 8) == 0)
+ nvp->boardrev_found = true;
+ } else if (!is_nvram_char(c) || c == ' ') {
+ brcmf_dbg(INFO, "warning: ln=%d:col=%d: '=' expected, skip invalid key entry\n",
+ nvp->line, nvp->column);
+ return COMMENT;
+ }
+
+ nvp->column++;
+ nvp->pos++;
+ return st;
+}
+
+static enum nvram_parser_state
+brcmf_nvram_handle_value(struct nvram_parser *nvp)
+{
+ char c;
+ char *skv;
+ char *ekv;
+ u32 cplen;
+
+ c = nvp->data[nvp->pos];
+ if (!is_nvram_char(c)) {
+ /* key,value pair complete */
+ ekv = (u8 *)&nvp->data[nvp->pos];
+ skv = (u8 *)&nvp->data[nvp->entry];
+ cplen = ekv - skv;
+ if (nvp->nvram_len + cplen + 1 >= BRCMF_FW_MAX_NVRAM_SIZE)
+ return END;
+ /* copy to output buffer */
+ memcpy(&nvp->nvram[nvp->nvram_len], skv, cplen);
+ nvp->nvram_len += cplen;
+ nvp->nvram[nvp->nvram_len] = '\0';
+ nvp->nvram_len++;
+ return IDLE;
+ }
+ nvp->pos++;
+ nvp->column++;
+ return VALUE;
+}
+
+static enum nvram_parser_state
+brcmf_nvram_handle_comment(struct nvram_parser *nvp)
+{
+ char *eoc, *sol;
+
+ sol = (char *)&nvp->data[nvp->pos];
+ eoc = strchr(sol, '\n');
+ if (!eoc) {
+ eoc = strchr(sol, '\0');
+ if (!eoc)
+ return END;
+ }
+
+ /* eat all moving to next line */
+ nvp->line++;
+ nvp->column = 1;
+ nvp->pos += (eoc - sol) + 1;
+ return IDLE;
+}
+
+static enum nvram_parser_state brcmf_nvram_handle_end(struct nvram_parser *nvp)
+{
+ /* final state */
+ return END;
+}
+
+static enum nvram_parser_state
+(*nv_parser_states[])(struct nvram_parser *nvp) = {
+ brcmf_nvram_handle_idle,
+ brcmf_nvram_handle_key,
+ brcmf_nvram_handle_value,
+ brcmf_nvram_handle_comment,
+ brcmf_nvram_handle_end
+};
+
+static int brcmf_init_nvram_parser(struct nvram_parser *nvp,
+ const u8 *data, size_t data_len)
+{
+ size_t size;
+
+ memset(nvp, 0, sizeof(*nvp));
+ nvp->data = data;
+ /* Limit size to MAX_NVRAM_SIZE, some files contain lot of comment */
+ if (data_len > BRCMF_FW_MAX_NVRAM_SIZE)
+ size = BRCMF_FW_MAX_NVRAM_SIZE;
+ else
+ size = data_len;
+ /* Alloc for extra 0 byte + roundup by 4 + length field */
+ size += 1 + 3 + sizeof(u32);
+ nvp->nvram = kzalloc(size, GFP_KERNEL);
+ if (!nvp->nvram)
+ return -ENOMEM;
+
+ nvp->line = 1;
+ nvp->column = 1;
+ return 0;
+}
+
+/* brcmf_fw_strip_multi_v1 :Some nvram files contain settings for multiple
+ * devices. Strip it down for one device, use domain_nr/bus_nr to determine
+ * which data is to be returned. v1 is the version where nvram is stored
+ * compressed and "devpath" maps to index for valid entries.
+ */
+static void brcmf_fw_strip_multi_v1(struct nvram_parser *nvp, u16 domain_nr,
+ u16 bus_nr)
+{
+ /* Device path with a leading '=' key-value separator */
+ char pci_path[] = "=pci/?/?";
+ size_t pci_len;
+ char pcie_path[] = "=pcie/?/?";
+ size_t pcie_len;
+
+ u32 i, j;
+ bool found;
+ u8 *nvram;
+ u8 id;
+
+ nvram = kzalloc(nvp->nvram_len + 1 + 3 + sizeof(u32), GFP_KERNEL);
+ if (!nvram)
+ goto fail;
+
+ /* min length: devpath0=pcie/1/4/ + 0:x=y */
+ if (nvp->nvram_len < BRCMF_FW_NVRAM_DEVPATH_LEN + 6)
+ goto fail;
+
+ /* First search for the devpathX and see if it is the configuration
+ * for domain_nr/bus_nr. Search complete nvp
+ */
+ snprintf(pci_path, sizeof(pci_path), "=pci/%d/%d", domain_nr,
+ bus_nr);
+ pci_len = strlen(pci_path);
+ snprintf(pcie_path, sizeof(pcie_path), "=pcie/%d/%d", domain_nr,
+ bus_nr);
+ pcie_len = strlen(pcie_path);
+ found = false;
+ i = 0;
+ while (i < nvp->nvram_len - BRCMF_FW_NVRAM_DEVPATH_LEN) {
+ /* Format: devpathX=pcie/Y/Z/
+ * Y = domain_nr, Z = bus_nr, X = virtual ID
+ */
+ if (strncmp(&nvp->nvram[i], "devpath", 7) == 0 &&
+ (!strncmp(&nvp->nvram[i + 8], pci_path, pci_len) ||
+ !strncmp(&nvp->nvram[i + 8], pcie_path, pcie_len))) {
+ id = nvp->nvram[i + 7] - '0';
+ found = true;
+ break;
+ }
+ while (nvp->nvram[i] != 0)
+ i++;
+ i++;
+ }
+ if (!found)
+ goto fail;
+
+ /* Now copy all valid entries, release old nvram and assign new one */
+ i = 0;
+ j = 0;
+ while (i < nvp->nvram_len) {
+ if ((nvp->nvram[i] - '0' == id) && (nvp->nvram[i + 1] == ':')) {
+ i += 2;
+ if (strncmp(&nvp->nvram[i], "boardrev", 8) == 0)
+ nvp->boardrev_found = true;
+ while (nvp->nvram[i] != 0) {
+ nvram[j] = nvp->nvram[i];
+ i++;
+ j++;
+ }
+ nvram[j] = 0;
+ j++;
+ }
+ while (nvp->nvram[i] != 0)
+ i++;
+ i++;
+ }
+ kfree(nvp->nvram);
+ nvp->nvram = nvram;
+ nvp->nvram_len = j;
+ return;
+
+fail:
+ kfree(nvram);
+ nvp->nvram_len = 0;
+}
+
+/* brcmf_fw_strip_multi_v2 :Some nvram files contain settings for multiple
+ * devices. Strip it down for one device, use domain_nr/bus_nr to determine
+ * which data is to be returned. v2 is the version where nvram is stored
+ * uncompressed, all relevant valid entries are identified by
+ * pcie/domain_nr/bus_nr:
+ */
+static void brcmf_fw_strip_multi_v2(struct nvram_parser *nvp, u16 domain_nr,
+ u16 bus_nr)
+{
+ char prefix[BRCMF_FW_NVRAM_PCIEDEV_LEN];
+ size_t len;
+ u32 i, j;
+ u8 *nvram;
+
+ nvram = kzalloc(nvp->nvram_len + 1 + 3 + sizeof(u32), GFP_KERNEL);
+ if (!nvram)
+ goto fail;
+
+ /* Copy all valid entries, release old nvram and assign new one.
+ * Valid entries are of type pcie/X/Y/ where X = domain_nr and
+ * Y = bus_nr.
+ */
+ snprintf(prefix, sizeof(prefix), "pcie/%d/%d/", domain_nr, bus_nr);
+ len = strlen(prefix);
+ i = 0;
+ j = 0;
+ while (i < nvp->nvram_len - len) {
+ if (strncmp(&nvp->nvram[i], prefix, len) == 0) {
+ i += len;
+ if (strncmp(&nvp->nvram[i], "boardrev", 8) == 0)
+ nvp->boardrev_found = true;
+ while (nvp->nvram[i] != 0) {
+ nvram[j] = nvp->nvram[i];
+ i++;
+ j++;
+ }
+ nvram[j] = 0;
+ j++;
+ }
+ while (nvp->nvram[i] != 0)
+ i++;
+ i++;
+ }
+ kfree(nvp->nvram);
+ nvp->nvram = nvram;
+ nvp->nvram_len = j;
+ return;
+fail:
+ kfree(nvram);
+ nvp->nvram_len = 0;
+}
+
+static void brcmf_fw_add_defaults(struct nvram_parser *nvp)
+{
+ if (nvp->boardrev_found)
+ return;
+
+ memcpy(&nvp->nvram[nvp->nvram_len], &BRCMF_FW_DEFAULT_BOARDREV,
+ strlen(BRCMF_FW_DEFAULT_BOARDREV));
+ nvp->nvram_len += strlen(BRCMF_FW_DEFAULT_BOARDREV);
+ nvp->nvram[nvp->nvram_len] = '\0';
+ nvp->nvram_len++;
+}
+
+/* brcmf_nvram_strip :Takes a buffer of "<var>=<value>\n" lines read from a fil
+ * and ending in a NUL. Removes carriage returns, empty lines, comment lines,
+ * and converts newlines to NULs. Shortens buffer as needed and pads with NULs.
+ * End of buffer is completed with token identifying length of buffer.
+ */
+static void *brcmf_fw_nvram_strip(const u8 *data, size_t data_len,
+ u32 *new_length, u16 domain_nr, u16 bus_nr)
+{
+ struct nvram_parser nvp;
+ u32 pad;
+ u32 token;
+ __le32 token_le;
+
+ if (brcmf_init_nvram_parser(&nvp, data, data_len) < 0)
+ return NULL;
+
+ while (nvp.pos < data_len) {
+ nvp.state = nv_parser_states[nvp.state](&nvp);
+ if (nvp.state == END)
+ break;
+ }
+ if (nvp.multi_dev_v1) {
+ nvp.boardrev_found = false;
+ brcmf_fw_strip_multi_v1(&nvp, domain_nr, bus_nr);
+ } else if (nvp.multi_dev_v2) {
+ nvp.boardrev_found = false;
+ brcmf_fw_strip_multi_v2(&nvp, domain_nr, bus_nr);
+ }
+
+ if (nvp.nvram_len == 0) {
+ kfree(nvp.nvram);
+ return NULL;
+ }
+
+ brcmf_fw_add_defaults(&nvp);
+
+ pad = nvp.nvram_len;
+ *new_length = roundup(nvp.nvram_len + 1, 4);
+ while (pad != *new_length) {
+ nvp.nvram[pad] = 0;
+ pad++;
+ }
+
+ token = *new_length / 4;
+ token = (~token << 16) | (token & 0x0000FFFF);
+ token_le = cpu_to_le32(token);
+
+ memcpy(&nvp.nvram[*new_length], &token_le, sizeof(token_le));
+ *new_length += sizeof(token_le);
+
+ return nvp.nvram;
+}
+
+void brcmf_fw_nvram_free(void *nvram)
+{
+ kfree(nvram);
+}
+
+struct brcmf_fw {
+ struct device *dev;
+ u16 flags;
+ const struct firmware *code;
+ const char *nvram_name;
+ u16 domain_nr;
+ u16 bus_nr;
+ void (*done)(struct device *dev, int err, const struct firmware *fw,
+ void *nvram_image, u32 nvram_len);
+};
+
+static void brcmf_fw_request_nvram_done(const struct firmware *fw, void *ctx)
+{
+ struct brcmf_fw *fwctx = ctx;
+ u32 nvram_length = 0;
+ void *nvram = NULL;
+ u8 *data = NULL;
+ size_t data_len;
+ bool raw_nvram;
+
+ brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(fwctx->dev));
+ if (fw && fw->data) {
+ data = (u8 *)fw->data;
+ data_len = fw->size;
+ raw_nvram = false;
+ } else {
+ data = bcm47xx_nvram_get_contents(&data_len);
+ if (!data && !(fwctx->flags & BRCMF_FW_REQ_NV_OPTIONAL))
+ goto fail;
+ raw_nvram = true;
+ }
+
+ if (data)
+ nvram = brcmf_fw_nvram_strip(data, data_len, &nvram_length,
+ fwctx->domain_nr, fwctx->bus_nr);
+
+ if (raw_nvram)
+ bcm47xx_nvram_release_contents(data);
+ release_firmware(fw);
+ if (!nvram && !(fwctx->flags & BRCMF_FW_REQ_NV_OPTIONAL))
+ goto fail;
+
+ fwctx->done(fwctx->dev, 0, fwctx->code, nvram, nvram_length);
+ kfree(fwctx);
+ return;
+
+fail:
+ brcmf_dbg(TRACE, "failed: dev=%s\n", dev_name(fwctx->dev));
+ release_firmware(fwctx->code);
+ fwctx->done(fwctx->dev, -ENOENT, NULL, NULL, 0);
+ kfree(fwctx);
+}
+
+static void brcmf_fw_request_code_done(const struct firmware *fw, void *ctx)
+{
+ struct brcmf_fw *fwctx = ctx;
+ int ret = 0;
+
+ brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(fwctx->dev));
+ if (!fw) {
+ ret = -ENOENT;
+ goto fail;
+ }
+ /* only requested code so done here */
+ if (!(fwctx->flags & BRCMF_FW_REQUEST_NVRAM))
+ goto done;
+
+ fwctx->code = fw;
+ ret = request_firmware_nowait(THIS_MODULE, true, fwctx->nvram_name,
+ fwctx->dev, GFP_KERNEL, fwctx,
+ brcmf_fw_request_nvram_done);
+
+ /* pass NULL to nvram callback for bcm47xx fallback */
+ if (ret)
+ brcmf_fw_request_nvram_done(NULL, fwctx);
+ return;
+
+fail:
+ brcmf_dbg(TRACE, "failed: dev=%s\n", dev_name(fwctx->dev));
+done:
+ fwctx->done(fwctx->dev, ret, fw, NULL, 0);
+ kfree(fwctx);
+}
+
+int brcmf_fw_get_firmwares_pcie(struct device *dev, u16 flags,
+ const char *code, const char *nvram,
+ void (*fw_cb)(struct device *dev, int err,
+ const struct firmware *fw,
+ void *nvram_image, u32 nvram_len),
+ u16 domain_nr, u16 bus_nr)
+{
+ struct brcmf_fw *fwctx;
+
+ brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(dev));
+ if (!fw_cb || !code)
+ return -EINVAL;
+
+ if ((flags & BRCMF_FW_REQUEST_NVRAM) && !nvram)
+ return -EINVAL;
+
+ fwctx = kzalloc(sizeof(*fwctx), GFP_KERNEL);
+ if (!fwctx)
+ return -ENOMEM;
+
+ fwctx->dev = dev;
+ fwctx->flags = flags;
+ fwctx->done = fw_cb;
+ if (flags & BRCMF_FW_REQUEST_NVRAM)
+ fwctx->nvram_name = nvram;
+ fwctx->domain_nr = domain_nr;
+ fwctx->bus_nr = bus_nr;
+
+ return request_firmware_nowait(THIS_MODULE, true, code, dev,
+ GFP_KERNEL, fwctx,
+ brcmf_fw_request_code_done);
+}
+
+int brcmf_fw_get_firmwares(struct device *dev, u16 flags,
+ const char *code, const char *nvram,
+ void (*fw_cb)(struct device *dev, int err,
+ const struct firmware *fw,
+ void *nvram_image, u32 nvram_len))
+{
+ return brcmf_fw_get_firmwares_pcie(dev, flags, code, nvram, fw_cb, 0,
+ 0);
+}
+
+int brcmf_fw_map_chip_to_name(u32 chip, u32 chiprev,
+ struct brcmf_firmware_mapping mapping_table[],
+ u32 table_size, char fw_name[BRCMF_FW_NAME_LEN],
+ char nvram_name[BRCMF_FW_NAME_LEN])
+{
+ u32 i;
+ char end;
+
+ for (i = 0; i < table_size; i++) {
+ if (mapping_table[i].chipid == chip &&
+ mapping_table[i].revmask & BIT(chiprev))
+ break;
+ }
+
+ if (i == table_size) {
+ brcmf_err("Unknown chipid %d [%d]\n", chip, chiprev);
+ return -ENODEV;
+ }
+
+ /* check if firmware path is provided by module parameter */
+ if (brcmf_mp_global.firmware_path[0] != '\0') {
+ strlcpy(fw_name, brcmf_mp_global.firmware_path,
+ BRCMF_FW_NAME_LEN);
+ if ((nvram_name) && (mapping_table[i].nvram))
+ strlcpy(nvram_name, brcmf_mp_global.firmware_path,
+ BRCMF_FW_NAME_LEN);
+
+ end = brcmf_mp_global.firmware_path[
+ strlen(brcmf_mp_global.firmware_path) - 1];
+ if (end != '/') {
+ strlcat(fw_name, "/", BRCMF_FW_NAME_LEN);
+ if ((nvram_name) && (mapping_table[i].nvram))
+ strlcat(nvram_name, "/", BRCMF_FW_NAME_LEN);
+ }
+ }
+ strlcat(fw_name, mapping_table[i].fw, BRCMF_FW_NAME_LEN);
+ if ((nvram_name) && (mapping_table[i].nvram))
+ strlcat(nvram_name, mapping_table[i].nvram, BRCMF_FW_NAME_LEN);
+
+ return 0;
+}
+
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.h
new file mode 100644
index 0000000..8fa4b7e
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2013 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#ifndef BRCMFMAC_FIRMWARE_H
+#define BRCMFMAC_FIRMWARE_H
+
+#define BRCMF_FW_REQUEST 0x000F
+#define BRCMF_FW_REQUEST_NVRAM 0x0001
+#define BRCMF_FW_REQ_FLAGS 0x00F0
+#define BRCMF_FW_REQ_NV_OPTIONAL 0x0010
+
+#define BRCMF_FW_NAME_LEN 320
+
+#define BRCMF_FW_DEFAULT_PATH "brcm/"
+
+/**
+ * struct brcmf_firmware_mapping - Used to map chipid/revmask to firmware
+ * filename and nvram filename. Each bus type implementation should create
+ * a table of firmware mappings (using the macros defined below).
+ *
+ * @chipid: ID of chip.
+ * @revmask: bitmask of revisions, e.g. 0x10 means rev 4 only, 0xf means rev 0-3
+ * @fw: name of the firmware file.
+ * @nvram: name of nvram file.
+ */
+struct brcmf_firmware_mapping {
+ u32 chipid;
+ u32 revmask;
+ const char *fw;
+ const char *nvram;
+};
+
+#define BRCMF_FW_NVRAM_DEF(fw_nvram_name, fw, nvram) \
+static const char BRCM_ ## fw_nvram_name ## _FIRMWARE_NAME[] = \
+ BRCMF_FW_DEFAULT_PATH fw; \
+static const char BRCM_ ## fw_nvram_name ## _NVRAM_NAME[] = \
+ BRCMF_FW_DEFAULT_PATH nvram; \
+MODULE_FIRMWARE(BRCMF_FW_DEFAULT_PATH fw);
+
+#define BRCMF_FW_DEF(fw_name, fw) \
+static const char BRCM_ ## fw_name ## _FIRMWARE_NAME[] = \
+ BRCMF_FW_DEFAULT_PATH fw; \
+MODULE_FIRMWARE(BRCMF_FW_DEFAULT_PATH fw) \
+
+#define BRCMF_FW_NVRAM_ENTRY(chipid, mask, name) \
+ { chipid, mask, \
+ BRCM_ ## name ## _FIRMWARE_NAME, BRCM_ ## name ## _NVRAM_NAME }
+
+#define BRCMF_FW_ENTRY(chipid, mask, name) \
+ { chipid, mask, BRCM_ ## name ## _FIRMWARE_NAME, NULL }
+
+int brcmf_fw_map_chip_to_name(u32 chip, u32 chiprev,
+ struct brcmf_firmware_mapping mapping_table[],
+ u32 table_size, char fw_name[BRCMF_FW_NAME_LEN],
+ char nvram_name[BRCMF_FW_NAME_LEN]);
+void brcmf_fw_nvram_free(void *nvram);
+/*
+ * Request firmware(s) asynchronously. When the asynchronous request
+ * fails it will not use the callback, but call device_release_driver()
+ * instead which will call the driver .remove() callback.
+ */
+int brcmf_fw_get_firmwares_pcie(struct device *dev, u16 flags,
+ const char *code, const char *nvram,
+ void (*fw_cb)(struct device *dev, int err,
+ const struct firmware *fw,
+ void *nvram_image, u32 nvram_len),
+ u16 domain_nr, u16 bus_nr);
+int brcmf_fw_get_firmwares(struct device *dev, u16 flags,
+ const char *code, const char *nvram,
+ void (*fw_cb)(struct device *dev, int err,
+ const struct firmware *fw,
+ void *nvram_image, u32 nvram_len));
+
+#endif /* BRCMFMAC_FIRMWARE_H */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.c
new file mode 100644
index 0000000..d0b738d
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.c
@@ -0,0 +1,519 @@
+/* Copyright (c) 2014 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+
+#include <linux/types.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <brcmu_utils.h>
+
+#include "core.h"
+#include "debug.h"
+#include "bus.h"
+#include "proto.h"
+#include "flowring.h"
+#include "msgbuf.h"
+#include "common.h"
+
+
+#define BRCMF_FLOWRING_HIGH 1024
+#define BRCMF_FLOWRING_LOW (BRCMF_FLOWRING_HIGH - 256)
+#define BRCMF_FLOWRING_INVALID_IFIDX 0xff
+
+#define BRCMF_FLOWRING_HASH_AP(da, fifo, ifidx) (da[5] * 2 + fifo + ifidx * 16)
+#define BRCMF_FLOWRING_HASH_STA(fifo, ifidx) (fifo + ifidx * 16)
+
+static const u8 brcmf_flowring_prio2fifo[] = {
+ 1,
+ 0,
+ 0,
+ 1,
+ 2,
+ 2,
+ 3,
+ 3
+};
+
+
+static bool
+brcmf_flowring_is_tdls_mac(struct brcmf_flowring *flow, u8 mac[ETH_ALEN])
+{
+ struct brcmf_flowring_tdls_entry *search;
+
+ search = flow->tdls_entry;
+
+ while (search) {
+ if (memcmp(search->mac, mac, ETH_ALEN) == 0)
+ return true;
+ search = search->next;
+ }
+
+ return false;
+}
+
+
+u32 brcmf_flowring_lookup(struct brcmf_flowring *flow, u8 da[ETH_ALEN],
+ u8 prio, u8 ifidx)
+{
+ struct brcmf_flowring_hash *hash;
+ u16 hash_idx;
+ u32 i;
+ bool found;
+ bool sta;
+ u8 fifo;
+ u8 *mac;
+
+ fifo = brcmf_flowring_prio2fifo[prio];
+ sta = (flow->addr_mode[ifidx] == ADDR_INDIRECT);
+ mac = da;
+ if ((!sta) && (is_multicast_ether_addr(da))) {
+ mac = (u8 *)ALLFFMAC;
+ fifo = 0;
+ }
+ if ((sta) && (flow->tdls_active) &&
+ (brcmf_flowring_is_tdls_mac(flow, da))) {
+ sta = false;
+ }
+ hash_idx = sta ? BRCMF_FLOWRING_HASH_STA(fifo, ifidx) :
+ BRCMF_FLOWRING_HASH_AP(mac, fifo, ifidx);
+ hash_idx &= (BRCMF_FLOWRING_HASHSIZE - 1);
+ found = false;
+ hash = flow->hash;
+ for (i = 0; i < BRCMF_FLOWRING_HASHSIZE; i++) {
+ if ((sta || (memcmp(hash[hash_idx].mac, mac, ETH_ALEN) == 0)) &&
+ (hash[hash_idx].fifo == fifo) &&
+ (hash[hash_idx].ifidx == ifidx)) {
+ found = true;
+ break;
+ }
+ hash_idx++;
+ hash_idx &= (BRCMF_FLOWRING_HASHSIZE - 1);
+ }
+ if (found)
+ return hash[hash_idx].flowid;
+
+ return BRCMF_FLOWRING_INVALID_ID;
+}
+
+
+u32 brcmf_flowring_create(struct brcmf_flowring *flow, u8 da[ETH_ALEN],
+ u8 prio, u8 ifidx)
+{
+ struct brcmf_flowring_ring *ring;
+ struct brcmf_flowring_hash *hash;
+ u16 hash_idx;
+ u32 i;
+ bool found;
+ u8 fifo;
+ bool sta;
+ u8 *mac;
+
+ fifo = brcmf_flowring_prio2fifo[prio];
+ sta = (flow->addr_mode[ifidx] == ADDR_INDIRECT);
+ mac = da;
+ if ((!sta) && (is_multicast_ether_addr(da))) {
+ mac = (u8 *)ALLFFMAC;
+ fifo = 0;
+ }
+ if ((sta) && (flow->tdls_active) &&
+ (brcmf_flowring_is_tdls_mac(flow, da))) {
+ sta = false;
+ }
+ hash_idx = sta ? BRCMF_FLOWRING_HASH_STA(fifo, ifidx) :
+ BRCMF_FLOWRING_HASH_AP(mac, fifo, ifidx);
+ hash_idx &= (BRCMF_FLOWRING_HASHSIZE - 1);
+ found = false;
+ hash = flow->hash;
+ for (i = 0; i < BRCMF_FLOWRING_HASHSIZE; i++) {
+ if ((hash[hash_idx].ifidx == BRCMF_FLOWRING_INVALID_IFIDX) &&
+ (is_zero_ether_addr(hash[hash_idx].mac))) {
+ found = true;
+ break;
+ }
+ hash_idx++;
+ hash_idx &= (BRCMF_FLOWRING_HASHSIZE - 1);
+ }
+ if (found) {
+ for (i = 0; i < flow->nrofrings; i++) {
+ if (flow->rings[i] == NULL)
+ break;
+ }
+ if (i == flow->nrofrings)
+ return -ENOMEM;
+
+ ring = kzalloc(sizeof(*ring), GFP_ATOMIC);
+ if (!ring)
+ return -ENOMEM;
+
+ memcpy(hash[hash_idx].mac, mac, ETH_ALEN);
+ hash[hash_idx].fifo = fifo;
+ hash[hash_idx].ifidx = ifidx;
+ hash[hash_idx].flowid = i;
+
+ ring->hash_id = hash_idx;
+ ring->status = RING_CLOSED;
+ skb_queue_head_init(&ring->skblist);
+ flow->rings[i] = ring;
+
+ return i;
+ }
+ return BRCMF_FLOWRING_INVALID_ID;
+}
+
+
+u8 brcmf_flowring_tid(struct brcmf_flowring *flow, u16 flowid)
+{
+ struct brcmf_flowring_ring *ring;
+
+ ring = flow->rings[flowid];
+
+ return flow->hash[ring->hash_id].fifo;
+}
+
+
+static void brcmf_flowring_block(struct brcmf_flowring *flow, u16 flowid,
+ bool blocked)
+{
+ struct brcmf_flowring_ring *ring;
+ struct brcmf_bus *bus_if;
+ struct brcmf_pub *drvr;
+ struct brcmf_if *ifp;
+ bool currently_blocked;
+ int i;
+ u8 ifidx;
+ unsigned long flags;
+
+ spin_lock_irqsave(&flow->block_lock, flags);
+
+ ring = flow->rings[flowid];
+ if (ring->blocked == blocked) {
+ spin_unlock_irqrestore(&flow->block_lock, flags);
+ return;
+ }
+ ifidx = brcmf_flowring_ifidx_get(flow, flowid);
+
+ currently_blocked = false;
+ for (i = 0; i < flow->nrofrings; i++) {
+ if ((flow->rings[i]) && (i != flowid)) {
+ ring = flow->rings[i];
+ if ((ring->status == RING_OPEN) &&
+ (brcmf_flowring_ifidx_get(flow, i) == ifidx)) {
+ if (ring->blocked) {
+ currently_blocked = true;
+ break;
+ }
+ }
+ }
+ }
+ flow->rings[flowid]->blocked = blocked;
+ if (currently_blocked) {
+ spin_unlock_irqrestore(&flow->block_lock, flags);
+ return;
+ }
+
+ bus_if = dev_get_drvdata(flow->dev);
+ drvr = bus_if->drvr;
+ ifp = brcmf_get_ifp(drvr, ifidx);
+ brcmf_txflowblock_if(ifp, BRCMF_NETIF_STOP_REASON_FLOW, blocked);
+
+ spin_unlock_irqrestore(&flow->block_lock, flags);
+}
+
+
+void brcmf_flowring_delete(struct brcmf_flowring *flow, u16 flowid)
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(flow->dev);
+ struct brcmf_flowring_ring *ring;
+ struct brcmf_if *ifp;
+ u16 hash_idx;
+ u8 ifidx;
+ struct sk_buff *skb;
+
+ ring = flow->rings[flowid];
+ if (!ring)
+ return;
+
+ ifidx = brcmf_flowring_ifidx_get(flow, flowid);
+ ifp = brcmf_get_ifp(bus_if->drvr, ifidx);
+
+ brcmf_flowring_block(flow, flowid, false);
+ hash_idx = ring->hash_id;
+ flow->hash[hash_idx].ifidx = BRCMF_FLOWRING_INVALID_IFIDX;
+ eth_zero_addr(flow->hash[hash_idx].mac);
+ flow->rings[flowid] = NULL;
+
+ skb = skb_dequeue(&ring->skblist);
+ while (skb) {
+ brcmf_txfinalize(ifp, skb, false);
+ skb = skb_dequeue(&ring->skblist);
+ }
+
+ kfree(ring);
+}
+
+
+u32 brcmf_flowring_enqueue(struct brcmf_flowring *flow, u16 flowid,
+ struct sk_buff *skb)
+{
+ struct brcmf_flowring_ring *ring;
+
+ ring = flow->rings[flowid];
+
+ skb_queue_tail(&ring->skblist, skb);
+
+ if (!ring->blocked &&
+ (skb_queue_len(&ring->skblist) > BRCMF_FLOWRING_HIGH)) {
+ brcmf_flowring_block(flow, flowid, true);
+ brcmf_dbg(MSGBUF, "Flowcontrol: BLOCK for ring %d\n", flowid);
+ /* To prevent (work around) possible race condition, check
+ * queue len again. It is also possible to use locking to
+ * protect, but that is undesirable for every enqueue and
+ * dequeue. This simple check will solve a possible race
+ * condition if it occurs.
+ */
+ if (skb_queue_len(&ring->skblist) < BRCMF_FLOWRING_LOW)
+ brcmf_flowring_block(flow, flowid, false);
+ }
+ return skb_queue_len(&ring->skblist);
+}
+
+
+struct sk_buff *brcmf_flowring_dequeue(struct brcmf_flowring *flow, u16 flowid)
+{
+ struct brcmf_flowring_ring *ring;
+ struct sk_buff *skb;
+
+ ring = flow->rings[flowid];
+ if (ring->status != RING_OPEN)
+ return NULL;
+
+ skb = skb_dequeue(&ring->skblist);
+
+ if (ring->blocked &&
+ (skb_queue_len(&ring->skblist) < BRCMF_FLOWRING_LOW)) {
+ brcmf_flowring_block(flow, flowid, false);
+ brcmf_dbg(MSGBUF, "Flowcontrol: OPEN for ring %d\n", flowid);
+ }
+
+ return skb;
+}
+
+
+void brcmf_flowring_reinsert(struct brcmf_flowring *flow, u16 flowid,
+ struct sk_buff *skb)
+{
+ struct brcmf_flowring_ring *ring;
+
+ ring = flow->rings[flowid];
+
+ skb_queue_head(&ring->skblist, skb);
+}
+
+
+u32 brcmf_flowring_qlen(struct brcmf_flowring *flow, u16 flowid)
+{
+ struct brcmf_flowring_ring *ring;
+
+ ring = flow->rings[flowid];
+ if (!ring)
+ return 0;
+
+ if (ring->status != RING_OPEN)
+ return 0;
+
+ return skb_queue_len(&ring->skblist);
+}
+
+
+void brcmf_flowring_open(struct brcmf_flowring *flow, u16 flowid)
+{
+ struct brcmf_flowring_ring *ring;
+
+ ring = flow->rings[flowid];
+ if (!ring) {
+ brcmf_err("Ring NULL, for flowid %d\n", flowid);
+ return;
+ }
+
+ ring->status = RING_OPEN;
+}
+
+
+u8 brcmf_flowring_ifidx_get(struct brcmf_flowring *flow, u16 flowid)
+{
+ struct brcmf_flowring_ring *ring;
+ u16 hash_idx;
+
+ ring = flow->rings[flowid];
+ hash_idx = ring->hash_id;
+
+ return flow->hash[hash_idx].ifidx;
+}
+
+
+struct brcmf_flowring *brcmf_flowring_attach(struct device *dev, u16 nrofrings)
+{
+ struct brcmf_flowring *flow;
+ u32 i;
+
+ flow = kzalloc(sizeof(*flow), GFP_KERNEL);
+ if (flow) {
+ flow->dev = dev;
+ flow->nrofrings = nrofrings;
+ spin_lock_init(&flow->block_lock);
+ for (i = 0; i < ARRAY_SIZE(flow->addr_mode); i++)
+ flow->addr_mode[i] = ADDR_INDIRECT;
+ for (i = 0; i < ARRAY_SIZE(flow->hash); i++)
+ flow->hash[i].ifidx = BRCMF_FLOWRING_INVALID_IFIDX;
+ flow->rings = kcalloc(nrofrings, sizeof(*flow->rings),
+ GFP_KERNEL);
+ if (!flow->rings) {
+ kfree(flow);
+ flow = NULL;
+ }
+ }
+
+ return flow;
+}
+
+
+void brcmf_flowring_detach(struct brcmf_flowring *flow)
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(flow->dev);
+ struct brcmf_pub *drvr = bus_if->drvr;
+ struct brcmf_flowring_tdls_entry *search;
+ struct brcmf_flowring_tdls_entry *remove;
+ u16 flowid;
+
+ for (flowid = 0; flowid < flow->nrofrings; flowid++) {
+ if (flow->rings[flowid])
+ brcmf_msgbuf_delete_flowring(drvr, flowid);
+ }
+
+ search = flow->tdls_entry;
+ while (search) {
+ remove = search;
+ search = search->next;
+ kfree(remove);
+ }
+ kfree(flow->rings);
+ kfree(flow);
+}
+
+
+void brcmf_flowring_configure_addr_mode(struct brcmf_flowring *flow, int ifidx,
+ enum proto_addr_mode addr_mode)
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(flow->dev);
+ struct brcmf_pub *drvr = bus_if->drvr;
+ u32 i;
+ u16 flowid;
+
+ if (flow->addr_mode[ifidx] != addr_mode) {
+ for (i = 0; i < ARRAY_SIZE(flow->hash); i++) {
+ if (flow->hash[i].ifidx == ifidx) {
+ flowid = flow->hash[i].flowid;
+ if (flow->rings[flowid]->status != RING_OPEN)
+ continue;
+ flow->rings[flowid]->status = RING_CLOSING;
+ brcmf_msgbuf_delete_flowring(drvr, flowid);
+ }
+ }
+ flow->addr_mode[ifidx] = addr_mode;
+ }
+}
+
+
+void brcmf_flowring_delete_peer(struct brcmf_flowring *flow, int ifidx,
+ u8 peer[ETH_ALEN])
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(flow->dev);
+ struct brcmf_pub *drvr = bus_if->drvr;
+ struct brcmf_flowring_hash *hash;
+ struct brcmf_flowring_tdls_entry *prev;
+ struct brcmf_flowring_tdls_entry *search;
+ u32 i;
+ u16 flowid;
+ bool sta;
+
+ sta = (flow->addr_mode[ifidx] == ADDR_INDIRECT);
+
+ search = flow->tdls_entry;
+ prev = NULL;
+ while (search) {
+ if (memcmp(search->mac, peer, ETH_ALEN) == 0) {
+ sta = false;
+ break;
+ }
+ prev = search;
+ search = search->next;
+ }
+
+ hash = flow->hash;
+ for (i = 0; i < BRCMF_FLOWRING_HASHSIZE; i++) {
+ if ((sta || (memcmp(hash[i].mac, peer, ETH_ALEN) == 0)) &&
+ (hash[i].ifidx == ifidx)) {
+ flowid = flow->hash[i].flowid;
+ if (flow->rings[flowid]->status == RING_OPEN) {
+ flow->rings[flowid]->status = RING_CLOSING;
+ brcmf_msgbuf_delete_flowring(drvr, flowid);
+ }
+ }
+ }
+
+ if (search) {
+ if (prev)
+ prev->next = search->next;
+ else
+ flow->tdls_entry = search->next;
+ kfree(search);
+ if (flow->tdls_entry == NULL)
+ flow->tdls_active = false;
+ }
+}
+
+
+void brcmf_flowring_add_tdls_peer(struct brcmf_flowring *flow, int ifidx,
+ u8 peer[ETH_ALEN])
+{
+ struct brcmf_flowring_tdls_entry *tdls_entry;
+ struct brcmf_flowring_tdls_entry *search;
+
+ tdls_entry = kzalloc(sizeof(*tdls_entry), GFP_ATOMIC);
+ if (tdls_entry == NULL)
+ return;
+
+ memcpy(tdls_entry->mac, peer, ETH_ALEN);
+ tdls_entry->next = NULL;
+ if (flow->tdls_entry == NULL) {
+ flow->tdls_entry = tdls_entry;
+ } else {
+ search = flow->tdls_entry;
+ if (memcmp(search->mac, peer, ETH_ALEN) == 0)
+ goto free_entry;
+ while (search->next) {
+ search = search->next;
+ if (memcmp(search->mac, peer, ETH_ALEN) == 0)
+ goto free_entry;
+ }
+ search->next = tdls_entry;
+ }
+
+ flow->tdls_active = true;
+ return;
+
+free_entry:
+ kfree(tdls_entry);
+}
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.h
new file mode 100644
index 0000000..068e68d
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.h
@@ -0,0 +1,84 @@
+/* Copyright (c) 2014 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#ifndef BRCMFMAC_FLOWRING_H
+#define BRCMFMAC_FLOWRING_H
+
+
+#define BRCMF_FLOWRING_HASHSIZE 512 /* has to be 2^x */
+#define BRCMF_FLOWRING_INVALID_ID 0xFFFFFFFF
+
+
+struct brcmf_flowring_hash {
+ u8 mac[ETH_ALEN];
+ u8 fifo;
+ u8 ifidx;
+ u16 flowid;
+};
+
+enum ring_status {
+ RING_CLOSED,
+ RING_CLOSING,
+ RING_OPEN
+};
+
+struct brcmf_flowring_ring {
+ u16 hash_id;
+ bool blocked;
+ enum ring_status status;
+ struct sk_buff_head skblist;
+};
+
+struct brcmf_flowring_tdls_entry {
+ u8 mac[ETH_ALEN];
+ struct brcmf_flowring_tdls_entry *next;
+};
+
+struct brcmf_flowring {
+ struct device *dev;
+ struct brcmf_flowring_hash hash[BRCMF_FLOWRING_HASHSIZE];
+ struct brcmf_flowring_ring **rings;
+ spinlock_t block_lock;
+ enum proto_addr_mode addr_mode[BRCMF_MAX_IFS];
+ u16 nrofrings;
+ bool tdls_active;
+ struct brcmf_flowring_tdls_entry *tdls_entry;
+};
+
+
+u32 brcmf_flowring_lookup(struct brcmf_flowring *flow, u8 da[ETH_ALEN],
+ u8 prio, u8 ifidx);
+u32 brcmf_flowring_create(struct brcmf_flowring *flow, u8 da[ETH_ALEN],
+ u8 prio, u8 ifidx);
+void brcmf_flowring_delete(struct brcmf_flowring *flow, u16 flowid);
+void brcmf_flowring_open(struct brcmf_flowring *flow, u16 flowid);
+u8 brcmf_flowring_tid(struct brcmf_flowring *flow, u16 flowid);
+u32 brcmf_flowring_enqueue(struct brcmf_flowring *flow, u16 flowid,
+ struct sk_buff *skb);
+struct sk_buff *brcmf_flowring_dequeue(struct brcmf_flowring *flow, u16 flowid);
+void brcmf_flowring_reinsert(struct brcmf_flowring *flow, u16 flowid,
+ struct sk_buff *skb);
+u32 brcmf_flowring_qlen(struct brcmf_flowring *flow, u16 flowid);
+u8 brcmf_flowring_ifidx_get(struct brcmf_flowring *flow, u16 flowid);
+struct brcmf_flowring *brcmf_flowring_attach(struct device *dev, u16 nrofrings);
+void brcmf_flowring_detach(struct brcmf_flowring *flow);
+void brcmf_flowring_configure_addr_mode(struct brcmf_flowring *flow, int ifidx,
+ enum proto_addr_mode addr_mode);
+void brcmf_flowring_delete_peer(struct brcmf_flowring *flow, int ifidx,
+ u8 peer[ETH_ALEN]);
+void brcmf_flowring_add_tdls_peer(struct brcmf_flowring *flow, int ifidx,
+ u8 peer[ETH_ALEN]);
+
+
+#endif /* BRCMFMAC_FLOWRING_H */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c
new file mode 100644
index 0000000..79db122
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c
@@ -0,0 +1,552 @@
+/*
+ * Copyright (c) 2012 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <linux/netdevice.h>
+
+#include "brcmu_wifi.h"
+#include "brcmu_utils.h"
+
+#include "cfg80211.h"
+#include "core.h"
+#include "debug.h"
+#include "tracepoint.h"
+#include "fweh.h"
+#include "fwil.h"
+#include "proto.h"
+
+/**
+ * struct brcmf_fweh_queue_item - event item on event queue.
+ *
+ * @q: list element for queuing.
+ * @code: event code.
+ * @ifidx: interface index related to this event.
+ * @ifaddr: ethernet address for interface.
+ * @emsg: common parameters of the firmware event message.
+ * @data: event specific data part of the firmware event.
+ */
+struct brcmf_fweh_queue_item {
+ struct list_head q;
+ enum brcmf_fweh_event_code code;
+ u8 ifidx;
+ u8 ifaddr[ETH_ALEN];
+ struct brcmf_event_msg_be emsg;
+ u32 datalen;
+ u8 data[0];
+};
+
+/**
+ * struct brcmf_fweh_event_name - code, name mapping entry.
+ */
+struct brcmf_fweh_event_name {
+ enum brcmf_fweh_event_code code;
+ const char *name;
+};
+
+#ifdef DEBUG
+#define BRCMF_ENUM_DEF(id, val) \
+ { val, #id },
+
+/* array for mapping code to event name */
+static struct brcmf_fweh_event_name fweh_event_names[] = {
+ BRCMF_FWEH_EVENT_ENUM_DEFLIST
+};
+#undef BRCMF_ENUM_DEF
+
+/**
+ * brcmf_fweh_event_name() - returns name for given event code.
+ *
+ * @code: code to lookup.
+ */
+const char *brcmf_fweh_event_name(enum brcmf_fweh_event_code code)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(fweh_event_names); i++) {
+ if (fweh_event_names[i].code == code)
+ return fweh_event_names[i].name;
+ }
+ return "unknown";
+}
+#else
+const char *brcmf_fweh_event_name(enum brcmf_fweh_event_code code)
+{
+ return "nodebug";
+}
+#endif
+
+/**
+ * brcmf_fweh_queue_event() - create and queue event.
+ *
+ * @fweh: firmware event handling info.
+ * @event: event queue entry.
+ */
+static void brcmf_fweh_queue_event(struct brcmf_fweh_info *fweh,
+ struct brcmf_fweh_queue_item *event)
+{
+ ulong flags;
+
+ spin_lock_irqsave(&fweh->evt_q_lock, flags);
+ list_add_tail(&event->q, &fweh->event_q);
+ spin_unlock_irqrestore(&fweh->evt_q_lock, flags);
+ schedule_work(&fweh->event_work);
+}
+
+static int brcmf_fweh_call_event_handler(struct brcmf_if *ifp,
+ enum brcmf_fweh_event_code code,
+ struct brcmf_event_msg *emsg,
+ void *data)
+{
+ struct brcmf_fweh_info *fweh;
+ int err = -EINVAL;
+
+ if (ifp) {
+ fweh = &ifp->drvr->fweh;
+
+ /* handle the event if valid interface and handler */
+ if (fweh->evt_handler[code])
+ err = fweh->evt_handler[code](ifp, emsg, data);
+ else
+ brcmf_err("unhandled event %d ignored\n", code);
+ } else {
+ brcmf_err("no interface object\n");
+ }
+ return err;
+}
+
+/**
+ * brcmf_fweh_handle_if_event() - handle IF event.
+ *
+ * @drvr: driver information object.
+ * @item: queue entry.
+ * @ifpp: interface object (may change upon ADD action).
+ */
+static void brcmf_fweh_handle_if_event(struct brcmf_pub *drvr,
+ struct brcmf_event_msg *emsg,
+ void *data)
+{
+ struct brcmf_if_event *ifevent = data;
+ struct brcmf_if *ifp;
+ bool is_p2pdev;
+ int err = 0;
+
+ brcmf_dbg(EVENT, "action: %u ifidx: %u bsscfgidx: %u flags: %u role: %u\n",
+ ifevent->action, ifevent->ifidx, ifevent->bsscfgidx,
+ ifevent->flags, ifevent->role);
+
+ /* The P2P Device interface event must not be ignored contrary to what
+ * firmware tells us. Older firmware uses p2p noif, with sta role.
+ * This should be accepted when p2pdev_setup is ongoing. TDLS setup will
+ * use the same ifevent and should be ignored.
+ */
+ is_p2pdev = ((ifevent->flags & BRCMF_E_IF_FLAG_NOIF) &&
+ (ifevent->role == BRCMF_E_IF_ROLE_P2P_CLIENT ||
+ ((ifevent->role == BRCMF_E_IF_ROLE_STA) &&
+ (drvr->fweh.p2pdev_setup_ongoing))));
+ if (!is_p2pdev && (ifevent->flags & BRCMF_E_IF_FLAG_NOIF)) {
+ brcmf_dbg(EVENT, "event can be ignored\n");
+ return;
+ }
+ if (ifevent->ifidx >= BRCMF_MAX_IFS) {
+ brcmf_err("invalid interface index: %u\n", ifevent->ifidx);
+ return;
+ }
+
+ ifp = drvr->iflist[ifevent->bsscfgidx];
+
+ if (ifevent->action == BRCMF_E_IF_ADD) {
+ brcmf_dbg(EVENT, "adding %s (%pM)\n", emsg->ifname,
+ emsg->addr);
+ ifp = brcmf_add_if(drvr, ifevent->bsscfgidx, ifevent->ifidx,
+ is_p2pdev, emsg->ifname, emsg->addr);
+ if (IS_ERR(ifp))
+ return;
+ if (!is_p2pdev)
+ brcmf_proto_add_if(drvr, ifp);
+ if (!drvr->fweh.evt_handler[BRCMF_E_IF])
+ if (brcmf_net_attach(ifp, false) < 0)
+ return;
+ }
+
+ if (ifp && ifevent->action == BRCMF_E_IF_CHANGE)
+ brcmf_proto_reset_if(drvr, ifp);
+
+ err = brcmf_fweh_call_event_handler(ifp, emsg->event_code, emsg, data);
+
+ if (ifp && ifevent->action == BRCMF_E_IF_DEL) {
+ bool armed = brcmf_cfg80211_vif_event_armed(drvr->config);
+
+ /* Default handling in case no-one waits for this event */
+ if (!armed)
+ brcmf_remove_interface(ifp, false);
+ }
+}
+
+/**
+ * brcmf_fweh_dequeue_event() - get event from the queue.
+ *
+ * @fweh: firmware event handling info.
+ */
+static struct brcmf_fweh_queue_item *
+brcmf_fweh_dequeue_event(struct brcmf_fweh_info *fweh)
+{
+ struct brcmf_fweh_queue_item *event = NULL;
+ ulong flags;
+
+ spin_lock_irqsave(&fweh->evt_q_lock, flags);
+ if (!list_empty(&fweh->event_q)) {
+ event = list_first_entry(&fweh->event_q,
+ struct brcmf_fweh_queue_item, q);
+ list_del(&event->q);
+ }
+ spin_unlock_irqrestore(&fweh->evt_q_lock, flags);
+
+ return event;
+}
+
+/**
+ * brcmf_fweh_event_worker() - firmware event worker.
+ *
+ * @work: worker object.
+ */
+static void brcmf_fweh_event_worker(struct work_struct *work)
+{
+ struct brcmf_pub *drvr;
+ struct brcmf_if *ifp;
+ struct brcmf_fweh_info *fweh;
+ struct brcmf_fweh_queue_item *event;
+ int err = 0;
+ struct brcmf_event_msg_be *emsg_be;
+ struct brcmf_event_msg emsg;
+
+ fweh = container_of(work, struct brcmf_fweh_info, event_work);
+ drvr = container_of(fweh, struct brcmf_pub, fweh);
+
+ while ((event = brcmf_fweh_dequeue_event(fweh))) {
+ if (event->code == BRCMF_E_ESCAN_RESULT)
+ brcmf_dbg(EVENT, "event %s (%u) ifidx %u bsscfg %u addr %pM\n",
+ brcmf_fweh_event_name(event->code), event->code,
+ event->emsg.ifidx, event->emsg.bsscfgidx,
+ event->emsg.addr);
+ else
+ brcmf_dbg(INFO, "event %s (%u) ifidx %u bsscfg %u addr %pM\n",
+ brcmf_fweh_event_name(event->code), event->code,
+ event->emsg.ifidx, event->emsg.bsscfgidx,
+ event->emsg.addr);
+
+ /* convert event message */
+ emsg_be = &event->emsg;
+ emsg.version = be16_to_cpu(emsg_be->version);
+ emsg.flags = be16_to_cpu(emsg_be->flags);
+ emsg.event_code = event->code;
+ emsg.status = be32_to_cpu(emsg_be->status);
+ emsg.reason = be32_to_cpu(emsg_be->reason);
+ emsg.auth_type = be32_to_cpu(emsg_be->auth_type);
+ emsg.datalen = be32_to_cpu(emsg_be->datalen);
+ memcpy(emsg.addr, emsg_be->addr, ETH_ALEN);
+ memcpy(emsg.ifname, emsg_be->ifname, sizeof(emsg.ifname));
+ emsg.ifidx = emsg_be->ifidx;
+ emsg.bsscfgidx = emsg_be->bsscfgidx;
+
+ if (event->code == BRCMF_E_ESCAN_RESULT)
+ brcmf_dbg(EVENT, " version %u flags %u status %u reason %u\n",
+ emsg.version, emsg.flags, emsg.status, emsg.reason);
+ else
+ brcmf_dbg(INFO, " version %u flags %u status %u reason %u\n",
+ emsg.version, emsg.flags, emsg.status, emsg.reason);
+
+ brcmf_dbg_hex_dump(BRCMF_EVENT_ON(), event->data,
+ min_t(u32, emsg.datalen, 64),
+ "event payload, len=%d\n", emsg.datalen);
+ if (emsg.datalen > event->datalen) {
+ brcmf_err("event invalid length header=%d, msg=%d\n",
+ event->datalen, emsg.datalen);
+ goto event_free;
+ }
+
+ /* special handling of interface event */
+ if (event->code == BRCMF_E_IF) {
+ brcmf_fweh_handle_if_event(drvr, &emsg, event->data);
+ goto event_free;
+ }
+
+ if (event->code == BRCMF_E_TDLS_PEER_EVENT)
+ ifp = drvr->iflist[0];
+ else
+ ifp = drvr->iflist[emsg.bsscfgidx];
+ err = brcmf_fweh_call_event_handler(ifp, event->code, &emsg,
+ event->data);
+ if (err) {
+ brcmf_err("event handler failed (%d)\n",
+ event->code);
+ err = 0;
+ }
+event_free:
+ kfree(event);
+ }
+}
+
+/**
+ * brcmf_fweh_p2pdev_setup() - P2P device setup ongoing (or not).
+ *
+ * @ifp: ifp on which setup is taking place or finished.
+ * @ongoing: p2p device setup in progress (or not).
+ */
+void brcmf_fweh_p2pdev_setup(struct brcmf_if *ifp, bool ongoing)
+{
+ ifp->drvr->fweh.p2pdev_setup_ongoing = ongoing;
+}
+
+/**
+ * brcmf_fweh_attach() - initialize firmware event handling.
+ *
+ * @drvr: driver information object.
+ */
+void brcmf_fweh_attach(struct brcmf_pub *drvr)
+{
+ struct brcmf_fweh_info *fweh = &drvr->fweh;
+ INIT_WORK(&fweh->event_work, brcmf_fweh_event_worker);
+ spin_lock_init(&fweh->evt_q_lock);
+ INIT_LIST_HEAD(&fweh->event_q);
+}
+
+/**
+ * brcmf_fweh_detach() - cleanup firmware event handling.
+ *
+ * @drvr: driver information object.
+ */
+void brcmf_fweh_detach(struct brcmf_pub *drvr)
+{
+ struct brcmf_fweh_info *fweh = &drvr->fweh;
+ struct brcmf_if *ifp = brcmf_get_ifp(drvr, 0);
+ s8 eventmask[BRCMF_EVENTING_MASK_LEN];
+
+ if (ifp) {
+ /* clear all events */
+ memset(eventmask, 0, BRCMF_EVENTING_MASK_LEN);
+ (void)brcmf_fil_iovar_data_set(ifp, "event_msgs",
+ eventmask,
+ BRCMF_EVENTING_MASK_LEN);
+ }
+ /* cancel the worker */
+ cancel_work_sync(&fweh->event_work);
+ WARN_ON(!list_empty(&fweh->event_q));
+ memset(fweh->evt_handler, 0, sizeof(fweh->evt_handler));
+}
+
+/**
+ * brcmf_fweh_register() - register handler for given event code.
+ *
+ * @drvr: driver information object.
+ * @code: event code.
+ * @handler: handler for the given event code.
+ */
+int brcmf_fweh_register(struct brcmf_pub *drvr, enum brcmf_fweh_event_code code,
+ brcmf_fweh_handler_t handler)
+{
+ if (drvr->fweh.evt_handler[code]) {
+ brcmf_err("event code %d already registered\n", code);
+ return -ENOSPC;
+ }
+ drvr->fweh.evt_handler[code] = handler;
+ brcmf_dbg(TRACE, "event handler registered for %s\n",
+ brcmf_fweh_event_name(code));
+ return 0;
+}
+
+/**
+ * brcmf_fweh_unregister() - remove handler for given code.
+ *
+ * @drvr: driver information object.
+ * @code: event code.
+ */
+void brcmf_fweh_unregister(struct brcmf_pub *drvr,
+ enum brcmf_fweh_event_code code)
+{
+ brcmf_dbg(TRACE, "event handler cleared for %s\n",
+ brcmf_fweh_event_name(code));
+ drvr->fweh.evt_handler[code] = NULL;
+}
+
+typedef enum event_msgs_ext_command {
+ EVENTMSGS_NONE = 0,
+ EVENTMSGS_SET_BIT = 1,
+ EVENTMSGS_RESET_BIT = 2,
+ EVENTMSGS_SET_MASK = 3
+} event_msgs_ext_command_t;
+
+#define EVENTMSGS_VER 1
+#define EVENTMSGS_EXT_STRUCT_SIZE offsetof(eventmsgs_ext_t, mask[0])
+
+/* len - for SET it would be mask size from the application to the firmware
+ * for GET it would be actual firmware mask size
+ * maxgetsize - is only used for GET. indicate max mask size that the
+ * application can read from the firmware
+ */
+typedef struct eventmsgs_ext
+{
+ u8 ver;
+ u8 command;
+ u8 len;
+ u8 maxgetsize;
+ u8 mask[1];
+} eventmsgs_ext_t;
+
+/**
+ * brcmf_fweh_activate_events() - enables firmware events registered.
+ *
+ * @ifp: primary interface object.
+ */
+int brcmf_fweh_activate_events(struct brcmf_if *ifp)
+{
+ int i, err, ext = 0;
+ s8 buf[EVENTMSGS_EXT_STRUCT_SIZE+BRCMF_EVENTING_MASK_LEN], *eventmask;
+ eventmsgs_ext_t *eventmask_msg;
+
+ memset(buf, 0, sizeof(buf));
+ eventmask_msg = (eventmsgs_ext_t *)buf;
+ eventmask = buf + EVENTMSGS_EXT_STRUCT_SIZE;
+ for (i = 0; i < BRCMF_E_LAST; i++) {
+ if (ifp->drvr->fweh.evt_handler[i]) {
+ brcmf_dbg(EVENT, "enable event %s\n",
+ brcmf_fweh_event_name(i));
+ setbit(eventmask, i);
+ if (i > 128)
+ ext = 1;
+ }
+ }
+
+ /* want to handle IF event as well */
+ brcmf_dbg(EVENT, "enable event IF\n");
+ setbit(eventmask, BRCMF_E_IF);
+
+ err = brcmf_fil_iovar_data_set(ifp, "event_msgs",
+ eventmask, BRCMF_EVENTING_MASK_LEN);
+ if (err)
+ brcmf_err("Set event_msgs error (%d)\n", err);
+
+ if (ext) {
+ /* Write updated Event mask */
+ eventmask_msg->ver = EVENTMSGS_VER;
+ eventmask_msg->command = EVENTMSGS_SET_MASK;
+ eventmask_msg->len = BRCMF_EVENTING_MASK_LEN;
+ err = brcmf_fil_iovar_data_set(ifp, "event_msgs_ext",
+ buf, sizeof(buf));
+ if (err)
+ brcmf_err("Set event_msgs_ext error (%d)\n", err);
+ }
+
+ return err;
+}
+
+/**
+ * brcmf_fweh_process_event() - process skb as firmware event.
+ *
+ * @drvr: driver information object.
+ * @event_packet: event packet to process.
+ *
+ * If the packet buffer contains a firmware event message it will
+ * dispatch the event to a registered handler (using worker).
+ */
+void brcmf_fweh_process_event(struct brcmf_pub *drvr,
+ struct brcmf_event *event_packet,
+ u32 packet_len)
+{
+ enum brcmf_fweh_event_code code;
+ struct brcmf_fweh_info *fweh = &drvr->fweh;
+ struct brcmf_fweh_queue_item *event;
+ gfp_t alloc_flag = GFP_KERNEL;
+ void *data;
+ u32 datalen;
+ u32 extralen = 0;
+
+ /* get event info */
+ code = get_unaligned_be32(&event_packet->msg.event_type);
+ datalen = get_unaligned_be32(&event_packet->msg.datalen);
+ data = &event_packet[1];
+
+ if (code >= BRCMF_E_LAST)
+ return;
+
+ if (code != BRCMF_E_IF && !fweh->evt_handler[code])
+ return;
+
+ if (datalen > BRCMF_DCMD_MAXLEN)
+ return;
+
+ if (in_interrupt())
+ alloc_flag = GFP_ATOMIC;
+
+ /* reserve ie room for actual ssid for hidden AP */
+ if (code == BRCMF_E_ESCAN_RESULT)
+ extralen = IEEE80211_MAX_SSID_LEN;
+
+ event = kzalloc(sizeof(*event) + datalen + extralen, alloc_flag);
+ if (!event)
+ return;
+
+ event->code = code;
+ event->ifidx = event_packet->msg.ifidx;
+
+ /* use memcpy to get aligned event message */
+ memcpy(&event->emsg, &event_packet->msg, sizeof(event->emsg));
+ memcpy(event->data, data, datalen);
+ event->datalen = datalen;
+ memcpy(event->ifaddr, event_packet->eth.h_dest, ETH_ALEN);
+
+ brcmf_fweh_queue_event(fweh, event);
+}
+
+int brcmf_fweh_add_remove_events(struct brcmf_if *ifp, u16 *events, u32 cnt, bool add)
+{
+ int i, event = 0, err;
+ s8 buf[EVENTMSGS_EXT_STRUCT_SIZE+BRCMF_EVENTING_MASK_LEN], *eventmask;
+ eventmsgs_ext_t *eventmask_msg;
+
+ memset(buf, 0, sizeof(buf));
+ eventmask_msg = (eventmsgs_ext_t *)buf;
+ eventmask = buf + EVENTMSGS_EXT_STRUCT_SIZE;
+
+ eventmask_msg->ver = EVENTMSGS_VER;
+ eventmask_msg->len = BRCMF_EVENTING_MASK_LEN;
+ err = brcmf_fil_iovar_data_get(ifp, "event_msgs_ext",
+ buf, sizeof(buf));
+ if (err) {
+ brcmf_err("Get event_msgs_ext error (%d)\n", err);
+ return err;
+ }
+
+ for (i = 0; i < cnt; i++) {
+ event = events[i];
+ if (ifp->drvr->fweh.evt_handler[event]) {
+ brcmf_dbg(EVENT, "%s event %s\n",
+ add ? "add" : "remove",
+ brcmf_fweh_event_name(event));
+ if (add)
+ setbit(eventmask, event);
+ else
+ clrbit(eventmask, event);
+ }
+ }
+
+ /* Write updated Event mask */
+ eventmask_msg->ver = EVENTMSGS_VER;
+ eventmask_msg->command = EVENTMSGS_SET_MASK;
+ eventmask_msg->len = BRCMF_EVENTING_MASK_LEN;
+ err = brcmf_fil_iovar_data_set(ifp, "event_msgs_ext",
+ buf, sizeof(buf));
+ if (err)
+ brcmf_err("Set event_msgs_ext error (%d)\n", err);
+
+ return err;
+}
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h
new file mode 100644
index 0000000..7189414
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h
@@ -0,0 +1,369 @@
+/*
+ * Copyright (c) 2012 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+
+#ifndef FWEH_H_
+#define FWEH_H_
+
+#include <asm/unaligned.h>
+#include <linux/skbuff.h>
+#include <linux/if_ether.h>
+#include <linux/if.h>
+
+/* formward declarations */
+struct brcmf_pub;
+struct brcmf_if;
+struct brcmf_cfg80211_info;
+
+/* list of firmware events */
+#define BRCMF_FWEH_EVENT_ENUM_DEFLIST \
+ BRCMF_ENUM_DEF(SET_SSID, 0) \
+ BRCMF_ENUM_DEF(JOIN, 1) \
+ BRCMF_ENUM_DEF(START, 2) \
+ BRCMF_ENUM_DEF(AUTH, 3) \
+ BRCMF_ENUM_DEF(AUTH_IND, 4) \
+ BRCMF_ENUM_DEF(DEAUTH, 5) \
+ BRCMF_ENUM_DEF(DEAUTH_IND, 6) \
+ BRCMF_ENUM_DEF(ASSOC, 7) \
+ BRCMF_ENUM_DEF(ASSOC_IND, 8) \
+ BRCMF_ENUM_DEF(REASSOC, 9) \
+ BRCMF_ENUM_DEF(REASSOC_IND, 10) \
+ BRCMF_ENUM_DEF(DISASSOC, 11) \
+ BRCMF_ENUM_DEF(DISASSOC_IND, 12) \
+ BRCMF_ENUM_DEF(QUIET_START, 13) \
+ BRCMF_ENUM_DEF(QUIET_END, 14) \
+ BRCMF_ENUM_DEF(BEACON_RX, 15) \
+ BRCMF_ENUM_DEF(LINK, 16) \
+ BRCMF_ENUM_DEF(MIC_ERROR, 17) \
+ BRCMF_ENUM_DEF(NDIS_LINK, 18) \
+ BRCMF_ENUM_DEF(ROAM, 19) \
+ BRCMF_ENUM_DEF(TXFAIL, 20) \
+ BRCMF_ENUM_DEF(PMKID_CACHE, 21) \
+ BRCMF_ENUM_DEF(RETROGRADE_TSF, 22) \
+ BRCMF_ENUM_DEF(PRUNE, 23) \
+ BRCMF_ENUM_DEF(AUTOAUTH, 24) \
+ BRCMF_ENUM_DEF(EAPOL_MSG, 25) \
+ BRCMF_ENUM_DEF(SCAN_COMPLETE, 26) \
+ BRCMF_ENUM_DEF(ADDTS_IND, 27) \
+ BRCMF_ENUM_DEF(DELTS_IND, 28) \
+ BRCMF_ENUM_DEF(BCNSENT_IND, 29) \
+ BRCMF_ENUM_DEF(BCNRX_MSG, 30) \
+ BRCMF_ENUM_DEF(BCNLOST_MSG, 31) \
+ BRCMF_ENUM_DEF(ROAM_PREP, 32) \
+ BRCMF_ENUM_DEF(PFN_NET_FOUND, 33) \
+ BRCMF_ENUM_DEF(PFN_NET_LOST, 34) \
+ BRCMF_ENUM_DEF(RESET_COMPLETE, 35) \
+ BRCMF_ENUM_DEF(JOIN_START, 36) \
+ BRCMF_ENUM_DEF(ROAM_START, 37) \
+ BRCMF_ENUM_DEF(ASSOC_START, 38) \
+ BRCMF_ENUM_DEF(IBSS_ASSOC, 39) \
+ BRCMF_ENUM_DEF(RADIO, 40) \
+ BRCMF_ENUM_DEF(PSM_WATCHDOG, 41) \
+ BRCMF_ENUM_DEF(PROBREQ_MSG, 44) \
+ BRCMF_ENUM_DEF(SCAN_CONFIRM_IND, 45) \
+ BRCMF_ENUM_DEF(PSK_SUP, 46) \
+ BRCMF_ENUM_DEF(COUNTRY_CODE_CHANGED, 47) \
+ BRCMF_ENUM_DEF(EXCEEDED_MEDIUM_TIME, 48) \
+ BRCMF_ENUM_DEF(ICV_ERROR, 49) \
+ BRCMF_ENUM_DEF(UNICAST_DECODE_ERROR, 50) \
+ BRCMF_ENUM_DEF(MULTICAST_DECODE_ERROR, 51) \
+ BRCMF_ENUM_DEF(TRACE, 52) \
+ BRCMF_ENUM_DEF(IF, 54) \
+ BRCMF_ENUM_DEF(P2P_DISC_LISTEN_COMPLETE, 55) \
+ BRCMF_ENUM_DEF(RSSI, 56) \
+ BRCMF_ENUM_DEF(EXTLOG_MSG, 58) \
+ BRCMF_ENUM_DEF(ACTION_FRAME, 59) \
+ BRCMF_ENUM_DEF(ACTION_FRAME_COMPLETE, 60) \
+ BRCMF_ENUM_DEF(PRE_ASSOC_IND, 61) \
+ BRCMF_ENUM_DEF(PRE_REASSOC_IND, 62) \
+ BRCMF_ENUM_DEF(CHANNEL_ADOPTED, 63) \
+ BRCMF_ENUM_DEF(AP_STARTED, 64) \
+ BRCMF_ENUM_DEF(DFS_AP_STOP, 65) \
+ BRCMF_ENUM_DEF(DFS_AP_RESUME, 66) \
+ BRCMF_ENUM_DEF(ESCAN_RESULT, 69) \
+ BRCMF_ENUM_DEF(ACTION_FRAME_OFF_CHAN_COMPLETE, 70) \
+ BRCMF_ENUM_DEF(PROBERESP_MSG, 71) \
+ BRCMF_ENUM_DEF(P2P_PROBEREQ_MSG, 72) \
+ BRCMF_ENUM_DEF(DCS_REQUEST, 73) \
+ BRCMF_ENUM_DEF(FIFO_CREDIT_MAP, 74) \
+ BRCMF_ENUM_DEF(ACTION_FRAME_RX, 75) \
+ BRCMF_ENUM_DEF(TDLS_PEER_EVENT, 92) \
+ BRCMF_ENUM_DEF(BCMC_CREDIT_SUPPORT, 127) \
+ BRCMF_ENUM_DEF(PKT_FILTER, 164)
+
+#define BRCMF_ENUM_DEF(id, val) \
+ BRCMF_E_##id = (val),
+
+/* firmware event codes sent by the dongle */
+enum brcmf_fweh_event_code {
+ BRCMF_FWEH_EVENT_ENUM_DEFLIST
+ /* this determines event mask length which must match
+ * minimum length check in device firmware so it is
+ * hard-coded here.
+ */
+ BRCMF_E_LAST = 165
+};
+#undef BRCMF_ENUM_DEF
+
+#define BRCMF_EVENTING_MASK_LEN DIV_ROUND_UP(BRCMF_E_LAST, 8)
+
+/* flags field values in struct brcmf_event_msg */
+#define BRCMF_EVENT_MSG_LINK 0x01
+#define BRCMF_EVENT_MSG_FLUSHTXQ 0x02
+#define BRCMF_EVENT_MSG_GROUP 0x04
+
+/* status field values in struct brcmf_event_msg */
+#define BRCMF_E_STATUS_SUCCESS 0
+#define BRCMF_E_STATUS_FAIL 1
+#define BRCMF_E_STATUS_TIMEOUT 2
+#define BRCMF_E_STATUS_NO_NETWORKS 3
+#define BRCMF_E_STATUS_ABORT 4
+#define BRCMF_E_STATUS_NO_ACK 5
+#define BRCMF_E_STATUS_UNSOLICITED 6
+#define BRCMF_E_STATUS_ATTEMPT 7
+#define BRCMF_E_STATUS_PARTIAL 8
+#define BRCMF_E_STATUS_NEWSCAN 9
+#define BRCMF_E_STATUS_NEWASSOC 10
+#define BRCMF_E_STATUS_11HQUIET 11
+#define BRCMF_E_STATUS_SUPPRESS 12
+#define BRCMF_E_STATUS_NOCHANS 13
+#define BRCMF_E_STATUS_CS_ABORT 15
+#define BRCMF_E_STATUS_ERROR 16
+
+/* status field values for PSK_SUP event */
+#define BRCMF_E_STATUS_FWSUP_WAIT_M1 4
+#define BRCMF_E_STATUS_FWSUP_PREP_M2 5
+#define BRCMF_E_STATUS_FWSUP_COMPLETED 6
+#define BRCMF_E_STATUS_FWSUP_TIMEOUT 7
+#define BRCMF_E_STATUS_FWSUP_WAIT_M3 8
+#define BRCMF_E_STATUS_FWSUP_PREP_M4 9
+#define BRCMF_E_STATUS_FWSUP_WAIT_G1 10
+#define BRCMF_E_STATUS_FWSUP_PREP_G2 11
+
+/* reason field values in struct brcmf_event_msg */
+#define BRCMF_E_REASON_INITIAL_ASSOC 0
+#define BRCMF_E_REASON_LOW_RSSI 1
+#define BRCMF_E_REASON_DEAUTH 2
+#define BRCMF_E_REASON_DISASSOC 3
+#define BRCMF_E_REASON_BCNS_LOST 4
+#define BRCMF_E_REASON_MINTXRATE 9
+#define BRCMF_E_REASON_TXFAIL 10
+
+#define BRCMF_E_REASON_LINK_BSSCFG_DIS 4
+#define BRCMF_E_REASON_FAST_ROAM_FAILED 5
+#define BRCMF_E_REASON_DIRECTED_ROAM 6
+#define BRCMF_E_REASON_TSPEC_REJECTED 7
+#define BRCMF_E_REASON_BETTER_AP 8
+
+#define BRCMF_E_REASON_TDLS_PEER_DISCOVERED 0
+#define BRCMF_E_REASON_TDLS_PEER_CONNECTED 1
+#define BRCMF_E_REASON_TDLS_PEER_DISCONNECTED 2
+
+/* reason field values for PSK_SUP event */
+#define BRCMF_E_REASON_FWSUP_OTHER 0
+#define BRCMF_E_REASON_FWSUP_DECRYPT_KEY_DATA 1
+#define BRCMF_E_REASON_FWSUP_BAD_UCAST_WEP128 2
+#define BRCMF_E_REASON_FWSUP_BAD_UCAST_WEP40 3
+#define BRCMF_E_REASON_FWSUP_UNSUP_KEY_LEN 4
+#define BRCMF_E_REASON_FWSUP_PW_KEY_CIPHER 5
+#define BRCMF_E_REASON_FWSUP_MSG3_TOO_MANY_IE 6
+#define BRCMF_E_REASON_FWSUP_MSG3_IE_MISMATCH 7
+#define BRCMF_E_REASON_FWSUP_NO_INSTALL_FLAG 8
+#define BRCMF_E_REASON_FWSUP_MSG3_NO_GTK 9
+#define BRCMF_E_REASON_FWSUP_GRP_KEY_CIPHER 10
+#define BRCMF_E_REASON_FWSUP_GRP_MSG1_NO_GTK 11
+#define BRCMF_E_REASON_FWSUP_GTK_DECRYPT_FAIL 12
+#define BRCMF_E_REASON_FWSUP_SEND_FAIL 13
+#define BRCMF_E_REASON_FWSUP_DEAUTH 14
+#define BRCMF_E_REASON_FWSUP_WPA_PSK_TMO 15
+#define BRCMF_E_REASON_FWSUP_WPA_PSK_M1_TMO 16
+#define BRCMF_E_REASON_FWSUP_WPA_PSK_M3_TMO 17
+
+/* action field values for brcmf_ifevent */
+#define BRCMF_E_IF_ADD 1
+#define BRCMF_E_IF_DEL 2
+#define BRCMF_E_IF_CHANGE 3
+
+/* flag field values for brcmf_ifevent */
+#define BRCMF_E_IF_FLAG_NOIF 1
+
+/* role field values for brcmf_ifevent */
+#define BRCMF_E_IF_ROLE_STA 0
+#define BRCMF_E_IF_ROLE_AP 1
+#define BRCMF_E_IF_ROLE_WDS 2
+#define BRCMF_E_IF_ROLE_P2P_GO 3
+#define BRCMF_E_IF_ROLE_P2P_CLIENT 4
+
+/**
+ * definitions for event packet validation.
+ */
+#define BRCM_OUI "\x00\x10\x18"
+#define BCMILCP_BCM_SUBTYPE_EVENT 1
+
+/**
+ * definitions for event pkt filter.
+ */
+#define BRCMF_E_PKT_FILTER_TIMEOUT 1
+
+/**
+ * struct brcm_ethhdr - broadcom specific ether header.
+ *
+ * @subtype: subtype for this packet.
+ * @length: TODO: length of appended data.
+ * @version: version indication.
+ * @oui: OUI of this packet.
+ * @usr_subtype: subtype for this OUI.
+ */
+struct brcm_ethhdr {
+ __be16 subtype;
+ __be16 length;
+ u8 version;
+ u8 oui[3];
+ __be16 usr_subtype;
+} __packed;
+
+struct brcmf_event_msg_be {
+ __be16 version;
+ __be16 flags;
+ __be32 event_type;
+ __be32 status;
+ __be32 reason;
+ __be32 auth_type;
+ __be32 datalen;
+ u8 addr[ETH_ALEN];
+ char ifname[IFNAMSIZ];
+ u8 ifidx;
+ u8 bsscfgidx;
+} __packed;
+
+/**
+ * struct brcmf_event - contents of broadcom event packet.
+ *
+ * @eth: standard ether header.
+ * @hdr: broadcom specific ether header.
+ * @msg: common part of the actual event message.
+ */
+struct brcmf_event {
+ struct ethhdr eth;
+ struct brcm_ethhdr hdr;
+ struct brcmf_event_msg_be msg;
+} __packed;
+
+/**
+ * struct brcmf_event_msg - firmware event message.
+ *
+ * @version: version information.
+ * @flags: event flags.
+ * @event_code: firmware event code.
+ * @status: status information.
+ * @reason: reason code.
+ * @auth_type: authentication type.
+ * @datalen: lenght of event data buffer.
+ * @addr: ether address.
+ * @ifname: interface name.
+ * @ifidx: interface index.
+ * @bsscfgidx: bsscfg index.
+ */
+struct brcmf_event_msg {
+ u16 version;
+ u16 flags;
+ u32 event_code;
+ u32 status;
+ u32 reason;
+ s32 auth_type;
+ u32 datalen;
+ u8 addr[ETH_ALEN];
+ char ifname[IFNAMSIZ];
+ u8 ifidx;
+ u8 bsscfgidx;
+};
+
+struct brcmf_if_event {
+ u8 ifidx;
+ u8 action;
+ u8 flags;
+ u8 bsscfgidx;
+ u8 role;
+};
+
+typedef int (*brcmf_fweh_handler_t)(struct brcmf_if *ifp,
+ const struct brcmf_event_msg *evtmsg,
+ void *data);
+
+/**
+ * struct brcmf_fweh_info - firmware event handling information.
+ *
+ * @p2pdev_setup_ongoing: P2P device creation in progress.
+ * @event_work: event worker.
+ * @evt_q_lock: lock for event queue protection.
+ * @event_q: event queue.
+ * @evt_handler: registered event handlers.
+ */
+struct brcmf_fweh_info {
+ bool p2pdev_setup_ongoing;
+ struct work_struct event_work;
+ spinlock_t evt_q_lock;
+ struct list_head event_q;
+ int (*evt_handler[BRCMF_E_LAST])(struct brcmf_if *ifp,
+ const struct brcmf_event_msg *evtmsg,
+ void *data);
+};
+
+const char *brcmf_fweh_event_name(enum brcmf_fweh_event_code code);
+
+void brcmf_fweh_attach(struct brcmf_pub *drvr);
+void brcmf_fweh_detach(struct brcmf_pub *drvr);
+int brcmf_fweh_register(struct brcmf_pub *drvr, enum brcmf_fweh_event_code code,
+ int (*handler)(struct brcmf_if *ifp,
+ const struct brcmf_event_msg *evtmsg,
+ void *data));
+void brcmf_fweh_unregister(struct brcmf_pub *drvr,
+ enum brcmf_fweh_event_code code);
+int brcmf_fweh_activate_events(struct brcmf_if *ifp);
+void brcmf_fweh_process_event(struct brcmf_pub *drvr,
+ struct brcmf_event *event_packet,
+ u32 packet_len);
+void brcmf_fweh_p2pdev_setup(struct brcmf_if *ifp, bool ongoing);
+int brcmf_fweh_add_remove_events(struct brcmf_if *ifp, u16 *events, u32 cnt, bool add);
+
+static inline void brcmf_fweh_process_skb(struct brcmf_pub *drvr,
+ struct sk_buff *skb)
+{
+ struct brcmf_event *event_packet;
+ u16 usr_stype;
+
+ /* only process events when protocol matches */
+ if (skb->protocol != cpu_to_be16(ETH_P_LINK_CTL))
+ return;
+
+ if ((skb->len + ETH_HLEN) < sizeof(*event_packet))
+ return;
+
+ /* check for BRCM oui match */
+ event_packet = (struct brcmf_event *)skb_mac_header(skb);
+ if (memcmp(BRCM_OUI, &event_packet->hdr.oui[0],
+ sizeof(event_packet->hdr.oui)))
+ return;
+
+ /* final match on usr_subtype */
+ usr_stype = get_unaligned_be16(&event_packet->hdr.usr_subtype);
+ if (usr_stype != BCMILCP_BCM_SUBTYPE_EVENT)
+ return;
+
+ brcmf_fweh_process_event(drvr, event_packet, skb->len + ETH_HLEN);
+}
+
+#endif /* FWEH_H_ */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.c
new file mode 100644
index 0000000..f6a2df9
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.c
@@ -0,0 +1,421 @@
+/*
+ * Copyright (c) 2012 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* FWIL is the Firmware Interface Layer. In this module the support functions
+ * are located to set and get variables to and from the firmware.
+ */
+
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <brcmu_utils.h>
+#include <brcmu_wifi.h>
+#include "core.h"
+#include "bus.h"
+#include "debug.h"
+#include "tracepoint.h"
+#include "fwil.h"
+#include "proto.h"
+
+
+#define MAX_HEX_DUMP_LEN 64
+
+#ifdef DEBUG
+static const char * const brcmf_fil_errstr[] = {
+ "BCME_OK",
+ "BCME_ERROR",
+ "BCME_BADARG",
+ "BCME_BADOPTION",
+ "BCME_NOTUP",
+ "BCME_NOTDOWN",
+ "BCME_NOTAP",
+ "BCME_NOTSTA",
+ "BCME_BADKEYIDX",
+ "BCME_RADIOOFF",
+ "BCME_NOTBANDLOCKED",
+ "BCME_NOCLK",
+ "BCME_BADRATESET",
+ "BCME_BADBAND",
+ "BCME_BUFTOOSHORT",
+ "BCME_BUFTOOLONG",
+ "BCME_BUSY",
+ "BCME_NOTASSOCIATED",
+ "BCME_BADSSIDLEN",
+ "BCME_OUTOFRANGECHAN",
+ "BCME_BADCHAN",
+ "BCME_BADADDR",
+ "BCME_NORESOURCE",
+ "BCME_UNSUPPORTED",
+ "BCME_BADLEN",
+ "BCME_NOTREADY",
+ "BCME_EPERM",
+ "BCME_NOMEM",
+ "BCME_ASSOCIATED",
+ "BCME_RANGE",
+ "BCME_NOTFOUND",
+ "BCME_WME_NOT_ENABLED",
+ "BCME_TSPEC_NOTFOUND",
+ "BCME_ACM_NOTSUPPORTED",
+ "BCME_NOT_WME_ASSOCIATION",
+ "BCME_SDIO_ERROR",
+ "BCME_DONGLE_DOWN",
+ "BCME_VERSION",
+ "BCME_TXFAIL",
+ "BCME_RXFAIL",
+ "BCME_NODEVICE",
+ "BCME_NMODE_DISABLED",
+ "BCME_NONRESIDENT",
+ "BCME_SCANREJECT",
+ "BCME_USAGE_ERROR",
+ "BCME_IOCTL_ERROR",
+ "BCME_SERIAL_PORT_ERR",
+ "BCME_DISABLED",
+ "BCME_DECERR",
+ "BCME_ENCERR",
+ "BCME_MICERR",
+ "BCME_REPLAY",
+ "BCME_IE_NOTFOUND",
+};
+
+static const char *brcmf_fil_get_errstr(u32 err)
+{
+ if (err >= ARRAY_SIZE(brcmf_fil_errstr))
+ return "(unknown)";
+
+ return brcmf_fil_errstr[err];
+}
+#else
+static const char *brcmf_fil_get_errstr(u32 err)
+{
+ return "";
+}
+#endif /* DEBUG */
+
+static s32
+brcmf_fil_cmd_data(struct brcmf_if *ifp, u32 cmd, void *data, u32 len, bool set)
+{
+ struct brcmf_pub *drvr = ifp->drvr;
+ s32 err;
+
+ if (drvr->bus_if->state != BRCMF_BUS_UP) {
+ brcmf_err("bus is down. we have nothing to do.\n");
+ return -EIO;
+ }
+
+ if (data != NULL)
+ len = min_t(uint, len, BRCMF_DCMD_MAXLEN);
+ if (set)
+ err = brcmf_proto_set_dcmd(drvr, ifp->ifidx, cmd, data, len);
+ else
+ err = brcmf_proto_query_dcmd(drvr, ifp->ifidx, cmd, data, len);
+
+ if (err >= 0)
+ return 0;
+
+ brcmf_dbg(FIL, "Failed: %s (%d)\n",
+ brcmf_fil_get_errstr((u32)(-err)), err);
+
+ return err;
+}
+
+s32
+brcmf_fil_cmd_data_set(struct brcmf_if *ifp, u32 cmd, void *data, u32 len)
+{
+ s32 err;
+
+ mutex_lock(&ifp->drvr->proto_block);
+
+ brcmf_dbg(FIL, "ifidx=%d, cmd=%d, len=%d\n", ifp->ifidx, cmd, len);
+ brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
+ min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
+
+ err = brcmf_fil_cmd_data(ifp, cmd, data, len, true);
+ mutex_unlock(&ifp->drvr->proto_block);
+
+ return err;
+}
+
+s32
+brcmf_fil_cmd_data_get(struct brcmf_if *ifp, u32 cmd, void *data, u32 len)
+{
+ s32 err;
+
+ mutex_lock(&ifp->drvr->proto_block);
+ err = brcmf_fil_cmd_data(ifp, cmd, data, len, false);
+
+ brcmf_dbg(FIL, "ifidx=%d, cmd=%d, len=%d\n", ifp->ifidx, cmd, len);
+ brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
+ min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
+
+ mutex_unlock(&ifp->drvr->proto_block);
+
+ return err;
+}
+
+
+s32
+brcmf_fil_cmd_int_set(struct brcmf_if *ifp, u32 cmd, u32 data)
+{
+ s32 err;
+ __le32 data_le = cpu_to_le32(data);
+
+ mutex_lock(&ifp->drvr->proto_block);
+ brcmf_dbg(FIL, "ifidx=%d, cmd=%d, value=%d\n", ifp->ifidx, cmd, data);
+ err = brcmf_fil_cmd_data(ifp, cmd, &data_le, sizeof(data_le), true);
+ mutex_unlock(&ifp->drvr->proto_block);
+
+ return err;
+}
+
+s32
+brcmf_fil_cmd_int_get(struct brcmf_if *ifp, u32 cmd, u32 *data)
+{
+ s32 err;
+ __le32 data_le = cpu_to_le32(*data);
+
+ mutex_lock(&ifp->drvr->proto_block);
+ err = brcmf_fil_cmd_data(ifp, cmd, &data_le, sizeof(data_le), false);
+ mutex_unlock(&ifp->drvr->proto_block);
+ *data = le32_to_cpu(data_le);
+ brcmf_dbg(FIL, "ifidx=%d, cmd=%d, value=%d\n", ifp->ifidx, cmd, *data);
+
+ return err;
+}
+
+static u32
+brcmf_create_iovar(char *name, const char *data, u32 datalen,
+ char *buf, u32 buflen)
+{
+ u32 len;
+
+ len = strlen(name) + 1;
+
+ if ((len + datalen) > buflen)
+ return 0;
+
+ memcpy(buf, name, len);
+
+ /* append data onto the end of the name string */
+ if (data && datalen)
+ memcpy(&buf[len], data, datalen);
+
+ return len + datalen;
+}
+
+
+s32
+brcmf_fil_iovar_data_set(struct brcmf_if *ifp, char *name, const void *data,
+ u32 len)
+{
+ struct brcmf_pub *drvr = ifp->drvr;
+ s32 err;
+ u32 buflen;
+
+ mutex_lock(&drvr->proto_block);
+
+ brcmf_dbg(FIL, "ifidx=%d, name=%s, len=%d\n", ifp->ifidx, name, len);
+ brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
+ min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
+
+ buflen = brcmf_create_iovar(name, data, len, drvr->proto_buf,
+ sizeof(drvr->proto_buf));
+ if (buflen) {
+ err = brcmf_fil_cmd_data(ifp, BRCMF_C_SET_VAR, drvr->proto_buf,
+ buflen, true);
+ } else {
+ err = -EPERM;
+ brcmf_err("Creating iovar failed\n");
+ }
+
+ mutex_unlock(&drvr->proto_block);
+ return err;
+}
+
+s32
+brcmf_fil_iovar_data_get(struct brcmf_if *ifp, char *name, void *data,
+ u32 len)
+{
+ struct brcmf_pub *drvr = ifp->drvr;
+ s32 err;
+ u32 buflen;
+
+ mutex_lock(&drvr->proto_block);
+
+ buflen = brcmf_create_iovar(name, data, len, drvr->proto_buf,
+ sizeof(drvr->proto_buf));
+ if (buflen) {
+ err = brcmf_fil_cmd_data(ifp, BRCMF_C_GET_VAR, drvr->proto_buf,
+ buflen, false);
+ if (err == 0)
+ memcpy(data, drvr->proto_buf, len);
+ } else {
+ err = -EPERM;
+ brcmf_err("Creating iovar failed\n");
+ }
+
+ brcmf_dbg(FIL, "ifidx=%d, name=%s, len=%d\n", ifp->ifidx, name, len);
+ brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
+ min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
+
+ mutex_unlock(&drvr->proto_block);
+ return err;
+}
+
+s32
+brcmf_fil_iovar_int_set(struct brcmf_if *ifp, char *name, u32 data)
+{
+ __le32 data_le = cpu_to_le32(data);
+
+ return brcmf_fil_iovar_data_set(ifp, name, &data_le, sizeof(data_le));
+}
+
+s32
+brcmf_fil_iovar_int_get(struct brcmf_if *ifp, char *name, u32 *data)
+{
+ __le32 data_le = cpu_to_le32(*data);
+ s32 err;
+
+ err = brcmf_fil_iovar_data_get(ifp, name, &data_le, sizeof(data_le));
+ if (err == 0)
+ *data = le32_to_cpu(data_le);
+ return err;
+}
+
+static u32
+brcmf_create_bsscfg(s32 bsscfgidx, char *name, char *data, u32 datalen,
+ char *buf, u32 buflen)
+{
+ const s8 *prefix = "bsscfg:";
+ s8 *p;
+ u32 prefixlen;
+ u32 namelen;
+ u32 iolen;
+ __le32 bsscfgidx_le;
+
+ if (bsscfgidx == 0)
+ return brcmf_create_iovar(name, data, datalen, buf, buflen);
+
+ prefixlen = strlen(prefix);
+ namelen = strlen(name) + 1; /* lengh of iovar name + null */
+ iolen = prefixlen + namelen + sizeof(bsscfgidx_le) + datalen;
+
+ if (buflen < iolen) {
+ brcmf_err("buffer is too short\n");
+ return 0;
+ }
+
+ p = buf;
+
+ /* copy prefix, no null */
+ memcpy(p, prefix, prefixlen);
+ p += prefixlen;
+
+ /* copy iovar name including null */
+ memcpy(p, name, namelen);
+ p += namelen;
+
+ /* bss config index as first data */
+ bsscfgidx_le = cpu_to_le32(bsscfgidx);
+ memcpy(p, &bsscfgidx_le, sizeof(bsscfgidx_le));
+ p += sizeof(bsscfgidx_le);
+
+ /* parameter buffer follows */
+ if (datalen)
+ memcpy(p, data, datalen);
+
+ return iolen;
+}
+
+s32
+brcmf_fil_bsscfg_data_set(struct brcmf_if *ifp, char *name,
+ void *data, u32 len)
+{
+ struct brcmf_pub *drvr = ifp->drvr;
+ s32 err;
+ u32 buflen;
+
+ mutex_lock(&drvr->proto_block);
+
+ brcmf_dbg(FIL, "ifidx=%d, bsscfgidx=%d, name=%s, len=%d\n", ifp->ifidx,
+ ifp->bsscfgidx, name, len);
+ brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
+ min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
+
+ buflen = brcmf_create_bsscfg(ifp->bsscfgidx, name, data, len,
+ drvr->proto_buf, sizeof(drvr->proto_buf));
+ if (buflen) {
+ err = brcmf_fil_cmd_data(ifp, BRCMF_C_SET_VAR, drvr->proto_buf,
+ buflen, true);
+ } else {
+ err = -EPERM;
+ brcmf_err("Creating bsscfg failed\n");
+ }
+
+ mutex_unlock(&drvr->proto_block);
+ return err;
+}
+
+s32
+brcmf_fil_bsscfg_data_get(struct brcmf_if *ifp, char *name,
+ void *data, u32 len)
+{
+ struct brcmf_pub *drvr = ifp->drvr;
+ s32 err;
+ u32 buflen;
+
+ mutex_lock(&drvr->proto_block);
+
+ buflen = brcmf_create_bsscfg(ifp->bsscfgidx, name, data, len,
+ drvr->proto_buf, sizeof(drvr->proto_buf));
+ if (buflen) {
+ err = brcmf_fil_cmd_data(ifp, BRCMF_C_GET_VAR, drvr->proto_buf,
+ buflen, false);
+ if (err == 0)
+ memcpy(data, drvr->proto_buf, len);
+ } else {
+ err = -EPERM;
+ brcmf_err("Creating bsscfg failed\n");
+ }
+ brcmf_dbg(FIL, "ifidx=%d, bsscfgidx=%d, name=%s, len=%d\n", ifp->ifidx,
+ ifp->bsscfgidx, name, len);
+ brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
+ min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
+
+ mutex_unlock(&drvr->proto_block);
+ return err;
+
+}
+
+s32
+brcmf_fil_bsscfg_int_set(struct brcmf_if *ifp, char *name, u32 data)
+{
+ __le32 data_le = cpu_to_le32(data);
+
+ return brcmf_fil_bsscfg_data_set(ifp, name, &data_le,
+ sizeof(data_le));
+}
+
+s32
+brcmf_fil_bsscfg_int_get(struct brcmf_if *ifp, char *name, u32 *data)
+{
+ __le32 data_le = cpu_to_le32(*data);
+ s32 err;
+
+ err = brcmf_fil_bsscfg_data_get(ifp, name, &data_le,
+ sizeof(data_le));
+ if (err == 0)
+ *data = le32_to_cpu(data_le);
+ return err;
+}
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.h
new file mode 100644
index 0000000..5056157
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2012 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _fwil_h_
+#define _fwil_h_
+
+/*******************************************************************************
+ * Dongle command codes that are interpreted by firmware
+ ******************************************************************************/
+#define BRCMF_C_GET_VERSION 1
+#define BRCMF_C_UP 2
+#define BRCMF_C_DOWN 3
+#define BRCMF_C_SET_PROMISC 10
+#define BRCMF_C_GET_RATE 12
+#define BRCMF_C_GET_INFRA 19
+#define BRCMF_C_SET_INFRA 20
+#define BRCMF_C_GET_AUTH 21
+#define BRCMF_C_SET_AUTH 22
+#define BRCMF_C_GET_BSSID 23
+#define BRCMF_C_GET_SSID 25
+#define BRCMF_C_SET_SSID 26
+#define BRCMF_C_TERMINATED 28
+#define BRCMF_C_GET_CHANNEL 29
+#define BRCMF_C_SET_CHANNEL 30
+#define BRCMF_C_GET_SRL 31
+#define BRCMF_C_SET_SRL 32
+#define BRCMF_C_GET_LRL 33
+#define BRCMF_C_SET_LRL 34
+#define BRCMF_C_GET_RADIO 37
+#define BRCMF_C_SET_RADIO 38
+#define BRCMF_C_GET_PHYTYPE 39
+#define BRCMF_C_SET_KEY 45
+#define BRCMF_C_GET_REGULATORY 46
+#define BRCMF_C_SET_REGULATORY 47
+#define BRCMF_C_SET_PASSIVE_SCAN 49
+#define BRCMF_C_SCAN 50
+#define BRCMF_C_SCAN_RESULTS 51
+#define BRCMF_C_DISASSOC 52
+#define BRCMF_C_REASSOC 53
+#define BRCMF_C_SET_ROAM_TRIGGER 55
+#define BRCMF_C_SET_ROAM_DELTA 57
+#define BRCMF_C_GET_BCNPRD 75
+#define BRCMF_C_SET_BCNPRD 76
+#define BRCMF_C_GET_DTIMPRD 77
+#define BRCMF_C_SET_DTIMPRD 78
+#define BRCMF_C_SET_COUNTRY 84
+#define BRCMF_C_GET_PM 85
+#define BRCMF_C_SET_PM 86
+#define BRCMF_C_GET_REVINFO 98
+#define BRCMF_C_GET_CURR_RATESET 114
+#define BRCMF_C_GET_AP 117
+#define BRCMF_C_SET_AP 118
+#define BRCMF_C_SET_SCB_AUTHORIZE 121
+#define BRCMF_C_SET_SCB_DEAUTHORIZE 122
+#define BRCMF_C_GET_RSSI 127
+#define BRCMF_C_GET_WSEC 133
+#define BRCMF_C_SET_WSEC 134
+#define BRCMF_C_GET_PHY_NOISE 135
+#define BRCMF_C_GET_BSS_INFO 136
+#define BRCMF_C_GET_GET_PKTCNTS 137
+#define BRCMF_C_GET_BANDLIST 140
+#define BRCMF_C_GET_BAND 141
+#define BRCMF_C_SET_BAND 142
+#define BRCMF_C_SET_SCB_TIMEOUT 158
+#define BRCMF_C_GET_ASSOCLIST 159
+#define BRCMF_C_GET_PHYLIST 180
+#define BRCMF_C_SET_SCAN_CHANNEL_TIME 185
+#define BRCMF_C_SET_SCAN_UNASSOC_TIME 187
+#define BRCMF_C_SCB_DEAUTHENTICATE_FOR_REASON 201
+#define BRCMF_C_SET_ASSOC_PREFER 205
+#define BRCMF_C_GET_VALID_CHANNELS 217
+#define BRCMF_C_GET_KEY_PRIMARY 235
+#define BRCMF_C_SET_KEY_PRIMARY 236
+#define BRCMF_C_SET_SCAN_PASSIVE_TIME 258
+#define BRCMF_C_GET_VAR 262
+#define BRCMF_C_SET_VAR 263
+#define BRCMF_C_SET_WSEC_PMK 268
+
+s32 brcmf_fil_cmd_data_set(struct brcmf_if *ifp, u32 cmd, void *data, u32 len);
+s32 brcmf_fil_cmd_data_get(struct brcmf_if *ifp, u32 cmd, void *data, u32 len);
+s32 brcmf_fil_cmd_int_set(struct brcmf_if *ifp, u32 cmd, u32 data);
+s32 brcmf_fil_cmd_int_get(struct brcmf_if *ifp, u32 cmd, u32 *data);
+
+s32 brcmf_fil_iovar_data_set(struct brcmf_if *ifp, char *name, const void *data,
+ u32 len);
+s32 brcmf_fil_iovar_data_get(struct brcmf_if *ifp, char *name, void *data,
+ u32 len);
+s32 brcmf_fil_iovar_int_set(struct brcmf_if *ifp, char *name, u32 data);
+s32 brcmf_fil_iovar_int_get(struct brcmf_if *ifp, char *name, u32 *data);
+
+s32 brcmf_fil_bsscfg_data_set(struct brcmf_if *ifp, char *name, void *data,
+ u32 len);
+s32 brcmf_fil_bsscfg_data_get(struct brcmf_if *ifp, char *name, void *data,
+ u32 len);
+s32 brcmf_fil_bsscfg_int_set(struct brcmf_if *ifp, char *name, u32 data);
+s32 brcmf_fil_bsscfg_int_get(struct brcmf_if *ifp, char *name, u32 *data);
+
+#endif /* _fwil_h_ */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
new file mode 100644
index 0000000..0f32116
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
@@ -0,0 +1,895 @@
+/*
+ * Copyright (c) 2012 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+
+#ifndef FWIL_TYPES_H_
+#define FWIL_TYPES_H_
+
+#include <linux/if_ether.h>
+
+
+#define BRCMF_FIL_ACTION_FRAME_SIZE 1800
+
+/* ARP Offload feature flags for arp_ol iovar */
+#define BRCMF_ARP_OL_AGENT 0x00000001
+#define BRCMF_ARP_OL_SNOOP 0x00000002
+#define BRCMF_ARP_OL_HOST_AUTO_REPLY 0x00000004
+#define BRCMF_ARP_OL_PEER_AUTO_REPLY 0x00000008
+
+#define BRCMF_BSS_INFO_VERSION 109 /* curr ver of brcmf_bss_info_le struct */
+#define BRCMF_BSS_FROM_BEACON 0x01 /* bss_info derived from beacon */
+#define BRCMF_BSS_FROM_CACHE 0x02
+#define BRCMF_BSS_RSSI_ON_CHANNEL 0x04
+
+#define BRCMF_STA_WME 0x00000002 /* WMM association */
+#define BRCMF_STA_AUTHE 0x00000008 /* Authenticated */
+#define BRCMF_STA_ASSOC 0x00000010 /* Associated */
+#define BRCMF_STA_AUTHO 0x00000020 /* Authorized */
+#define BRCMF_STA_SCBSTATS 0x00004000 /* Per STA debug stats */
+
+/* size of brcmf_scan_params not including variable length array */
+#define BRCMF_SCAN_PARAMS_FIXED_SIZE 64
+
+/* masks for channel and ssid count */
+#define BRCMF_SCAN_PARAMS_COUNT_MASK 0x0000ffff
+#define BRCMF_SCAN_PARAMS_NSSID_SHIFT 16
+
+#define BRCMF_WSEC_MAX_PSK_LEN 32
+#define BRCMF_WSEC_PASSPHRASE BIT(0)
+
+/* primary (ie tx) key */
+#define BRCMF_PRIMARY_KEY (1 << 1)
+#define DOT11_BSSTYPE_ANY 2
+#define BRCMF_ESCAN_REQ_VERSION 1
+
+#define BRCMF_MAXRATES_IN_SET 16 /* max # of rates in rateset */
+
+/* OBSS Coex Auto/On/Off */
+#define BRCMF_OBSS_COEX_AUTO (-1)
+#define BRCMF_OBSS_COEX_OFF 0
+#define BRCMF_OBSS_COEX_ON 1
+
+/* WOWL bits */
+/* Wakeup on Magic packet: */
+#define BRCMF_WOWL_MAGIC (1 << 0)
+/* Wakeup on Netpattern */
+#define BRCMF_WOWL_NET (1 << 1)
+/* Wakeup on loss-of-link due to Disassoc/Deauth: */
+#define BRCMF_WOWL_DIS (1 << 2)
+/* Wakeup on retrograde TSF: */
+#define BRCMF_WOWL_RETR (1 << 3)
+/* Wakeup on loss of beacon: */
+#define BRCMF_WOWL_BCN (1 << 4)
+/* Wakeup after test: */
+#define BRCMF_WOWL_TST (1 << 5)
+/* Wakeup after PTK refresh: */
+#define BRCMF_WOWL_M1 (1 << 6)
+/* Wakeup after receipt of EAP-Identity Req: */
+#define BRCMF_WOWL_EAPID (1 << 7)
+/* Wakeind via PME(0) or GPIO(1): */
+#define BRCMF_WOWL_PME_GPIO (1 << 8)
+/* need tkip phase 1 key to be updated by the driver: */
+#define BRCMF_WOWL_NEEDTKIP1 (1 << 9)
+/* enable wakeup if GTK fails: */
+#define BRCMF_WOWL_GTK_FAILURE (1 << 10)
+/* support extended magic packets: */
+#define BRCMF_WOWL_EXTMAGPAT (1 << 11)
+/* support ARP/NS/keepalive offloading: */
+#define BRCMF_WOWL_ARPOFFLOAD (1 << 12)
+/* read protocol version for EAPOL frames: */
+#define BRCMF_WOWL_WPA2 (1 << 13)
+/* If the bit is set, use key rotaton: */
+#define BRCMF_WOWL_KEYROT (1 << 14)
+/* If the bit is set, frm received was bcast frame: */
+#define BRCMF_WOWL_BCAST (1 << 15)
+/* If the bit is set, scan offload is enabled: */
+#define BRCMF_WOWL_SCANOL (1 << 16)
+/* Wakeup on tcpkeep alive timeout: */
+#define BRCMF_WOWL_TCPKEEP_TIME (1 << 17)
+/* Wakeup on mDNS Conflict Resolution: */
+#define BRCMF_WOWL_MDNS_CONFLICT (1 << 18)
+/* Wakeup on mDNS Service Connect: */
+#define BRCMF_WOWL_MDNS_SERVICE (1 << 19)
+/* tcp keepalive got data: */
+#define BRCMF_WOWL_TCPKEEP_DATA (1 << 20)
+/* Firmware died in wowl mode: */
+#define BRCMF_WOWL_FW_HALT (1 << 21)
+/* Enable detection of radio button changes: */
+#define BRCMF_WOWL_ENAB_HWRADIO (1 << 22)
+/* Offloads detected MIC failure(s): */
+#define BRCMF_WOWL_MIC_FAIL (1 << 23)
+/* Wakeup in Unassociated state (Net/Magic Pattern): */
+#define BRCMF_WOWL_UNASSOC (1 << 24)
+/* Wakeup if received matched secured pattern: */
+#define BRCMF_WOWL_SECURE (1 << 25)
+/* Wakeup on finding preferred network */
+#define BRCMF_WOWL_PFN_FOUND (1 << 27)
+/* Wakeup on receiving pairwise key EAP packets: */
+#define WIPHY_WOWL_EAP_PK (1 << 28)
+/* Link Down indication in WoWL mode: */
+#define BRCMF_WOWL_LINKDOWN (1 << 31)
+
+#define BRCMF_WOWL_MAXPATTERNS 8
+#define BRCMF_WOWL_MAXPATTERNSIZE 128
+
+#define BRCMF_COUNTRY_BUF_SZ 4
+#define BRCMF_ANT_MAX 4
+
+#define BRCMF_MAX_ASSOCLIST 128
+
+#define BRCMF_TXBF_SU_BFE_CAP BIT(0)
+#define BRCMF_TXBF_MU_BFE_CAP BIT(1)
+#define BRCMF_TXBF_SU_BFR_CAP BIT(0)
+#define BRCMF_TXBF_MU_BFR_CAP BIT(1)
+
+#define BRCMF_MAXPMKID 16 /* max # PMKID cache entries */
+#define BRCMF_NUMCHANNELS 64
+
+#define BRCMF_PFN_MACADDR_CFG_VER 1
+#define BRCMF_PFN_MAC_OUI_ONLY BIT(0)
+#define BRCMF_PFN_SET_MAC_UNASSOC BIT(1)
+
+#define BRCMF_MCSSET_LEN 16
+
+#define BRCMF_RSN_KCK_LENGTH 16
+#define BRCMF_RSN_KEK_LENGTH 16
+#define BRCMF_RSN_REPLAY_LEN 8
+
+#define BRCMF_MFP_NONE 0
+#define BRCMF_MFP_CAPABLE 1
+#define BRCMF_MFP_REQUIRED 2
+
+/* MAX_CHUNK_LEN is the amount of the clm file we send in each ioctl.
+ * It is relatively small because dongles (FW) have a small maximum size
+ * input payload restriction for ioctls.
+ */
+#define MAX_CHUNK_LEN 1400
+
+#define DLOAD_HANDLER_VER 1 /* Downloader version */
+#define DLOAD_FLAG_VER_MASK 0xf000 /* Downloader version mask */
+#define DLOAD_FLAG_VER_SHIFT 12 /* Downloader version shift */
+
+#define DL_CRC_NOT_INUSE 0x0001
+#define DL_BEGIN 0x0002
+#define DL_END 0x0004
+
+#define DL_TYPE_CLM 2
+
+/* join preference types for join_pref iovar */
+enum brcmf_join_pref_types {
+ BRCMF_JOIN_PREF_RSSI = 1,
+ BRCMF_JOIN_PREF_WPA,
+ BRCMF_JOIN_PREF_BAND,
+ BRCMF_JOIN_PREF_RSSI_DELTA,
+};
+
+enum brcmf_fil_p2p_if_types {
+ BRCMF_FIL_P2P_IF_CLIENT,
+ BRCMF_FIL_P2P_IF_GO,
+ BRCMF_FIL_P2P_IF_DYNBCN_GO,
+ BRCMF_FIL_P2P_IF_DEV,
+};
+
+enum brcmf_wowl_pattern_type {
+ BRCMF_WOWL_PATTERN_TYPE_BITMAP = 0,
+ BRCMF_WOWL_PATTERN_TYPE_ARP,
+ BRCMF_WOWL_PATTERN_TYPE_NA
+};
+
+struct brcmf_fil_p2p_if_le {
+ u8 addr[ETH_ALEN];
+ __le16 type;
+ __le16 chspec;
+};
+
+struct brcmf_fil_chan_info_le {
+ __le32 hw_channel;
+ __le32 target_channel;
+ __le32 scan_channel;
+};
+
+struct brcmf_fil_action_frame_le {
+ u8 da[ETH_ALEN];
+ __le16 len;
+ __le32 packet_id;
+ u8 data[BRCMF_FIL_ACTION_FRAME_SIZE];
+};
+
+struct brcmf_fil_af_params_le {
+ __le32 channel;
+ __le32 dwell_time;
+ u8 bssid[ETH_ALEN];
+ u8 pad[2];
+ struct brcmf_fil_action_frame_le action_frame;
+};
+
+struct brcmf_fil_bss_enable_le {
+ __le32 bsscfgidx;
+ __le32 enable;
+};
+
+struct brcmf_fil_bwcap_le {
+ __le32 band;
+ __le32 bw_cap;
+};
+
+/**
+ * struct tdls_iovar - common structure for tdls iovars.
+ *
+ * @ea: ether address of peer station.
+ * @mode: mode value depending on specific tdls iovar.
+ * @chanspec: channel specification.
+ * @pad: unused (for future use).
+ */
+struct brcmf_tdls_iovar_le {
+ u8 ea[ETH_ALEN]; /* Station address */
+ u8 mode; /* mode: depends on iovar */
+ __le16 chanspec;
+ __le32 pad; /* future */
+};
+
+enum brcmf_tdls_manual_ep_ops {
+ BRCMF_TDLS_MANUAL_EP_CREATE = 1,
+ BRCMF_TDLS_MANUAL_EP_DELETE = 3,
+ BRCMF_TDLS_MANUAL_EP_DISCOVERY = 6
+};
+
+/* Pattern matching filter. Specifies an offset within received packets to
+ * start matching, the pattern to match, the size of the pattern, and a bitmask
+ * that indicates which bits within the pattern should be matched.
+ */
+struct brcmf_pkt_filter_pattern_le {
+ /*
+ * Offset within received packet to start pattern matching.
+ * Offset '0' is the first byte of the ethernet header.
+ */
+ __le32 offset;
+ /* Size of the pattern. Bitmask must be the same size.*/
+ __le32 size_bytes;
+ /*
+ * Variable length mask and pattern data. mask starts at offset 0.
+ * Pattern immediately follows mask.
+ */
+ u8 mask_and_pattern[1];
+};
+
+/* IOVAR "pkt_filter_add" parameter. Used to install packet filters. */
+struct brcmf_pkt_filter_le {
+ __le32 id; /* Unique filter id, specified by app. */
+ __le32 type; /* Filter type (WL_PKT_FILTER_TYPE_xxx). */
+ __le32 negate_match; /* Negate the result of filter matches */
+ union { /* Filter definitions */
+ struct brcmf_pkt_filter_pattern_le pattern; /* Filter pattern */
+ } u;
+};
+
+/* IOVAR "pkt_filter_enable" parameter. */
+struct brcmf_pkt_filter_enable_le {
+ __le32 id; /* Unique filter id */
+ __le32 enable; /* Enable/disable bool */
+};
+
+/* BSS info structure
+ * Applications MUST CHECK ie_offset field and length field to access IEs and
+ * next bss_info structure in a vector (in struct brcmf_scan_results)
+ */
+struct brcmf_bss_info_le {
+ __le32 version; /* version field */
+ __le32 length; /* byte length of data in this record,
+ * starting at version and including IEs
+ */
+ u8 BSSID[ETH_ALEN];
+ __le16 beacon_period; /* units are Kusec */
+ __le16 capability; /* Capability information */
+ u8 SSID_len;
+ u8 SSID[32];
+ struct {
+ __le32 count; /* # rates in this set */
+ u8 rates[16]; /* rates in 500kbps units w/hi bit set if basic */
+ } rateset; /* supported rates */
+ __le16 chanspec; /* chanspec for bss */
+ __le16 atim_window; /* units are Kusec */
+ u8 dtim_period; /* DTIM period */
+ __le16 RSSI; /* receive signal strength (in dBm) */
+ s8 phy_noise; /* noise (in dBm) */
+
+ u8 n_cap; /* BSS is 802.11N Capable */
+ /* 802.11N BSS Capabilities (based on HT_CAP_*): */
+ __le32 nbss_cap;
+ u8 ctl_ch; /* 802.11N BSS control channel number */
+ __le32 reserved32[1]; /* Reserved for expansion of BSS properties */
+ u8 flags; /* flags */
+ u8 reserved[3]; /* Reserved for expansion of BSS properties */
+ u8 basic_mcs[BRCMF_MCSSET_LEN]; /* 802.11N BSS required MCS set */
+
+ __le16 ie_offset; /* offset at which IEs start, from beginning */
+ __le32 ie_length; /* byte length of Information Elements */
+ __le16 SNR; /* average SNR of during frame reception */
+ /* Add new fields here */
+ /* variable length Information Elements */
+};
+
+struct brcm_rateset_le {
+ /* # rates in this set */
+ __le32 count;
+ /* rates in 500kbps units w/hi bit set if basic */
+ u8 rates[BRCMF_MAXRATES_IN_SET];
+};
+
+struct brcmf_ssid_le {
+ __le32 SSID_len;
+ unsigned char SSID[IEEE80211_MAX_SSID_LEN];
+};
+
+struct brcmf_scan_params_le {
+ struct brcmf_ssid_le ssid_le; /* default: {0, ""} */
+ u8 bssid[ETH_ALEN]; /* default: bcast */
+ s8 bss_type; /* default: any,
+ * DOT11_BSSTYPE_ANY/INFRASTRUCTURE/INDEPENDENT
+ */
+ u8 scan_type; /* flags, 0 use default */
+ __le32 nprobes; /* -1 use default, number of probes per channel */
+ __le32 active_time; /* -1 use default, dwell time per channel for
+ * active scanning
+ */
+ __le32 passive_time; /* -1 use default, dwell time per channel
+ * for passive scanning
+ */
+ __le32 home_time; /* -1 use default, dwell time for the
+ * home channel between channel scans
+ */
+ __le32 channel_num; /* count of channels and ssids that follow
+ *
+ * low half is count of channels in
+ * channel_list, 0 means default (use all
+ * available channels)
+ *
+ * high half is entries in struct brcmf_ssid
+ * array that follows channel_list, aligned for
+ * s32 (4 bytes) meaning an odd channel count
+ * implies a 2-byte pad between end of
+ * channel_list and first ssid
+ *
+ * if ssid count is zero, single ssid in the
+ * fixed parameter portion is assumed, otherwise
+ * ssid in the fixed portion is ignored
+ */
+ __le16 channel_list[1]; /* list of chanspecs */
+};
+
+struct brcmf_scan_results {
+ u32 buflen;
+ u32 version;
+ u32 count;
+ struct brcmf_bss_info_le bss_info_le[];
+};
+
+struct brcmf_escan_params_le {
+ __le32 version;
+ __le16 action;
+ __le16 sync_id;
+ struct brcmf_scan_params_le params_le;
+};
+
+struct brcmf_escan_result_le {
+ __le32 buflen;
+ __le32 version;
+ __le16 sync_id;
+ __le16 bss_count;
+ struct brcmf_bss_info_le bss_info_le;
+};
+
+#define WL_ESCAN_RESULTS_FIXED_SIZE (sizeof(struct brcmf_escan_result_le) - \
+ sizeof(struct brcmf_bss_info_le))
+
+/* used for association with a specific BSSID and chanspec list */
+struct brcmf_assoc_params_le {
+ /* 00:00:00:00:00:00: broadcast scan */
+ u8 bssid[ETH_ALEN];
+ /* 0: all available channels, otherwise count of chanspecs in
+ * chanspec_list */
+ __le32 chanspec_num;
+ /* list of chanspecs */
+ __le16 chanspec_list[1];
+};
+
+/**
+ * struct join_pref params - parameters for preferred join selection.
+ *
+ * @type: preference type (see enum brcmf_join_pref_types).
+ * @len: length of bytes following (currently always 2).
+ * @rssi_gain: signal gain for selection (only when @type is RSSI_DELTA).
+ * @band: band to which selection preference applies.
+ * This is used if @type is BAND or RSSI_DELTA.
+ */
+struct brcmf_join_pref_params {
+ u8 type;
+ u8 len;
+ u8 rssi_gain;
+ u8 band;
+};
+
+/* used for join with or without a specific bssid and channel list */
+struct brcmf_join_params {
+ struct brcmf_ssid_le ssid_le;
+ struct brcmf_assoc_params_le params_le;
+};
+
+/* scan params for extended join */
+struct brcmf_join_scan_params_le {
+ u8 scan_type; /* 0 use default, active or passive scan */
+ __le32 nprobes; /* -1 use default, nr of probes per channel */
+ __le32 active_time; /* -1 use default, dwell time per channel for
+ * active scanning
+ */
+ __le32 passive_time; /* -1 use default, dwell time per channel
+ * for passive scanning
+ */
+ __le32 home_time; /* -1 use default, dwell time for the home
+ * channel between channel scans
+ */
+};
+
+/* extended join params */
+struct brcmf_ext_join_params_le {
+ struct brcmf_ssid_le ssid_le; /* {0, ""}: wildcard scan */
+ struct brcmf_join_scan_params_le scan_le;
+ struct brcmf_assoc_params_le assoc_le;
+};
+
+struct brcmf_wsec_key {
+ u32 index; /* key index */
+ u32 len; /* key length */
+ u8 data[WLAN_MAX_KEY_LEN]; /* key data */
+ u32 pad_1[18];
+ u32 algo; /* CRYPTO_ALGO_AES_CCM, CRYPTO_ALGO_WEP128, etc */
+ u32 flags; /* misc flags */
+ u32 pad_2[3];
+ u32 iv_initialized; /* has IV been initialized already? */
+ u32 pad_3;
+ /* Rx IV */
+ struct {
+ u32 hi; /* upper 32 bits of IV */
+ u16 lo; /* lower 16 bits of IV */
+ } rxiv;
+ u32 pad_4[2];
+ u8 ea[ETH_ALEN]; /* per station */
+};
+
+/*
+ * dongle requires same struct as above but with fields in little endian order
+ */
+struct brcmf_wsec_key_le {
+ __le32 index; /* key index */
+ __le32 len; /* key length */
+ u8 data[WLAN_MAX_KEY_LEN]; /* key data */
+ __le32 pad_1[18];
+ __le32 algo; /* CRYPTO_ALGO_AES_CCM, CRYPTO_ALGO_WEP128, etc */
+ __le32 flags; /* misc flags */
+ __le32 pad_2[3];
+ __le32 iv_initialized; /* has IV been initialized already? */
+ __le32 pad_3;
+ /* Rx IV */
+ struct {
+ __le32 hi; /* upper 32 bits of IV */
+ __le16 lo; /* lower 16 bits of IV */
+ } rxiv;
+ __le32 pad_4[2];
+ u8 ea[ETH_ALEN]; /* per station */
+};
+
+/**
+ * struct brcmf_wsec_pmk_le - firmware pmk material.
+ *
+ * @key_len: number of octets in key material.
+ * @flags: key handling qualifiers.
+ * @key: PMK key material.
+ */
+struct brcmf_wsec_pmk_le {
+ __le16 key_len;
+ __le16 flags;
+ u8 key[2 * BRCMF_WSEC_MAX_PSK_LEN + 1];
+};
+
+/* Used to get specific STA parameters */
+struct brcmf_scb_val_le {
+ __le32 val;
+ u8 ea[ETH_ALEN];
+};
+
+/* channel encoding */
+struct brcmf_channel_info_le {
+ __le32 hw_channel;
+ __le32 target_channel;
+ __le32 scan_channel;
+};
+
+struct brcmf_sta_info_le {
+ __le16 ver; /* version of this struct */
+ __le16 len; /* length in bytes of this structure */
+ __le16 cap; /* sta's advertised capabilities */
+ __le32 flags; /* flags defined below */
+ __le32 idle; /* time since data pkt rx'd from sta */
+ u8 ea[ETH_ALEN]; /* Station address */
+ __le32 count; /* # rates in this set */
+ u8 rates[BRCMF_MAXRATES_IN_SET]; /* rates in 500kbps units */
+ /* w/hi bit set if basic */
+ __le32 in; /* seconds elapsed since associated */
+ __le32 listen_interval_inms; /* Min Listen interval in ms for STA */
+ __le32 tx_pkts; /* # of packets transmitted */
+ __le32 tx_failures; /* # of packets failed */
+ __le32 rx_ucast_pkts; /* # of unicast packets received */
+ __le32 rx_mcast_pkts; /* # of multicast packets received */
+ __le32 tx_rate; /* Rate of last successful tx frame */
+ __le32 rx_rate; /* Rate of last successful rx frame */
+ __le32 rx_decrypt_succeeds; /* # of packet decrypted successfully */
+ __le32 rx_decrypt_failures; /* # of packet decrypted failed */
+ __le32 tx_tot_pkts; /* # of tx pkts (ucast + mcast) */
+ __le32 rx_tot_pkts; /* # of data packets recvd (uni + mcast) */
+ __le32 tx_mcast_pkts; /* # of mcast pkts txed */
+ __le64 tx_tot_bytes; /* data bytes txed (ucast + mcast) */
+ __le64 rx_tot_bytes; /* data bytes recvd (ucast + mcast) */
+ __le64 tx_ucast_bytes; /* data bytes txed (ucast) */
+ __le64 tx_mcast_bytes; /* # data bytes txed (mcast) */
+ __le64 rx_ucast_bytes; /* data bytes recvd (ucast) */
+ __le64 rx_mcast_bytes; /* data bytes recvd (mcast) */
+ s8 rssi[BRCMF_ANT_MAX]; /* per antenna rssi */
+ s8 nf[BRCMF_ANT_MAX]; /* per antenna noise floor */
+ __le16 aid; /* association ID */
+ __le16 ht_capabilities; /* advertised ht caps */
+ __le16 vht_flags; /* converted vht flags */
+ __le32 tx_pkts_retry_cnt; /* # of frames where a retry was
+ * exhausted.
+ */
+ __le32 tx_pkts_retry_exhausted; /* # of user frames where a retry
+ * was exhausted
+ */
+ s8 rx_lastpkt_rssi[BRCMF_ANT_MAX]; /* Per antenna RSSI of last
+ * received data frame.
+ */
+ /* TX WLAN retry/failure statistics:
+ * Separated for host requested frames and locally generated frames.
+ * Include unicast frame only where the retries/failures can be counted.
+ */
+ __le32 tx_pkts_total; /* # user frames sent successfully */
+ __le32 tx_pkts_retries; /* # user frames retries */
+ __le32 tx_pkts_fw_total; /* # FW generated sent successfully */
+ __le32 tx_pkts_fw_retries; /* # retries for FW generated frames */
+ __le32 tx_pkts_fw_retry_exhausted; /* # FW generated where a retry
+ * was exhausted
+ */
+ __le32 rx_pkts_retried; /* # rx with retry bit set */
+ __le32 tx_rate_fallback; /* lowest fallback TX rate */
+};
+
+struct brcmf_chanspec_list {
+ __le32 count; /* # of entries */
+ __le32 element[1]; /* variable length uint32 list */
+};
+
+/*
+ * WLC_E_PROBRESP_MSG
+ * WLC_E_P2P_PROBREQ_MSG
+ * WLC_E_ACTION_FRAME_RX
+ */
+struct brcmf_rx_mgmt_data {
+ __be16 version;
+ __be16 chanspec;
+ __be32 rssi;
+ __be32 mactime;
+ __be32 rate;
+};
+
+/**
+ * struct brcmf_fil_wowl_pattern_le - wowl pattern configuration struct.
+ *
+ * @cmd: "add", "del" or "clr".
+ * @masksize: Size of the mask in #of bytes
+ * @offset: Pattern byte offset in packet
+ * @patternoffset: Offset of start of pattern. Starting from field masksize.
+ * @patternsize: Size of the pattern itself in #of bytes
+ * @id: id
+ * @reasonsize: Size of the wakeup reason code
+ * @type: Type of pattern (enum brcmf_wowl_pattern_type)
+ */
+struct brcmf_fil_wowl_pattern_le {
+ u8 cmd[4];
+ __le32 masksize;
+ __le32 offset;
+ __le32 patternoffset;
+ __le32 patternsize;
+ __le32 id;
+ __le32 reasonsize;
+ __le32 type;
+ /* u8 mask[] - Mask follows the structure above */
+ /* u8 pattern[] - Pattern follows the mask is at 'patternoffset' */
+};
+
+struct brcmf_mbss_ssid_le {
+ __le32 bsscfgidx;
+ __le32 SSID_len;
+ unsigned char SSID[32];
+};
+
+/**
+ * struct brcmf_fil_country_le - country configuration structure.
+ *
+ * @country_abbrev: null-terminated country code used in the country IE.
+ * @rev: revision specifier for ccode. on set, -1 indicates unspecified.
+ * @ccode: null-terminated built-in country code.
+ */
+struct brcmf_fil_country_le {
+ char country_abbrev[BRCMF_COUNTRY_BUF_SZ];
+ __le32 rev;
+ char ccode[BRCMF_COUNTRY_BUF_SZ];
+};
+
+/**
+ * struct brcmf_rev_info_le - device revision info.
+ *
+ * @vendorid: PCI vendor id.
+ * @deviceid: device id of chip.
+ * @radiorev: radio revision.
+ * @chiprev: chip revision.
+ * @corerev: core revision.
+ * @boardid: board identifier (usu. PCI sub-device id).
+ * @boardvendor: board vendor (usu. PCI sub-vendor id).
+ * @boardrev: board revision.
+ * @driverrev: driver version.
+ * @ucoderev: microcode version.
+ * @bus: bus type.
+ * @chipnum: chip number.
+ * @phytype: phy type.
+ * @phyrev: phy revision.
+ * @anarev: anacore rev.
+ * @chippkg: chip package info.
+ * @nvramrev: nvram revision number.
+ */
+struct brcmf_rev_info_le {
+ __le32 vendorid;
+ __le32 deviceid;
+ __le32 radiorev;
+ __le32 chiprev;
+ __le32 corerev;
+ __le32 boardid;
+ __le32 boardvendor;
+ __le32 boardrev;
+ __le32 driverrev;
+ __le32 ucoderev;
+ __le32 bus;
+ __le32 chipnum;
+ __le32 phytype;
+ __le32 phyrev;
+ __le32 anarev;
+ __le32 chippkg;
+ __le32 nvramrev;
+};
+
+/**
+ * struct brcmf_assoclist_le - request assoc list.
+ *
+ * @count: indicates number of stations.
+ * @mac: MAC addresses of stations.
+ */
+struct brcmf_assoclist_le {
+ __le32 count;
+ u8 mac[BRCMF_MAX_ASSOCLIST][ETH_ALEN];
+};
+
+/**
+ * struct brcmf_wowl_wakeind_le - Wakeup indicators
+ * Note: note both fields contain same information.
+ *
+ * @pci_wakeind: Whether PCI PMECSR PMEStatus bit was set.
+ * @ucode_wakeind: What wakeup-event indication was set by ucode
+ */
+struct brcmf_wowl_wakeind_le {
+ __le32 pci_wakeind;
+ __le32 ucode_wakeind;
+};
+
+/**
+ * struct brcmf_pmksa - PMK Security Association
+ *
+ * @bssid: The AP's BSSID.
+ * @pmkid: he PMK material itself.
+ */
+struct brcmf_pmksa {
+ u8 bssid[ETH_ALEN];
+ u8 pmkid[WLAN_PMKID_LEN];
+};
+
+/**
+ * struct brcmf_pmk_list_le - List of pmksa's.
+ *
+ * @npmk: Number of pmksa's.
+ * @pmk: PMK SA information.
+ */
+struct brcmf_pmk_list_le {
+ __le32 npmk;
+ struct brcmf_pmksa pmk[BRCMF_MAXPMKID];
+};
+
+/**
+ * struct brcmf_pno_param_le - PNO scan configuration parameters
+ *
+ * @version: PNO parameters version.
+ * @scan_freq: scan frequency.
+ * @lost_network_timeout: #sec. to declare discovered network as lost.
+ * @flags: Bit field to control features of PFN such as sort criteria auto
+ * enable switch and background scan.
+ * @rssi_margin: Margin to avoid jitter for choosing a PFN based on RSSI sort
+ * criteria.
+ * @bestn: number of best networks in each scan.
+ * @mscan: number of scans recorded.
+ * @repeat: minimum number of scan intervals before scan frequency changes
+ * in adaptive scan.
+ * @exp: exponent of 2 for maximum scan interval.
+ * @slow_freq: slow scan period.
+ */
+struct brcmf_pno_param_le {
+ __le32 version;
+ __le32 scan_freq;
+ __le32 lost_network_timeout;
+ __le16 flags;
+ __le16 rssi_margin;
+ u8 bestn;
+ u8 mscan;
+ u8 repeat;
+ u8 exp;
+ __le32 slow_freq;
+};
+
+/**
+ * struct brcmf_pno_config_le - PNO channel configuration.
+ *
+ * @reporttype: determines what is reported.
+ * @channel_num: number of channels specified in @channel_list.
+ * @channel_list: channels to use in PNO scan.
+ * @flags: reserved.
+ */
+struct brcmf_pno_config_le {
+ __le32 reporttype;
+ __le32 channel_num;
+ __le16 channel_list[BRCMF_NUMCHANNELS];
+ __le32 flags;
+};
+
+/**
+ * struct brcmf_pno_net_param_le - scan parameters per preferred network.
+ *
+ * @ssid: ssid name and its length.
+ * @flags: bit2: hidden.
+ * @infra: BSS vs IBSS.
+ * @auth: Open vs Closed.
+ * @wpa_auth: WPA type.
+ * @wsec: wsec value.
+ */
+struct brcmf_pno_net_param_le {
+ struct brcmf_ssid_le ssid;
+ __le32 flags;
+ __le32 infra;
+ __le32 auth;
+ __le32 wpa_auth;
+ __le32 wsec;
+};
+
+/**
+ * struct brcmf_pno_net_info_le - information per found network.
+ *
+ * @bssid: BSS network identifier.
+ * @channel: channel number only.
+ * @SSID_len: length of ssid.
+ * @SSID: ssid characters.
+ * @RSSI: receive signal strength (in dBm).
+ * @timestamp: age in seconds.
+ */
+struct brcmf_pno_net_info_le {
+ u8 bssid[ETH_ALEN];
+ u8 channel;
+ u8 SSID_len;
+ u8 SSID[32];
+ __le16 RSSI;
+ __le16 timestamp;
+};
+
+/**
+ * struct brcmf_pno_scanresults_le - result returned in PNO NET FOUND event.
+ *
+ * @version: PNO version identifier.
+ * @status: indicates completion status of PNO scan.
+ * @count: amount of brcmf_pno_net_info_le entries appended.
+ */
+struct brcmf_pno_scanresults_le {
+ __le32 version;
+ __le32 status;
+ __le32 count;
+};
+
+struct brcmf_pno_scanresults_v2_le {
+ __le32 version;
+ __le32 status;
+ __le32 count;
+ __le32 scan_ch_bucket;
+};
+
+/**
+ * struct brcmf_pno_macaddr_le - to configure PNO macaddr randomization.
+ *
+ * @version: PNO version identifier.
+ * @flags: Flags defining how mac addrss should be used.
+ * @mac: MAC address.
+ */
+struct brcmf_pno_macaddr_le {
+ u8 version;
+ u8 flags;
+ u8 mac[ETH_ALEN];
+};
+
+/**
+ * struct brcmf_pktcnt_le - packet counters.
+ *
+ * @rx_good_pkt: packets (MSDUs & MMPDUs) received from this station
+ * @rx_bad_pkt: failed rx packets
+ * @tx_good_pkt: packets (MSDUs & MMPDUs) transmitted to this station
+ * @tx_bad_pkt: failed tx packets
+ * @rx_ocast_good_pkt: unicast packets destined for others
+ */
+struct brcmf_pktcnt_le {
+ __le32 rx_good_pkt;
+ __le32 rx_bad_pkt;
+ __le32 tx_good_pkt;
+ __le32 tx_bad_pkt;
+ __le32 rx_ocast_good_pkt;
+};
+
+/**
+ * struct brcmf_gtk_keyinfo_le - GTP rekey data
+ *
+ * @kck: key confirmation key.
+ * @kek: key encryption key.
+ * @replay_counter: replay counter.
+ */
+struct brcmf_gtk_keyinfo_le {
+ u8 kck[BRCMF_RSN_KCK_LENGTH];
+ u8 kek[BRCMF_RSN_KEK_LENGTH];
+ u8 replay_counter[BRCMF_RSN_REPLAY_LEN];
+};
+
+/**
+ * struct brcmf_dload_data_le - data passing to firmware for downloading
+ */
+struct brcmf_dload_data_le {
+ __le16 flag;
+ __le16 dload_type;
+ __le32 len;
+ __le32 crc;
+ u8 data[1];
+};
+
+typedef struct wl_mkeep_alive_pkt {
+ __le16 version; /* Version for mkeep_alive */
+ __le16 length; /* length of fixed parameters in the structure */
+ __le32 period_msec;
+ __le16 len_bytes;
+ u8 keep_alive_id; /* 0 - 3 for N = 4 */
+ u8 data[1];
+} wl_mkeep_alive_pkt_t;
+#define WL_MKEEP_ALIVE_VERSION 1
+#define WL_MKEEP_ALIVE_FIXED_LEN offsetof(wl_mkeep_alive_pkt_t, data)
+#define WL_MKEEP_ALIVE_PRECISION 500
+
+#endif /* FWIL_TYPES_H_ */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c
new file mode 100644
index 0000000..f59642b
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c
@@ -0,0 +1,2480 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/if_ether.h>
+#include <linux/spinlock.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/err.h>
+#include <linux/jiffies.h>
+#include <net/cfg80211.h>
+
+#include <brcmu_utils.h>
+#include <brcmu_wifi.h>
+#include "core.h"
+#include "debug.h"
+#include "bus.h"
+#include "fwil.h"
+#include "fwil_types.h"
+#include "fweh.h"
+#include "fwsignal.h"
+#include "p2p.h"
+#include "cfg80211.h"
+#include "proto.h"
+#include "bcdc.h"
+#include "common.h"
+
+/**
+ * DOC: Firmware Signalling
+ *
+ * Firmware can send signals to host and vice versa, which are passed in the
+ * data packets using TLV based header. This signalling layer is on top of the
+ * BDC bus protocol layer.
+ */
+
+/*
+ * single definition for firmware-driver flow control tlv's.
+ *
+ * each tlv is specified by BRCMF_FWS_TLV_DEF(name, ID, length).
+ * A length value 0 indicates variable length tlv.
+ */
+#define BRCMF_FWS_TLV_DEFLIST \
+ BRCMF_FWS_TLV_DEF(MAC_OPEN, 1, 1) \
+ BRCMF_FWS_TLV_DEF(MAC_CLOSE, 2, 1) \
+ BRCMF_FWS_TLV_DEF(MAC_REQUEST_CREDIT, 3, 2) \
+ BRCMF_FWS_TLV_DEF(TXSTATUS, 4, 4) \
+ BRCMF_FWS_TLV_DEF(PKTTAG, 5, 4) \
+ BRCMF_FWS_TLV_DEF(MACDESC_ADD, 6, 8) \
+ BRCMF_FWS_TLV_DEF(MACDESC_DEL, 7, 8) \
+ BRCMF_FWS_TLV_DEF(RSSI, 8, 1) \
+ BRCMF_FWS_TLV_DEF(INTERFACE_OPEN, 9, 1) \
+ BRCMF_FWS_TLV_DEF(INTERFACE_CLOSE, 10, 1) \
+ BRCMF_FWS_TLV_DEF(FIFO_CREDITBACK, 11, 6) \
+ BRCMF_FWS_TLV_DEF(PENDING_TRAFFIC_BMP, 12, 2) \
+ BRCMF_FWS_TLV_DEF(MAC_REQUEST_PACKET, 13, 3) \
+ BRCMF_FWS_TLV_DEF(HOST_REORDER_RXPKTS, 14, 10) \
+ BRCMF_FWS_TLV_DEF(TRANS_ID, 18, 6) \
+ BRCMF_FWS_TLV_DEF(COMP_TXSTATUS, 19, 1) \
+ BRCMF_FWS_TLV_DEF(FILLER, 255, 0)
+
+/*
+ * enum brcmf_fws_tlv_type - definition of tlv identifiers.
+ */
+#define BRCMF_FWS_TLV_DEF(name, id, len) \
+ BRCMF_FWS_TYPE_ ## name = id,
+enum brcmf_fws_tlv_type {
+ BRCMF_FWS_TLV_DEFLIST
+ BRCMF_FWS_TYPE_INVALID
+};
+#undef BRCMF_FWS_TLV_DEF
+
+/*
+ * enum brcmf_fws_tlv_len - definition of tlv lengths.
+ */
+#define BRCMF_FWS_TLV_DEF(name, id, len) \
+ BRCMF_FWS_TYPE_ ## name ## _LEN = (len),
+enum brcmf_fws_tlv_len {
+ BRCMF_FWS_TLV_DEFLIST
+};
+#undef BRCMF_FWS_TLV_DEF
+
+/* AMPDU rx reordering definitions */
+#define BRCMF_RXREORDER_FLOWID_OFFSET 0
+#define BRCMF_RXREORDER_MAXIDX_OFFSET 2
+#define BRCMF_RXREORDER_FLAGS_OFFSET 4
+#define BRCMF_RXREORDER_CURIDX_OFFSET 6
+#define BRCMF_RXREORDER_EXPIDX_OFFSET 8
+
+#define BRCMF_RXREORDER_DEL_FLOW 0x01
+#define BRCMF_RXREORDER_FLUSH_ALL 0x02
+#define BRCMF_RXREORDER_CURIDX_VALID 0x04
+#define BRCMF_RXREORDER_EXPIDX_VALID 0x08
+#define BRCMF_RXREORDER_NEW_HOLE 0x10
+
+#ifdef DEBUG
+/*
+ * brcmf_fws_tlv_names - array of tlv names.
+ */
+#define BRCMF_FWS_TLV_DEF(name, id, len) \
+ { id, #name },
+static struct {
+ enum brcmf_fws_tlv_type id;
+ const char *name;
+} brcmf_fws_tlv_names[] = {
+ BRCMF_FWS_TLV_DEFLIST
+};
+#undef BRCMF_FWS_TLV_DEF
+
+
+static const char *brcmf_fws_get_tlv_name(enum brcmf_fws_tlv_type id)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(brcmf_fws_tlv_names); i++)
+ if (brcmf_fws_tlv_names[i].id == id)
+ return brcmf_fws_tlv_names[i].name;
+
+ return "INVALID";
+}
+#else
+static const char *brcmf_fws_get_tlv_name(enum brcmf_fws_tlv_type id)
+{
+ return "NODEBUG";
+}
+#endif /* DEBUG */
+
+/*
+ * The PKTTAG tlv has additional bytes when firmware-signalling
+ * mode has REUSESEQ flag set.
+ */
+#define BRCMF_FWS_TYPE_SEQ_LEN 2
+
+/*
+ * flags used to enable tlv signalling from firmware.
+ */
+#define BRCMF_FWS_FLAGS_RSSI_SIGNALS 0x0001
+#define BRCMF_FWS_FLAGS_XONXOFF_SIGNALS 0x0002
+#define BRCMF_FWS_FLAGS_CREDIT_STATUS_SIGNALS 0x0004
+#define BRCMF_FWS_FLAGS_HOST_PROPTXSTATUS_ACTIVE 0x0008
+#define BRCMF_FWS_FLAGS_PSQ_GENERATIONFSM_ENABLE 0x0010
+#define BRCMF_FWS_FLAGS_PSQ_ZERO_BUFFER_ENABLE 0x0020
+#define BRCMF_FWS_FLAGS_HOST_RXREORDER_ACTIVE 0x0040
+
+#define BRCMF_FWS_MAC_DESC_TABLE_SIZE 32
+#define BRCMF_FWS_MAC_DESC_ID_INVALID 0xff
+
+#define BRCMF_FWS_HOSTIF_FLOWSTATE_OFF 0
+#define BRCMF_FWS_HOSTIF_FLOWSTATE_ON 1
+#define BRCMF_FWS_FLOWCONTROL_HIWATER 128
+#define BRCMF_FWS_FLOWCONTROL_LOWATER 64
+
+#define BRCMF_FWS_PSQ_PREC_COUNT ((BRCMF_FWS_FIFO_COUNT + 1) * 2)
+#define BRCMF_FWS_PSQ_LEN 256
+
+#define BRCMF_FWS_HTOD_FLAG_PKTFROMHOST 0x01
+#define BRCMF_FWS_HTOD_FLAG_PKT_REQUESTED 0x02
+
+#define BRCMF_FWS_RET_OK_NOSCHEDULE 0
+#define BRCMF_FWS_RET_OK_SCHEDULE 1
+
+#define BRCMF_FWS_MODE_REUSESEQ_SHIFT 3 /* seq reuse */
+#define BRCMF_FWS_MODE_SET_REUSESEQ(x, val) ((x) = \
+ ((x) & ~(1 << BRCMF_FWS_MODE_REUSESEQ_SHIFT)) | \
+ (((val) & 1) << BRCMF_FWS_MODE_REUSESEQ_SHIFT))
+#define BRCMF_FWS_MODE_GET_REUSESEQ(x) \
+ (((x) >> BRCMF_FWS_MODE_REUSESEQ_SHIFT) & 1)
+
+/**
+ * enum brcmf_fws_skb_state - indicates processing state of skb.
+ *
+ * @BRCMF_FWS_SKBSTATE_NEW: sk_buff is newly arrived in the driver.
+ * @BRCMF_FWS_SKBSTATE_DELAYED: sk_buff had to wait on queue.
+ * @BRCMF_FWS_SKBSTATE_SUPPRESSED: sk_buff has been suppressed by firmware.
+ * @BRCMF_FWS_SKBSTATE_TIM: allocated for TIM update info.
+ */
+enum brcmf_fws_skb_state {
+ BRCMF_FWS_SKBSTATE_NEW,
+ BRCMF_FWS_SKBSTATE_DELAYED,
+ BRCMF_FWS_SKBSTATE_SUPPRESSED,
+ BRCMF_FWS_SKBSTATE_TIM
+};
+
+/**
+ * struct brcmf_skbuff_cb - control buffer associated with skbuff.
+ *
+ * @bus_flags: 2 bytes reserved for bus specific parameters
+ * @if_flags: holds interface index and packet related flags.
+ * @htod: host to device packet identifier (used in PKTTAG tlv).
+ * @htod_seq: this 16-bit is original seq number for every suppress packet.
+ * @state: transmit state of the packet.
+ * @mac: descriptor related to destination for this packet.
+ *
+ * This information is stored in control buffer struct sk_buff::cb, which
+ * provides 48 bytes of storage so this structure should not exceed that.
+ */
+struct brcmf_skbuff_cb {
+ u16 bus_flags;
+ u16 if_flags;
+ u32 htod;
+ u16 htod_seq;
+ enum brcmf_fws_skb_state state;
+ struct brcmf_fws_mac_descriptor *mac;
+};
+
+/*
+ * macro casting skbuff control buffer to struct brcmf_skbuff_cb.
+ */
+#define brcmf_skbcb(skb) ((struct brcmf_skbuff_cb *)((skb)->cb))
+
+/*
+ * sk_buff control if flags
+ *
+ * b[11] - packet sent upon firmware request.
+ * b[10] - packet only contains signalling data.
+ * b[9] - packet is a tx packet.
+ * b[8] - packet used requested credit
+ * b[7] - interface in AP mode.
+ * b[3:0] - interface index.
+ */
+#define BRCMF_SKB_IF_FLAGS_REQUESTED_MASK 0x0800
+#define BRCMF_SKB_IF_FLAGS_REQUESTED_SHIFT 11
+#define BRCMF_SKB_IF_FLAGS_SIGNAL_ONLY_MASK 0x0400
+#define BRCMF_SKB_IF_FLAGS_SIGNAL_ONLY_SHIFT 10
+#define BRCMF_SKB_IF_FLAGS_TRANSMIT_MASK 0x0200
+#define BRCMF_SKB_IF_FLAGS_TRANSMIT_SHIFT 9
+#define BRCMF_SKB_IF_FLAGS_REQ_CREDIT_MASK 0x0100
+#define BRCMF_SKB_IF_FLAGS_REQ_CREDIT_SHIFT 8
+#define BRCMF_SKB_IF_FLAGS_IF_AP_MASK 0x0080
+#define BRCMF_SKB_IF_FLAGS_IF_AP_SHIFT 7
+#define BRCMF_SKB_IF_FLAGS_INDEX_MASK 0x000f
+#define BRCMF_SKB_IF_FLAGS_INDEX_SHIFT 0
+
+#define brcmf_skb_if_flags_set_field(skb, field, value) \
+ brcmu_maskset16(&(brcmf_skbcb(skb)->if_flags), \
+ BRCMF_SKB_IF_FLAGS_ ## field ## _MASK, \
+ BRCMF_SKB_IF_FLAGS_ ## field ## _SHIFT, (value))
+#define brcmf_skb_if_flags_get_field(skb, field) \
+ brcmu_maskget16(brcmf_skbcb(skb)->if_flags, \
+ BRCMF_SKB_IF_FLAGS_ ## field ## _MASK, \
+ BRCMF_SKB_IF_FLAGS_ ## field ## _SHIFT)
+
+/*
+ * sk_buff control packet identifier
+ *
+ * 32-bit packet identifier used in PKTTAG tlv from host to dongle.
+ *
+ * - Generated at the host (e.g. dhd)
+ * - Seen as a generic sequence number by firmware except for the flags field.
+ *
+ * Generation : b[31] => generation number for this packet [host->fw]
+ * OR, current generation number [fw->host]
+ * Flags : b[30:27] => command, status flags
+ * FIFO-AC : b[26:24] => AC-FIFO id
+ * h-slot : b[23:8] => hanger-slot
+ * freerun : b[7:0] => A free running counter
+ */
+#define BRCMF_SKB_HTOD_TAG_GENERATION_MASK 0x80000000
+#define BRCMF_SKB_HTOD_TAG_GENERATION_SHIFT 31
+#define BRCMF_SKB_HTOD_TAG_FLAGS_MASK 0x78000000
+#define BRCMF_SKB_HTOD_TAG_FLAGS_SHIFT 27
+#define BRCMF_SKB_HTOD_TAG_FIFO_MASK 0x07000000
+#define BRCMF_SKB_HTOD_TAG_FIFO_SHIFT 24
+#define BRCMF_SKB_HTOD_TAG_HSLOT_MASK 0x00ffff00
+#define BRCMF_SKB_HTOD_TAG_HSLOT_SHIFT 8
+#define BRCMF_SKB_HTOD_TAG_FREERUN_MASK 0x000000ff
+#define BRCMF_SKB_HTOD_TAG_FREERUN_SHIFT 0
+
+#define brcmf_skb_htod_tag_set_field(skb, field, value) \
+ brcmu_maskset32(&(brcmf_skbcb(skb)->htod), \
+ BRCMF_SKB_HTOD_TAG_ ## field ## _MASK, \
+ BRCMF_SKB_HTOD_TAG_ ## field ## _SHIFT, (value))
+#define brcmf_skb_htod_tag_get_field(skb, field) \
+ brcmu_maskget32(brcmf_skbcb(skb)->htod, \
+ BRCMF_SKB_HTOD_TAG_ ## field ## _MASK, \
+ BRCMF_SKB_HTOD_TAG_ ## field ## _SHIFT)
+
+#define BRCMF_SKB_HTOD_SEQ_FROMFW_MASK 0x2000
+#define BRCMF_SKB_HTOD_SEQ_FROMFW_SHIFT 13
+#define BRCMF_SKB_HTOD_SEQ_FROMDRV_MASK 0x1000
+#define BRCMF_SKB_HTOD_SEQ_FROMDRV_SHIFT 12
+#define BRCMF_SKB_HTOD_SEQ_NR_MASK 0x0fff
+#define BRCMF_SKB_HTOD_SEQ_NR_SHIFT 0
+
+#define brcmf_skb_htod_seq_set_field(skb, field, value) \
+ brcmu_maskset16(&(brcmf_skbcb(skb)->htod_seq), \
+ BRCMF_SKB_HTOD_SEQ_ ## field ## _MASK, \
+ BRCMF_SKB_HTOD_SEQ_ ## field ## _SHIFT, (value))
+#define brcmf_skb_htod_seq_get_field(skb, field) \
+ brcmu_maskget16(brcmf_skbcb(skb)->htod_seq, \
+ BRCMF_SKB_HTOD_SEQ_ ## field ## _MASK, \
+ BRCMF_SKB_HTOD_SEQ_ ## field ## _SHIFT)
+
+#define BRCMF_FWS_TXSTAT_GENERATION_MASK 0x80000000
+#define BRCMF_FWS_TXSTAT_GENERATION_SHIFT 31
+#define BRCMF_FWS_TXSTAT_FLAGS_MASK 0x78000000
+#define BRCMF_FWS_TXSTAT_FLAGS_SHIFT 27
+#define BRCMF_FWS_TXSTAT_FIFO_MASK 0x07000000
+#define BRCMF_FWS_TXSTAT_FIFO_SHIFT 24
+#define BRCMF_FWS_TXSTAT_HSLOT_MASK 0x00FFFF00
+#define BRCMF_FWS_TXSTAT_HSLOT_SHIFT 8
+#define BRCMF_FWS_TXSTAT_FREERUN_MASK 0x000000FF
+#define BRCMF_FWS_TXSTAT_FREERUN_SHIFT 0
+
+#define brcmf_txstatus_get_field(txs, field) \
+ brcmu_maskget32(txs, BRCMF_FWS_TXSTAT_ ## field ## _MASK, \
+ BRCMF_FWS_TXSTAT_ ## field ## _SHIFT)
+
+/* How long to defer borrowing in jiffies */
+#define BRCMF_FWS_BORROW_DEFER_PERIOD (HZ / 10)
+
+/**
+ * enum brcmf_fws_fifo - fifo indices used by dongle firmware.
+ *
+ * @BRCMF_FWS_FIFO_FIRST: first fifo, ie. background.
+ * @BRCMF_FWS_FIFO_AC_BK: fifo for background traffic.
+ * @BRCMF_FWS_FIFO_AC_BE: fifo for best-effort traffic.
+ * @BRCMF_FWS_FIFO_AC_VI: fifo for video traffic.
+ * @BRCMF_FWS_FIFO_AC_VO: fifo for voice traffic.
+ * @BRCMF_FWS_FIFO_BCMC: fifo for broadcast/multicast (AP only).
+ * @BRCMF_FWS_FIFO_ATIM: fifo for ATIM (AP only).
+ * @BRCMF_FWS_FIFO_COUNT: number of fifos.
+ */
+enum brcmf_fws_fifo {
+ BRCMF_FWS_FIFO_FIRST,
+ BRCMF_FWS_FIFO_AC_BK = BRCMF_FWS_FIFO_FIRST,
+ BRCMF_FWS_FIFO_AC_BE,
+ BRCMF_FWS_FIFO_AC_VI,
+ BRCMF_FWS_FIFO_AC_VO,
+ BRCMF_FWS_FIFO_BCMC,
+ BRCMF_FWS_FIFO_ATIM,
+ BRCMF_FWS_FIFO_COUNT
+};
+
+/**
+ * enum brcmf_fws_txstatus - txstatus flag values.
+ *
+ * @BRCMF_FWS_TXSTATUS_DISCARD:
+ * host is free to discard the packet.
+ * @BRCMF_FWS_TXSTATUS_CORE_SUPPRESS:
+ * 802.11 core suppressed the packet.
+ * @BRCMF_FWS_TXSTATUS_FW_PS_SUPPRESS:
+ * firmware suppress the packet as device is already in PS mode.
+ * @BRCMF_FWS_TXSTATUS_FW_TOSSED:
+ * firmware tossed the packet.
+ * @BRCMF_FWS_TXSTATUS_HOST_TOSSED:
+ * host tossed the packet.
+ */
+enum brcmf_fws_txstatus {
+ BRCMF_FWS_TXSTATUS_DISCARD,
+ BRCMF_FWS_TXSTATUS_CORE_SUPPRESS,
+ BRCMF_FWS_TXSTATUS_FW_PS_SUPPRESS,
+ BRCMF_FWS_TXSTATUS_FW_TOSSED,
+ BRCMF_FWS_TXSTATUS_HOST_TOSSED
+};
+
+enum brcmf_fws_fcmode {
+ BRCMF_FWS_FCMODE_NONE,
+ BRCMF_FWS_FCMODE_IMPLIED_CREDIT,
+ BRCMF_FWS_FCMODE_EXPLICIT_CREDIT
+};
+
+enum brcmf_fws_mac_desc_state {
+ BRCMF_FWS_STATE_OPEN = 1,
+ BRCMF_FWS_STATE_CLOSE
+};
+
+/**
+ * struct brcmf_fws_mac_descriptor - firmware signalling data per node/interface
+ *
+ * @occupied: slot is in use.
+ * @mac_handle: handle for mac entry determined by firmware.
+ * @interface_id: interface index.
+ * @state: current state.
+ * @suppressed: mac entry is suppressed.
+ * @generation: generation bit.
+ * @ac_bitmap: ac queue bitmap.
+ * @requested_credit: credits requested by firmware.
+ * @ea: ethernet address.
+ * @seq: per-node free-running sequence.
+ * @psq: power-save queue.
+ * @transit_count: packet in transit to firmware.
+ */
+struct brcmf_fws_mac_descriptor {
+ char name[16];
+ u8 occupied;
+ u8 mac_handle;
+ u8 interface_id;
+ u8 state;
+ bool suppressed;
+ u8 generation;
+ u8 ac_bitmap;
+ u8 requested_credit;
+ u8 requested_packet;
+ u8 ea[ETH_ALEN];
+ u8 seq[BRCMF_FWS_FIFO_COUNT];
+ struct pktq psq;
+ int transit_count;
+ int suppr_transit_count;
+ bool send_tim_signal;
+ u8 traffic_pending_bmp;
+ u8 traffic_lastreported_bmp;
+};
+
+#define BRCMF_FWS_HANGER_MAXITEMS 1024
+
+/**
+ * enum brcmf_fws_hanger_item_state - state of hanger item.
+ *
+ * @BRCMF_FWS_HANGER_ITEM_STATE_FREE: item is free for use.
+ * @BRCMF_FWS_HANGER_ITEM_STATE_INUSE: item is in use.
+ * @BRCMF_FWS_HANGER_ITEM_STATE_INUSE_SUPPRESSED: item was suppressed.
+ */
+enum brcmf_fws_hanger_item_state {
+ BRCMF_FWS_HANGER_ITEM_STATE_FREE = 1,
+ BRCMF_FWS_HANGER_ITEM_STATE_INUSE,
+ BRCMF_FWS_HANGER_ITEM_STATE_INUSE_SUPPRESSED
+};
+
+
+/**
+ * struct brcmf_fws_hanger_item - single entry for tx pending packet.
+ *
+ * @state: entry is either free or occupied.
+ * @pkt: packet itself.
+ */
+struct brcmf_fws_hanger_item {
+ enum brcmf_fws_hanger_item_state state;
+ struct sk_buff *pkt;
+};
+
+/**
+ * struct brcmf_fws_hanger - holds packets awaiting firmware txstatus.
+ *
+ * @pushed: packets pushed to await txstatus.
+ * @popped: packets popped upon handling txstatus.
+ * @failed_to_push: packets that could not be pushed.
+ * @failed_to_pop: packets that could not be popped.
+ * @failed_slotfind: packets for which failed to find an entry.
+ * @slot_pos: last returned item index for a free entry.
+ * @items: array of hanger items.
+ */
+struct brcmf_fws_hanger {
+ u32 pushed;
+ u32 popped;
+ u32 failed_to_push;
+ u32 failed_to_pop;
+ u32 failed_slotfind;
+ u32 slot_pos;
+ struct brcmf_fws_hanger_item items[BRCMF_FWS_HANGER_MAXITEMS];
+};
+
+struct brcmf_fws_macdesc_table {
+ struct brcmf_fws_mac_descriptor nodes[BRCMF_FWS_MAC_DESC_TABLE_SIZE];
+ struct brcmf_fws_mac_descriptor iface[BRCMF_MAX_IFS];
+ struct brcmf_fws_mac_descriptor other;
+};
+
+struct brcmf_fws_stats {
+ u32 tlv_parse_failed;
+ u32 tlv_invalid_type;
+ u32 header_only_pkt;
+ u32 header_pulls;
+ u32 pkt2bus;
+ u32 send_pkts[5];
+ u32 requested_sent[5];
+ u32 generic_error;
+ u32 mac_update_failed;
+ u32 mac_ps_update_failed;
+ u32 if_update_failed;
+ u32 packet_request_failed;
+ u32 credit_request_failed;
+ u32 rollback_success;
+ u32 rollback_failed;
+ u32 delayq_full_error;
+ u32 supprq_full_error;
+ u32 txs_indicate;
+ u32 txs_discard;
+ u32 txs_supp_core;
+ u32 txs_supp_ps;
+ u32 txs_tossed;
+ u32 txs_host_tossed;
+ u32 bus_flow_block;
+ u32 fws_flow_block;
+};
+
+struct brcmf_fws_info {
+ struct brcmf_pub *drvr;
+ spinlock_t spinlock;
+ ulong flags;
+ struct brcmf_fws_stats stats;
+ struct brcmf_fws_hanger hanger;
+ enum brcmf_fws_fcmode fcmode;
+ bool fw_signals;
+ bool bcmc_credit_check;
+ struct brcmf_fws_macdesc_table desc;
+ struct workqueue_struct *fws_wq;
+ struct work_struct fws_dequeue_work;
+ u32 fifo_enqpkt[BRCMF_FWS_FIFO_COUNT];
+ int fifo_credit[BRCMF_FWS_FIFO_COUNT];
+ int credits_borrowed[BRCMF_FWS_FIFO_AC_VO + 1];
+ int deq_node_pos[BRCMF_FWS_FIFO_COUNT];
+ u32 fifo_credit_map;
+ u32 fifo_delay_map;
+ unsigned long borrow_defer_timestamp;
+ bool bus_flow_blocked;
+ bool creditmap_received;
+ u8 mode;
+ bool avoid_queueing;
+};
+
+/*
+ * brcmf_fws_prio2fifo - mapping from 802.1d priority to firmware fifo index.
+ */
+static const int brcmf_fws_prio2fifo[] = {
+ BRCMF_FWS_FIFO_AC_BE,
+ BRCMF_FWS_FIFO_AC_BK,
+ BRCMF_FWS_FIFO_AC_BK,
+ BRCMF_FWS_FIFO_AC_BE,
+ BRCMF_FWS_FIFO_AC_VI,
+ BRCMF_FWS_FIFO_AC_VI,
+ BRCMF_FWS_FIFO_AC_VO,
+ BRCMF_FWS_FIFO_AC_VO
+};
+
+#define BRCMF_FWS_TLV_DEF(name, id, len) \
+ case BRCMF_FWS_TYPE_ ## name: \
+ return len;
+
+/**
+ * brcmf_fws_get_tlv_len() - returns defined length for given tlv id.
+ *
+ * @fws: firmware-signalling information.
+ * @id: identifier of the TLV.
+ *
+ * Return: the specified length for the given TLV; Otherwise -EINVAL.
+ */
+static int brcmf_fws_get_tlv_len(struct brcmf_fws_info *fws,
+ enum brcmf_fws_tlv_type id)
+{
+ switch (id) {
+ BRCMF_FWS_TLV_DEFLIST
+ default:
+ fws->stats.tlv_invalid_type++;
+ break;
+ }
+ return -EINVAL;
+}
+#undef BRCMF_FWS_TLV_DEF
+
+static void brcmf_fws_lock(struct brcmf_fws_info *fws)
+ __acquires(&fws->spinlock)
+{
+ spin_lock_irqsave(&fws->spinlock, fws->flags);
+}
+
+static void brcmf_fws_unlock(struct brcmf_fws_info *fws)
+ __releases(&fws->spinlock)
+{
+ spin_unlock_irqrestore(&fws->spinlock, fws->flags);
+}
+
+static bool brcmf_fws_ifidx_match(struct sk_buff *skb, void *arg)
+{
+ u32 ifidx = brcmf_skb_if_flags_get_field(skb, INDEX);
+ return ifidx == *(int *)arg;
+}
+
+static void brcmf_fws_psq_flush(struct brcmf_fws_info *fws, struct pktq *q,
+ int ifidx)
+{
+ bool (*matchfn)(struct sk_buff *, void *) = NULL;
+ struct sk_buff *skb;
+ int prec;
+
+ if (ifidx != -1)
+ matchfn = brcmf_fws_ifidx_match;
+ for (prec = 0; prec < q->num_prec; prec++) {
+ skb = brcmu_pktq_pdeq_match(q, prec, matchfn, &ifidx);
+ while (skb) {
+ brcmu_pkt_buf_free_skb(skb);
+ skb = brcmu_pktq_pdeq_match(q, prec, matchfn, &ifidx);
+ }
+ }
+}
+
+static void brcmf_fws_hanger_init(struct brcmf_fws_hanger *hanger)
+{
+ int i;
+
+ memset(hanger, 0, sizeof(*hanger));
+ for (i = 0; i < ARRAY_SIZE(hanger->items); i++)
+ hanger->items[i].state = BRCMF_FWS_HANGER_ITEM_STATE_FREE;
+}
+
+static u32 brcmf_fws_hanger_get_free_slot(struct brcmf_fws_hanger *h)
+{
+ u32 i;
+
+ i = (h->slot_pos + 1) % BRCMF_FWS_HANGER_MAXITEMS;
+
+ while (i != h->slot_pos) {
+ if (h->items[i].state == BRCMF_FWS_HANGER_ITEM_STATE_FREE) {
+ h->slot_pos = i;
+ goto done;
+ }
+ i++;
+ if (i == BRCMF_FWS_HANGER_MAXITEMS)
+ i = 0;
+ }
+ brcmf_err("all slots occupied\n");
+ h->failed_slotfind++;
+ i = BRCMF_FWS_HANGER_MAXITEMS;
+done:
+ return i;
+}
+
+static int brcmf_fws_hanger_pushpkt(struct brcmf_fws_hanger *h,
+ struct sk_buff *pkt, u32 slot_id)
+{
+ if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS)
+ return -ENOENT;
+
+ if (h->items[slot_id].state != BRCMF_FWS_HANGER_ITEM_STATE_FREE) {
+ brcmf_err("slot is not free\n");
+ h->failed_to_push++;
+ return -EINVAL;
+ }
+
+ h->items[slot_id].state = BRCMF_FWS_HANGER_ITEM_STATE_INUSE;
+ h->items[slot_id].pkt = pkt;
+ h->pushed++;
+ return 0;
+}
+
+static inline int brcmf_fws_hanger_poppkt(struct brcmf_fws_hanger *h,
+ u32 slot_id, struct sk_buff **pktout,
+ bool remove_item)
+{
+ if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS)
+ return -ENOENT;
+
+ if (h->items[slot_id].state == BRCMF_FWS_HANGER_ITEM_STATE_FREE) {
+ brcmf_err("entry not in use\n");
+ h->failed_to_pop++;
+ return -EINVAL;
+ }
+
+ *pktout = h->items[slot_id].pkt;
+ if (remove_item) {
+ h->items[slot_id].state = BRCMF_FWS_HANGER_ITEM_STATE_FREE;
+ h->items[slot_id].pkt = NULL;
+ h->popped++;
+ }
+ return 0;
+}
+
+static int brcmf_fws_hanger_mark_suppressed(struct brcmf_fws_hanger *h,
+ u32 slot_id)
+{
+ if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS)
+ return -ENOENT;
+
+ if (h->items[slot_id].state == BRCMF_FWS_HANGER_ITEM_STATE_FREE) {
+ brcmf_err("entry not in use\n");
+ return -EINVAL;
+ }
+
+ h->items[slot_id].state = BRCMF_FWS_HANGER_ITEM_STATE_INUSE_SUPPRESSED;
+ return 0;
+}
+
+static void brcmf_fws_hanger_cleanup(struct brcmf_fws_info *fws,
+ bool (*fn)(struct sk_buff *, void *),
+ int ifidx)
+{
+ struct brcmf_fws_hanger *h = &fws->hanger;
+ struct sk_buff *skb;
+ int i;
+ enum brcmf_fws_hanger_item_state s;
+
+ for (i = 0; i < ARRAY_SIZE(h->items); i++) {
+ s = h->items[i].state;
+ if (s == BRCMF_FWS_HANGER_ITEM_STATE_INUSE ||
+ s == BRCMF_FWS_HANGER_ITEM_STATE_INUSE_SUPPRESSED) {
+ skb = h->items[i].pkt;
+ if (fn == NULL || fn(skb, &ifidx)) {
+ /* suppress packets freed from psq */
+ if (s == BRCMF_FWS_HANGER_ITEM_STATE_INUSE)
+ brcmu_pkt_buf_free_skb(skb);
+ h->items[i].state =
+ BRCMF_FWS_HANGER_ITEM_STATE_FREE;
+ }
+ }
+ }
+}
+
+static void brcmf_fws_macdesc_set_name(struct brcmf_fws_info *fws,
+ struct brcmf_fws_mac_descriptor *desc)
+{
+ if (desc == &fws->desc.other)
+ strlcpy(desc->name, "MAC-OTHER", sizeof(desc->name));
+ else if (desc->mac_handle)
+ scnprintf(desc->name, sizeof(desc->name), "MAC-%d:%d",
+ desc->mac_handle, desc->interface_id);
+ else
+ scnprintf(desc->name, sizeof(desc->name), "MACIF:%d",
+ desc->interface_id);
+}
+
+static void brcmf_fws_macdesc_init(struct brcmf_fws_mac_descriptor *desc,
+ u8 *addr, u8 ifidx)
+{
+ brcmf_dbg(TRACE,
+ "enter: desc %p ea=%pM, ifidx=%u\n", desc, addr, ifidx);
+ desc->occupied = 1;
+ desc->state = BRCMF_FWS_STATE_OPEN;
+ desc->requested_credit = 0;
+ desc->requested_packet = 0;
+ /* depending on use may need ifp->bsscfgidx instead */
+ desc->interface_id = ifidx;
+ desc->ac_bitmap = 0xff; /* update this when handling APSD */
+ if (addr)
+ memcpy(&desc->ea[0], addr, ETH_ALEN);
+}
+
+static
+void brcmf_fws_macdesc_deinit(struct brcmf_fws_mac_descriptor *desc)
+{
+ brcmf_dbg(TRACE,
+ "enter: ea=%pM, ifidx=%u\n", desc->ea, desc->interface_id);
+ desc->occupied = 0;
+ desc->state = BRCMF_FWS_STATE_CLOSE;
+ desc->requested_credit = 0;
+ desc->requested_packet = 0;
+}
+
+static struct brcmf_fws_mac_descriptor *
+brcmf_fws_macdesc_lookup(struct brcmf_fws_info *fws, u8 *ea)
+{
+ struct brcmf_fws_mac_descriptor *entry;
+ int i;
+
+ if (ea == NULL)
+ return ERR_PTR(-EINVAL);
+
+ entry = &fws->desc.nodes[0];
+ for (i = 0; i < ARRAY_SIZE(fws->desc.nodes); i++) {
+ if (entry->occupied && !memcmp(entry->ea, ea, ETH_ALEN))
+ return entry;
+ entry++;
+ }
+
+ return ERR_PTR(-ENOENT);
+}
+
+static struct brcmf_fws_mac_descriptor*
+brcmf_fws_macdesc_find(struct brcmf_fws_info *fws, struct brcmf_if *ifp, u8 *da)
+{
+ struct brcmf_fws_mac_descriptor *entry = &fws->desc.other;
+ bool multicast;
+
+ multicast = is_multicast_ether_addr(da);
+
+ /* Multicast destination, STA and P2P clients get the interface entry.
+ * STA/GC gets the Mac Entry for TDLS destinations, TDLS destinations
+ * have their own entry.
+ */
+ if (multicast && ifp->fws_desc) {
+ entry = ifp->fws_desc;
+ goto done;
+ }
+
+ entry = brcmf_fws_macdesc_lookup(fws, da);
+ if (IS_ERR(entry))
+ entry = ifp->fws_desc;
+
+done:
+ return entry;
+}
+
+static bool brcmf_fws_macdesc_closed(struct brcmf_fws_info *fws,
+ struct brcmf_fws_mac_descriptor *entry,
+ int fifo)
+{
+ struct brcmf_fws_mac_descriptor *if_entry;
+ bool closed;
+
+ /* for unique destination entries the related interface
+ * may be closed.
+ */
+ if (entry->mac_handle) {
+ if_entry = &fws->desc.iface[entry->interface_id];
+ if (if_entry->state == BRCMF_FWS_STATE_CLOSE)
+ return true;
+ }
+ /* an entry is closed when the state is closed and
+ * the firmware did not request anything.
+ */
+ closed = entry->state == BRCMF_FWS_STATE_CLOSE &&
+ !entry->requested_credit && !entry->requested_packet;
+
+ /* Or firmware does not allow traffic for given fifo */
+ return closed || !(entry->ac_bitmap & BIT(fifo));
+}
+
+static void brcmf_fws_macdesc_cleanup(struct brcmf_fws_info *fws,
+ struct brcmf_fws_mac_descriptor *entry,
+ int ifidx)
+{
+ if (entry->occupied && (ifidx == -1 || ifidx == entry->interface_id)) {
+ brcmf_fws_psq_flush(fws, &entry->psq, ifidx);
+ entry->occupied = !!(entry->psq.len);
+ }
+}
+
+static void brcmf_fws_bus_txq_cleanup(struct brcmf_fws_info *fws,
+ bool (*fn)(struct sk_buff *, void *),
+ int ifidx)
+{
+ struct brcmf_fws_hanger_item *hi;
+ struct pktq *txq;
+ struct sk_buff *skb;
+ int prec;
+ u32 hslot;
+
+ txq = brcmf_bus_gettxq(fws->drvr->bus_if);
+ if (IS_ERR(txq)) {
+ brcmf_dbg(TRACE, "no txq to clean up\n");
+ return;
+ }
+
+ for (prec = 0; prec < txq->num_prec; prec++) {
+ skb = brcmu_pktq_pdeq_match(txq, prec, fn, &ifidx);
+ while (skb) {
+ hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT);
+ hi = &fws->hanger.items[hslot];
+ WARN_ON(skb != hi->pkt);
+ hi->state = BRCMF_FWS_HANGER_ITEM_STATE_FREE;
+ brcmu_pkt_buf_free_skb(skb);
+ skb = brcmu_pktq_pdeq_match(txq, prec, fn, &ifidx);
+ }
+ }
+}
+
+static void brcmf_fws_cleanup(struct brcmf_fws_info *fws, int ifidx)
+{
+ int i;
+ struct brcmf_fws_mac_descriptor *table;
+ bool (*matchfn)(struct sk_buff *, void *) = NULL;
+
+ if (fws == NULL)
+ return;
+
+ if (ifidx != -1)
+ matchfn = brcmf_fws_ifidx_match;
+
+ /* cleanup individual nodes */
+ table = &fws->desc.nodes[0];
+ for (i = 0; i < ARRAY_SIZE(fws->desc.nodes); i++)
+ brcmf_fws_macdesc_cleanup(fws, &table[i], ifidx);
+
+ brcmf_fws_macdesc_cleanup(fws, &fws->desc.other, ifidx);
+ brcmf_fws_bus_txq_cleanup(fws, matchfn, ifidx);
+ brcmf_fws_hanger_cleanup(fws, matchfn, ifidx);
+}
+
+static u8 brcmf_fws_hdrpush(struct brcmf_fws_info *fws, struct sk_buff *skb)
+{
+ struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac;
+ u8 *wlh;
+ u16 data_offset = 0;
+ u8 fillers;
+ __le32 pkttag = cpu_to_le32(brcmf_skbcb(skb)->htod);
+ __le16 pktseq = cpu_to_le16(brcmf_skbcb(skb)->htod_seq);
+
+ brcmf_dbg(TRACE, "enter: %s, idx=%d hslot=%d htod %X seq %X\n",
+ entry->name, brcmf_skb_if_flags_get_field(skb, INDEX),
+ (le32_to_cpu(pkttag) >> 8) & 0xffff,
+ brcmf_skbcb(skb)->htod, brcmf_skbcb(skb)->htod_seq);
+ if (entry->send_tim_signal)
+ data_offset += 2 + BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN;
+ if (BRCMF_FWS_MODE_GET_REUSESEQ(fws->mode))
+ data_offset += BRCMF_FWS_TYPE_SEQ_LEN;
+ /* +2 is for Type[1] and Len[1] in TLV, plus TIM signal */
+ data_offset += 2 + BRCMF_FWS_TYPE_PKTTAG_LEN;
+ fillers = round_up(data_offset, 4) - data_offset;
+ data_offset += fillers;
+
+ skb_push(skb, data_offset);
+ wlh = skb->data;
+
+ wlh[0] = BRCMF_FWS_TYPE_PKTTAG;
+ wlh[1] = BRCMF_FWS_TYPE_PKTTAG_LEN;
+ memcpy(&wlh[2], &pkttag, sizeof(pkttag));
+ if (BRCMF_FWS_MODE_GET_REUSESEQ(fws->mode)) {
+ wlh[1] += BRCMF_FWS_TYPE_SEQ_LEN;
+ memcpy(&wlh[2 + BRCMF_FWS_TYPE_PKTTAG_LEN], &pktseq,
+ sizeof(pktseq));
+ }
+ wlh += wlh[1] + 2;
+
+ if (entry->send_tim_signal) {
+ entry->send_tim_signal = 0;
+ wlh[0] = BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP;
+ wlh[1] = BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN;
+ wlh[2] = entry->mac_handle;
+ wlh[3] = entry->traffic_pending_bmp;
+ brcmf_dbg(TRACE, "adding TIM info: handle %d bmp 0x%X\n",
+ entry->mac_handle, entry->traffic_pending_bmp);
+ wlh += BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN + 2;
+ entry->traffic_lastreported_bmp = entry->traffic_pending_bmp;
+ }
+ if (fillers)
+ memset(wlh, BRCMF_FWS_TYPE_FILLER, fillers);
+
+ return (u8)(data_offset >> 2);
+}
+
+static bool brcmf_fws_tim_update(struct brcmf_fws_info *fws,
+ struct brcmf_fws_mac_descriptor *entry,
+ int fifo, bool send_immediately)
+{
+ struct sk_buff *skb;
+ struct brcmf_skbuff_cb *skcb;
+ s32 err;
+ u32 len;
+ u8 data_offset;
+ int ifidx;
+
+ /* check delayedQ and suppressQ in one call using bitmap */
+ if (brcmu_pktq_mlen(&entry->psq, 3 << (fifo * 2)) == 0)
+ entry->traffic_pending_bmp &= ~NBITVAL(fifo);
+ else
+ entry->traffic_pending_bmp |= NBITVAL(fifo);
+
+ entry->send_tim_signal = false;
+ if (entry->traffic_lastreported_bmp != entry->traffic_pending_bmp)
+ entry->send_tim_signal = true;
+ if (send_immediately && entry->send_tim_signal &&
+ entry->state == BRCMF_FWS_STATE_CLOSE) {
+ /* create a dummy packet and sent that. The traffic */
+ /* bitmap info will automatically be attached to that packet */
+ len = BRCMF_FWS_TYPE_PKTTAG_LEN + 2 +
+ BRCMF_FWS_TYPE_SEQ_LEN +
+ BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN + 2 +
+ 4 + fws->drvr->hdrlen;
+ skb = brcmu_pkt_buf_get_skb(len);
+ if (skb == NULL)
+ return false;
+ skb_pull(skb, len);
+ skcb = brcmf_skbcb(skb);
+ skcb->mac = entry;
+ skcb->state = BRCMF_FWS_SKBSTATE_TIM;
+ skcb->htod = 0;
+ skcb->htod_seq = 0;
+ data_offset = brcmf_fws_hdrpush(fws, skb);
+ ifidx = brcmf_skb_if_flags_get_field(skb, INDEX);
+ brcmf_fws_unlock(fws);
+ err = brcmf_proto_txdata(fws->drvr, ifidx, data_offset, skb);
+ brcmf_fws_lock(fws);
+ if (err)
+ brcmu_pkt_buf_free_skb(skb);
+ return true;
+ }
+ return false;
+}
+
+static void
+brcmf_fws_flow_control_check(struct brcmf_fws_info *fws, struct pktq *pq,
+ u8 if_id)
+{
+ struct brcmf_if *ifp = brcmf_get_ifp(fws->drvr, if_id);
+
+ if (WARN_ON(!ifp))
+ return;
+
+ if ((ifp->netif_stop & BRCMF_NETIF_STOP_REASON_FWS_FC) &&
+ pq->len <= BRCMF_FWS_FLOWCONTROL_LOWATER)
+ brcmf_txflowblock_if(ifp,
+ BRCMF_NETIF_STOP_REASON_FWS_FC, false);
+ if (!(ifp->netif_stop & BRCMF_NETIF_STOP_REASON_FWS_FC) &&
+ pq->len >= BRCMF_FWS_FLOWCONTROL_HIWATER) {
+ fws->stats.fws_flow_block++;
+ brcmf_txflowblock_if(ifp, BRCMF_NETIF_STOP_REASON_FWS_FC, true);
+ }
+ return;
+}
+
+static int brcmf_fws_rssi_indicate(struct brcmf_fws_info *fws, s8 rssi)
+{
+ brcmf_dbg(CTL, "rssi %d\n", rssi);
+ return 0;
+}
+
+static
+int brcmf_fws_macdesc_indicate(struct brcmf_fws_info *fws, u8 type, u8 *data)
+{
+ struct brcmf_fws_mac_descriptor *entry, *existing;
+ u8 mac_handle;
+ u8 ifidx;
+ u8 *addr;
+
+ mac_handle = *data++;
+ ifidx = *data++;
+ addr = data;
+
+ entry = &fws->desc.nodes[mac_handle & 0x1F];
+ if (type == BRCMF_FWS_TYPE_MACDESC_DEL) {
+ if (entry->occupied) {
+ brcmf_dbg(TRACE, "deleting %s mac %pM\n",
+ entry->name, addr);
+ brcmf_fws_lock(fws);
+ brcmf_fws_macdesc_cleanup(fws, entry, -1);
+ brcmf_fws_macdesc_deinit(entry);
+ brcmf_fws_unlock(fws);
+ } else
+ fws->stats.mac_update_failed++;
+ return 0;
+ }
+
+ existing = brcmf_fws_macdesc_lookup(fws, addr);
+ if (IS_ERR(existing)) {
+ if (!entry->occupied) {
+ brcmf_fws_lock(fws);
+ entry->mac_handle = mac_handle;
+ brcmf_fws_macdesc_init(entry, addr, ifidx);
+ brcmf_fws_macdesc_set_name(fws, entry);
+ brcmu_pktq_init(&entry->psq, BRCMF_FWS_PSQ_PREC_COUNT,
+ BRCMF_FWS_PSQ_LEN);
+ brcmf_fws_unlock(fws);
+ brcmf_dbg(TRACE, "add %s mac %pM\n", entry->name, addr);
+ } else {
+ fws->stats.mac_update_failed++;
+ }
+ } else {
+ if (entry != existing) {
+ brcmf_dbg(TRACE, "copy mac %s\n", existing->name);
+ brcmf_fws_lock(fws);
+ memcpy(entry, existing,
+ offsetof(struct brcmf_fws_mac_descriptor, psq));
+ entry->mac_handle = mac_handle;
+ brcmf_fws_macdesc_deinit(existing);
+ brcmf_fws_macdesc_set_name(fws, entry);
+ brcmf_fws_unlock(fws);
+ brcmf_dbg(TRACE, "relocate %s mac %pM\n", entry->name,
+ addr);
+ } else {
+ brcmf_dbg(TRACE, "use existing\n");
+ WARN_ON(entry->mac_handle != mac_handle);
+ /* TODO: what should we do here: continue, reinit, .. */
+ }
+ }
+ return 0;
+}
+
+static int brcmf_fws_macdesc_state_indicate(struct brcmf_fws_info *fws,
+ u8 type, u8 *data)
+{
+ struct brcmf_fws_mac_descriptor *entry;
+ u8 mac_handle;
+ int ret;
+
+ mac_handle = data[0];
+ entry = &fws->desc.nodes[mac_handle & 0x1F];
+ if (!entry->occupied) {
+ fws->stats.mac_ps_update_failed++;
+ return -ESRCH;
+ }
+ brcmf_fws_lock(fws);
+ /* a state update should wipe old credits */
+ entry->requested_credit = 0;
+ entry->requested_packet = 0;
+ if (type == BRCMF_FWS_TYPE_MAC_OPEN) {
+ entry->state = BRCMF_FWS_STATE_OPEN;
+ ret = BRCMF_FWS_RET_OK_SCHEDULE;
+ } else {
+ entry->state = BRCMF_FWS_STATE_CLOSE;
+ brcmf_fws_tim_update(fws, entry, BRCMF_FWS_FIFO_AC_BK, false);
+ brcmf_fws_tim_update(fws, entry, BRCMF_FWS_FIFO_AC_BE, false);
+ brcmf_fws_tim_update(fws, entry, BRCMF_FWS_FIFO_AC_VI, false);
+ brcmf_fws_tim_update(fws, entry, BRCMF_FWS_FIFO_AC_VO, true);
+ ret = BRCMF_FWS_RET_OK_NOSCHEDULE;
+ }
+ brcmf_fws_unlock(fws);
+ return ret;
+}
+
+static int brcmf_fws_interface_state_indicate(struct brcmf_fws_info *fws,
+ u8 type, u8 *data)
+{
+ struct brcmf_fws_mac_descriptor *entry;
+ u8 ifidx;
+ int ret;
+
+ ifidx = data[0];
+
+ if (ifidx >= BRCMF_MAX_IFS) {
+ ret = -ERANGE;
+ goto fail;
+ }
+
+ entry = &fws->desc.iface[ifidx];
+ if (!entry->occupied) {
+ ret = -ESRCH;
+ goto fail;
+ }
+
+ brcmf_dbg(TRACE, "%s (%d): %s\n", brcmf_fws_get_tlv_name(type), type,
+ entry->name);
+ brcmf_fws_lock(fws);
+ switch (type) {
+ case BRCMF_FWS_TYPE_INTERFACE_OPEN:
+ entry->state = BRCMF_FWS_STATE_OPEN;
+ ret = BRCMF_FWS_RET_OK_SCHEDULE;
+ break;
+ case BRCMF_FWS_TYPE_INTERFACE_CLOSE:
+ entry->state = BRCMF_FWS_STATE_CLOSE;
+ ret = BRCMF_FWS_RET_OK_NOSCHEDULE;
+ break;
+ default:
+ ret = -EINVAL;
+ brcmf_fws_unlock(fws);
+ goto fail;
+ }
+ brcmf_fws_unlock(fws);
+ return ret;
+
+fail:
+ fws->stats.if_update_failed++;
+ return ret;
+}
+
+static int brcmf_fws_request_indicate(struct brcmf_fws_info *fws, u8 type,
+ u8 *data)
+{
+ struct brcmf_fws_mac_descriptor *entry;
+
+ entry = &fws->desc.nodes[data[1] & 0x1F];
+ if (!entry->occupied) {
+ if (type == BRCMF_FWS_TYPE_MAC_REQUEST_CREDIT)
+ fws->stats.credit_request_failed++;
+ else
+ fws->stats.packet_request_failed++;
+ return -ESRCH;
+ }
+
+ brcmf_dbg(TRACE, "%s (%d): %s cnt %d bmp %d\n",
+ brcmf_fws_get_tlv_name(type), type, entry->name,
+ data[0], data[2]);
+ brcmf_fws_lock(fws);
+ if (type == BRCMF_FWS_TYPE_MAC_REQUEST_CREDIT)
+ entry->requested_credit = data[0];
+ else
+ entry->requested_packet = data[0];
+
+ entry->ac_bitmap = data[2];
+ brcmf_fws_unlock(fws);
+ return BRCMF_FWS_RET_OK_SCHEDULE;
+}
+
+static void
+brcmf_fws_macdesc_use_req_credit(struct brcmf_fws_mac_descriptor *entry,
+ struct sk_buff *skb)
+{
+ if (entry->requested_credit > 0) {
+ entry->requested_credit--;
+ brcmf_skb_if_flags_set_field(skb, REQUESTED, 1);
+ brcmf_skb_if_flags_set_field(skb, REQ_CREDIT, 1);
+ if (entry->state != BRCMF_FWS_STATE_CLOSE)
+ brcmf_err("requested credit set while mac not closed!\n");
+ } else if (entry->requested_packet > 0) {
+ entry->requested_packet--;
+ brcmf_skb_if_flags_set_field(skb, REQUESTED, 1);
+ brcmf_skb_if_flags_set_field(skb, REQ_CREDIT, 0);
+ if (entry->state != BRCMF_FWS_STATE_CLOSE)
+ brcmf_err("requested packet set while mac not closed!\n");
+ } else {
+ brcmf_skb_if_flags_set_field(skb, REQUESTED, 0);
+ brcmf_skb_if_flags_set_field(skb, REQ_CREDIT, 0);
+ }
+}
+
+static void brcmf_fws_macdesc_return_req_credit(struct sk_buff *skb)
+{
+ struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac;
+
+ if ((brcmf_skb_if_flags_get_field(skb, REQ_CREDIT)) &&
+ (entry->state == BRCMF_FWS_STATE_CLOSE))
+ entry->requested_credit++;
+}
+
+static void brcmf_fws_return_credits(struct brcmf_fws_info *fws,
+ u8 fifo, u8 credits)
+{
+ int lender_ac;
+ int *borrowed;
+ int *fifo_credit;
+
+ if (!credits)
+ return;
+
+ fws->fifo_credit_map |= 1 << fifo;
+
+ if ((fifo == BRCMF_FWS_FIFO_AC_BE) &&
+ (fws->credits_borrowed[0])) {
+ for (lender_ac = BRCMF_FWS_FIFO_AC_VO; lender_ac >= 0;
+ lender_ac--) {
+ borrowed = &fws->credits_borrowed[lender_ac];
+ if (*borrowed) {
+ fws->fifo_credit_map |= (1 << lender_ac);
+ fifo_credit = &fws->fifo_credit[lender_ac];
+ if (*borrowed >= credits) {
+ *borrowed -= credits;
+ *fifo_credit += credits;
+ return;
+ } else {
+ credits -= *borrowed;
+ *fifo_credit += *borrowed;
+ *borrowed = 0;
+ }
+ }
+ }
+ }
+
+ fws->fifo_credit[fifo] += credits;
+}
+
+static void brcmf_fws_schedule_deq(struct brcmf_fws_info *fws)
+{
+ /* only schedule dequeue when there are credits for delayed traffic */
+ if ((fws->fifo_credit_map & fws->fifo_delay_map) ||
+ (!brcmf_fws_fc_active(fws) && fws->fifo_delay_map))
+ queue_work(fws->fws_wq, &fws->fws_dequeue_work);
+}
+
+static int brcmf_fws_enq(struct brcmf_fws_info *fws,
+ enum brcmf_fws_skb_state state, int fifo,
+ struct sk_buff *p)
+{
+ int prec = 2 * fifo;
+ u32 *qfull_stat = &fws->stats.delayq_full_error;
+ struct brcmf_fws_mac_descriptor *entry;
+ struct pktq *pq;
+ struct sk_buff_head *queue;
+ struct sk_buff *p_head;
+ struct sk_buff *p_tail;
+ u32 fr_new;
+ u32 fr_compare;
+
+ entry = brcmf_skbcb(p)->mac;
+ if (entry == NULL) {
+ brcmf_err("no mac descriptor found for skb %p\n", p);
+ return -ENOENT;
+ }
+
+ brcmf_dbg(DATA, "enter: fifo %d skb %p\n", fifo, p);
+ if (state == BRCMF_FWS_SKBSTATE_SUPPRESSED) {
+ prec += 1;
+ qfull_stat = &fws->stats.supprq_full_error;
+
+ /* Fix out of order delivery of frames. Dont assume frame */
+ /* can be inserted at the end, but look for correct position */
+ pq = &entry->psq;
+ if (pktq_full(pq) || pktq_pfull(pq, prec)) {
+ *qfull_stat += 1;
+ return -ENFILE;
+ }
+ queue = &pq->q[prec].skblist;
+
+ p_head = skb_peek(queue);
+ p_tail = skb_peek_tail(queue);
+ fr_new = brcmf_skb_htod_tag_get_field(p, FREERUN);
+
+ while (p_head != p_tail) {
+ fr_compare = brcmf_skb_htod_tag_get_field(p_tail,
+ FREERUN);
+ /* be sure to handle wrap of 256 */
+ if (((fr_new > fr_compare) &&
+ ((fr_new - fr_compare) < 128)) ||
+ ((fr_new < fr_compare) &&
+ ((fr_compare - fr_new) > 128)))
+ break;
+ p_tail = skb_queue_prev(queue, p_tail);
+ }
+ /* Position found. Determine what to do */
+ if (p_tail == NULL) {
+ /* empty list */
+ __skb_queue_tail(queue, p);
+ } else {
+ fr_compare = brcmf_skb_htod_tag_get_field(p_tail,
+ FREERUN);
+ if (((fr_new > fr_compare) &&
+ ((fr_new - fr_compare) < 128)) ||
+ ((fr_new < fr_compare) &&
+ ((fr_compare - fr_new) > 128))) {
+ /* After tail */
+ __skb_queue_after(queue, p_tail, p);
+ } else {
+ /* Before tail */
+ __skb_insert(p, p_tail->prev, p_tail, queue);
+ }
+ }
+
+ /* Complete the counters and statistics */
+ pq->len++;
+ if (pq->hi_prec < prec)
+ pq->hi_prec = (u8) prec;
+ } else if (brcmu_pktq_penq(&entry->psq, prec, p) == NULL) {
+ *qfull_stat += 1;
+ return -ENFILE;
+ }
+
+ /* increment total enqueued packet count */
+ fws->fifo_delay_map |= 1 << fifo;
+ fws->fifo_enqpkt[fifo]++;
+
+ /* update the sk_buff state */
+ brcmf_skbcb(p)->state = state;
+
+ /*
+ * A packet has been pushed so update traffic
+ * availability bitmap, if applicable
+ */
+ brcmf_fws_tim_update(fws, entry, fifo, true);
+ brcmf_fws_flow_control_check(fws, &entry->psq,
+ brcmf_skb_if_flags_get_field(p, INDEX));
+ return 0;
+}
+
+static struct sk_buff *brcmf_fws_deq(struct brcmf_fws_info *fws, int fifo)
+{
+ struct brcmf_fws_mac_descriptor *table;
+ struct brcmf_fws_mac_descriptor *entry;
+ struct sk_buff *p;
+ int num_nodes;
+ int node_pos;
+ int prec_out;
+ int pmsk;
+ int i;
+
+ table = (struct brcmf_fws_mac_descriptor *)&fws->desc;
+ num_nodes = sizeof(fws->desc) / sizeof(struct brcmf_fws_mac_descriptor);
+ node_pos = fws->deq_node_pos[fifo];
+
+ for (i = 0; i < num_nodes; i++) {
+ entry = &table[(node_pos + i) % num_nodes];
+ if (!entry->occupied ||
+ brcmf_fws_macdesc_closed(fws, entry, fifo))
+ continue;
+
+ if (entry->suppressed)
+ pmsk = 2;
+ else
+ pmsk = 3;
+ p = brcmu_pktq_mdeq(&entry->psq, pmsk << (fifo * 2), &prec_out);
+ if (p == NULL) {
+ if (entry->suppressed) {
+ if (entry->suppr_transit_count)
+ continue;
+ entry->suppressed = false;
+ p = brcmu_pktq_mdeq(&entry->psq,
+ 1 << (fifo * 2), &prec_out);
+ }
+ }
+ if (p == NULL)
+ continue;
+
+ brcmf_fws_macdesc_use_req_credit(entry, p);
+
+ /* move dequeue position to ensure fair round-robin */
+ fws->deq_node_pos[fifo] = (node_pos + i + 1) % num_nodes;
+ brcmf_fws_flow_control_check(fws, &entry->psq,
+ brcmf_skb_if_flags_get_field(p,
+ INDEX)
+ );
+ /*
+ * A packet has been picked up, update traffic
+ * availability bitmap, if applicable
+ */
+ brcmf_fws_tim_update(fws, entry, fifo, false);
+
+ /*
+ * decrement total enqueued fifo packets and
+ * clear delay bitmap if done.
+ */
+ fws->fifo_enqpkt[fifo]--;
+ if (fws->fifo_enqpkt[fifo] == 0)
+ fws->fifo_delay_map &= ~(1 << fifo);
+ goto done;
+ }
+ p = NULL;
+done:
+ brcmf_dbg(DATA, "exit: fifo %d skb %p\n", fifo, p);
+ return p;
+}
+
+static int brcmf_fws_txstatus_suppressed(struct brcmf_fws_info *fws, int fifo,
+ struct sk_buff *skb,
+ u32 genbit, u16 seq)
+{
+ struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac;
+ u32 hslot;
+ int ret;
+
+ hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT);
+
+ /* this packet was suppressed */
+ if (!entry->suppressed) {
+ entry->suppressed = true;
+ entry->suppr_transit_count = entry->transit_count;
+ brcmf_dbg(DATA, "suppress %s: transit %d\n",
+ entry->name, entry->transit_count);
+ }
+
+ entry->generation = genbit;
+
+ brcmf_skb_htod_tag_set_field(skb, GENERATION, genbit);
+ brcmf_skbcb(skb)->htod_seq = seq;
+ if (brcmf_skb_htod_seq_get_field(skb, FROMFW)) {
+ brcmf_skb_htod_seq_set_field(skb, FROMDRV, 1);
+ brcmf_skb_htod_seq_set_field(skb, FROMFW, 0);
+ } else {
+ brcmf_skb_htod_seq_set_field(skb, FROMDRV, 0);
+ }
+ ret = brcmf_fws_enq(fws, BRCMF_FWS_SKBSTATE_SUPPRESSED, fifo, skb);
+
+ if (ret != 0) {
+ /* suppress q is full drop this packet */
+ brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &skb, true);
+ } else {
+ /* Mark suppressed to avoid a double free during wlfc cleanup */
+ brcmf_fws_hanger_mark_suppressed(&fws->hanger, hslot);
+ }
+
+ return ret;
+}
+
+static int
+brcmf_fws_txs_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot,
+ u32 genbit, u16 seq)
+{
+ u32 fifo;
+ int ret;
+ bool remove_from_hanger = true;
+ struct sk_buff *skb;
+ struct brcmf_skbuff_cb *skcb;
+ struct brcmf_fws_mac_descriptor *entry = NULL;
+ struct brcmf_if *ifp;
+
+ brcmf_dbg(DATA, "flags %d\n", flags);
+
+ if (flags == BRCMF_FWS_TXSTATUS_DISCARD)
+ fws->stats.txs_discard++;
+ else if (flags == BRCMF_FWS_TXSTATUS_CORE_SUPPRESS) {
+ fws->stats.txs_supp_core++;
+ remove_from_hanger = false;
+ } else if (flags == BRCMF_FWS_TXSTATUS_FW_PS_SUPPRESS) {
+ fws->stats.txs_supp_ps++;
+ remove_from_hanger = false;
+ } else if (flags == BRCMF_FWS_TXSTATUS_FW_TOSSED)
+ fws->stats.txs_tossed++;
+ else if (flags == BRCMF_FWS_TXSTATUS_HOST_TOSSED)
+ fws->stats.txs_host_tossed++;
+ else
+ brcmf_err("unexpected txstatus\n");
+
+ ret = brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &skb,
+ remove_from_hanger);
+ if (ret != 0) {
+ brcmf_err("no packet in hanger slot: hslot=%d\n", hslot);
+ return ret;
+ }
+
+ skcb = brcmf_skbcb(skb);
+ entry = skcb->mac;
+ if (WARN_ON(!entry)) {
+ brcmu_pkt_buf_free_skb(skb);
+ return -EINVAL;
+ }
+ entry->transit_count--;
+ if (entry->suppressed && entry->suppr_transit_count)
+ entry->suppr_transit_count--;
+
+ brcmf_dbg(DATA, "%s flags %d htod %X seq %X\n", entry->name, flags,
+ skcb->htod, seq);
+
+ /* pick up the implicit credit from this packet */
+ fifo = brcmf_skb_htod_tag_get_field(skb, FIFO);
+ if ((fws->fcmode == BRCMF_FWS_FCMODE_IMPLIED_CREDIT) ||
+ (brcmf_skb_if_flags_get_field(skb, REQ_CREDIT)) ||
+ (flags == BRCMF_FWS_TXSTATUS_HOST_TOSSED)) {
+ brcmf_fws_return_credits(fws, fifo, 1);
+ brcmf_fws_schedule_deq(fws);
+ }
+ brcmf_fws_macdesc_return_req_credit(skb);
+
+ ret = brcmf_proto_hdrpull(fws->drvr, false, skb, &ifp);
+ if (ret) {
+ brcmu_pkt_buf_free_skb(skb);
+ return -EINVAL;
+ }
+ if (!remove_from_hanger)
+ ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb,
+ genbit, seq);
+ if (remove_from_hanger || ret)
+ brcmf_txfinalize(ifp, skb, true);
+
+ return 0;
+}
+
+static int brcmf_fws_fifocreditback_indicate(struct brcmf_fws_info *fws,
+ u8 *data)
+{
+ int i;
+
+ if (fws->fcmode != BRCMF_FWS_FCMODE_EXPLICIT_CREDIT) {
+ brcmf_dbg(INFO, "ignored\n");
+ return BRCMF_FWS_RET_OK_NOSCHEDULE;
+ }
+
+ brcmf_dbg(DATA, "enter: data %pM\n", data);
+ brcmf_fws_lock(fws);
+ for (i = 0; i < BRCMF_FWS_FIFO_COUNT; i++)
+ brcmf_fws_return_credits(fws, i, data[i]);
+
+ brcmf_dbg(DATA, "map: credit %x delay %x\n", fws->fifo_credit_map,
+ fws->fifo_delay_map);
+ brcmf_fws_unlock(fws);
+ return BRCMF_FWS_RET_OK_SCHEDULE;
+}
+
+static int brcmf_fws_txstatus_indicate(struct brcmf_fws_info *fws, u8 *data)
+{
+ __le32 status_le;
+ __le16 seq_le;
+ u32 status;
+ u32 hslot;
+ u32 genbit;
+ u8 flags;
+ u16 seq;
+
+ fws->stats.txs_indicate++;
+ memcpy(&status_le, data, sizeof(status_le));
+ status = le32_to_cpu(status_le);
+ flags = brcmf_txstatus_get_field(status, FLAGS);
+ hslot = brcmf_txstatus_get_field(status, HSLOT);
+ genbit = brcmf_txstatus_get_field(status, GENERATION);
+ if (BRCMF_FWS_MODE_GET_REUSESEQ(fws->mode)) {
+ memcpy(&seq_le, &data[BRCMF_FWS_TYPE_PKTTAG_LEN],
+ sizeof(seq_le));
+ seq = le16_to_cpu(seq_le);
+ } else {
+ seq = 0;
+ }
+
+ brcmf_fws_lock(fws);
+ brcmf_fws_txs_process(fws, flags, hslot, genbit, seq);
+ brcmf_fws_unlock(fws);
+ return BRCMF_FWS_RET_OK_NOSCHEDULE;
+}
+
+static int brcmf_fws_dbg_seqnum_check(struct brcmf_fws_info *fws, u8 *data)
+{
+ __le32 timestamp;
+
+ memcpy(×tamp, &data[2], sizeof(timestamp));
+ brcmf_dbg(CTL, "received: seq %d, timestamp %d\n", data[1],
+ le32_to_cpu(timestamp));
+ return 0;
+}
+
+static int brcmf_fws_notify_credit_map(struct brcmf_if *ifp,
+ const struct brcmf_event_msg *e,
+ void *data)
+{
+ struct brcmf_fws_info *fws = drvr_to_fws(ifp->drvr);
+ int i;
+ u8 *credits = data;
+
+ if (e->datalen < BRCMF_FWS_FIFO_COUNT) {
+ brcmf_err("event payload too small (%d)\n", e->datalen);
+ return -EINVAL;
+ }
+ if (fws->creditmap_received)
+ return 0;
+
+ fws->creditmap_received = true;
+
+ brcmf_dbg(TRACE, "enter: credits %pM\n", credits);
+ brcmf_fws_lock(fws);
+ for (i = 0; i < ARRAY_SIZE(fws->fifo_credit); i++) {
+ if (*credits)
+ fws->fifo_credit_map |= 1 << i;
+ else
+ fws->fifo_credit_map &= ~(1 << i);
+ fws->fifo_credit[i] = *credits++;
+ }
+ brcmf_fws_schedule_deq(fws);
+ brcmf_fws_unlock(fws);
+ return 0;
+}
+
+static int brcmf_fws_notify_bcmc_credit_support(struct brcmf_if *ifp,
+ const struct brcmf_event_msg *e,
+ void *data)
+{
+ struct brcmf_fws_info *fws = drvr_to_fws(ifp->drvr);
+
+ if (fws) {
+ brcmf_fws_lock(fws);
+ fws->bcmc_credit_check = true;
+ brcmf_fws_unlock(fws);
+ }
+ return 0;
+}
+
+static void brcmf_rxreorder_get_skb_list(struct brcmf_ampdu_rx_reorder *rfi,
+ u8 start, u8 end,
+ struct sk_buff_head *skb_list)
+{
+ /* initialize return list */
+ __skb_queue_head_init(skb_list);
+
+ if (rfi->pend_pkts == 0) {
+ brcmf_dbg(INFO, "no packets in reorder queue\n");
+ return;
+ }
+
+ do {
+ if (rfi->pktslots[start]) {
+ __skb_queue_tail(skb_list, rfi->pktslots[start]);
+ rfi->pktslots[start] = NULL;
+ }
+ start++;
+ if (start > rfi->max_idx)
+ start = 0;
+ } while (start != end);
+ rfi->pend_pkts -= skb_queue_len(skb_list);
+}
+
+void brcmf_fws_rxreorder(struct brcmf_if *ifp, struct sk_buff *pkt)
+{
+ u8 *reorder_data;
+ u8 flow_id, max_idx, cur_idx, exp_idx, end_idx;
+ struct brcmf_ampdu_rx_reorder *rfi;
+ struct sk_buff_head reorder_list;
+ struct sk_buff *pnext;
+ u8 flags;
+ u32 buf_size;
+
+ reorder_data = ((struct brcmf_skb_reorder_data *)pkt->cb)->reorder;
+ flow_id = reorder_data[BRCMF_RXREORDER_FLOWID_OFFSET];
+ flags = reorder_data[BRCMF_RXREORDER_FLAGS_OFFSET];
+
+ /* validate flags and flow id */
+ if (flags == 0xFF) {
+ brcmf_err("invalid flags...so ignore this packet\n");
+ brcmf_netif_rx(ifp, pkt);
+ return;
+ }
+
+ rfi = ifp->drvr->reorder_flows[flow_id];
+ if (flags & BRCMF_RXREORDER_DEL_FLOW) {
+ brcmf_dbg(INFO, "flow-%d: delete\n",
+ flow_id);
+
+ if (rfi == NULL) {
+ brcmf_dbg(INFO, "received flags to cleanup, but no flow (%d) yet\n",
+ flow_id);
+ brcmf_netif_rx(ifp, pkt);
+ return;
+ }
+
+ brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, rfi->exp_idx,
+ &reorder_list);
+ /* add the last packet */
+ __skb_queue_tail(&reorder_list, pkt);
+ kfree(rfi);
+ ifp->drvr->reorder_flows[flow_id] = NULL;
+ goto netif_rx;
+ }
+ /* from here on we need a flow reorder instance */
+ if (rfi == NULL) {
+ buf_size = sizeof(*rfi);
+ max_idx = reorder_data[BRCMF_RXREORDER_MAXIDX_OFFSET];
+
+ buf_size += (max_idx + 1) * sizeof(pkt);
+
+ /* allocate space for flow reorder info */
+ brcmf_dbg(INFO, "flow-%d: start, maxidx %d\n",
+ flow_id, max_idx);
+ rfi = kzalloc(buf_size, GFP_ATOMIC);
+ if (rfi == NULL) {
+ brcmf_err("failed to alloc buffer\n");
+ brcmf_netif_rx(ifp, pkt);
+ return;
+ }
+
+ ifp->drvr->reorder_flows[flow_id] = rfi;
+ rfi->pktslots = (struct sk_buff **)(rfi + 1);
+ rfi->max_idx = max_idx;
+ }
+ if (flags & BRCMF_RXREORDER_NEW_HOLE) {
+ if (rfi->pend_pkts) {
+ brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx,
+ rfi->exp_idx,
+ &reorder_list);
+ WARN_ON(rfi->pend_pkts);
+ } else {
+ __skb_queue_head_init(&reorder_list);
+ }
+ rfi->cur_idx = reorder_data[BRCMF_RXREORDER_CURIDX_OFFSET];
+ rfi->exp_idx = reorder_data[BRCMF_RXREORDER_EXPIDX_OFFSET];
+ rfi->max_idx = reorder_data[BRCMF_RXREORDER_MAXIDX_OFFSET];
+ rfi->pktslots[rfi->cur_idx] = pkt;
+ rfi->pend_pkts++;
+ brcmf_dbg(DATA, "flow-%d: new hole %d (%d), pending %d\n",
+ flow_id, rfi->cur_idx, rfi->exp_idx, rfi->pend_pkts);
+ } else if (flags & BRCMF_RXREORDER_CURIDX_VALID) {
+ cur_idx = reorder_data[BRCMF_RXREORDER_CURIDX_OFFSET];
+ exp_idx = reorder_data[BRCMF_RXREORDER_EXPIDX_OFFSET];
+
+ if ((exp_idx == rfi->exp_idx) && (cur_idx != rfi->exp_idx)) {
+ /* still in the current hole */
+ /* enqueue the current on the buffer chain */
+ if (rfi->pktslots[cur_idx] != NULL) {
+ brcmf_dbg(INFO, "HOLE: ERROR buffer pending..free it\n");
+ brcmu_pkt_buf_free_skb(rfi->pktslots[cur_idx]);
+ rfi->pktslots[cur_idx] = NULL;
+ }
+ rfi->pktslots[cur_idx] = pkt;
+ rfi->pend_pkts++;
+ rfi->cur_idx = cur_idx;
+ brcmf_dbg(DATA, "flow-%d: store pkt %d (%d), pending %d\n",
+ flow_id, cur_idx, exp_idx, rfi->pend_pkts);
+
+ /* can return now as there is no reorder
+ * list to process.
+ */
+ return;
+ }
+ if (rfi->exp_idx == cur_idx) {
+ if (rfi->pktslots[cur_idx] != NULL) {
+ brcmf_dbg(INFO, "error buffer pending..free it\n");
+ brcmu_pkt_buf_free_skb(rfi->pktslots[cur_idx]);
+ rfi->pktslots[cur_idx] = NULL;
+ }
+ rfi->pktslots[cur_idx] = pkt;
+ rfi->pend_pkts++;
+
+ /* got the expected one. flush from current to expected
+ * and update expected
+ */
+ brcmf_dbg(DATA, "flow-%d: expected %d (%d), pending %d\n",
+ flow_id, cur_idx, exp_idx, rfi->pend_pkts);
+
+ rfi->cur_idx = cur_idx;
+ rfi->exp_idx = exp_idx;
+
+ brcmf_rxreorder_get_skb_list(rfi, cur_idx, exp_idx,
+ &reorder_list);
+ brcmf_dbg(DATA, "flow-%d: freeing buffers %d, pending %d\n",
+ flow_id, skb_queue_len(&reorder_list),
+ rfi->pend_pkts);
+ } else {
+ u8 end_idx;
+
+ brcmf_dbg(DATA, "flow-%d (0x%x): both moved, old %d/%d, new %d/%d\n",
+ flow_id, flags, rfi->cur_idx, rfi->exp_idx,
+ cur_idx, exp_idx);
+ if (flags & BRCMF_RXREORDER_FLUSH_ALL)
+ end_idx = rfi->exp_idx;
+ else
+ end_idx = exp_idx;
+
+ /* flush pkts first */
+ brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, end_idx,
+ &reorder_list);
+
+ if (exp_idx == ((cur_idx + 1) % (rfi->max_idx + 1))) {
+ __skb_queue_tail(&reorder_list, pkt);
+ } else {
+ rfi->pktslots[cur_idx] = pkt;
+ rfi->pend_pkts++;
+ }
+ rfi->exp_idx = exp_idx;
+ rfi->cur_idx = cur_idx;
+ }
+ } else {
+ /* explicity window move updating the expected index */
+ exp_idx = reorder_data[BRCMF_RXREORDER_EXPIDX_OFFSET];
+
+ brcmf_dbg(DATA, "flow-%d (0x%x): change expected: %d -> %d\n",
+ flow_id, flags, rfi->exp_idx, exp_idx);
+ if (flags & BRCMF_RXREORDER_FLUSH_ALL)
+ end_idx = rfi->exp_idx;
+ else
+ end_idx = exp_idx;
+
+ brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, end_idx,
+ &reorder_list);
+ __skb_queue_tail(&reorder_list, pkt);
+ /* set the new expected idx */
+ rfi->exp_idx = exp_idx;
+ }
+netif_rx:
+ skb_queue_walk_safe(&reorder_list, pkt, pnext) {
+ __skb_unlink(pkt, &reorder_list);
+ brcmf_netif_rx(ifp, pkt);
+ }
+}
+
+void brcmf_fws_hdrpull(struct brcmf_if *ifp, s16 siglen, struct sk_buff *skb)
+{
+ struct brcmf_skb_reorder_data *rd;
+ struct brcmf_fws_info *fws = drvr_to_fws(ifp->drvr);
+ u8 *signal_data;
+ s16 data_len;
+ u8 type;
+ u8 len;
+ u8 *data;
+ s32 status;
+ s32 err;
+
+ brcmf_dbg(HDRS, "enter: ifidx %d, skblen %u, sig %d\n",
+ ifp->ifidx, skb->len, siglen);
+
+ WARN_ON(siglen > skb->len);
+
+ if (!siglen)
+ return;
+ /* if flow control disabled, skip to packet data and leave */
+ if ((!fws) || (!fws->fw_signals)) {
+ skb_pull(skb, siglen);
+ return;
+ }
+
+ fws->stats.header_pulls++;
+ data_len = siglen;
+ signal_data = skb->data;
+
+ status = BRCMF_FWS_RET_OK_NOSCHEDULE;
+ while (data_len > 0) {
+ /* extract tlv info */
+ type = signal_data[0];
+
+ /* FILLER type is actually not a TLV, but
+ * a single byte that can be skipped.
+ */
+ if (type == BRCMF_FWS_TYPE_FILLER) {
+ signal_data += 1;
+ data_len -= 1;
+ continue;
+ }
+ len = signal_data[1];
+ data = signal_data + 2;
+
+ brcmf_dbg(HDRS, "tlv type=%s (%d), len=%d (%d)\n",
+ brcmf_fws_get_tlv_name(type), type, len,
+ brcmf_fws_get_tlv_len(fws, type));
+
+ /* abort parsing when length invalid */
+ if (data_len < len + 2)
+ break;
+
+ if (len < brcmf_fws_get_tlv_len(fws, type))
+ break;
+
+ err = BRCMF_FWS_RET_OK_NOSCHEDULE;
+ switch (type) {
+ case BRCMF_FWS_TYPE_COMP_TXSTATUS:
+ break;
+ case BRCMF_FWS_TYPE_HOST_REORDER_RXPKTS:
+ rd = (struct brcmf_skb_reorder_data *)skb->cb;
+ rd->reorder = data;
+ break;
+ case BRCMF_FWS_TYPE_MACDESC_ADD:
+ case BRCMF_FWS_TYPE_MACDESC_DEL:
+ brcmf_fws_macdesc_indicate(fws, type, data);
+ break;
+ case BRCMF_FWS_TYPE_MAC_OPEN:
+ case BRCMF_FWS_TYPE_MAC_CLOSE:
+ err = brcmf_fws_macdesc_state_indicate(fws, type, data);
+ break;
+ case BRCMF_FWS_TYPE_INTERFACE_OPEN:
+ case BRCMF_FWS_TYPE_INTERFACE_CLOSE:
+ err = brcmf_fws_interface_state_indicate(fws, type,
+ data);
+ break;
+ case BRCMF_FWS_TYPE_MAC_REQUEST_CREDIT:
+ case BRCMF_FWS_TYPE_MAC_REQUEST_PACKET:
+ err = brcmf_fws_request_indicate(fws, type, data);
+ break;
+ case BRCMF_FWS_TYPE_TXSTATUS:
+ brcmf_fws_txstatus_indicate(fws, data);
+ break;
+ case BRCMF_FWS_TYPE_FIFO_CREDITBACK:
+ err = brcmf_fws_fifocreditback_indicate(fws, data);
+ break;
+ case BRCMF_FWS_TYPE_RSSI:
+ brcmf_fws_rssi_indicate(fws, *data);
+ break;
+ case BRCMF_FWS_TYPE_TRANS_ID:
+ brcmf_fws_dbg_seqnum_check(fws, data);
+ break;
+ case BRCMF_FWS_TYPE_PKTTAG:
+ case BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP:
+ default:
+ fws->stats.tlv_invalid_type++;
+ break;
+ }
+ if (err == BRCMF_FWS_RET_OK_SCHEDULE)
+ status = BRCMF_FWS_RET_OK_SCHEDULE;
+ signal_data += len + 2;
+ data_len -= len + 2;
+ }
+
+ if (data_len != 0)
+ fws->stats.tlv_parse_failed++;
+
+ if (status == BRCMF_FWS_RET_OK_SCHEDULE)
+ brcmf_fws_schedule_deq(fws);
+
+ /* signalling processing result does
+ * not affect the actual ethernet packet.
+ */
+ skb_pull(skb, siglen);
+
+ /* this may be a signal-only packet
+ */
+ if (skb->len == 0)
+ fws->stats.header_only_pkt++;
+}
+
+static u8 brcmf_fws_precommit_skb(struct brcmf_fws_info *fws, int fifo,
+ struct sk_buff *p)
+{
+ struct brcmf_skbuff_cb *skcb = brcmf_skbcb(p);
+ struct brcmf_fws_mac_descriptor *entry = skcb->mac;
+ u8 flags;
+
+ if (skcb->state != BRCMF_FWS_SKBSTATE_SUPPRESSED)
+ brcmf_skb_htod_tag_set_field(p, GENERATION, entry->generation);
+ flags = BRCMF_FWS_HTOD_FLAG_PKTFROMHOST;
+ if (brcmf_skb_if_flags_get_field(p, REQUESTED)) {
+ /*
+ * Indicate that this packet is being sent in response to an
+ * explicit request from the firmware side.
+ */
+ flags |= BRCMF_FWS_HTOD_FLAG_PKT_REQUESTED;
+ }
+ brcmf_skb_htod_tag_set_field(p, FLAGS, flags);
+ return brcmf_fws_hdrpush(fws, p);
+}
+
+static void brcmf_fws_rollback_toq(struct brcmf_fws_info *fws,
+ struct sk_buff *skb, int fifo)
+{
+ struct brcmf_fws_mac_descriptor *entry;
+ struct sk_buff *pktout;
+ int qidx, hslot;
+ int rc = 0;
+
+ entry = brcmf_skbcb(skb)->mac;
+ if (entry->occupied) {
+ qidx = 2 * fifo;
+ if (brcmf_skbcb(skb)->state == BRCMF_FWS_SKBSTATE_SUPPRESSED)
+ qidx++;
+
+ pktout = brcmu_pktq_penq_head(&entry->psq, qidx, skb);
+ if (pktout == NULL) {
+ brcmf_err("%s queue %d full\n", entry->name, qidx);
+ rc = -ENOSPC;
+ }
+ } else {
+ brcmf_err("%s entry removed\n", entry->name);
+ rc = -ENOENT;
+ }
+
+ if (rc) {
+ fws->stats.rollback_failed++;
+ hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT);
+ brcmf_fws_txs_process(fws, BRCMF_FWS_TXSTATUS_HOST_TOSSED,
+ hslot, 0, 0);
+ } else {
+ fws->stats.rollback_success++;
+ brcmf_fws_return_credits(fws, fifo, 1);
+ brcmf_fws_macdesc_return_req_credit(skb);
+ }
+}
+
+static int brcmf_fws_borrow_credit(struct brcmf_fws_info *fws)
+{
+ int lender_ac;
+
+ if (time_after(fws->borrow_defer_timestamp, jiffies)) {
+ fws->fifo_credit_map &= ~(1 << BRCMF_FWS_FIFO_AC_BE);
+ return -ENAVAIL;
+ }
+
+ for (lender_ac = 0; lender_ac <= BRCMF_FWS_FIFO_AC_VO; lender_ac++) {
+ if (fws->fifo_credit[lender_ac]) {
+ fws->credits_borrowed[lender_ac]++;
+ fws->fifo_credit[lender_ac]--;
+ if (fws->fifo_credit[lender_ac] == 0)
+ fws->fifo_credit_map &= ~(1 << lender_ac);
+ fws->fifo_credit_map |= (1 << BRCMF_FWS_FIFO_AC_BE);
+ brcmf_dbg(DATA, "borrow credit from: %d\n", lender_ac);
+ return 0;
+ }
+ }
+ fws->fifo_credit_map &= ~(1 << BRCMF_FWS_FIFO_AC_BE);
+ return -ENAVAIL;
+}
+
+static int brcmf_fws_commit_skb(struct brcmf_fws_info *fws, int fifo,
+ struct sk_buff *skb)
+{
+ struct brcmf_skbuff_cb *skcb = brcmf_skbcb(skb);
+ struct brcmf_fws_mac_descriptor *entry;
+ int rc;
+ u8 ifidx;
+ u8 data_offset;
+
+ entry = skcb->mac;
+ if (IS_ERR(entry))
+ return PTR_ERR(entry);
+
+ data_offset = brcmf_fws_precommit_skb(fws, fifo, skb);
+ entry->transit_count++;
+ if (entry->suppressed)
+ entry->suppr_transit_count++;
+ ifidx = brcmf_skb_if_flags_get_field(skb, INDEX);
+ brcmf_fws_unlock(fws);
+ rc = brcmf_proto_txdata(fws->drvr, ifidx, data_offset, skb);
+ brcmf_fws_lock(fws);
+ brcmf_dbg(DATA, "%s flags %X htod %X bus_tx %d\n", entry->name,
+ skcb->if_flags, skcb->htod, rc);
+ if (rc < 0) {
+ entry->transit_count--;
+ if (entry->suppressed)
+ entry->suppr_transit_count--;
+ (void)brcmf_proto_hdrpull(fws->drvr, false, skb, NULL);
+ goto rollback;
+ }
+
+ fws->stats.pkt2bus++;
+ fws->stats.send_pkts[fifo]++;
+ if (brcmf_skb_if_flags_get_field(skb, REQUESTED))
+ fws->stats.requested_sent[fifo]++;
+
+ return rc;
+
+rollback:
+ brcmf_fws_rollback_toq(fws, skb, fifo);
+ return rc;
+}
+
+static int brcmf_fws_assign_htod(struct brcmf_fws_info *fws, struct sk_buff *p,
+ int fifo)
+{
+ struct brcmf_skbuff_cb *skcb = brcmf_skbcb(p);
+ int rc, hslot;
+
+ skcb->htod = 0;
+ skcb->htod_seq = 0;
+ hslot = brcmf_fws_hanger_get_free_slot(&fws->hanger);
+ brcmf_skb_htod_tag_set_field(p, HSLOT, hslot);
+ brcmf_skb_htod_tag_set_field(p, FREERUN, skcb->mac->seq[fifo]);
+ brcmf_skb_htod_tag_set_field(p, FIFO, fifo);
+ rc = brcmf_fws_hanger_pushpkt(&fws->hanger, p, hslot);
+ if (!rc)
+ skcb->mac->seq[fifo]++;
+ else
+ fws->stats.generic_error++;
+ return rc;
+}
+
+int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb)
+{
+ struct brcmf_fws_info *fws = drvr_to_fws(ifp->drvr);
+ struct brcmf_skbuff_cb *skcb = brcmf_skbcb(skb);
+ struct ethhdr *eh = (struct ethhdr *)(skb->data);
+ int fifo = BRCMF_FWS_FIFO_BCMC;
+ bool multicast = is_multicast_ether_addr(eh->h_dest);
+ int rc = 0;
+
+ brcmf_dbg(DATA, "tx proto=0x%X\n", ntohs(eh->h_proto));
+
+ /* set control buffer information */
+ skcb->if_flags = 0;
+ skcb->state = BRCMF_FWS_SKBSTATE_NEW;
+ brcmf_skb_if_flags_set_field(skb, INDEX, ifp->ifidx);
+ if (!multicast)
+ fifo = brcmf_fws_prio2fifo[skb->priority];
+
+ brcmf_fws_lock(fws);
+ if (fifo != BRCMF_FWS_FIFO_AC_BE && fifo < BRCMF_FWS_FIFO_BCMC)
+ fws->borrow_defer_timestamp = jiffies +
+ BRCMF_FWS_BORROW_DEFER_PERIOD;
+
+ skcb->mac = brcmf_fws_macdesc_find(fws, ifp, eh->h_dest);
+ brcmf_dbg(DATA, "%s mac %pM multi %d fifo %d\n", skcb->mac->name,
+ eh->h_dest, multicast, fifo);
+ if (!brcmf_fws_assign_htod(fws, skb, fifo)) {
+ brcmf_fws_enq(fws, BRCMF_FWS_SKBSTATE_DELAYED, fifo, skb);
+ brcmf_fws_schedule_deq(fws);
+ } else {
+ brcmf_err("drop skb: no hanger slot\n");
+ brcmf_txfinalize(ifp, skb, false);
+ rc = -ENOMEM;
+ }
+ brcmf_fws_unlock(fws);
+
+ return rc;
+}
+
+void brcmf_fws_reset_interface(struct brcmf_if *ifp)
+{
+ struct brcmf_fws_mac_descriptor *entry = ifp->fws_desc;
+
+ brcmf_dbg(TRACE, "enter: bsscfgidx=%d\n", ifp->bsscfgidx);
+ if (!entry)
+ return;
+
+ brcmf_fws_macdesc_init(entry, ifp->mac_addr, ifp->ifidx);
+}
+
+void brcmf_fws_add_interface(struct brcmf_if *ifp)
+{
+ struct brcmf_fws_info *fws = drvr_to_fws(ifp->drvr);
+ struct brcmf_fws_mac_descriptor *entry;
+
+ if (!ifp->ndev || !brcmf_fws_queue_skbs(fws))
+ return;
+
+ entry = &fws->desc.iface[ifp->ifidx];
+ ifp->fws_desc = entry;
+ brcmf_fws_macdesc_init(entry, ifp->mac_addr, ifp->ifidx);
+ brcmf_fws_macdesc_set_name(fws, entry);
+ brcmu_pktq_init(&entry->psq, BRCMF_FWS_PSQ_PREC_COUNT,
+ BRCMF_FWS_PSQ_LEN);
+ brcmf_dbg(TRACE, "added %s\n", entry->name);
+}
+
+void brcmf_fws_del_interface(struct brcmf_if *ifp)
+{
+ struct brcmf_fws_mac_descriptor *entry = ifp->fws_desc;
+ struct brcmf_fws_info *fws = drvr_to_fws(ifp->drvr);
+
+ if (!entry)
+ return;
+
+ brcmf_fws_lock(fws);
+ ifp->fws_desc = NULL;
+ brcmf_dbg(TRACE, "deleting %s\n", entry->name);
+ brcmf_fws_macdesc_deinit(entry);
+ brcmf_fws_cleanup(fws, ifp->ifidx);
+ brcmf_fws_unlock(fws);
+}
+
+static void brcmf_fws_dequeue_worker(struct work_struct *worker)
+{
+ struct brcmf_fws_info *fws;
+ struct brcmf_pub *drvr;
+ struct sk_buff *skb;
+ int fifo;
+ u32 hslot;
+ u32 ifidx;
+ int ret;
+
+ fws = container_of(worker, struct brcmf_fws_info, fws_dequeue_work);
+ drvr = fws->drvr;
+
+ brcmf_fws_lock(fws);
+ for (fifo = BRCMF_FWS_FIFO_BCMC; fifo >= 0 && !fws->bus_flow_blocked;
+ fifo--) {
+ if (!brcmf_fws_fc_active(fws)) {
+ while ((skb = brcmf_fws_deq(fws, fifo)) != NULL) {
+ hslot = brcmf_skb_htod_tag_get_field(skb,
+ HSLOT);
+ brcmf_fws_hanger_poppkt(&fws->hanger, hslot,
+ &skb, true);
+ ifidx = brcmf_skb_if_flags_get_field(skb,
+ INDEX);
+ /* Use proto layer to send data frame */
+ brcmf_fws_unlock(fws);
+ ret = brcmf_proto_txdata(drvr, ifidx, 0, skb);
+ brcmf_fws_lock(fws);
+ if (ret < 0)
+ brcmf_txfinalize(brcmf_get_ifp(drvr,
+ ifidx),
+ skb, false);
+ if (fws->bus_flow_blocked)
+ break;
+ }
+ continue;
+ }
+ while ((fws->fifo_credit[fifo]) || ((!fws->bcmc_credit_check) &&
+ (fifo == BRCMF_FWS_FIFO_BCMC))) {
+ skb = brcmf_fws_deq(fws, fifo);
+ if (!skb)
+ break;
+ fws->fifo_credit[fifo]--;
+ if (brcmf_fws_commit_skb(fws, fifo, skb))
+ break;
+ if (fws->bus_flow_blocked)
+ break;
+ }
+ if ((fifo == BRCMF_FWS_FIFO_AC_BE) &&
+ (fws->fifo_credit[fifo] == 0) &&
+ (!fws->bus_flow_blocked)) {
+ while (brcmf_fws_borrow_credit(fws) == 0) {
+ skb = brcmf_fws_deq(fws, fifo);
+ if (!skb) {
+ brcmf_fws_return_credits(fws, fifo, 1);
+ break;
+ }
+ if (brcmf_fws_commit_skb(fws, fifo, skb))
+ break;
+ if (fws->bus_flow_blocked)
+ break;
+ }
+ }
+ }
+ brcmf_fws_unlock(fws);
+}
+
+#ifdef DEBUG
+static int brcmf_debugfs_fws_stats_read(struct seq_file *seq, void *data)
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(seq->private);
+ struct brcmf_fws_stats *fwstats = &(drvr_to_fws(bus_if->drvr)->stats);
+
+ seq_printf(seq,
+ "header_pulls: %u\n"
+ "header_only_pkt: %u\n"
+ "tlv_parse_failed: %u\n"
+ "tlv_invalid_type: %u\n"
+ "mac_update_fails: %u\n"
+ "ps_update_fails: %u\n"
+ "if_update_fails: %u\n"
+ "pkt2bus: %u\n"
+ "generic_error: %u\n"
+ "rollback_success: %u\n"
+ "rollback_failed: %u\n"
+ "delayq_full: %u\n"
+ "supprq_full: %u\n"
+ "txs_indicate: %u\n"
+ "txs_discard: %u\n"
+ "txs_suppr_core: %u\n"
+ "txs_suppr_ps: %u\n"
+ "txs_tossed: %u\n"
+ "txs_host_tossed: %u\n"
+ "bus_flow_block: %u\n"
+ "fws_flow_block: %u\n"
+ "send_pkts: BK:%u BE:%u VO:%u VI:%u BCMC:%u\n"
+ "requested_sent: BK:%u BE:%u VO:%u VI:%u BCMC:%u\n",
+ fwstats->header_pulls,
+ fwstats->header_only_pkt,
+ fwstats->tlv_parse_failed,
+ fwstats->tlv_invalid_type,
+ fwstats->mac_update_failed,
+ fwstats->mac_ps_update_failed,
+ fwstats->if_update_failed,
+ fwstats->pkt2bus,
+ fwstats->generic_error,
+ fwstats->rollback_success,
+ fwstats->rollback_failed,
+ fwstats->delayq_full_error,
+ fwstats->supprq_full_error,
+ fwstats->txs_indicate,
+ fwstats->txs_discard,
+ fwstats->txs_supp_core,
+ fwstats->txs_supp_ps,
+ fwstats->txs_tossed,
+ fwstats->txs_host_tossed,
+ fwstats->bus_flow_block,
+ fwstats->fws_flow_block,
+ fwstats->send_pkts[0], fwstats->send_pkts[1],
+ fwstats->send_pkts[2], fwstats->send_pkts[3],
+ fwstats->send_pkts[4],
+ fwstats->requested_sent[0],
+ fwstats->requested_sent[1],
+ fwstats->requested_sent[2],
+ fwstats->requested_sent[3],
+ fwstats->requested_sent[4]);
+
+ return 0;
+}
+#else
+static int brcmf_debugfs_fws_stats_read(struct seq_file *seq, void *data)
+{
+ return 0;
+}
+#endif
+
+struct brcmf_fws_info *brcmf_fws_attach(struct brcmf_pub *drvr)
+{
+ struct brcmf_fws_info *fws;
+ struct brcmf_if *ifp;
+ u32 tlv = BRCMF_FWS_FLAGS_RSSI_SIGNALS;
+ int rc;
+ u32 mode;
+
+ fws = kzalloc(sizeof(*fws), GFP_KERNEL);
+ if (!fws) {
+ rc = -ENOMEM;
+ goto fail;
+ }
+
+ spin_lock_init(&fws->spinlock);
+
+ /* store drvr reference */
+ fws->drvr = drvr;
+ fws->fcmode = drvr->settings->fcmode;
+
+ if ((drvr->bus_if->always_use_fws_queue == false) &&
+ (fws->fcmode == BRCMF_FWS_FCMODE_NONE)) {
+ fws->avoid_queueing = true;
+ brcmf_dbg(INFO, "FWS queueing will be avoided\n");
+ return fws;
+ }
+
+ fws->fws_wq = create_singlethread_workqueue("brcmf_fws_wq");
+ if (fws->fws_wq == NULL) {
+ brcmf_err("workqueue creation failed\n");
+ rc = -EBADF;
+ goto fail;
+ }
+ INIT_WORK(&fws->fws_dequeue_work, brcmf_fws_dequeue_worker);
+
+ /* enable firmware signalling if fcmode active */
+ if (fws->fcmode != BRCMF_FWS_FCMODE_NONE)
+ tlv |= BRCMF_FWS_FLAGS_XONXOFF_SIGNALS |
+ BRCMF_FWS_FLAGS_CREDIT_STATUS_SIGNALS |
+ BRCMF_FWS_FLAGS_HOST_PROPTXSTATUS_ACTIVE |
+ BRCMF_FWS_FLAGS_HOST_RXREORDER_ACTIVE;
+
+ rc = brcmf_fweh_register(drvr, BRCMF_E_FIFO_CREDIT_MAP,
+ brcmf_fws_notify_credit_map);
+ if (rc < 0) {
+ brcmf_err("register credit map handler failed\n");
+ goto fail;
+ }
+ rc = brcmf_fweh_register(drvr, BRCMF_E_BCMC_CREDIT_SUPPORT,
+ brcmf_fws_notify_bcmc_credit_support);
+ if (rc < 0) {
+ brcmf_err("register bcmc credit handler failed\n");
+ brcmf_fweh_unregister(drvr, BRCMF_E_FIFO_CREDIT_MAP);
+ goto fail;
+ }
+
+ /* Setting the iovar may fail if feature is unsupported
+ * so leave the rc as is so driver initialization can
+ * continue. Set mode back to none indicating not enabled.
+ */
+ fws->fw_signals = true;
+ ifp = brcmf_get_ifp(drvr, 0);
+ if (brcmf_fil_iovar_int_set(ifp, "tlv", tlv)) {
+ brcmf_err("failed to set bdcv2 tlv signaling\n");
+ fws->fcmode = BRCMF_FWS_FCMODE_NONE;
+ fws->fw_signals = false;
+ }
+
+ if (brcmf_fil_iovar_int_set(ifp, "ampdu_hostreorder", 1))
+ brcmf_dbg(INFO, "enabling AMPDU host-reorder failed\n");
+
+ /* Enable seq number reuse, if supported */
+ if (brcmf_fil_iovar_int_get(ifp, "wlfc_mode", &mode) == 0) {
+ if (BRCMF_FWS_MODE_GET_REUSESEQ(mode)) {
+ mode = 0;
+ BRCMF_FWS_MODE_SET_REUSESEQ(mode, 1);
+ if (brcmf_fil_iovar_int_set(ifp,
+ "wlfc_mode", mode) == 0) {
+ BRCMF_FWS_MODE_SET_REUSESEQ(fws->mode, 1);
+ }
+ }
+ }
+
+ brcmf_fws_hanger_init(&fws->hanger);
+ brcmf_fws_macdesc_init(&fws->desc.other, NULL, 0);
+ brcmf_fws_macdesc_set_name(fws, &fws->desc.other);
+ brcmf_dbg(INFO, "added %s\n", fws->desc.other.name);
+ brcmu_pktq_init(&fws->desc.other.psq, BRCMF_FWS_PSQ_PREC_COUNT,
+ BRCMF_FWS_PSQ_LEN);
+
+ /* create debugfs file for statistics */
+ brcmf_debugfs_add_entry(drvr, "fws_stats",
+ brcmf_debugfs_fws_stats_read);
+
+ brcmf_dbg(INFO, "%s bdcv2 tlv signaling [%x]\n",
+ fws->fw_signals ? "enabled" : "disabled", tlv);
+ return fws;
+
+fail:
+ brcmf_fws_detach(fws);
+ return ERR_PTR(rc);
+}
+
+void brcmf_fws_detach(struct brcmf_fws_info *fws)
+{
+ if (!fws)
+ return;
+
+ if (fws->fws_wq)
+ destroy_workqueue(fws->fws_wq);
+
+ /* cleanup */
+ brcmf_fws_lock(fws);
+ brcmf_fws_cleanup(fws, -1);
+ brcmf_fws_unlock(fws);
+
+ /* free top structure */
+ kfree(fws);
+}
+
+bool brcmf_fws_queue_skbs(struct brcmf_fws_info *fws)
+{
+ return !fws->avoid_queueing;
+}
+
+bool brcmf_fws_fc_active(struct brcmf_fws_info *fws)
+{
+ if (!fws->creditmap_received)
+ return false;
+
+ return fws->fcmode != BRCMF_FWS_FCMODE_NONE;
+}
+
+void brcmf_fws_bustxfail(struct brcmf_fws_info *fws, struct sk_buff *skb)
+{
+ u32 hslot;
+
+ if (brcmf_skbcb(skb)->state == BRCMF_FWS_SKBSTATE_TIM) {
+ brcmu_pkt_buf_free_skb(skb);
+ return;
+ }
+ brcmf_fws_lock(fws);
+ hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT);
+ brcmf_fws_txs_process(fws, BRCMF_FWS_TXSTATUS_HOST_TOSSED, hslot, 0, 0);
+ brcmf_fws_unlock(fws);
+}
+
+void brcmf_fws_bus_blocked(struct brcmf_pub *drvr, bool flow_blocked)
+{
+ struct brcmf_fws_info *fws = drvr_to_fws(drvr);
+ struct brcmf_if *ifp;
+ int i;
+
+ if (fws->avoid_queueing) {
+ for (i = 0; i < BRCMF_MAX_IFS; i++) {
+ ifp = drvr->iflist[i];
+ if (!ifp || !ifp->ndev)
+ continue;
+ brcmf_txflowblock_if(ifp, BRCMF_NETIF_STOP_REASON_FLOW,
+ flow_blocked);
+ }
+ } else {
+ fws->bus_flow_blocked = flow_blocked;
+ if (!flow_blocked)
+ brcmf_fws_schedule_deq(fws);
+ else
+ fws->stats.bus_flow_block++;
+ }
+}
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h
new file mode 100644
index 0000000..ba07bd9
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2012 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+
+#ifndef FWSIGNAL_H_
+#define FWSIGNAL_H_
+
+struct brcmf_fws_info *brcmf_fws_attach(struct brcmf_pub *drvr);
+void brcmf_fws_detach(struct brcmf_fws_info *fws);
+bool brcmf_fws_queue_skbs(struct brcmf_fws_info *fws);
+bool brcmf_fws_fc_active(struct brcmf_fws_info *fws);
+void brcmf_fws_hdrpull(struct brcmf_if *ifp, s16 siglen, struct sk_buff *skb);
+int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb);
+
+void brcmf_fws_reset_interface(struct brcmf_if *ifp);
+void brcmf_fws_add_interface(struct brcmf_if *ifp);
+void brcmf_fws_del_interface(struct brcmf_if *ifp);
+void brcmf_fws_bustxfail(struct brcmf_fws_info *fws, struct sk_buff *skb);
+void brcmf_fws_bus_blocked(struct brcmf_pub *drvr, bool flow_blocked);
+void brcmf_fws_rxreorder(struct brcmf_if *ifp, struct sk_buff *skb);
+
+#endif /* FWSIGNAL_H_ */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c
new file mode 100644
index 0000000..d2c834c
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c
@@ -0,0 +1,1577 @@
+/* Copyright (c) 2014 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*******************************************************************************
+ * Communicates with the dongle by using dcmd codes.
+ * For certain dcmd codes, the dongle interprets string data from the host.
+ ******************************************************************************/
+
+#include <linux/types.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+
+#include <brcmu_utils.h>
+#include <brcmu_wifi.h>
+
+#include "core.h"
+#include "debug.h"
+#include "proto.h"
+#include "msgbuf.h"
+#include "commonring.h"
+#include "flowring.h"
+#include "bus.h"
+#include "tracepoint.h"
+
+
+#define MSGBUF_IOCTL_RESP_TIMEOUT msecs_to_jiffies(2000)
+
+#define MSGBUF_TYPE_GEN_STATUS 0x1
+#define MSGBUF_TYPE_RING_STATUS 0x2
+#define MSGBUF_TYPE_FLOW_RING_CREATE 0x3
+#define MSGBUF_TYPE_FLOW_RING_CREATE_CMPLT 0x4
+#define MSGBUF_TYPE_FLOW_RING_DELETE 0x5
+#define MSGBUF_TYPE_FLOW_RING_DELETE_CMPLT 0x6
+#define MSGBUF_TYPE_FLOW_RING_FLUSH 0x7
+#define MSGBUF_TYPE_FLOW_RING_FLUSH_CMPLT 0x8
+#define MSGBUF_TYPE_IOCTLPTR_REQ 0x9
+#define MSGBUF_TYPE_IOCTLPTR_REQ_ACK 0xA
+#define MSGBUF_TYPE_IOCTLRESP_BUF_POST 0xB
+#define MSGBUF_TYPE_IOCTL_CMPLT 0xC
+#define MSGBUF_TYPE_EVENT_BUF_POST 0xD
+#define MSGBUF_TYPE_WL_EVENT 0xE
+#define MSGBUF_TYPE_TX_POST 0xF
+#define MSGBUF_TYPE_TX_STATUS 0x10
+#define MSGBUF_TYPE_RXBUF_POST 0x11
+#define MSGBUF_TYPE_RX_CMPLT 0x12
+#define MSGBUF_TYPE_LPBK_DMAXFER 0x13
+#define MSGBUF_TYPE_LPBK_DMAXFER_CMPLT 0x14
+
+#define NR_TX_PKTIDS 2048
+#define NR_RX_PKTIDS 1024
+
+#define BRCMF_IOCTL_REQ_PKTID 0xFFFE
+
+#define BRCMF_MSGBUF_MAX_PKT_SIZE 2048
+#define BRCMF_MSGBUF_RXBUFPOST_THRESHOLD 32
+#define BRCMF_MSGBUF_MAX_IOCTLRESPBUF_POST 8
+#define BRCMF_MSGBUF_MAX_EVENTBUF_POST 8
+
+#define BRCMF_MSGBUF_PKT_FLAGS_FRAME_802_3 0x01
+#define BRCMF_MSGBUF_PKT_FLAGS_PRIO_SHIFT 5
+
+#define BRCMF_MSGBUF_TX_FLUSH_CNT1 32
+#define BRCMF_MSGBUF_TX_FLUSH_CNT2 96
+
+#define BRCMF_MSGBUF_DELAY_TXWORKER_THRS 96
+#define BRCMF_MSGBUF_TRICKLE_TXWORKER_THRS 32
+#define BRCMF_MSGBUF_UPDATE_RX_PTR_THRS 48
+
+
+struct msgbuf_common_hdr {
+ u8 msgtype;
+ u8 ifidx;
+ u8 flags;
+ u8 rsvd0;
+ __le32 request_id;
+};
+
+struct msgbuf_ioctl_req_hdr {
+ struct msgbuf_common_hdr msg;
+ __le32 cmd;
+ __le16 trans_id;
+ __le16 input_buf_len;
+ __le16 output_buf_len;
+ __le16 rsvd0[3];
+ struct msgbuf_buf_addr req_buf_addr;
+ __le32 rsvd1[2];
+};
+
+struct msgbuf_tx_msghdr {
+ struct msgbuf_common_hdr msg;
+ u8 txhdr[ETH_HLEN];
+ u8 flags;
+ u8 seg_cnt;
+ struct msgbuf_buf_addr metadata_buf_addr;
+ struct msgbuf_buf_addr data_buf_addr;
+ __le16 metadata_buf_len;
+ __le16 data_len;
+ __le32 rsvd0;
+};
+
+struct msgbuf_rx_bufpost {
+ struct msgbuf_common_hdr msg;
+ __le16 metadata_buf_len;
+ __le16 data_buf_len;
+ __le32 rsvd0;
+ struct msgbuf_buf_addr metadata_buf_addr;
+ struct msgbuf_buf_addr data_buf_addr;
+};
+
+struct msgbuf_rx_ioctl_resp_or_event {
+ struct msgbuf_common_hdr msg;
+ __le16 host_buf_len;
+ __le16 rsvd0[3];
+ struct msgbuf_buf_addr host_buf_addr;
+ __le32 rsvd1[4];
+};
+
+struct msgbuf_completion_hdr {
+ __le16 status;
+ __le16 flow_ring_id;
+};
+
+struct msgbuf_rx_event {
+ struct msgbuf_common_hdr msg;
+ struct msgbuf_completion_hdr compl_hdr;
+ __le16 event_data_len;
+ __le16 seqnum;
+ __le16 rsvd0[4];
+};
+
+struct msgbuf_ioctl_resp_hdr {
+ struct msgbuf_common_hdr msg;
+ struct msgbuf_completion_hdr compl_hdr;
+ __le16 resp_len;
+ __le16 trans_id;
+ __le32 cmd;
+ __le32 rsvd0;
+};
+
+struct msgbuf_tx_status {
+ struct msgbuf_common_hdr msg;
+ struct msgbuf_completion_hdr compl_hdr;
+ __le16 metadata_len;
+ __le16 tx_status;
+};
+
+struct msgbuf_rx_complete {
+ struct msgbuf_common_hdr msg;
+ struct msgbuf_completion_hdr compl_hdr;
+ __le16 metadata_len;
+ __le16 data_len;
+ __le16 data_offset;
+ __le16 flags;
+ __le32 rx_status_0;
+ __le32 rx_status_1;
+ __le32 rsvd0;
+};
+
+struct msgbuf_tx_flowring_create_req {
+ struct msgbuf_common_hdr msg;
+ u8 da[ETH_ALEN];
+ u8 sa[ETH_ALEN];
+ u8 tid;
+ u8 if_flags;
+ __le16 flow_ring_id;
+ u8 tc;
+ u8 priority;
+ __le16 int_vector;
+ __le16 max_items;
+ __le16 len_item;
+ struct msgbuf_buf_addr flow_ring_addr;
+};
+
+struct msgbuf_tx_flowring_delete_req {
+ struct msgbuf_common_hdr msg;
+ __le16 flow_ring_id;
+ __le16 reason;
+ __le32 rsvd0[7];
+};
+
+struct msgbuf_flowring_create_resp {
+ struct msgbuf_common_hdr msg;
+ struct msgbuf_completion_hdr compl_hdr;
+ __le32 rsvd0[3];
+};
+
+struct msgbuf_flowring_delete_resp {
+ struct msgbuf_common_hdr msg;
+ struct msgbuf_completion_hdr compl_hdr;
+ __le32 rsvd0[3];
+};
+
+struct msgbuf_flowring_flush_resp {
+ struct msgbuf_common_hdr msg;
+ struct msgbuf_completion_hdr compl_hdr;
+ __le32 rsvd0[3];
+};
+
+struct brcmf_msgbuf_work_item {
+ struct list_head queue;
+ u32 flowid;
+ int ifidx;
+ u8 sa[ETH_ALEN];
+ u8 da[ETH_ALEN];
+};
+
+struct brcmf_msgbuf {
+ struct brcmf_pub *drvr;
+
+ struct brcmf_commonring **commonrings;
+ struct brcmf_commonring **flowrings;
+ dma_addr_t *flowring_dma_handle;
+
+ u16 max_flowrings;
+ u16 max_submissionrings;
+ u16 max_completionrings;
+
+ u16 rx_dataoffset;
+ u32 max_rxbufpost;
+ u16 rx_metadata_offset;
+ u32 rxbufpost;
+
+ u32 max_ioctlrespbuf;
+ u32 cur_ioctlrespbuf;
+ u32 max_eventbuf;
+ u32 cur_eventbuf;
+
+ void *ioctbuf;
+ dma_addr_t ioctbuf_handle;
+ u32 ioctbuf_phys_hi;
+ u32 ioctbuf_phys_lo;
+ int ioctl_resp_status;
+ u32 ioctl_resp_ret_len;
+ u32 ioctl_resp_pktid;
+
+ u16 data_seq_no;
+ u16 ioctl_seq_no;
+ u32 reqid;
+ wait_queue_head_t ioctl_resp_wait;
+ bool ctl_completed;
+
+ struct brcmf_msgbuf_pktids *tx_pktids;
+ struct brcmf_msgbuf_pktids *rx_pktids;
+ struct brcmf_flowring *flow;
+
+ struct workqueue_struct *txflow_wq;
+ struct work_struct txflow_work;
+ unsigned long *flow_map;
+ unsigned long *txstatus_done_map;
+
+ struct work_struct flowring_work;
+ spinlock_t flowring_work_lock;
+ struct list_head work_queue;
+};
+
+struct brcmf_msgbuf_pktid {
+ atomic_t allocated;
+ u16 data_offset;
+ struct sk_buff *skb;
+ dma_addr_t physaddr;
+};
+
+struct brcmf_msgbuf_pktids {
+ u32 array_size;
+ u32 last_allocated_idx;
+ enum dma_data_direction direction;
+ struct brcmf_msgbuf_pktid *array;
+};
+
+static void brcmf_msgbuf_rxbuf_ioctlresp_post(struct brcmf_msgbuf *msgbuf);
+
+
+static struct brcmf_msgbuf_pktids *
+brcmf_msgbuf_init_pktids(u32 nr_array_entries,
+ enum dma_data_direction direction)
+{
+ struct brcmf_msgbuf_pktid *array;
+ struct brcmf_msgbuf_pktids *pktids;
+
+ array = kcalloc(nr_array_entries, sizeof(*array), GFP_KERNEL);
+ if (!array)
+ return NULL;
+
+ pktids = kzalloc(sizeof(*pktids), GFP_KERNEL);
+ if (!pktids) {
+ kfree(array);
+ return NULL;
+ }
+ pktids->array = array;
+ pktids->array_size = nr_array_entries;
+
+ return pktids;
+}
+
+
+static int
+brcmf_msgbuf_alloc_pktid(struct device *dev,
+ struct brcmf_msgbuf_pktids *pktids,
+ struct sk_buff *skb, u16 data_offset,
+ dma_addr_t *physaddr, u32 *idx)
+{
+ struct brcmf_msgbuf_pktid *array;
+ u32 count;
+
+ array = pktids->array;
+
+ *physaddr = dma_map_single(dev, skb->data + data_offset,
+ skb->len - data_offset, pktids->direction);
+
+ if (dma_mapping_error(dev, *physaddr)) {
+ brcmf_err("dma_map_single failed !!\n");
+ return -ENOMEM;
+ }
+
+ *idx = pktids->last_allocated_idx;
+
+ count = 0;
+ do {
+ (*idx)++;
+ if (*idx == pktids->array_size)
+ *idx = 0;
+ if (array[*idx].allocated.counter == 0)
+ if (atomic_cmpxchg(&array[*idx].allocated, 0, 1) == 0)
+ break;
+ count++;
+ } while (count < pktids->array_size);
+
+ if (count == pktids->array_size)
+ return -ENOMEM;
+
+ array[*idx].data_offset = data_offset;
+ array[*idx].physaddr = *physaddr;
+ array[*idx].skb = skb;
+
+ pktids->last_allocated_idx = *idx;
+
+ return 0;
+}
+
+
+static struct sk_buff *
+brcmf_msgbuf_get_pktid(struct device *dev, struct brcmf_msgbuf_pktids *pktids,
+ u32 idx)
+{
+ struct brcmf_msgbuf_pktid *pktid;
+ struct sk_buff *skb;
+
+ if (idx >= pktids->array_size) {
+ brcmf_err("Invalid packet id %d (max %d)\n", idx,
+ pktids->array_size);
+ return NULL;
+ }
+ if (pktids->array[idx].allocated.counter) {
+ pktid = &pktids->array[idx];
+ dma_unmap_single(dev, pktid->physaddr,
+ pktid->skb->len - pktid->data_offset,
+ pktids->direction);
+ skb = pktid->skb;
+ pktid->allocated.counter = 0;
+ return skb;
+ } else {
+ brcmf_err("Invalid packet id %d (not in use)\n", idx);
+ }
+
+ return NULL;
+}
+
+
+static void
+brcmf_msgbuf_release_array(struct device *dev,
+ struct brcmf_msgbuf_pktids *pktids)
+{
+ struct brcmf_msgbuf_pktid *array;
+ struct brcmf_msgbuf_pktid *pktid;
+ u32 count;
+
+ array = pktids->array;
+ count = 0;
+ do {
+ if (array[count].allocated.counter) {
+ pktid = &array[count];
+ dma_unmap_single(dev, pktid->physaddr,
+ pktid->skb->len - pktid->data_offset,
+ pktids->direction);
+ brcmu_pkt_buf_free_skb(pktid->skb);
+ }
+ count++;
+ } while (count < pktids->array_size);
+
+ kfree(array);
+ kfree(pktids);
+}
+
+
+static void brcmf_msgbuf_release_pktids(struct brcmf_msgbuf *msgbuf)
+{
+ if (msgbuf->rx_pktids)
+ brcmf_msgbuf_release_array(msgbuf->drvr->bus_if->dev,
+ msgbuf->rx_pktids);
+ if (msgbuf->tx_pktids)
+ brcmf_msgbuf_release_array(msgbuf->drvr->bus_if->dev,
+ msgbuf->tx_pktids);
+}
+
+
+static int brcmf_msgbuf_tx_ioctl(struct brcmf_pub *drvr, int ifidx,
+ uint cmd, void *buf, uint len)
+{
+ struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;
+ struct brcmf_commonring *commonring;
+ struct msgbuf_ioctl_req_hdr *request;
+ u16 buf_len;
+ void *ret_ptr;
+ int err;
+
+ commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_CONTROL_SUBMIT];
+ brcmf_commonring_lock(commonring);
+ ret_ptr = brcmf_commonring_reserve_for_write(commonring);
+ if (!ret_ptr) {
+ brcmf_err("Failed to reserve space in commonring\n");
+ brcmf_commonring_unlock(commonring);
+ return -ENOMEM;
+ }
+
+ msgbuf->reqid++;
+
+ request = (struct msgbuf_ioctl_req_hdr *)ret_ptr;
+ request->msg.msgtype = MSGBUF_TYPE_IOCTLPTR_REQ;
+ request->msg.ifidx = (u8)ifidx;
+ request->msg.flags = 0;
+ request->msg.request_id = cpu_to_le32(BRCMF_IOCTL_REQ_PKTID);
+ request->cmd = cpu_to_le32(cmd);
+ request->output_buf_len = cpu_to_le16(len);
+ request->trans_id = cpu_to_le16(msgbuf->reqid);
+
+ buf_len = min_t(u16, len, BRCMF_TX_IOCTL_MAX_MSG_SIZE);
+ request->input_buf_len = cpu_to_le16(buf_len);
+ request->req_buf_addr.high_addr = cpu_to_le32(msgbuf->ioctbuf_phys_hi);
+ request->req_buf_addr.low_addr = cpu_to_le32(msgbuf->ioctbuf_phys_lo);
+ if (buf)
+ memcpy(msgbuf->ioctbuf, buf, buf_len);
+ else
+ memset(msgbuf->ioctbuf, 0, buf_len);
+
+ err = brcmf_commonring_write_complete(commonring);
+ brcmf_commonring_unlock(commonring);
+
+ return err;
+}
+
+
+static int brcmf_msgbuf_ioctl_resp_wait(struct brcmf_msgbuf *msgbuf)
+{
+ return wait_event_timeout(msgbuf->ioctl_resp_wait,
+ msgbuf->ctl_completed,
+ MSGBUF_IOCTL_RESP_TIMEOUT);
+}
+
+
+static void brcmf_msgbuf_ioctl_resp_wake(struct brcmf_msgbuf *msgbuf)
+{
+ msgbuf->ctl_completed = true;
+ wake_up(&msgbuf->ioctl_resp_wait);
+}
+
+
+static int brcmf_msgbuf_query_dcmd(struct brcmf_pub *drvr, int ifidx,
+ uint cmd, void *buf, uint len)
+{
+ struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;
+ struct sk_buff *skb = NULL;
+ int timeout;
+ int err;
+
+ brcmf_dbg(MSGBUF, "ifidx=%d, cmd=%d, len=%d\n", ifidx, cmd, len);
+ msgbuf->ctl_completed = false;
+ err = brcmf_msgbuf_tx_ioctl(drvr, ifidx, cmd, buf, len);
+ if (err)
+ return err;
+
+ timeout = brcmf_msgbuf_ioctl_resp_wait(msgbuf);
+ if (!timeout) {
+ brcmf_err("Timeout on response for query command\n");
+ return -EIO;
+ }
+
+ skb = brcmf_msgbuf_get_pktid(msgbuf->drvr->bus_if->dev,
+ msgbuf->rx_pktids,
+ msgbuf->ioctl_resp_pktid);
+ if (msgbuf->ioctl_resp_ret_len != 0) {
+ if (!skb)
+ return -EBADF;
+
+ memcpy(buf, skb->data, (len < msgbuf->ioctl_resp_ret_len) ?
+ len : msgbuf->ioctl_resp_ret_len);
+ }
+ brcmu_pkt_buf_free_skb(skb);
+
+ return msgbuf->ioctl_resp_status;
+}
+
+
+static int brcmf_msgbuf_set_dcmd(struct brcmf_pub *drvr, int ifidx,
+ uint cmd, void *buf, uint len)
+{
+ return brcmf_msgbuf_query_dcmd(drvr, ifidx, cmd, buf, len);
+}
+
+
+static int brcmf_msgbuf_hdrpull(struct brcmf_pub *drvr, bool do_fws,
+ struct sk_buff *skb, struct brcmf_if **ifp)
+{
+ return -ENODEV;
+}
+
+static void brcmf_msgbuf_rxreorder(struct brcmf_if *ifp, struct sk_buff *skb)
+{
+}
+
+static void
+brcmf_msgbuf_remove_flowring(struct brcmf_msgbuf *msgbuf, u16 flowid)
+{
+ u32 dma_sz;
+ void *dma_buf;
+
+ brcmf_dbg(MSGBUF, "Removing flowring %d\n", flowid);
+
+ dma_sz = BRCMF_H2D_TXFLOWRING_MAX_ITEM * BRCMF_H2D_TXFLOWRING_ITEMSIZE;
+ dma_buf = msgbuf->flowrings[flowid]->buf_addr;
+ dma_free_coherent(msgbuf->drvr->bus_if->dev, dma_sz, dma_buf,
+ msgbuf->flowring_dma_handle[flowid]);
+
+ brcmf_flowring_delete(msgbuf->flow, flowid);
+}
+
+
+static struct brcmf_msgbuf_work_item *
+brcmf_msgbuf_dequeue_work(struct brcmf_msgbuf *msgbuf)
+{
+ struct brcmf_msgbuf_work_item *work = NULL;
+ ulong flags;
+
+ spin_lock_irqsave(&msgbuf->flowring_work_lock, flags);
+ if (!list_empty(&msgbuf->work_queue)) {
+ work = list_first_entry(&msgbuf->work_queue,
+ struct brcmf_msgbuf_work_item, queue);
+ list_del(&work->queue);
+ }
+ spin_unlock_irqrestore(&msgbuf->flowring_work_lock, flags);
+
+ return work;
+}
+
+
+static u32
+brcmf_msgbuf_flowring_create_worker(struct brcmf_msgbuf *msgbuf,
+ struct brcmf_msgbuf_work_item *work)
+{
+ struct msgbuf_tx_flowring_create_req *create;
+ struct brcmf_commonring *commonring;
+ void *ret_ptr;
+ u32 flowid;
+ void *dma_buf;
+ u32 dma_sz;
+ u64 address;
+ int err;
+
+ flowid = work->flowid;
+ dma_sz = BRCMF_H2D_TXFLOWRING_MAX_ITEM * BRCMF_H2D_TXFLOWRING_ITEMSIZE;
+ dma_buf = dma_alloc_coherent(msgbuf->drvr->bus_if->dev, dma_sz,
+ &msgbuf->flowring_dma_handle[flowid],
+ GFP_KERNEL);
+ if (!dma_buf) {
+ brcmf_err("dma_alloc_coherent failed\n");
+ brcmf_flowring_delete(msgbuf->flow, flowid);
+ return BRCMF_FLOWRING_INVALID_ID;
+ }
+
+ brcmf_commonring_config(msgbuf->flowrings[flowid],
+ BRCMF_H2D_TXFLOWRING_MAX_ITEM,
+ BRCMF_H2D_TXFLOWRING_ITEMSIZE, dma_buf);
+
+ commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_CONTROL_SUBMIT];
+ brcmf_commonring_lock(commonring);
+ ret_ptr = brcmf_commonring_reserve_for_write(commonring);
+ if (!ret_ptr) {
+ brcmf_err("Failed to reserve space in commonring\n");
+ brcmf_commonring_unlock(commonring);
+ brcmf_msgbuf_remove_flowring(msgbuf, flowid);
+ return BRCMF_FLOWRING_INVALID_ID;
+ }
+
+ create = (struct msgbuf_tx_flowring_create_req *)ret_ptr;
+ create->msg.msgtype = MSGBUF_TYPE_FLOW_RING_CREATE;
+ create->msg.ifidx = work->ifidx;
+ create->msg.request_id = 0;
+ create->tid = brcmf_flowring_tid(msgbuf->flow, flowid);
+ create->flow_ring_id = cpu_to_le16(flowid +
+ BRCMF_H2D_MSGRING_FLOWRING_IDSTART);
+ memcpy(create->sa, work->sa, ETH_ALEN);
+ memcpy(create->da, work->da, ETH_ALEN);
+ address = (u64)msgbuf->flowring_dma_handle[flowid];
+ create->flow_ring_addr.high_addr = cpu_to_le32(address >> 32);
+ create->flow_ring_addr.low_addr = cpu_to_le32(address & 0xffffffff);
+ create->max_items = cpu_to_le16(BRCMF_H2D_TXFLOWRING_MAX_ITEM);
+ create->len_item = cpu_to_le16(BRCMF_H2D_TXFLOWRING_ITEMSIZE);
+
+ brcmf_dbg(MSGBUF, "Send Flow Create Req flow ID %d for peer %pM prio %d ifindex %d\n",
+ flowid, work->da, create->tid, work->ifidx);
+
+ err = brcmf_commonring_write_complete(commonring);
+ brcmf_commonring_unlock(commonring);
+ if (err) {
+ brcmf_err("Failed to write commonring\n");
+ brcmf_msgbuf_remove_flowring(msgbuf, flowid);
+ return BRCMF_FLOWRING_INVALID_ID;
+ }
+
+ return flowid;
+}
+
+
+static void brcmf_msgbuf_flowring_worker(struct work_struct *work)
+{
+ struct brcmf_msgbuf *msgbuf;
+ struct brcmf_msgbuf_work_item *create;
+
+ msgbuf = container_of(work, struct brcmf_msgbuf, flowring_work);
+
+ while ((create = brcmf_msgbuf_dequeue_work(msgbuf))) {
+ brcmf_msgbuf_flowring_create_worker(msgbuf, create);
+ kfree(create);
+ }
+}
+
+
+static u32 brcmf_msgbuf_flowring_create(struct brcmf_msgbuf *msgbuf, int ifidx,
+ struct sk_buff *skb)
+{
+ struct brcmf_msgbuf_work_item *create;
+ struct ethhdr *eh = (struct ethhdr *)(skb->data);
+ u32 flowid;
+ ulong flags;
+
+ create = kzalloc(sizeof(*create), GFP_ATOMIC);
+ if (create == NULL)
+ return BRCMF_FLOWRING_INVALID_ID;
+
+ flowid = brcmf_flowring_create(msgbuf->flow, eh->h_dest,
+ skb->priority, ifidx);
+ if (flowid == BRCMF_FLOWRING_INVALID_ID) {
+ kfree(create);
+ return flowid;
+ }
+
+ create->flowid = flowid;
+ create->ifidx = ifidx;
+ memcpy(create->sa, eh->h_source, ETH_ALEN);
+ memcpy(create->da, eh->h_dest, ETH_ALEN);
+
+ spin_lock_irqsave(&msgbuf->flowring_work_lock, flags);
+ list_add_tail(&create->queue, &msgbuf->work_queue);
+ spin_unlock_irqrestore(&msgbuf->flowring_work_lock, flags);
+ schedule_work(&msgbuf->flowring_work);
+
+ return flowid;
+}
+
+
+static void brcmf_msgbuf_txflow(struct brcmf_msgbuf *msgbuf, u16 flowid)
+{
+ struct brcmf_flowring *flow = msgbuf->flow;
+ struct brcmf_commonring *commonring;
+ void *ret_ptr;
+ u32 count;
+ struct sk_buff *skb;
+ dma_addr_t physaddr;
+ u32 pktid;
+ struct msgbuf_tx_msghdr *tx_msghdr;
+ u64 address;
+
+ commonring = msgbuf->flowrings[flowid];
+ if (!brcmf_commonring_write_available(commonring))
+ return;
+
+ brcmf_commonring_lock(commonring);
+
+ count = BRCMF_MSGBUF_TX_FLUSH_CNT2 - BRCMF_MSGBUF_TX_FLUSH_CNT1;
+ while (brcmf_flowring_qlen(flow, flowid)) {
+ skb = brcmf_flowring_dequeue(flow, flowid);
+ if (skb == NULL) {
+ brcmf_err("No SKB, but qlen %d\n",
+ brcmf_flowring_qlen(flow, flowid));
+ break;
+ }
+ skb_orphan(skb);
+ if (brcmf_msgbuf_alloc_pktid(msgbuf->drvr->bus_if->dev,
+ msgbuf->tx_pktids, skb, ETH_HLEN,
+ &physaddr, &pktid)) {
+ brcmf_flowring_reinsert(flow, flowid, skb);
+ brcmf_err("No PKTID available !!\n");
+ break;
+ }
+ ret_ptr = brcmf_commonring_reserve_for_write(commonring);
+ if (!ret_ptr) {
+ brcmf_msgbuf_get_pktid(msgbuf->drvr->bus_if->dev,
+ msgbuf->tx_pktids, pktid);
+ brcmf_flowring_reinsert(flow, flowid, skb);
+ break;
+ }
+ count++;
+
+ tx_msghdr = (struct msgbuf_tx_msghdr *)ret_ptr;
+
+ tx_msghdr->msg.msgtype = MSGBUF_TYPE_TX_POST;
+ tx_msghdr->msg.request_id = cpu_to_le32(pktid);
+ tx_msghdr->msg.ifidx = brcmf_flowring_ifidx_get(flow, flowid);
+ tx_msghdr->flags = BRCMF_MSGBUF_PKT_FLAGS_FRAME_802_3;
+ tx_msghdr->flags |= (skb->priority & 0x07) <<
+ BRCMF_MSGBUF_PKT_FLAGS_PRIO_SHIFT;
+ tx_msghdr->seg_cnt = 1;
+ memcpy(tx_msghdr->txhdr, skb->data, ETH_HLEN);
+ tx_msghdr->data_len = cpu_to_le16(skb->len - ETH_HLEN);
+ address = (u64)physaddr;
+ tx_msghdr->data_buf_addr.high_addr = cpu_to_le32(address >> 32);
+ tx_msghdr->data_buf_addr.low_addr =
+ cpu_to_le32(address & 0xffffffff);
+ tx_msghdr->metadata_buf_len = 0;
+ tx_msghdr->metadata_buf_addr.high_addr = 0;
+ tx_msghdr->metadata_buf_addr.low_addr = 0;
+ atomic_inc(&commonring->outstanding_tx);
+ if (count >= BRCMF_MSGBUF_TX_FLUSH_CNT2) {
+ brcmf_commonring_write_complete(commonring);
+ count = 0;
+ }
+ }
+ if (count)
+ brcmf_commonring_write_complete(commonring);
+ brcmf_commonring_unlock(commonring);
+}
+
+
+static void brcmf_msgbuf_txflow_worker(struct work_struct *worker)
+{
+ struct brcmf_msgbuf *msgbuf;
+ u32 flowid;
+
+ msgbuf = container_of(worker, struct brcmf_msgbuf, txflow_work);
+ for_each_set_bit(flowid, msgbuf->flow_map, msgbuf->max_flowrings) {
+ clear_bit(flowid, msgbuf->flow_map);
+ brcmf_msgbuf_txflow(msgbuf, flowid);
+ }
+}
+
+
+static int brcmf_msgbuf_schedule_txdata(struct brcmf_msgbuf *msgbuf, u32 flowid,
+ bool force)
+{
+ struct brcmf_commonring *commonring;
+
+ set_bit(flowid, msgbuf->flow_map);
+ commonring = msgbuf->flowrings[flowid];
+ if ((force) || (atomic_read(&commonring->outstanding_tx) <
+ BRCMF_MSGBUF_DELAY_TXWORKER_THRS))
+ queue_work(msgbuf->txflow_wq, &msgbuf->txflow_work);
+
+ return 0;
+}
+
+
+static int brcmf_msgbuf_tx_queue_data(struct brcmf_pub *drvr, int ifidx,
+ struct sk_buff *skb)
+{
+ struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;
+ struct brcmf_flowring *flow = msgbuf->flow;
+ struct ethhdr *eh = (struct ethhdr *)(skb->data);
+ u32 flowid;
+ u32 queue_count;
+ bool force;
+
+ flowid = brcmf_flowring_lookup(flow, eh->h_dest, skb->priority, ifidx);
+ if (flowid == BRCMF_FLOWRING_INVALID_ID) {
+ flowid = brcmf_msgbuf_flowring_create(msgbuf, ifidx, skb);
+ if (flowid == BRCMF_FLOWRING_INVALID_ID)
+ return -ENOMEM;
+ }
+ queue_count = brcmf_flowring_enqueue(flow, flowid, skb);
+ force = ((queue_count % BRCMF_MSGBUF_TRICKLE_TXWORKER_THRS) == 0);
+ brcmf_msgbuf_schedule_txdata(msgbuf, flowid, force);
+
+ return 0;
+}
+
+
+static void
+brcmf_msgbuf_configure_addr_mode(struct brcmf_pub *drvr, int ifidx,
+ enum proto_addr_mode addr_mode)
+{
+ struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;
+
+ brcmf_flowring_configure_addr_mode(msgbuf->flow, ifidx, addr_mode);
+}
+
+
+static void
+brcmf_msgbuf_delete_peer(struct brcmf_pub *drvr, int ifidx, u8 peer[ETH_ALEN])
+{
+ struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;
+
+ brcmf_flowring_delete_peer(msgbuf->flow, ifidx, peer);
+}
+
+
+static void
+brcmf_msgbuf_add_tdls_peer(struct brcmf_pub *drvr, int ifidx, u8 peer[ETH_ALEN])
+{
+ struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;
+
+ brcmf_flowring_add_tdls_peer(msgbuf->flow, ifidx, peer);
+}
+
+
+static void
+brcmf_msgbuf_process_ioctl_complete(struct brcmf_msgbuf *msgbuf, void *buf)
+{
+ struct msgbuf_ioctl_resp_hdr *ioctl_resp;
+
+ ioctl_resp = (struct msgbuf_ioctl_resp_hdr *)buf;
+
+ msgbuf->ioctl_resp_status =
+ (s16)le16_to_cpu(ioctl_resp->compl_hdr.status);
+ msgbuf->ioctl_resp_ret_len = le16_to_cpu(ioctl_resp->resp_len);
+ msgbuf->ioctl_resp_pktid = le32_to_cpu(ioctl_resp->msg.request_id);
+
+ brcmf_msgbuf_ioctl_resp_wake(msgbuf);
+
+ if (msgbuf->cur_ioctlrespbuf)
+ msgbuf->cur_ioctlrespbuf--;
+ brcmf_msgbuf_rxbuf_ioctlresp_post(msgbuf);
+}
+
+
+static void
+brcmf_msgbuf_process_txstatus(struct brcmf_msgbuf *msgbuf, void *buf)
+{
+ struct brcmf_commonring *commonring;
+ struct msgbuf_tx_status *tx_status;
+ u32 idx;
+ struct sk_buff *skb;
+ u16 flowid;
+
+ tx_status = (struct msgbuf_tx_status *)buf;
+ idx = le32_to_cpu(tx_status->msg.request_id);
+ flowid = le16_to_cpu(tx_status->compl_hdr.flow_ring_id);
+ flowid -= BRCMF_H2D_MSGRING_FLOWRING_IDSTART;
+ skb = brcmf_msgbuf_get_pktid(msgbuf->drvr->bus_if->dev,
+ msgbuf->tx_pktids, idx);
+ if (!skb)
+ return;
+
+ set_bit(flowid, msgbuf->txstatus_done_map);
+ commonring = msgbuf->flowrings[flowid];
+ atomic_dec(&commonring->outstanding_tx);
+
+ brcmf_txfinalize(brcmf_get_ifp(msgbuf->drvr, tx_status->msg.ifidx),
+ skb, true);
+}
+
+
+static u32 brcmf_msgbuf_rxbuf_data_post(struct brcmf_msgbuf *msgbuf, u32 count)
+{
+ struct brcmf_commonring *commonring;
+ void *ret_ptr;
+ struct sk_buff *skb;
+ u16 alloced;
+ u32 pktlen;
+ dma_addr_t physaddr;
+ struct msgbuf_rx_bufpost *rx_bufpost;
+ u64 address;
+ u32 pktid;
+ u32 i;
+
+ commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_RXPOST_SUBMIT];
+ ret_ptr = brcmf_commonring_reserve_for_write_multiple(commonring,
+ count,
+ &alloced);
+ if (!ret_ptr) {
+ brcmf_dbg(MSGBUF, "Failed to reserve space in commonring\n");
+ return 0;
+ }
+
+ for (i = 0; i < alloced; i++) {
+ rx_bufpost = (struct msgbuf_rx_bufpost *)ret_ptr;
+ memset(rx_bufpost, 0, sizeof(*rx_bufpost));
+
+ skb = brcmu_pkt_buf_get_skb(BRCMF_MSGBUF_MAX_PKT_SIZE);
+
+ if (skb == NULL) {
+ brcmf_err("Failed to alloc SKB\n");
+ brcmf_commonring_write_cancel(commonring, alloced - i);
+ break;
+ }
+
+ pktlen = skb->len;
+ if (brcmf_msgbuf_alloc_pktid(msgbuf->drvr->bus_if->dev,
+ msgbuf->rx_pktids, skb, 0,
+ &physaddr, &pktid)) {
+ dev_kfree_skb_any(skb);
+ brcmf_err("No PKTID available !!\n");
+ brcmf_commonring_write_cancel(commonring, alloced - i);
+ break;
+ }
+
+ if (msgbuf->rx_metadata_offset) {
+ address = (u64)physaddr;
+ rx_bufpost->metadata_buf_len =
+ cpu_to_le16(msgbuf->rx_metadata_offset);
+ rx_bufpost->metadata_buf_addr.high_addr =
+ cpu_to_le32(address >> 32);
+ rx_bufpost->metadata_buf_addr.low_addr =
+ cpu_to_le32(address & 0xffffffff);
+
+ skb_pull(skb, msgbuf->rx_metadata_offset);
+ pktlen = skb->len;
+ physaddr += msgbuf->rx_metadata_offset;
+ }
+ rx_bufpost->msg.msgtype = MSGBUF_TYPE_RXBUF_POST;
+ rx_bufpost->msg.request_id = cpu_to_le32(pktid);
+
+ address = (u64)physaddr;
+ rx_bufpost->data_buf_len = cpu_to_le16((u16)pktlen);
+ rx_bufpost->data_buf_addr.high_addr =
+ cpu_to_le32(address >> 32);
+ rx_bufpost->data_buf_addr.low_addr =
+ cpu_to_le32(address & 0xffffffff);
+
+ ret_ptr += brcmf_commonring_len_item(commonring);
+ }
+
+ if (i)
+ brcmf_commonring_write_complete(commonring);
+
+ return i;
+}
+
+
+static void
+brcmf_msgbuf_rxbuf_data_fill(struct brcmf_msgbuf *msgbuf)
+{
+ u32 fillbufs;
+ u32 retcount;
+
+ fillbufs = msgbuf->max_rxbufpost - msgbuf->rxbufpost;
+
+ while (fillbufs) {
+ retcount = brcmf_msgbuf_rxbuf_data_post(msgbuf, fillbufs);
+ if (!retcount)
+ break;
+ msgbuf->rxbufpost += retcount;
+ fillbufs -= retcount;
+ }
+}
+
+
+static void
+brcmf_msgbuf_update_rxbufpost_count(struct brcmf_msgbuf *msgbuf, u16 rxcnt)
+{
+ msgbuf->rxbufpost -= rxcnt;
+ if (msgbuf->rxbufpost <= (msgbuf->max_rxbufpost -
+ BRCMF_MSGBUF_RXBUFPOST_THRESHOLD))
+ brcmf_msgbuf_rxbuf_data_fill(msgbuf);
+}
+
+
+static u32
+brcmf_msgbuf_rxbuf_ctrl_post(struct brcmf_msgbuf *msgbuf, bool event_buf,
+ u32 count)
+{
+ struct brcmf_commonring *commonring;
+ void *ret_ptr;
+ struct sk_buff *skb;
+ u16 alloced;
+ u32 pktlen;
+ dma_addr_t physaddr;
+ struct msgbuf_rx_ioctl_resp_or_event *rx_bufpost;
+ u64 address;
+ u32 pktid;
+ u32 i;
+
+ commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_CONTROL_SUBMIT];
+ brcmf_commonring_lock(commonring);
+ ret_ptr = brcmf_commonring_reserve_for_write_multiple(commonring,
+ count,
+ &alloced);
+ if (!ret_ptr) {
+ brcmf_err("Failed to reserve space in commonring\n");
+ brcmf_commonring_unlock(commonring);
+ return 0;
+ }
+
+ for (i = 0; i < alloced; i++) {
+ rx_bufpost = (struct msgbuf_rx_ioctl_resp_or_event *)ret_ptr;
+ memset(rx_bufpost, 0, sizeof(*rx_bufpost));
+
+ skb = brcmu_pkt_buf_get_skb(BRCMF_MSGBUF_MAX_PKT_SIZE);
+
+ if (skb == NULL) {
+ brcmf_err("Failed to alloc SKB\n");
+ brcmf_commonring_write_cancel(commonring, alloced - i);
+ break;
+ }
+
+ pktlen = skb->len;
+ if (brcmf_msgbuf_alloc_pktid(msgbuf->drvr->bus_if->dev,
+ msgbuf->rx_pktids, skb, 0,
+ &physaddr, &pktid)) {
+ dev_kfree_skb_any(skb);
+ brcmf_err("No PKTID available !!\n");
+ brcmf_commonring_write_cancel(commonring, alloced - i);
+ break;
+ }
+ if (event_buf)
+ rx_bufpost->msg.msgtype = MSGBUF_TYPE_EVENT_BUF_POST;
+ else
+ rx_bufpost->msg.msgtype =
+ MSGBUF_TYPE_IOCTLRESP_BUF_POST;
+ rx_bufpost->msg.request_id = cpu_to_le32(pktid);
+
+ address = (u64)physaddr;
+ rx_bufpost->host_buf_len = cpu_to_le16((u16)pktlen);
+ rx_bufpost->host_buf_addr.high_addr =
+ cpu_to_le32(address >> 32);
+ rx_bufpost->host_buf_addr.low_addr =
+ cpu_to_le32(address & 0xffffffff);
+
+ ret_ptr += brcmf_commonring_len_item(commonring);
+ }
+
+ if (i)
+ brcmf_commonring_write_complete(commonring);
+
+ brcmf_commonring_unlock(commonring);
+
+ return i;
+}
+
+
+static void brcmf_msgbuf_rxbuf_ioctlresp_post(struct brcmf_msgbuf *msgbuf)
+{
+ u32 count;
+
+ count = msgbuf->max_ioctlrespbuf - msgbuf->cur_ioctlrespbuf;
+ count = brcmf_msgbuf_rxbuf_ctrl_post(msgbuf, false, count);
+ msgbuf->cur_ioctlrespbuf += count;
+}
+
+
+static void brcmf_msgbuf_rxbuf_event_post(struct brcmf_msgbuf *msgbuf)
+{
+ u32 count;
+
+ count = msgbuf->max_eventbuf - msgbuf->cur_eventbuf;
+ count = brcmf_msgbuf_rxbuf_ctrl_post(msgbuf, true, count);
+ msgbuf->cur_eventbuf += count;
+}
+
+
+static void brcmf_msgbuf_process_event(struct brcmf_msgbuf *msgbuf, void *buf)
+{
+ struct msgbuf_rx_event *event;
+ u32 idx;
+ u16 buflen;
+ struct sk_buff *skb;
+ struct brcmf_if *ifp;
+
+ event = (struct msgbuf_rx_event *)buf;
+ idx = le32_to_cpu(event->msg.request_id);
+ buflen = le16_to_cpu(event->event_data_len);
+
+ if (msgbuf->cur_eventbuf)
+ msgbuf->cur_eventbuf--;
+ brcmf_msgbuf_rxbuf_event_post(msgbuf);
+
+ skb = brcmf_msgbuf_get_pktid(msgbuf->drvr->bus_if->dev,
+ msgbuf->rx_pktids, idx);
+ if (!skb)
+ return;
+
+ if (msgbuf->rx_dataoffset)
+ skb_pull(skb, msgbuf->rx_dataoffset);
+
+ skb_trim(skb, buflen);
+
+ ifp = brcmf_get_ifp(msgbuf->drvr, event->msg.ifidx);
+ if (!ifp || !ifp->ndev) {
+ brcmf_err("Received pkt for invalid ifidx %d\n",
+ event->msg.ifidx);
+ goto exit;
+ }
+
+ skb->protocol = eth_type_trans(skb, ifp->ndev);
+
+ brcmf_fweh_process_skb(ifp->drvr, skb);
+
+exit:
+ brcmu_pkt_buf_free_skb(skb);
+}
+
+
+static void
+brcmf_msgbuf_process_rx_complete(struct brcmf_msgbuf *msgbuf, void *buf)
+{
+ struct msgbuf_rx_complete *rx_complete;
+ struct sk_buff *skb;
+ u16 data_offset;
+ u16 buflen;
+ u32 idx;
+ struct brcmf_if *ifp;
+
+ brcmf_msgbuf_update_rxbufpost_count(msgbuf, 1);
+
+ rx_complete = (struct msgbuf_rx_complete *)buf;
+ data_offset = le16_to_cpu(rx_complete->data_offset);
+ buflen = le16_to_cpu(rx_complete->data_len);
+ idx = le32_to_cpu(rx_complete->msg.request_id);
+
+ skb = brcmf_msgbuf_get_pktid(msgbuf->drvr->bus_if->dev,
+ msgbuf->rx_pktids, idx);
+ if (!skb)
+ return;
+
+ if (data_offset)
+ skb_pull(skb, data_offset);
+ else if (msgbuf->rx_dataoffset)
+ skb_pull(skb, msgbuf->rx_dataoffset);
+
+ skb_trim(skb, buflen);
+
+ ifp = brcmf_get_ifp(msgbuf->drvr, rx_complete->msg.ifidx);
+ if (!ifp || !ifp->ndev) {
+ brcmf_err("Received pkt for invalid ifidx %d\n",
+ rx_complete->msg.ifidx);
+ brcmu_pkt_buf_free_skb(skb);
+ return;
+ }
+
+ skb->protocol = eth_type_trans(skb, ifp->ndev);
+ brcmf_netif_rx(ifp, skb);
+}
+
+
+static void
+brcmf_msgbuf_process_flow_ring_create_response(struct brcmf_msgbuf *msgbuf,
+ void *buf)
+{
+ struct msgbuf_flowring_create_resp *flowring_create_resp;
+ u16 status;
+ u16 flowid;
+
+ flowring_create_resp = (struct msgbuf_flowring_create_resp *)buf;
+
+ flowid = le16_to_cpu(flowring_create_resp->compl_hdr.flow_ring_id);
+ flowid -= BRCMF_H2D_MSGRING_FLOWRING_IDSTART;
+ status = le16_to_cpu(flowring_create_resp->compl_hdr.status);
+
+ if (status) {
+ brcmf_err("Flowring creation failed, code %d\n", status);
+ brcmf_msgbuf_remove_flowring(msgbuf, flowid);
+ return;
+ }
+ brcmf_dbg(MSGBUF, "Flowring %d Create response status %d\n", flowid,
+ status);
+
+ brcmf_flowring_open(msgbuf->flow, flowid);
+
+ brcmf_msgbuf_schedule_txdata(msgbuf, flowid, true);
+}
+
+
+static void
+brcmf_msgbuf_process_flow_ring_delete_response(struct brcmf_msgbuf *msgbuf,
+ void *buf)
+{
+ struct msgbuf_flowring_delete_resp *flowring_delete_resp;
+ u16 status;
+ u16 flowid;
+
+ flowring_delete_resp = (struct msgbuf_flowring_delete_resp *)buf;
+
+ flowid = le16_to_cpu(flowring_delete_resp->compl_hdr.flow_ring_id);
+ flowid -= BRCMF_H2D_MSGRING_FLOWRING_IDSTART;
+ status = le16_to_cpu(flowring_delete_resp->compl_hdr.status);
+
+ if (status) {
+ brcmf_err("Flowring deletion failed, code %d\n", status);
+ brcmf_flowring_delete(msgbuf->flow, flowid);
+ return;
+ }
+ brcmf_dbg(MSGBUF, "Flowring %d Delete response status %d\n", flowid,
+ status);
+
+ brcmf_msgbuf_remove_flowring(msgbuf, flowid);
+}
+
+
+static void brcmf_msgbuf_process_msgtype(struct brcmf_msgbuf *msgbuf, void *buf)
+{
+ struct msgbuf_common_hdr *msg;
+
+ msg = (struct msgbuf_common_hdr *)buf;
+ switch (msg->msgtype) {
+ case MSGBUF_TYPE_FLOW_RING_CREATE_CMPLT:
+ brcmf_dbg(MSGBUF, "MSGBUF_TYPE_FLOW_RING_CREATE_CMPLT\n");
+ brcmf_msgbuf_process_flow_ring_create_response(msgbuf, buf);
+ break;
+ case MSGBUF_TYPE_FLOW_RING_DELETE_CMPLT:
+ brcmf_dbg(MSGBUF, "MSGBUF_TYPE_FLOW_RING_DELETE_CMPLT\n");
+ brcmf_msgbuf_process_flow_ring_delete_response(msgbuf, buf);
+ break;
+ case MSGBUF_TYPE_IOCTLPTR_REQ_ACK:
+ brcmf_dbg(MSGBUF, "MSGBUF_TYPE_IOCTLPTR_REQ_ACK\n");
+ break;
+ case MSGBUF_TYPE_IOCTL_CMPLT:
+ brcmf_dbg(MSGBUF, "MSGBUF_TYPE_IOCTL_CMPLT\n");
+ brcmf_msgbuf_process_ioctl_complete(msgbuf, buf);
+ break;
+ case MSGBUF_TYPE_WL_EVENT:
+ brcmf_dbg(MSGBUF, "MSGBUF_TYPE_WL_EVENT\n");
+ brcmf_msgbuf_process_event(msgbuf, buf);
+ break;
+ case MSGBUF_TYPE_TX_STATUS:
+ brcmf_dbg(MSGBUF, "MSGBUF_TYPE_TX_STATUS\n");
+ brcmf_msgbuf_process_txstatus(msgbuf, buf);
+ break;
+ case MSGBUF_TYPE_RX_CMPLT:
+ brcmf_dbg(MSGBUF, "MSGBUF_TYPE_RX_CMPLT\n");
+ brcmf_msgbuf_process_rx_complete(msgbuf, buf);
+ break;
+ default:
+ brcmf_err("Unsupported msgtype %d\n", msg->msgtype);
+ break;
+ }
+}
+
+
+static void brcmf_msgbuf_process_rx(struct brcmf_msgbuf *msgbuf,
+ struct brcmf_commonring *commonring)
+{
+ void *buf;
+ u16 count;
+ u16 processed;
+
+again:
+ buf = brcmf_commonring_get_read_ptr(commonring, &count);
+ if (buf == NULL)
+ return;
+
+ processed = 0;
+ while (count) {
+ brcmf_msgbuf_process_msgtype(msgbuf,
+ buf + msgbuf->rx_dataoffset);
+ buf += brcmf_commonring_len_item(commonring);
+ processed++;
+ if (processed == BRCMF_MSGBUF_UPDATE_RX_PTR_THRS) {
+ brcmf_commonring_read_complete(commonring, processed);
+ processed = 0;
+ }
+ count--;
+ }
+ if (processed)
+ brcmf_commonring_read_complete(commonring, processed);
+
+ if (commonring->r_ptr == 0)
+ goto again;
+}
+
+
+int brcmf_proto_msgbuf_rx_trigger(struct device *dev)
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+ struct brcmf_pub *drvr = bus_if->drvr;
+ struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;
+ struct brcmf_commonring *commonring;
+ void *buf;
+ u32 flowid;
+ int qlen;
+
+ buf = msgbuf->commonrings[BRCMF_D2H_MSGRING_RX_COMPLETE];
+ brcmf_msgbuf_process_rx(msgbuf, buf);
+ buf = msgbuf->commonrings[BRCMF_D2H_MSGRING_TX_COMPLETE];
+ brcmf_msgbuf_process_rx(msgbuf, buf);
+ buf = msgbuf->commonrings[BRCMF_D2H_MSGRING_CONTROL_COMPLETE];
+ brcmf_msgbuf_process_rx(msgbuf, buf);
+
+ for_each_set_bit(flowid, msgbuf->txstatus_done_map,
+ msgbuf->max_flowrings) {
+ clear_bit(flowid, msgbuf->txstatus_done_map);
+ commonring = msgbuf->flowrings[flowid];
+ qlen = brcmf_flowring_qlen(msgbuf->flow, flowid);
+ if ((qlen > BRCMF_MSGBUF_TRICKLE_TXWORKER_THRS) ||
+ ((qlen) && (atomic_read(&commonring->outstanding_tx) <
+ BRCMF_MSGBUF_TRICKLE_TXWORKER_THRS)))
+ brcmf_msgbuf_schedule_txdata(msgbuf, flowid, true);
+ }
+
+ return 0;
+}
+
+
+void brcmf_msgbuf_delete_flowring(struct brcmf_pub *drvr, u16 flowid)
+{
+ struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;
+ struct msgbuf_tx_flowring_delete_req *delete;
+ struct brcmf_commonring *commonring;
+ void *ret_ptr;
+ u8 ifidx;
+ int err;
+
+ commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_CONTROL_SUBMIT];
+ brcmf_commonring_lock(commonring);
+ ret_ptr = brcmf_commonring_reserve_for_write(commonring);
+ if (!ret_ptr) {
+ brcmf_err("FW unaware, flowring will be removed !!\n");
+ brcmf_commonring_unlock(commonring);
+ brcmf_msgbuf_remove_flowring(msgbuf, flowid);
+ return;
+ }
+
+ delete = (struct msgbuf_tx_flowring_delete_req *)ret_ptr;
+
+ ifidx = brcmf_flowring_ifidx_get(msgbuf->flow, flowid);
+
+ delete->msg.msgtype = MSGBUF_TYPE_FLOW_RING_DELETE;
+ delete->msg.ifidx = ifidx;
+ delete->msg.request_id = 0;
+
+ delete->flow_ring_id = cpu_to_le16(flowid +
+ BRCMF_H2D_MSGRING_FLOWRING_IDSTART);
+ delete->reason = 0;
+
+ brcmf_dbg(MSGBUF, "Send Flow Delete Req flow ID %d, ifindex %d\n",
+ flowid, ifidx);
+
+ err = brcmf_commonring_write_complete(commonring);
+ brcmf_commonring_unlock(commonring);
+ if (err) {
+ brcmf_err("Failed to submit RING_DELETE, flowring will be removed\n");
+ brcmf_msgbuf_remove_flowring(msgbuf, flowid);
+ }
+}
+
+#ifdef DEBUG
+static int brcmf_msgbuf_stats_read(struct seq_file *seq, void *data)
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(seq->private);
+ struct brcmf_pub *drvr = bus_if->drvr;
+ struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;
+ struct brcmf_commonring *commonring;
+ u16 i;
+ struct brcmf_flowring_ring *ring;
+ struct brcmf_flowring_hash *hash;
+
+ commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_CONTROL_SUBMIT];
+ seq_printf(seq, "h2d_ctl_submit: rp %4u, wp %4u, depth %4u\n",
+ commonring->r_ptr, commonring->w_ptr, commonring->depth);
+ commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_RXPOST_SUBMIT];
+ seq_printf(seq, "h2d_rx_submit: rp %4u, wp %4u, depth %4u\n",
+ commonring->r_ptr, commonring->w_ptr, commonring->depth);
+ commonring = msgbuf->commonrings[BRCMF_D2H_MSGRING_CONTROL_COMPLETE];
+ seq_printf(seq, "d2h_ctl_cmplt: rp %4u, wp %4u, depth %4u\n",
+ commonring->r_ptr, commonring->w_ptr, commonring->depth);
+ commonring = msgbuf->commonrings[BRCMF_D2H_MSGRING_TX_COMPLETE];
+ seq_printf(seq, "d2h_tx_cmplt: rp %4u, wp %4u, depth %4u\n",
+ commonring->r_ptr, commonring->w_ptr, commonring->depth);
+ commonring = msgbuf->commonrings[BRCMF_D2H_MSGRING_RX_COMPLETE];
+ seq_printf(seq, "d2h_rx_cmplt: rp %4u, wp %4u, depth %4u\n",
+ commonring->r_ptr, commonring->w_ptr, commonring->depth);
+
+ seq_printf(seq, "\nh2d_flowrings: depth %u\n",
+ BRCMF_H2D_TXFLOWRING_MAX_ITEM);
+ seq_puts(seq, "Active flowrings:\n");
+ hash = msgbuf->flow->hash;
+ for (i = 0; i < msgbuf->flow->nrofrings; i++) {
+ if (!msgbuf->flow->rings[i])
+ continue;
+ ring = msgbuf->flow->rings[i];
+ if (ring->status != RING_OPEN)
+ continue;
+ commonring = msgbuf->flowrings[i];
+ hash = &msgbuf->flow->hash[ring->hash_id];
+ seq_printf(seq, "id %3u: rp %4u, wp %4u, qlen %4u, blocked %u\n"
+ " ifidx %u, fifo %u, da %pM\n",
+ i, commonring->r_ptr, commonring->w_ptr,
+ skb_queue_len(&ring->skblist), ring->blocked,
+ hash->ifidx, hash->fifo, hash->mac);
+ }
+
+ return 0;
+}
+#else
+static int brcmf_msgbuf_stats_read(struct seq_file *seq, void *data)
+{
+ return 0;
+}
+#endif
+
+int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr)
+{
+ struct brcmf_bus_msgbuf *if_msgbuf;
+ struct brcmf_msgbuf *msgbuf;
+ u64 address;
+ u32 count;
+
+ if_msgbuf = drvr->bus_if->msgbuf;
+
+ if (if_msgbuf->max_flowrings >= BRCMF_FLOWRING_HASHSIZE) {
+ brcmf_err("driver not configured for this many flowrings %d\n",
+ if_msgbuf->max_flowrings);
+ if_msgbuf->max_flowrings = BRCMF_FLOWRING_HASHSIZE - 1;
+ }
+
+ msgbuf = kzalloc(sizeof(*msgbuf), GFP_KERNEL);
+ if (!msgbuf)
+ goto fail;
+
+ msgbuf->txflow_wq = create_singlethread_workqueue("msgbuf_txflow");
+ if (msgbuf->txflow_wq == NULL) {
+ brcmf_err("workqueue creation failed\n");
+ goto fail;
+ }
+ INIT_WORK(&msgbuf->txflow_work, brcmf_msgbuf_txflow_worker);
+ count = BITS_TO_LONGS(if_msgbuf->max_flowrings);
+ count = count * sizeof(unsigned long);
+ msgbuf->flow_map = kzalloc(count, GFP_KERNEL);
+ if (!msgbuf->flow_map)
+ goto fail;
+
+ msgbuf->txstatus_done_map = kzalloc(count, GFP_KERNEL);
+ if (!msgbuf->txstatus_done_map)
+ goto fail;
+
+ msgbuf->drvr = drvr;
+ msgbuf->ioctbuf = dma_alloc_coherent(drvr->bus_if->dev,
+ BRCMF_TX_IOCTL_MAX_MSG_SIZE,
+ &msgbuf->ioctbuf_handle,
+ GFP_KERNEL);
+ if (!msgbuf->ioctbuf)
+ goto fail;
+ address = (u64)msgbuf->ioctbuf_handle;
+ msgbuf->ioctbuf_phys_hi = address >> 32;
+ msgbuf->ioctbuf_phys_lo = address & 0xffffffff;
+
+ drvr->proto->hdrpull = brcmf_msgbuf_hdrpull;
+ drvr->proto->query_dcmd = brcmf_msgbuf_query_dcmd;
+ drvr->proto->set_dcmd = brcmf_msgbuf_set_dcmd;
+ drvr->proto->tx_queue_data = brcmf_msgbuf_tx_queue_data;
+ drvr->proto->configure_addr_mode = brcmf_msgbuf_configure_addr_mode;
+ drvr->proto->delete_peer = brcmf_msgbuf_delete_peer;
+ drvr->proto->add_tdls_peer = brcmf_msgbuf_add_tdls_peer;
+ drvr->proto->rxreorder = brcmf_msgbuf_rxreorder;
+ drvr->proto->pd = msgbuf;
+
+ init_waitqueue_head(&msgbuf->ioctl_resp_wait);
+
+ msgbuf->commonrings =
+ (struct brcmf_commonring **)if_msgbuf->commonrings;
+ msgbuf->flowrings = (struct brcmf_commonring **)if_msgbuf->flowrings;
+ msgbuf->max_flowrings = if_msgbuf->max_flowrings;
+ msgbuf->flowring_dma_handle = kzalloc(msgbuf->max_flowrings *
+ sizeof(*msgbuf->flowring_dma_handle), GFP_KERNEL);
+ if (!msgbuf->flowring_dma_handle)
+ goto fail;
+
+ msgbuf->rx_dataoffset = if_msgbuf->rx_dataoffset;
+ msgbuf->max_rxbufpost = if_msgbuf->max_rxbufpost;
+
+ msgbuf->max_ioctlrespbuf = BRCMF_MSGBUF_MAX_IOCTLRESPBUF_POST;
+ msgbuf->max_eventbuf = BRCMF_MSGBUF_MAX_EVENTBUF_POST;
+
+ msgbuf->tx_pktids = brcmf_msgbuf_init_pktids(NR_TX_PKTIDS,
+ DMA_TO_DEVICE);
+ if (!msgbuf->tx_pktids)
+ goto fail;
+ msgbuf->rx_pktids = brcmf_msgbuf_init_pktids(NR_RX_PKTIDS,
+ DMA_FROM_DEVICE);
+ if (!msgbuf->rx_pktids)
+ goto fail;
+
+ msgbuf->flow = brcmf_flowring_attach(drvr->bus_if->dev,
+ if_msgbuf->max_flowrings);
+ if (!msgbuf->flow)
+ goto fail;
+
+
+ brcmf_dbg(MSGBUF, "Feeding buffers, rx data %d, rx event %d, rx ioctl resp %d\n",
+ msgbuf->max_rxbufpost, msgbuf->max_eventbuf,
+ msgbuf->max_ioctlrespbuf);
+ count = 0;
+ do {
+ brcmf_msgbuf_rxbuf_data_fill(msgbuf);
+ if (msgbuf->max_rxbufpost != msgbuf->rxbufpost)
+ msleep(10);
+ else
+ break;
+ count++;
+ } while (count < 10);
+ brcmf_msgbuf_rxbuf_event_post(msgbuf);
+ brcmf_msgbuf_rxbuf_ioctlresp_post(msgbuf);
+
+ INIT_WORK(&msgbuf->flowring_work, brcmf_msgbuf_flowring_worker);
+ spin_lock_init(&msgbuf->flowring_work_lock);
+ INIT_LIST_HEAD(&msgbuf->work_queue);
+
+ brcmf_debugfs_add_entry(drvr, "msgbuf_stats", brcmf_msgbuf_stats_read);
+
+ return 0;
+
+fail:
+ if (msgbuf) {
+ kfree(msgbuf->flow_map);
+ kfree(msgbuf->txstatus_done_map);
+ brcmf_msgbuf_release_pktids(msgbuf);
+ kfree(msgbuf->flowring_dma_handle);
+ if (msgbuf->ioctbuf)
+ dma_free_coherent(drvr->bus_if->dev,
+ BRCMF_TX_IOCTL_MAX_MSG_SIZE,
+ msgbuf->ioctbuf,
+ msgbuf->ioctbuf_handle);
+ kfree(msgbuf);
+ }
+ return -ENOMEM;
+}
+
+
+void brcmf_proto_msgbuf_detach(struct brcmf_pub *drvr)
+{
+ struct brcmf_msgbuf *msgbuf;
+ struct brcmf_msgbuf_work_item *work;
+
+ brcmf_dbg(TRACE, "Enter\n");
+ if (drvr->proto->pd) {
+ msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;
+ cancel_work_sync(&msgbuf->flowring_work);
+ while (!list_empty(&msgbuf->work_queue)) {
+ work = list_first_entry(&msgbuf->work_queue,
+ struct brcmf_msgbuf_work_item,
+ queue);
+ list_del(&work->queue);
+ kfree(work);
+ }
+ kfree(msgbuf->flow_map);
+ kfree(msgbuf->txstatus_done_map);
+ if (msgbuf->txflow_wq)
+ destroy_workqueue(msgbuf->txflow_wq);
+
+ brcmf_flowring_detach(msgbuf->flow);
+ dma_free_coherent(drvr->bus_if->dev,
+ BRCMF_TX_IOCTL_MAX_MSG_SIZE,
+ msgbuf->ioctbuf, msgbuf->ioctbuf_handle);
+ brcmf_msgbuf_release_pktids(msgbuf);
+ kfree(msgbuf->flowring_dma_handle);
+ kfree(msgbuf);
+ drvr->proto->pd = NULL;
+ }
+}
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h
new file mode 100644
index 0000000..e1339de
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h
@@ -0,0 +1,51 @@
+/* Copyright (c) 2014 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#ifndef BRCMFMAC_MSGBUF_H
+#define BRCMFMAC_MSGBUF_H
+
+#ifdef CPTCFG_BRCMFMAC_PROTO_MSGBUF
+
+#define BRCMF_H2D_MSGRING_CONTROL_SUBMIT_MAX_ITEM 64
+#define BRCMF_H2D_MSGRING_RXPOST_SUBMIT_MAX_ITEM 512
+#define BRCMF_D2H_MSGRING_CONTROL_COMPLETE_MAX_ITEM 64
+#define BRCMF_D2H_MSGRING_TX_COMPLETE_MAX_ITEM 1024
+#define BRCMF_D2H_MSGRING_RX_COMPLETE_MAX_ITEM 512
+#define BRCMF_H2D_TXFLOWRING_MAX_ITEM 512
+
+#define BRCMF_H2D_MSGRING_CONTROL_SUBMIT_ITEMSIZE 40
+#define BRCMF_H2D_MSGRING_RXPOST_SUBMIT_ITEMSIZE 32
+#define BRCMF_D2H_MSGRING_CONTROL_COMPLETE_ITEMSIZE 24
+#define BRCMF_D2H_MSGRING_TX_COMPLETE_ITEMSIZE 16
+#define BRCMF_D2H_MSGRING_RX_COMPLETE_ITEMSIZE 32
+#define BRCMF_H2D_TXFLOWRING_ITEMSIZE 48
+
+struct msgbuf_buf_addr {
+ __le32 low_addr;
+ __le32 high_addr;
+};
+
+int brcmf_proto_msgbuf_rx_trigger(struct device *dev);
+void brcmf_msgbuf_delete_flowring(struct brcmf_pub *drvr, u16 flowid);
+int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr);
+void brcmf_proto_msgbuf_detach(struct brcmf_pub *drvr);
+#else
+static inline int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr)
+{
+ return 0;
+}
+static inline void brcmf_proto_msgbuf_detach(struct brcmf_pub *drvr) {}
+#endif
+
+#endif /* BRCMFMAC_MSGBUF_H */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c
new file mode 100644
index 0000000..a80fc39
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2014 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <linux/init.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+
+#include <defs.h>
+#include "debug.h"
+#include "core.h"
+#include "common.h"
+#include "of.h"
+
+void brcmf_of_probe(struct device *dev, enum brcmf_bus_type bus_type,
+ struct brcmf_mp_device *settings)
+{
+ struct brcmfmac_sdio_pd *sdio = &settings->bus.sdio;
+ struct device_node *np = dev->of_node;
+ int irq;
+ u32 irqf;
+ u32 val32;
+ u16 val16;
+
+ if (!np || bus_type != BRCMF_BUSTYPE_SDIO ||
+ !of_device_is_compatible(np, "brcm,bcm4329-fmac"))
+ return;
+
+ if (of_property_read_u32(np, "brcm,drive-strength", &val32) == 0)
+ sdio->drive_strength = val32;
+
+ sdio->broken_sg_support = of_property_read_bool(np,
+ "brcm,broken_sg_support");
+ if (of_property_read_u16(np, "brcm,sd_head_align", &val16) == 0)
+ sdio->sd_head_align = val16;
+ if (of_property_read_u16(np, "brcm,sd_sgentry_align", &val16) == 0)
+ sdio->sd_sgentry_align = val16;
+
+ /* make sure there are interrupts defined in the node */
+ if (!of_find_property(np, "interrupts", NULL))
+ return;
+
+ irq = irq_of_parse_and_map(np, 0);
+ if (!irq) {
+ brcmf_err("interrupt could not be mapped\n");
+ return;
+ }
+ irqf = irqd_get_trigger_type(irq_get_irq_data(irq));
+
+ sdio->oob_irq_supported = true;
+ sdio->oob_irq_nr = irq;
+ sdio->oob_irq_flags = irqf;
+ brcmf_info("Enable oob support, irq=%d\n", sdio->oob_irq_nr);
+}
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.h
new file mode 100644
index 0000000..95b7032
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2014 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#ifdef CONFIG_OF
+void brcmf_of_probe(struct device *dev, enum brcmf_bus_type bus_type,
+ struct brcmf_mp_device *settings);
+#else
+static void brcmf_of_probe(struct device *dev, enum brcmf_bus_type bus_type,
+ struct brcmf_mp_device *settings)
+{
+}
+#endif /* CONFIG_OF */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c
new file mode 100644
index 0000000..8e4dc1f
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c
@@ -0,0 +1,2407 @@
+/*
+ * Copyright (c) 2012 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <linux/slab.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/rtnetlink.h>
+#include <net/cfg80211.h>
+
+#include <brcmu_wifi.h>
+#include <brcmu_utils.h>
+#include <defs.h>
+#include "core.h"
+#include "debug.h"
+#include "fwil.h"
+#include "fwil_types.h"
+#include "p2p.h"
+#include "cfg80211.h"
+
+/* parameters used for p2p escan */
+#define P2PAPI_SCAN_NPROBES 1
+#define P2PAPI_SCAN_DWELL_TIME_MS 80
+#define P2PAPI_SCAN_SOCIAL_DWELL_TIME_MS 40
+#define P2PAPI_SCAN_HOME_TIME_MS 60
+#define P2PAPI_SCAN_NPROBS_TIME_MS 30
+#define P2PAPI_SCAN_AF_SEARCH_DWELL_TIME_MS 100
+#define WL_SCAN_CONNECT_DWELL_TIME_MS 200
+#define WL_SCAN_JOIN_PROBE_INTERVAL_MS 20
+
+#define BRCMF_P2P_WILDCARD_SSID "DIRECT-"
+#define BRCMF_P2P_WILDCARD_SSID_LEN (sizeof(BRCMF_P2P_WILDCARD_SSID) - 1)
+
+#define SOCIAL_CHAN_1 1
+#define SOCIAL_CHAN_2 6
+#define SOCIAL_CHAN_3 11
+#define IS_P2P_SOCIAL_CHANNEL(channel) ((channel == SOCIAL_CHAN_1) || \
+ (channel == SOCIAL_CHAN_2) || \
+ (channel == SOCIAL_CHAN_3))
+#define BRCMF_P2P_TEMP_CHAN SOCIAL_CHAN_3
+#define SOCIAL_CHAN_CNT 3
+#define AF_PEER_SEARCH_CNT 2
+
+#define BRCMF_SCB_TIMEOUT_VALUE 20
+
+#define P2P_VER 9 /* P2P version: 9=WiFi P2P v1.0 */
+#define P2P_PUB_AF_CATEGORY 0x04
+#define P2P_PUB_AF_ACTION 0x09
+#define P2P_AF_CATEGORY 0x7f
+#define P2P_OUI "\x50\x6F\x9A" /* P2P OUI */
+#define P2P_OUI_LEN 3 /* P2P OUI length */
+
+/* Action Frame Constants */
+#define DOT11_ACTION_HDR_LEN 2 /* action frame category + action */
+#define DOT11_ACTION_CAT_OFF 0 /* category offset */
+#define DOT11_ACTION_ACT_OFF 1 /* action offset */
+
+#define P2P_AF_DWELL_TIME 200
+#define P2P_AF_MIN_DWELL_TIME 100
+#define P2P_AF_MED_DWELL_TIME 400
+#define P2P_AF_LONG_DWELL_TIME 1000
+#define P2P_AF_TX_MAX_RETRY 1
+#define P2P_AF_MAX_WAIT_TIME msecs_to_jiffies(2000)
+#define P2P_INVALID_CHANNEL -1
+#define P2P_CHANNEL_SYNC_RETRY 5
+#define P2P_AF_FRM_SCAN_MAX_WAIT msecs_to_jiffies(1500)
+#define P2P_DEFAULT_SLEEP_TIME_VSDB 200
+
+/* WiFi P2P Public Action Frame OUI Subtypes */
+#define P2P_PAF_GON_REQ 0 /* Group Owner Negotiation Req */
+#define P2P_PAF_GON_RSP 1 /* Group Owner Negotiation Rsp */
+#define P2P_PAF_GON_CONF 2 /* Group Owner Negotiation Confirm */
+#define P2P_PAF_INVITE_REQ 3 /* P2P Invitation Request */
+#define P2P_PAF_INVITE_RSP 4 /* P2P Invitation Response */
+#define P2P_PAF_DEVDIS_REQ 5 /* Device Discoverability Request */
+#define P2P_PAF_DEVDIS_RSP 6 /* Device Discoverability Response */
+#define P2P_PAF_PROVDIS_REQ 7 /* Provision Discovery Request */
+#define P2P_PAF_PROVDIS_RSP 8 /* Provision Discovery Response */
+#define P2P_PAF_SUBTYPE_INVALID 255 /* Invalid Subtype */
+
+/* WiFi P2P Action Frame OUI Subtypes */
+#define P2P_AF_NOTICE_OF_ABSENCE 0 /* Notice of Absence */
+#define P2P_AF_PRESENCE_REQ 1 /* P2P Presence Request */
+#define P2P_AF_PRESENCE_RSP 2 /* P2P Presence Response */
+#define P2P_AF_GO_DISC_REQ 3 /* GO Discoverability Request */
+
+/* P2P Service Discovery related */
+#define P2PSD_ACTION_CATEGORY 0x04 /* Public action frame */
+#define P2PSD_ACTION_ID_GAS_IREQ 0x0a /* GAS Initial Request AF */
+#define P2PSD_ACTION_ID_GAS_IRESP 0x0b /* GAS Initial Response AF */
+#define P2PSD_ACTION_ID_GAS_CREQ 0x0c /* GAS Comback Request AF */
+#define P2PSD_ACTION_ID_GAS_CRESP 0x0d /* GAS Comback Response AF */
+
+#define BRCMF_P2P_DISABLE_TIMEOUT msecs_to_jiffies(500)
+/**
+ * struct brcmf_p2p_disc_st_le - set discovery state in firmware.
+ *
+ * @state: requested discovery state (see enum brcmf_p2p_disc_state).
+ * @chspec: channel parameter for %WL_P2P_DISC_ST_LISTEN state.
+ * @dwell: dwell time in ms for %WL_P2P_DISC_ST_LISTEN state.
+ */
+struct brcmf_p2p_disc_st_le {
+ u8 state;
+ __le16 chspec;
+ __le16 dwell;
+};
+
+/**
+ * enum brcmf_p2p_disc_state - P2P discovery state values
+ *
+ * @WL_P2P_DISC_ST_SCAN: P2P discovery with wildcard SSID and P2P IE.
+ * @WL_P2P_DISC_ST_LISTEN: P2P discovery off-channel for specified time.
+ * @WL_P2P_DISC_ST_SEARCH: P2P discovery with P2P wildcard SSID and P2P IE.
+ */
+enum brcmf_p2p_disc_state {
+ WL_P2P_DISC_ST_SCAN,
+ WL_P2P_DISC_ST_LISTEN,
+ WL_P2P_DISC_ST_SEARCH
+};
+
+/**
+ * struct brcmf_p2p_scan_le - P2P specific scan request.
+ *
+ * @type: type of scan method requested (values: 'E' or 'S').
+ * @reserved: reserved (ignored).
+ * @eparams: parameters used for type 'E'.
+ * @sparams: parameters used for type 'S'.
+ */
+struct brcmf_p2p_scan_le {
+ u8 type;
+ u8 reserved[3];
+ union {
+ struct brcmf_escan_params_le eparams;
+ struct brcmf_scan_params_le sparams;
+ };
+};
+
+/**
+ * struct brcmf_p2p_pub_act_frame - WiFi P2P Public Action Frame
+ *
+ * @category: P2P_PUB_AF_CATEGORY
+ * @action: P2P_PUB_AF_ACTION
+ * @oui[3]: P2P_OUI
+ * @oui_type: OUI type - P2P_VER
+ * @subtype: OUI subtype - P2P_TYPE_*
+ * @dialog_token: nonzero, identifies req/rsp transaction
+ * @elts[1]: Variable length information elements.
+ */
+struct brcmf_p2p_pub_act_frame {
+ u8 category;
+ u8 action;
+ u8 oui[3];
+ u8 oui_type;
+ u8 subtype;
+ u8 dialog_token;
+ u8 elts[1];
+};
+
+/**
+ * struct brcmf_p2p_action_frame - WiFi P2P Action Frame
+ *
+ * @category: P2P_AF_CATEGORY
+ * @OUI[3]: OUI - P2P_OUI
+ * @type: OUI Type - P2P_VER
+ * @subtype: OUI Subtype - P2P_AF_*
+ * @dialog_token: nonzero, identifies req/resp tranaction
+ * @elts[1]: Variable length information elements.
+ */
+struct brcmf_p2p_action_frame {
+ u8 category;
+ u8 oui[3];
+ u8 type;
+ u8 subtype;
+ u8 dialog_token;
+ u8 elts[1];
+};
+
+/**
+ * struct brcmf_p2psd_gas_pub_act_frame - Wi-Fi GAS Public Action Frame
+ *
+ * @category: 0x04 Public Action Frame
+ * @action: 0x6c Advertisement Protocol
+ * @dialog_token: nonzero, identifies req/rsp transaction
+ * @query_data[1]: Query Data. SD gas ireq SD gas iresp
+ */
+struct brcmf_p2psd_gas_pub_act_frame {
+ u8 category;
+ u8 action;
+ u8 dialog_token;
+ u8 query_data[1];
+};
+
+/**
+ * struct brcmf_config_af_params - Action Frame Parameters for tx.
+ *
+ * @mpc_onoff: To make sure to send successfully action frame, we have to
+ * turn off mpc 0: off, 1: on, (-1): do nothing
+ * @search_channel: 1: search peer's channel to send af
+ * extra_listen: keep the dwell time to get af response frame.
+ */
+struct brcmf_config_af_params {
+ s32 mpc_onoff;
+ bool search_channel;
+ bool extra_listen;
+};
+
+/**
+ * brcmf_p2p_is_pub_action() - true if p2p public type frame.
+ *
+ * @frame: action frame data.
+ * @frame_len: length of action frame data.
+ *
+ * Determine if action frame is p2p public action type
+ */
+static bool brcmf_p2p_is_pub_action(void *frame, u32 frame_len)
+{
+ struct brcmf_p2p_pub_act_frame *pact_frm;
+
+ if (frame == NULL)
+ return false;
+
+ pact_frm = (struct brcmf_p2p_pub_act_frame *)frame;
+ if (frame_len < sizeof(struct brcmf_p2p_pub_act_frame) - 1)
+ return false;
+
+ if (pact_frm->category == P2P_PUB_AF_CATEGORY &&
+ pact_frm->action == P2P_PUB_AF_ACTION &&
+ pact_frm->oui_type == P2P_VER &&
+ memcmp(pact_frm->oui, P2P_OUI, P2P_OUI_LEN) == 0)
+ return true;
+
+ return false;
+}
+
+/**
+ * brcmf_p2p_is_p2p_action() - true if p2p action type frame.
+ *
+ * @frame: action frame data.
+ * @frame_len: length of action frame data.
+ *
+ * Determine if action frame is p2p action type
+ */
+static bool brcmf_p2p_is_p2p_action(void *frame, u32 frame_len)
+{
+ struct brcmf_p2p_action_frame *act_frm;
+
+ if (frame == NULL)
+ return false;
+
+ act_frm = (struct brcmf_p2p_action_frame *)frame;
+ if (frame_len < sizeof(struct brcmf_p2p_action_frame) - 1)
+ return false;
+
+ if (act_frm->category == P2P_AF_CATEGORY &&
+ act_frm->type == P2P_VER &&
+ memcmp(act_frm->oui, P2P_OUI, P2P_OUI_LEN) == 0)
+ return true;
+
+ return false;
+}
+
+/**
+ * brcmf_p2p_is_gas_action() - true if p2p gas action type frame.
+ *
+ * @frame: action frame data.
+ * @frame_len: length of action frame data.
+ *
+ * Determine if action frame is p2p gas action type
+ */
+static bool brcmf_p2p_is_gas_action(void *frame, u32 frame_len)
+{
+ struct brcmf_p2psd_gas_pub_act_frame *sd_act_frm;
+
+ if (frame == NULL)
+ return false;
+
+ sd_act_frm = (struct brcmf_p2psd_gas_pub_act_frame *)frame;
+ if (frame_len < sizeof(struct brcmf_p2psd_gas_pub_act_frame) - 1)
+ return false;
+
+ if (sd_act_frm->category != P2PSD_ACTION_CATEGORY)
+ return false;
+
+ if (sd_act_frm->action == P2PSD_ACTION_ID_GAS_IREQ ||
+ sd_act_frm->action == P2PSD_ACTION_ID_GAS_IRESP ||
+ sd_act_frm->action == P2PSD_ACTION_ID_GAS_CREQ ||
+ sd_act_frm->action == P2PSD_ACTION_ID_GAS_CRESP)
+ return true;
+
+ return false;
+}
+
+/**
+ * brcmf_p2p_print_actframe() - debug print routine.
+ *
+ * @tx: Received or to be transmitted
+ * @frame: action frame data.
+ * @frame_len: length of action frame data.
+ *
+ * Print information about the p2p action frame
+ */
+
+#ifdef DEBUG
+
+static void brcmf_p2p_print_actframe(bool tx, void *frame, u32 frame_len)
+{
+ struct brcmf_p2p_pub_act_frame *pact_frm;
+ struct brcmf_p2p_action_frame *act_frm;
+ struct brcmf_p2psd_gas_pub_act_frame *sd_act_frm;
+
+ if (!frame || frame_len <= 2)
+ return;
+
+ if (brcmf_p2p_is_pub_action(frame, frame_len)) {
+ pact_frm = (struct brcmf_p2p_pub_act_frame *)frame;
+ switch (pact_frm->subtype) {
+ case P2P_PAF_GON_REQ:
+ brcmf_dbg(TRACE, "%s P2P Group Owner Negotiation Req Frame\n",
+ (tx) ? "TX" : "RX");
+ break;
+ case P2P_PAF_GON_RSP:
+ brcmf_dbg(TRACE, "%s P2P Group Owner Negotiation Rsp Frame\n",
+ (tx) ? "TX" : "RX");
+ break;
+ case P2P_PAF_GON_CONF:
+ brcmf_dbg(TRACE, "%s P2P Group Owner Negotiation Confirm Frame\n",
+ (tx) ? "TX" : "RX");
+ break;
+ case P2P_PAF_INVITE_REQ:
+ brcmf_dbg(TRACE, "%s P2P Invitation Request Frame\n",
+ (tx) ? "TX" : "RX");
+ break;
+ case P2P_PAF_INVITE_RSP:
+ brcmf_dbg(TRACE, "%s P2P Invitation Response Frame\n",
+ (tx) ? "TX" : "RX");
+ break;
+ case P2P_PAF_DEVDIS_REQ:
+ brcmf_dbg(TRACE, "%s P2P Device Discoverability Request Frame\n",
+ (tx) ? "TX" : "RX");
+ break;
+ case P2P_PAF_DEVDIS_RSP:
+ brcmf_dbg(TRACE, "%s P2P Device Discoverability Response Frame\n",
+ (tx) ? "TX" : "RX");
+ break;
+ case P2P_PAF_PROVDIS_REQ:
+ brcmf_dbg(TRACE, "%s P2P Provision Discovery Request Frame\n",
+ (tx) ? "TX" : "RX");
+ break;
+ case P2P_PAF_PROVDIS_RSP:
+ brcmf_dbg(TRACE, "%s P2P Provision Discovery Response Frame\n",
+ (tx) ? "TX" : "RX");
+ break;
+ default:
+ brcmf_dbg(TRACE, "%s Unknown P2P Public Action Frame\n",
+ (tx) ? "TX" : "RX");
+ break;
+ }
+ } else if (brcmf_p2p_is_p2p_action(frame, frame_len)) {
+ act_frm = (struct brcmf_p2p_action_frame *)frame;
+ switch (act_frm->subtype) {
+ case P2P_AF_NOTICE_OF_ABSENCE:
+ brcmf_dbg(TRACE, "%s P2P Notice of Absence Frame\n",
+ (tx) ? "TX" : "RX");
+ break;
+ case P2P_AF_PRESENCE_REQ:
+ brcmf_dbg(TRACE, "%s P2P Presence Request Frame\n",
+ (tx) ? "TX" : "RX");
+ break;
+ case P2P_AF_PRESENCE_RSP:
+ brcmf_dbg(TRACE, "%s P2P Presence Response Frame\n",
+ (tx) ? "TX" : "RX");
+ break;
+ case P2P_AF_GO_DISC_REQ:
+ brcmf_dbg(TRACE, "%s P2P Discoverability Request Frame\n",
+ (tx) ? "TX" : "RX");
+ break;
+ default:
+ brcmf_dbg(TRACE, "%s Unknown P2P Action Frame\n",
+ (tx) ? "TX" : "RX");
+ }
+
+ } else if (brcmf_p2p_is_gas_action(frame, frame_len)) {
+ sd_act_frm = (struct brcmf_p2psd_gas_pub_act_frame *)frame;
+ switch (sd_act_frm->action) {
+ case P2PSD_ACTION_ID_GAS_IREQ:
+ brcmf_dbg(TRACE, "%s P2P GAS Initial Request\n",
+ (tx) ? "TX" : "RX");
+ break;
+ case P2PSD_ACTION_ID_GAS_IRESP:
+ brcmf_dbg(TRACE, "%s P2P GAS Initial Response\n",
+ (tx) ? "TX" : "RX");
+ break;
+ case P2PSD_ACTION_ID_GAS_CREQ:
+ brcmf_dbg(TRACE, "%s P2P GAS Comback Request\n",
+ (tx) ? "TX" : "RX");
+ break;
+ case P2PSD_ACTION_ID_GAS_CRESP:
+ brcmf_dbg(TRACE, "%s P2P GAS Comback Response\n",
+ (tx) ? "TX" : "RX");
+ break;
+ default:
+ brcmf_dbg(TRACE, "%s Unknown P2P GAS Frame\n",
+ (tx) ? "TX" : "RX");
+ break;
+ }
+ }
+}
+
+#else
+
+static void brcmf_p2p_print_actframe(bool tx, void *frame, u32 frame_len)
+{
+}
+
+#endif
+
+
+/**
+ * brcmf_p2p_set_firmware() - prepare firmware for peer-to-peer operation.
+ *
+ * @ifp: ifp to use for iovars (primary).
+ * @p2p_mac: mac address to configure for p2p_da_override
+ */
+static int brcmf_p2p_set_firmware(struct brcmf_if *ifp, u8 *p2p_mac)
+{
+ s32 ret = 0;
+
+ brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
+ brcmf_fil_iovar_int_set(ifp, "apsta", 1);
+ brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1);
+
+ /* In case of COB type, firmware has default mac address
+ * After Initializing firmware, we have to set current mac address to
+ * firmware for P2P device address. This must be done with discovery
+ * disabled.
+ */
+ brcmf_fil_iovar_int_set(ifp, "p2p_disc", 0);
+
+ ret = brcmf_fil_iovar_data_set(ifp, "p2p_da_override", p2p_mac,
+ ETH_ALEN);
+ if (ret)
+ brcmf_err("failed to update device address ret %d\n", ret);
+
+ return ret;
+}
+
+/**
+ * brcmf_p2p_generate_bss_mac() - derive mac addresses for P2P.
+ *
+ * @p2p: P2P specific data.
+ * @dev_addr: optional device address.
+ *
+ * P2P needs mac addresses for P2P device and interface. If no device
+ * address it specified, these are derived from the primary net device, ie.
+ * the permanent ethernet address of the device.
+ */
+static void brcmf_p2p_generate_bss_mac(struct brcmf_p2p_info *p2p, u8 *dev_addr)
+{
+ struct brcmf_if *pri_ifp = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp;
+ bool local_admin = false;
+
+ if (!dev_addr || is_zero_ether_addr(dev_addr)) {
+ dev_addr = pri_ifp->mac_addr;
+ local_admin = true;
+ }
+
+ /* Generate the P2P Device Address. This consists of the device's
+ * primary MAC address with the locally administered bit set.
+ */
+ memcpy(p2p->dev_addr, dev_addr, ETH_ALEN);
+ if (local_admin)
+ p2p->dev_addr[0] |= 0x02;
+
+ /* Generate the P2P Interface Address. If the discovery and connection
+ * BSSCFGs need to simultaneously co-exist, then this address must be
+ * different from the P2P Device Address, but also locally administered.
+ */
+ memcpy(p2p->int_addr, p2p->dev_addr, ETH_ALEN);
+ p2p->int_addr[0] |= 0x02;
+ p2p->int_addr[4] ^= 0x80;
+}
+
+/**
+ * brcmf_p2p_scan_is_p2p_request() - is cfg80211 scan request a P2P scan.
+ *
+ * @request: the scan request as received from cfg80211.
+ *
+ * returns true if one of the ssids in the request matches the
+ * P2P wildcard ssid; otherwise returns false.
+ */
+static bool brcmf_p2p_scan_is_p2p_request(struct cfg80211_scan_request *request)
+{
+ struct cfg80211_ssid *ssids = request->ssids;
+ int i;
+
+ for (i = 0; i < request->n_ssids; i++) {
+ if (ssids[i].ssid_len != BRCMF_P2P_WILDCARD_SSID_LEN)
+ continue;
+
+ brcmf_dbg(INFO, "comparing ssid \"%s\"", ssids[i].ssid);
+ if (!memcmp(BRCMF_P2P_WILDCARD_SSID, ssids[i].ssid,
+ BRCMF_P2P_WILDCARD_SSID_LEN))
+ return true;
+ }
+ return false;
+}
+
+/**
+ * brcmf_p2p_set_discover_state - set discover state in firmware.
+ *
+ * @ifp: low-level interface object.
+ * @state: discover state to set.
+ * @chanspec: channel parameters (for state @WL_P2P_DISC_ST_LISTEN only).
+ * @listen_ms: duration to listen (for state @WL_P2P_DISC_ST_LISTEN only).
+ */
+static s32 brcmf_p2p_set_discover_state(struct brcmf_if *ifp, u8 state,
+ u16 chanspec, u16 listen_ms)
+{
+ struct brcmf_p2p_disc_st_le discover_state;
+ s32 ret = 0;
+ brcmf_dbg(TRACE, "enter\n");
+
+ discover_state.state = state;
+ discover_state.chspec = cpu_to_le16(chanspec);
+ discover_state.dwell = cpu_to_le16(listen_ms);
+ ret = brcmf_fil_bsscfg_data_set(ifp, "p2p_state", &discover_state,
+ sizeof(discover_state));
+ return ret;
+}
+
+/**
+ * brcmf_p2p_deinit_discovery() - disable P2P device discovery.
+ *
+ * @p2p: P2P specific data.
+ *
+ * Resets the discovery state and disables it in firmware.
+ */
+static s32 brcmf_p2p_deinit_discovery(struct brcmf_p2p_info *p2p)
+{
+ struct brcmf_cfg80211_vif *vif;
+
+ brcmf_dbg(TRACE, "enter\n");
+
+ /* Set the discovery state to SCAN */
+ vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
+ (void)brcmf_p2p_set_discover_state(vif->ifp, WL_P2P_DISC_ST_SCAN, 0, 0);
+
+ /* Disable P2P discovery in the firmware */
+ vif = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif;
+ (void)brcmf_fil_iovar_int_set(vif->ifp, "p2p_disc", 0);
+
+ return 0;
+}
+
+/**
+ * brcmf_p2p_enable_discovery() - initialize and configure discovery.
+ *
+ * @p2p: P2P specific data.
+ *
+ * Initializes the discovery device and configure the virtual interface.
+ */
+static int brcmf_p2p_enable_discovery(struct brcmf_p2p_info *p2p)
+{
+ struct brcmf_cfg80211_vif *vif;
+ s32 ret = 0;
+
+ brcmf_dbg(TRACE, "enter\n");
+ vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
+ if (!vif) {
+ brcmf_err("P2P config device not available\n");
+ ret = -EPERM;
+ goto exit;
+ }
+
+ if (test_bit(BRCMF_P2P_STATUS_ENABLED, &p2p->status)) {
+ brcmf_dbg(INFO, "P2P config device already configured\n");
+ goto exit;
+ }
+
+ /* Re-initialize P2P Discovery in the firmware */
+ vif = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif;
+ ret = brcmf_fil_iovar_int_set(vif->ifp, "p2p_disc", 1);
+ if (ret < 0) {
+ brcmf_err("set p2p_disc error\n");
+ goto exit;
+ }
+ vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
+ ret = brcmf_p2p_set_discover_state(vif->ifp, WL_P2P_DISC_ST_SCAN, 0, 0);
+ if (ret < 0) {
+ brcmf_err("unable to set WL_P2P_DISC_ST_SCAN\n");
+ goto exit;
+ }
+
+ /*
+ * Set wsec to any non-zero value in the discovery bsscfg
+ * to ensure our P2P probe responses have the privacy bit
+ * set in the 802.11 WPA IE. Some peer devices may not
+ * initiate WPS with us if this bit is not set.
+ */
+ ret = brcmf_fil_bsscfg_int_set(vif->ifp, "wsec", AES_ENABLED);
+ if (ret < 0) {
+ brcmf_err("wsec error %d\n", ret);
+ goto exit;
+ }
+
+ set_bit(BRCMF_P2P_STATUS_ENABLED, &p2p->status);
+exit:
+ return ret;
+}
+
+/**
+ * brcmf_p2p_escan() - initiate a P2P scan.
+ *
+ * @p2p: P2P specific data.
+ * @num_chans: number of channels to scan.
+ * @chanspecs: channel parameters for @num_chans channels.
+ * @search_state: P2P discover state to use.
+ * @bss_type: type of P2P bss.
+ */
+static s32 brcmf_p2p_escan(struct brcmf_p2p_info *p2p, u32 num_chans,
+ u16 chanspecs[], s32 search_state,
+ enum p2p_bss_type bss_type)
+{
+ s32 ret = 0;
+ s32 memsize = offsetof(struct brcmf_p2p_scan_le,
+ eparams.params_le.channel_list);
+ s32 nprobes;
+ s32 active;
+ u32 i;
+ u8 *memblk;
+ struct brcmf_cfg80211_vif *vif;
+ struct brcmf_p2p_scan_le *p2p_params;
+ struct brcmf_scan_params_le *sparams;
+
+ memsize += num_chans * sizeof(__le16);
+ memblk = kzalloc(memsize, GFP_KERNEL);
+ if (!memblk)
+ return -ENOMEM;
+
+ vif = p2p->bss_idx[bss_type].vif;
+ if (vif == NULL) {
+ brcmf_err("no vif for bss type %d\n", bss_type);
+ ret = -EINVAL;
+ goto exit;
+ }
+ p2p_params = (struct brcmf_p2p_scan_le *)memblk;
+ sparams = &p2p_params->eparams.params_le;
+
+ switch (search_state) {
+ case WL_P2P_DISC_ST_SEARCH:
+ /*
+ * If we in SEARCH STATE, we don't need to set SSID explictly
+ * because dongle use P2P WILDCARD internally by default, use
+ * null ssid, which it is already due to kzalloc.
+ */
+ break;
+ case WL_P2P_DISC_ST_SCAN:
+ /*
+ * wpa_supplicant has p2p_find command with type social or
+ * progressive. For progressive, we need to set the ssid to
+ * P2P WILDCARD because we just do broadcast scan unless
+ * setting SSID.
+ */
+ sparams->ssid_le.SSID_len =
+ cpu_to_le32(BRCMF_P2P_WILDCARD_SSID_LEN);
+ memcpy(sparams->ssid_le.SSID, BRCMF_P2P_WILDCARD_SSID,
+ BRCMF_P2P_WILDCARD_SSID_LEN);
+ break;
+ default:
+ brcmf_err(" invalid search state %d\n", search_state);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ brcmf_p2p_set_discover_state(vif->ifp, search_state, 0, 0);
+
+ /*
+ * set p2p scan parameters.
+ */
+ p2p_params->type = 'E';
+
+ /* determine the scan engine parameters */
+ sparams->bss_type = DOT11_BSSTYPE_ANY;
+ if (p2p->cfg->active_scan)
+ sparams->scan_type = 0;
+ else
+ sparams->scan_type = 1;
+
+ eth_broadcast_addr(sparams->bssid);
+ sparams->home_time = cpu_to_le32(P2PAPI_SCAN_HOME_TIME_MS);
+
+ /*
+ * SOCIAL_CHAN_CNT + 1 takes care of the Progressive scan
+ * supported by the supplicant.
+ */
+ if (num_chans == SOCIAL_CHAN_CNT || num_chans == (SOCIAL_CHAN_CNT + 1))
+ active = P2PAPI_SCAN_SOCIAL_DWELL_TIME_MS;
+ else if (num_chans == AF_PEER_SEARCH_CNT)
+ active = P2PAPI_SCAN_AF_SEARCH_DWELL_TIME_MS;
+ else if (brcmf_get_vif_state_any(p2p->cfg, BRCMF_VIF_STATUS_CONNECTED))
+ active = -1;
+ else
+ active = P2PAPI_SCAN_DWELL_TIME_MS;
+
+ /* Override scan params to find a peer for a connection */
+ if (num_chans == 1) {
+ active = WL_SCAN_CONNECT_DWELL_TIME_MS;
+ /* WAR to sync with presence period of VSDB GO.
+ * send probe request more frequently
+ */
+ nprobes = active / WL_SCAN_JOIN_PROBE_INTERVAL_MS;
+ } else {
+ nprobes = active / P2PAPI_SCAN_NPROBS_TIME_MS;
+ }
+
+ if (nprobes <= 0)
+ nprobes = 1;
+
+ brcmf_dbg(INFO, "nprobes # %d, active_time %d\n", nprobes, active);
+ sparams->active_time = cpu_to_le32(active);
+ sparams->nprobes = cpu_to_le32(nprobes);
+ sparams->passive_time = cpu_to_le32(-1);
+ sparams->channel_num = cpu_to_le32(num_chans &
+ BRCMF_SCAN_PARAMS_COUNT_MASK);
+ for (i = 0; i < num_chans; i++)
+ sparams->channel_list[i] = cpu_to_le16(chanspecs[i]);
+
+ /* set the escan specific parameters */
+ p2p_params->eparams.version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION);
+ p2p_params->eparams.action = cpu_to_le16(WL_ESCAN_ACTION_START);
+ p2p_params->eparams.sync_id = cpu_to_le16(0x1234);
+ /* perform p2p scan on primary device */
+ ret = brcmf_fil_bsscfg_data_set(vif->ifp, "p2p_scan", memblk, memsize);
+ if (!ret)
+ set_bit(BRCMF_SCAN_STATUS_BUSY, &p2p->cfg->scan_status);
+exit:
+ kfree(memblk);
+ return ret;
+}
+
+/**
+ * brcmf_p2p_run_escan() - escan callback for peer-to-peer.
+ *
+ * @cfg: driver private data for cfg80211 interface.
+ * @ndev: net device for which scan is requested.
+ * @request: scan request from cfg80211.
+ * @action: scan action.
+ *
+ * Determines the P2P discovery state based to scan request parameters and
+ * validates the channels in the request.
+ */
+static s32 brcmf_p2p_run_escan(struct brcmf_cfg80211_info *cfg,
+ struct brcmf_if *ifp,
+ struct cfg80211_scan_request *request)
+{
+ struct brcmf_p2p_info *p2p = &cfg->p2p;
+ s32 err = 0;
+ s32 search_state = WL_P2P_DISC_ST_SCAN;
+ struct brcmf_cfg80211_vif *vif;
+ struct net_device *dev = NULL;
+ int i, num_nodfs = 0;
+ u16 *chanspecs;
+
+ brcmf_dbg(TRACE, "enter\n");
+
+ if (!request) {
+ err = -EINVAL;
+ goto exit;
+ }
+
+ if (request->n_channels) {
+ chanspecs = kcalloc(request->n_channels, sizeof(*chanspecs),
+ GFP_KERNEL);
+ if (!chanspecs) {
+ err = -ENOMEM;
+ goto exit;
+ }
+ vif = p2p->bss_idx[P2PAPI_BSSCFG_CONNECTION].vif;
+ if (vif)
+ dev = vif->wdev.netdev;
+ if (request->n_channels == 3 &&
+ request->channels[0]->hw_value == SOCIAL_CHAN_1 &&
+ request->channels[1]->hw_value == SOCIAL_CHAN_2 &&
+ request->channels[2]->hw_value == SOCIAL_CHAN_3) {
+ /* SOCIAL CHANNELS 1, 6, 11 */
+ search_state = WL_P2P_DISC_ST_SEARCH;
+ brcmf_dbg(INFO, "P2P SEARCH PHASE START\n");
+ } else if (dev != NULL &&
+ vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) {
+ /* If you are already a GO, then do SEARCH only */
+ brcmf_dbg(INFO, "Already a GO. Do SEARCH Only\n");
+ search_state = WL_P2P_DISC_ST_SEARCH;
+ } else {
+ brcmf_dbg(INFO, "P2P SCAN STATE START\n");
+ }
+
+ /*
+ * no P2P scanning on passive or DFS channels.
+ */
+ for (i = 0; i < request->n_channels; i++) {
+ struct ieee80211_channel *chan = request->channels[i];
+
+ if (chan->flags & (IEEE80211_CHAN_RADAR |
+ IEEE80211_CHAN_NO_IR))
+ continue;
+
+ chanspecs[i] = channel_to_chanspec(&p2p->cfg->d11inf,
+ chan);
+ brcmf_dbg(INFO, "%d: chan=%d, channel spec=%x\n",
+ num_nodfs, chan->hw_value, chanspecs[i]);
+ num_nodfs++;
+ }
+ err = brcmf_p2p_escan(p2p, num_nodfs, chanspecs, search_state,
+ P2PAPI_BSSCFG_DEVICE);
+ kfree(chanspecs);
+ }
+exit:
+ if (err)
+ brcmf_err("error (%d)\n", err);
+ return err;
+}
+
+
+/**
+ * brcmf_p2p_find_listen_channel() - find listen channel in ie string.
+ *
+ * @ie: string of information elements.
+ * @ie_len: length of string.
+ *
+ * Scan ie for p2p ie and look for attribute 6 channel. If available determine
+ * channel and return it.
+ */
+static s32 brcmf_p2p_find_listen_channel(const u8 *ie, u32 ie_len)
+{
+ u8 channel_ie[5];
+ s32 listen_channel;
+ s32 err;
+
+ err = cfg80211_get_p2p_attr(ie, ie_len,
+ IEEE80211_P2P_ATTR_LISTEN_CHANNEL,
+ channel_ie, sizeof(channel_ie));
+ if (err < 0)
+ return err;
+
+ /* listen channel subel length format: */
+ /* 3(country) + 1(op. class) + 1(chan num) */
+ listen_channel = (s32)channel_ie[3 + 1];
+
+ if (listen_channel == SOCIAL_CHAN_1 ||
+ listen_channel == SOCIAL_CHAN_2 ||
+ listen_channel == SOCIAL_CHAN_3) {
+ brcmf_dbg(INFO, "Found my Listen Channel %d\n", listen_channel);
+ return listen_channel;
+ }
+
+ return -EPERM;
+}
+
+
+/**
+ * brcmf_p2p_scan_prep() - prepare scan based on request.
+ *
+ * @wiphy: wiphy device.
+ * @request: scan request from cfg80211.
+ * @vif: vif on which scan request is to be executed.
+ *
+ * Prepare the scan appropriately for type of scan requested. Overrides the
+ * escan .run() callback for peer-to-peer scanning.
+ */
+int brcmf_p2p_scan_prep(struct wiphy *wiphy,
+ struct cfg80211_scan_request *request,
+ struct brcmf_cfg80211_vif *vif)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct brcmf_p2p_info *p2p = &cfg->p2p;
+ int err = 0;
+
+ if (brcmf_p2p_scan_is_p2p_request(request)) {
+ /* find my listen channel */
+ err = brcmf_p2p_find_listen_channel(request->ie,
+ request->ie_len);
+ if (err < 0)
+ return err;
+
+ p2p->afx_hdl.my_listen_chan = err;
+
+ clear_bit(BRCMF_P2P_STATUS_GO_NEG_PHASE, &p2p->status);
+ brcmf_dbg(INFO, "P2P: GO_NEG_PHASE status cleared\n");
+
+ err = brcmf_p2p_enable_discovery(p2p);
+ if (err)
+ return err;
+
+ vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
+
+ /* override .run_escan() callback. */
+ cfg->escan_info.run = brcmf_p2p_run_escan;
+ }
+ err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_PRBREQ_FLAG,
+ request->ie, request->ie_len);
+ return err;
+}
+
+
+/**
+ * brcmf_p2p_discover_listen() - set firmware to discover listen state.
+ *
+ * @p2p: p2p device.
+ * @channel: channel nr for discover listen.
+ * @duration: time in ms to stay on channel.
+ *
+ */
+static s32
+brcmf_p2p_discover_listen(struct brcmf_p2p_info *p2p, u16 channel, u32 duration)
+{
+ struct brcmf_cfg80211_vif *vif;
+ struct brcmu_chan ch;
+ s32 err = 0;
+
+ vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
+ if (!vif) {
+ brcmf_err("Discovery is not set, so we have nothing to do\n");
+ err = -EPERM;
+ goto exit;
+ }
+
+ if (test_bit(BRCMF_P2P_STATUS_DISCOVER_LISTEN, &p2p->status)) {
+ brcmf_err("Previous LISTEN is not completed yet\n");
+ /* WAR: prevent cookie mismatch in wpa_supplicant return OK */
+ goto exit;
+ }
+
+ ch.chnum = channel;
+ ch.bw = BRCMU_CHAN_BW_20;
+ p2p->cfg->d11inf.encchspec(&ch);
+ err = brcmf_p2p_set_discover_state(vif->ifp, WL_P2P_DISC_ST_LISTEN,
+ ch.chspec, (u16)duration);
+ if (!err) {
+ set_bit(BRCMF_P2P_STATUS_DISCOVER_LISTEN, &p2p->status);
+ p2p->remain_on_channel_cookie++;
+ }
+exit:
+ return err;
+}
+
+
+/**
+ * brcmf_p2p_remain_on_channel() - put device on channel and stay there.
+ *
+ * @wiphy: wiphy device.
+ * @channel: channel to stay on.
+ * @duration: time in ms to remain on channel.
+ *
+ */
+int brcmf_p2p_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev,
+ struct ieee80211_channel *channel,
+ unsigned int duration, u64 *cookie)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct brcmf_p2p_info *p2p = &cfg->p2p;
+ s32 err;
+ u16 channel_nr;
+
+ channel_nr = ieee80211_frequency_to_channel(channel->center_freq);
+ brcmf_dbg(TRACE, "Enter, channel: %d, duration ms (%d)\n", channel_nr,
+ duration);
+
+ err = brcmf_p2p_enable_discovery(p2p);
+ if (err)
+ goto exit;
+ err = brcmf_p2p_discover_listen(p2p, channel_nr, duration);
+ if (err)
+ goto exit;
+
+ memcpy(&p2p->remain_on_channel, channel, sizeof(*channel));
+ *cookie = p2p->remain_on_channel_cookie;
+ cfg80211_ready_on_channel(wdev, *cookie, channel, duration, GFP_KERNEL);
+
+exit:
+ return err;
+}
+
+
+/**
+ * brcmf_p2p_notify_listen_complete() - p2p listen has completed.
+ *
+ * @ifp: interfac control.
+ * @e: event message. Not used, to make it usable for fweh event dispatcher.
+ * @data: payload of message. Not used.
+ *
+ */
+int brcmf_p2p_notify_listen_complete(struct brcmf_if *ifp,
+ const struct brcmf_event_msg *e,
+ void *data)
+{
+ struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
+ struct brcmf_p2p_info *p2p = &cfg->p2p;
+
+ brcmf_dbg(TRACE, "Enter\n");
+ if (test_and_clear_bit(BRCMF_P2P_STATUS_DISCOVER_LISTEN,
+ &p2p->status)) {
+ if (test_and_clear_bit(BRCMF_P2P_STATUS_WAITING_NEXT_AF_LISTEN,
+ &p2p->status)) {
+ clear_bit(BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME,
+ &p2p->status);
+ brcmf_dbg(INFO, "Listen DONE, wake up wait_next_af\n");
+ complete(&p2p->wait_next_af);
+ }
+
+ cfg80211_remain_on_channel_expired(&ifp->vif->wdev,
+ p2p->remain_on_channel_cookie,
+ &p2p->remain_on_channel,
+ GFP_KERNEL);
+ }
+ return 0;
+}
+
+
+/**
+ * brcmf_p2p_cancel_remain_on_channel() - cancel p2p listen state.
+ *
+ * @ifp: interfac control.
+ *
+ */
+void brcmf_p2p_cancel_remain_on_channel(struct brcmf_if *ifp)
+{
+ if (!ifp)
+ return;
+ brcmf_p2p_set_discover_state(ifp, WL_P2P_DISC_ST_SCAN, 0, 0);
+ brcmf_p2p_notify_listen_complete(ifp, NULL, NULL);
+}
+
+
+/**
+ * brcmf_p2p_act_frm_search() - search function for action frame.
+ *
+ * @p2p: p2p device.
+ * channel: channel on which action frame is to be trasmitted.
+ *
+ * search function to reach at common channel to send action frame. When
+ * channel is 0 then all social channels will be used to send af
+ */
+static s32 brcmf_p2p_act_frm_search(struct brcmf_p2p_info *p2p, u16 channel)
+{
+ s32 err;
+ u32 channel_cnt;
+ u16 *default_chan_list;
+ u32 i;
+ struct brcmu_chan ch;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ if (channel)
+ channel_cnt = AF_PEER_SEARCH_CNT;
+ else
+ channel_cnt = SOCIAL_CHAN_CNT;
+ default_chan_list = kzalloc(channel_cnt * sizeof(*default_chan_list),
+ GFP_KERNEL);
+ if (default_chan_list == NULL) {
+ brcmf_err("channel list allocation failed\n");
+ err = -ENOMEM;
+ goto exit;
+ }
+ ch.bw = BRCMU_CHAN_BW_20;
+ if (channel) {
+ ch.chnum = channel;
+ p2p->cfg->d11inf.encchspec(&ch);
+ /* insert same channel to the chan_list */
+ for (i = 0; i < channel_cnt; i++)
+ default_chan_list[i] = ch.chspec;
+ } else {
+ ch.chnum = SOCIAL_CHAN_1;
+ p2p->cfg->d11inf.encchspec(&ch);
+ default_chan_list[0] = ch.chspec;
+ ch.chnum = SOCIAL_CHAN_2;
+ p2p->cfg->d11inf.encchspec(&ch);
+ default_chan_list[1] = ch.chspec;
+ ch.chnum = SOCIAL_CHAN_3;
+ p2p->cfg->d11inf.encchspec(&ch);
+ default_chan_list[2] = ch.chspec;
+ }
+ err = brcmf_p2p_escan(p2p, channel_cnt, default_chan_list,
+ WL_P2P_DISC_ST_SEARCH, P2PAPI_BSSCFG_DEVICE);
+ kfree(default_chan_list);
+exit:
+ return err;
+}
+
+
+/**
+ * brcmf_p2p_afx_handler() - afx worker thread.
+ *
+ * @work:
+ *
+ */
+static void brcmf_p2p_afx_handler(struct work_struct *work)
+{
+ struct afx_hdl *afx_hdl = container_of(work, struct afx_hdl, afx_work);
+ struct brcmf_p2p_info *p2p = container_of(afx_hdl,
+ struct brcmf_p2p_info,
+ afx_hdl);
+ s32 err;
+
+ if (!afx_hdl->is_active)
+ return;
+
+ if (afx_hdl->is_listen && afx_hdl->my_listen_chan)
+ /* 100ms ~ 300ms */
+ err = brcmf_p2p_discover_listen(p2p, afx_hdl->my_listen_chan,
+ 100 * (1 + prandom_u32() % 3));
+ else
+ err = brcmf_p2p_act_frm_search(p2p, afx_hdl->peer_listen_chan);
+
+ if (err) {
+ brcmf_err("ERROR occurred! value is (%d)\n", err);
+ if (test_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL,
+ &p2p->status))
+ complete(&afx_hdl->act_frm_scan);
+ }
+}
+
+
+/**
+ * brcmf_p2p_af_searching_channel() - search channel.
+ *
+ * @p2p: p2p device info struct.
+ *
+ */
+static s32 brcmf_p2p_af_searching_channel(struct brcmf_p2p_info *p2p)
+{
+ struct afx_hdl *afx_hdl = &p2p->afx_hdl;
+ struct brcmf_cfg80211_vif *pri_vif;
+ unsigned long duration;
+ s32 retry;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ pri_vif = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif;
+
+ reinit_completion(&afx_hdl->act_frm_scan);
+ set_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL, &p2p->status);
+ afx_hdl->is_active = true;
+ afx_hdl->peer_chan = P2P_INVALID_CHANNEL;
+
+ /* Loop to wait until we find a peer's channel or the
+ * pending action frame tx is cancelled.
+ */
+ retry = 0;
+ duration = msecs_to_jiffies(P2P_AF_FRM_SCAN_MAX_WAIT);
+ while ((retry < P2P_CHANNEL_SYNC_RETRY) &&
+ (afx_hdl->peer_chan == P2P_INVALID_CHANNEL)) {
+ afx_hdl->is_listen = false;
+ brcmf_dbg(TRACE, "Scheduling action frame for sending.. (%d)\n",
+ retry);
+ /* search peer on peer's listen channel */
+ schedule_work(&afx_hdl->afx_work);
+ wait_for_completion_timeout(&afx_hdl->act_frm_scan, duration);
+ if ((afx_hdl->peer_chan != P2P_INVALID_CHANNEL) ||
+ (!test_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL,
+ &p2p->status)))
+ break;
+
+ if (afx_hdl->my_listen_chan) {
+ brcmf_dbg(TRACE, "Scheduling listen peer, channel=%d\n",
+ afx_hdl->my_listen_chan);
+ /* listen on my listen channel */
+ afx_hdl->is_listen = true;
+ schedule_work(&afx_hdl->afx_work);
+ wait_for_completion_timeout(&afx_hdl->act_frm_scan,
+ duration);
+ }
+ if ((afx_hdl->peer_chan != P2P_INVALID_CHANNEL) ||
+ (!test_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL,
+ &p2p->status)))
+ break;
+ retry++;
+
+ /* if sta is connected or connecting, sleep for a while before
+ * retry af tx or finding a peer
+ */
+ if (test_bit(BRCMF_VIF_STATUS_CONNECTED, &pri_vif->sme_state) ||
+ test_bit(BRCMF_VIF_STATUS_CONNECTING, &pri_vif->sme_state))
+ msleep(P2P_DEFAULT_SLEEP_TIME_VSDB);
+ }
+
+ brcmf_dbg(TRACE, "Completed search/listen peer_chan=%d\n",
+ afx_hdl->peer_chan);
+ afx_hdl->is_active = false;
+
+ clear_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL, &p2p->status);
+
+ return afx_hdl->peer_chan;
+}
+
+
+/**
+ * brcmf_p2p_scan_finding_common_channel() - was escan used for finding channel
+ *
+ * @cfg: common configuration struct.
+ * @bi: bss info struct, result from scan.
+ *
+ */
+bool brcmf_p2p_scan_finding_common_channel(struct brcmf_cfg80211_info *cfg,
+ struct brcmf_bss_info_le *bi)
+
+{
+ struct brcmf_p2p_info *p2p = &cfg->p2p;
+ struct afx_hdl *afx_hdl = &p2p->afx_hdl;
+ struct brcmu_chan ch;
+ u8 *ie;
+ s32 err;
+ u8 p2p_dev_addr[ETH_ALEN];
+
+ if (!test_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL, &p2p->status))
+ return false;
+
+ if (bi == NULL) {
+ brcmf_dbg(TRACE, "ACTION FRAME SCAN Done\n");
+ if (afx_hdl->peer_chan == P2P_INVALID_CHANNEL)
+ complete(&afx_hdl->act_frm_scan);
+ return true;
+ }
+
+ ie = ((u8 *)bi) + le16_to_cpu(bi->ie_offset);
+ memset(p2p_dev_addr, 0, sizeof(p2p_dev_addr));
+ err = cfg80211_get_p2p_attr(ie, le32_to_cpu(bi->ie_length),
+ IEEE80211_P2P_ATTR_DEVICE_INFO,
+ p2p_dev_addr, sizeof(p2p_dev_addr));
+ if (err < 0)
+ err = cfg80211_get_p2p_attr(ie, le32_to_cpu(bi->ie_length),
+ IEEE80211_P2P_ATTR_DEVICE_ID,
+ p2p_dev_addr, sizeof(p2p_dev_addr));
+ if ((err >= 0) &&
+ (ether_addr_equal(p2p_dev_addr, afx_hdl->tx_dst_addr))) {
+ if (!bi->ctl_ch) {
+ ch.chspec = le16_to_cpu(bi->chanspec);
+ cfg->d11inf.decchspec(&ch);
+ bi->ctl_ch = ch.control_ch_num;
+ }
+ afx_hdl->peer_chan = bi->ctl_ch;
+ brcmf_dbg(TRACE, "ACTION FRAME SCAN : Peer %pM found, channel : %d\n",
+ afx_hdl->tx_dst_addr, afx_hdl->peer_chan);
+ complete(&afx_hdl->act_frm_scan);
+ }
+ return true;
+}
+
+/**
+ * brcmf_p2p_stop_wait_next_action_frame() - finish scan if af tx complete.
+ *
+ * @cfg: common configuration struct.
+ *
+ */
+static void
+brcmf_p2p_stop_wait_next_action_frame(struct brcmf_cfg80211_info *cfg)
+{
+ struct brcmf_p2p_info *p2p = &cfg->p2p;
+ struct brcmf_if *ifp = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp;
+
+ if (test_bit(BRCMF_P2P_STATUS_SENDING_ACT_FRAME, &p2p->status) &&
+ (test_bit(BRCMF_P2P_STATUS_ACTION_TX_COMPLETED, &p2p->status) ||
+ test_bit(BRCMF_P2P_STATUS_ACTION_TX_NOACK, &p2p->status))) {
+ brcmf_dbg(TRACE, "*** Wake UP ** abort actframe iovar\n");
+ /* if channel is not zero, "actfame" uses off channel scan.
+ * So abort scan for off channel completion.
+ */
+ if (p2p->af_sent_channel)
+ brcmf_notify_escan_complete(cfg, ifp, true, true);
+ } else if (test_bit(BRCMF_P2P_STATUS_WAITING_NEXT_AF_LISTEN,
+ &p2p->status)) {
+ brcmf_dbg(TRACE, "*** Wake UP ** abort listen for next af frame\n");
+ /* So abort scan to cancel listen */
+ brcmf_notify_escan_complete(cfg, ifp, true, true);
+ }
+}
+
+
+/**
+ * brcmf_p2p_gon_req_collision() - Check if go negotiaton collission
+ *
+ * @p2p: p2p device info struct.
+ *
+ * return true if recevied action frame is to be dropped.
+ */
+static bool
+brcmf_p2p_gon_req_collision(struct brcmf_p2p_info *p2p, u8 *mac)
+{
+ struct brcmf_cfg80211_info *cfg = p2p->cfg;
+ struct brcmf_if *ifp;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ if (!test_bit(BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME, &p2p->status) ||
+ !p2p->gon_req_action)
+ return false;
+
+ brcmf_dbg(TRACE, "GO Negotiation Request COLLISION !!!\n");
+ /* if sa(peer) addr is less than da(my) addr, then this device
+ * process peer's gon request and block to send gon req.
+ * if not (sa addr > da addr),
+ * this device will process gon request and drop gon req of peer.
+ */
+ ifp = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif->ifp;
+ if (memcmp(mac, ifp->mac_addr, ETH_ALEN) < 0) {
+ brcmf_dbg(INFO, "Block transmit gon req !!!\n");
+ p2p->block_gon_req_tx = true;
+ /* if we are finding a common channel for sending af,
+ * do not scan more to block to send current gon req
+ */
+ if (test_and_clear_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL,
+ &p2p->status))
+ complete(&p2p->afx_hdl.act_frm_scan);
+ if (test_and_clear_bit(BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME,
+ &p2p->status))
+ brcmf_p2p_stop_wait_next_action_frame(cfg);
+ return false;
+ }
+
+ /* drop gon request of peer to process gon request by this device. */
+ brcmf_dbg(INFO, "Drop received gon req !!!\n");
+
+ return true;
+}
+
+
+/**
+ * brcmf_p2p_notify_action_frame_rx() - received action frame.
+ *
+ * @ifp: interfac control.
+ * @e: event message. Not used, to make it usable for fweh event dispatcher.
+ * @data: payload of message, containing action frame data.
+ *
+ */
+int brcmf_p2p_notify_action_frame_rx(struct brcmf_if *ifp,
+ const struct brcmf_event_msg *e,
+ void *data)
+{
+ struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
+ struct brcmf_p2p_info *p2p = &cfg->p2p;
+ struct afx_hdl *afx_hdl = &p2p->afx_hdl;
+ struct wireless_dev *wdev;
+ u32 mgmt_frame_len = e->datalen - sizeof(struct brcmf_rx_mgmt_data);
+ struct brcmf_rx_mgmt_data *rxframe = (struct brcmf_rx_mgmt_data *)data;
+ u8 *frame = (u8 *)(rxframe + 1);
+ struct brcmf_p2p_pub_act_frame *act_frm;
+ struct brcmf_p2psd_gas_pub_act_frame *sd_act_frm;
+ struct brcmu_chan ch;
+ struct ieee80211_mgmt *mgmt_frame;
+ s32 freq;
+ u16 mgmt_type;
+ u8 action;
+
+ if (e->datalen < sizeof(*rxframe)) {
+ brcmf_dbg(SCAN, "Event data to small. Ignore\n");
+ return 0;
+ }
+
+ ch.chspec = be16_to_cpu(rxframe->chanspec);
+ cfg->d11inf.decchspec(&ch);
+ /* Check if wpa_supplicant has registered for this frame */
+ brcmf_dbg(INFO, "ifp->vif->mgmt_rx_reg %04x\n", ifp->vif->mgmt_rx_reg);
+ mgmt_type = (IEEE80211_STYPE_ACTION & IEEE80211_FCTL_STYPE) >> 4;
+ if ((ifp->vif->mgmt_rx_reg & BIT(mgmt_type)) == 0)
+ return 0;
+
+ brcmf_p2p_print_actframe(false, frame, mgmt_frame_len);
+
+ action = P2P_PAF_SUBTYPE_INVALID;
+ if (brcmf_p2p_is_pub_action(frame, mgmt_frame_len)) {
+ act_frm = (struct brcmf_p2p_pub_act_frame *)frame;
+ action = act_frm->subtype;
+ if ((action == P2P_PAF_GON_REQ) &&
+ (brcmf_p2p_gon_req_collision(p2p, (u8 *)e->addr))) {
+ if (test_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL,
+ &p2p->status) &&
+ (ether_addr_equal(afx_hdl->tx_dst_addr, e->addr))) {
+ afx_hdl->peer_chan = ch.control_ch_num;
+ brcmf_dbg(INFO, "GON request: Peer found, channel=%d\n",
+ afx_hdl->peer_chan);
+ complete(&afx_hdl->act_frm_scan);
+ }
+ return 0;
+ }
+ /* After complete GO Negotiation, roll back to mpc mode */
+ if ((action == P2P_PAF_GON_CONF) ||
+ (action == P2P_PAF_PROVDIS_RSP))
+ brcmf_set_mpc(ifp, 1);
+ if (action == P2P_PAF_GON_CONF) {
+ brcmf_dbg(TRACE, "P2P: GO_NEG_PHASE status cleared\n");
+ clear_bit(BRCMF_P2P_STATUS_GO_NEG_PHASE, &p2p->status);
+ }
+ } else if (brcmf_p2p_is_gas_action(frame, mgmt_frame_len)) {
+ sd_act_frm = (struct brcmf_p2psd_gas_pub_act_frame *)frame;
+ action = sd_act_frm->action;
+ }
+
+ if (test_bit(BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME, &p2p->status) &&
+ (p2p->next_af_subtype == action)) {
+ brcmf_dbg(TRACE, "We got a right next frame! (%d)\n", action);
+ clear_bit(BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME,
+ &p2p->status);
+ /* Stop waiting for next AF. */
+ brcmf_p2p_stop_wait_next_action_frame(cfg);
+ }
+
+ mgmt_frame = kzalloc(offsetof(struct ieee80211_mgmt, u) +
+ mgmt_frame_len, GFP_KERNEL);
+ if (!mgmt_frame) {
+ brcmf_err("No memory available for action frame\n");
+ return -ENOMEM;
+ }
+ memcpy(mgmt_frame->da, ifp->mac_addr, ETH_ALEN);
+ brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSSID, mgmt_frame->bssid,
+ ETH_ALEN);
+ memcpy(mgmt_frame->sa, e->addr, ETH_ALEN);
+ mgmt_frame->frame_control = cpu_to_le16(IEEE80211_STYPE_ACTION);
+ memcpy(&mgmt_frame->u, frame, mgmt_frame_len);
+ mgmt_frame_len += offsetof(struct ieee80211_mgmt, u);
+
+ freq = ieee80211_channel_to_frequency(ch.control_ch_num,
+ ch.band == BRCMU_CHAN_BAND_2G ?
+ NL80211_BAND_2GHZ :
+ NL80211_BAND_5GHZ);
+
+ wdev = &ifp->vif->wdev;
+ cfg80211_rx_mgmt(wdev, freq, 0, (u8 *)mgmt_frame, mgmt_frame_len, 0);
+
+ kfree(mgmt_frame);
+ return 0;
+}
+
+
+/**
+ * brcmf_p2p_notify_action_tx_complete() - transmit action frame complete
+ *
+ * @ifp: interfac control.
+ * @e: event message. Not used, to make it usable for fweh event dispatcher.
+ * @data: not used.
+ *
+ */
+int brcmf_p2p_notify_action_tx_complete(struct brcmf_if *ifp,
+ const struct brcmf_event_msg *e,
+ void *data)
+{
+ struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
+ struct brcmf_p2p_info *p2p = &cfg->p2p;
+
+ brcmf_dbg(INFO, "Enter: event %s, status=%d\n",
+ e->event_code == BRCMF_E_ACTION_FRAME_OFF_CHAN_COMPLETE ?
+ "ACTION_FRAME_OFF_CHAN_COMPLETE" : "ACTION_FRAME_COMPLETE",
+ e->status);
+
+ if (!test_bit(BRCMF_P2P_STATUS_SENDING_ACT_FRAME, &p2p->status))
+ return 0;
+
+ if (e->event_code == BRCMF_E_ACTION_FRAME_COMPLETE) {
+ if (e->status == BRCMF_E_STATUS_SUCCESS)
+ set_bit(BRCMF_P2P_STATUS_ACTION_TX_COMPLETED,
+ &p2p->status);
+ else {
+ set_bit(BRCMF_P2P_STATUS_ACTION_TX_NOACK, &p2p->status);
+ /* If there is no ack, we don't need to wait for
+ * WLC_E_ACTION_FRAME_OFFCHAN_COMPLETE event
+ */
+ brcmf_p2p_stop_wait_next_action_frame(cfg);
+ }
+
+ } else {
+ complete(&p2p->send_af_done);
+ }
+ return 0;
+}
+
+
+/**
+ * brcmf_p2p_tx_action_frame() - send action frame over fil.
+ *
+ * @p2p: p2p info struct for vif.
+ * @af_params: action frame data/info.
+ *
+ * Send an action frame immediately without doing channel synchronization.
+ *
+ * This function waits for a completion event before returning.
+ * The WLC_E_ACTION_FRAME_COMPLETE event will be received when the action
+ * frame is transmitted.
+ */
+static s32 brcmf_p2p_tx_action_frame(struct brcmf_p2p_info *p2p,
+ struct brcmf_fil_af_params_le *af_params)
+{
+ struct brcmf_cfg80211_vif *vif;
+ s32 err = 0;
+ s32 timeout = 0;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ reinit_completion(&p2p->send_af_done);
+ clear_bit(BRCMF_P2P_STATUS_ACTION_TX_COMPLETED, &p2p->status);
+ clear_bit(BRCMF_P2P_STATUS_ACTION_TX_NOACK, &p2p->status);
+
+ vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
+ err = brcmf_fil_bsscfg_data_set(vif->ifp, "actframe", af_params,
+ sizeof(*af_params));
+ if (err) {
+ brcmf_err(" sending action frame has failed\n");
+ goto exit;
+ }
+
+ p2p->af_sent_channel = le32_to_cpu(af_params->channel);
+ p2p->af_tx_sent_jiffies = jiffies;
+
+ timeout = wait_for_completion_timeout(&p2p->send_af_done,
+ P2P_AF_MAX_WAIT_TIME);
+
+ if (test_bit(BRCMF_P2P_STATUS_ACTION_TX_COMPLETED, &p2p->status)) {
+ brcmf_dbg(TRACE, "TX action frame operation is success\n");
+ } else {
+ err = -EIO;
+ brcmf_dbg(TRACE, "TX action frame operation has failed\n");
+ }
+ /* clear status bit for action tx */
+ clear_bit(BRCMF_P2P_STATUS_ACTION_TX_COMPLETED, &p2p->status);
+ clear_bit(BRCMF_P2P_STATUS_ACTION_TX_NOACK, &p2p->status);
+
+exit:
+ return err;
+}
+
+
+/**
+ * brcmf_p2p_pub_af_tx() - public action frame tx routine.
+ *
+ * @cfg: driver private data for cfg80211 interface.
+ * @af_params: action frame data/info.
+ * @config_af_params: configuration data for action frame.
+ *
+ * routine which transmits ation frame public type.
+ */
+static s32 brcmf_p2p_pub_af_tx(struct brcmf_cfg80211_info *cfg,
+ struct brcmf_fil_af_params_le *af_params,
+ struct brcmf_config_af_params *config_af_params)
+{
+ struct brcmf_p2p_info *p2p = &cfg->p2p;
+ struct brcmf_fil_action_frame_le *action_frame;
+ struct brcmf_p2p_pub_act_frame *act_frm;
+ s32 err = 0;
+ u16 ie_len;
+
+ action_frame = &af_params->action_frame;
+ act_frm = (struct brcmf_p2p_pub_act_frame *)(action_frame->data);
+
+ config_af_params->extra_listen = true;
+
+ switch (act_frm->subtype) {
+ case P2P_PAF_GON_REQ:
+ brcmf_dbg(TRACE, "P2P: GO_NEG_PHASE status set\n");
+ set_bit(BRCMF_P2P_STATUS_GO_NEG_PHASE, &p2p->status);
+ config_af_params->mpc_onoff = 0;
+ config_af_params->search_channel = true;
+ p2p->next_af_subtype = act_frm->subtype + 1;
+ p2p->gon_req_action = true;
+ /* increase dwell time to wait for RESP frame */
+ af_params->dwell_time = cpu_to_le32(P2P_AF_MED_DWELL_TIME);
+ break;
+ case P2P_PAF_GON_RSP:
+ p2p->next_af_subtype = act_frm->subtype + 1;
+ /* increase dwell time to wait for CONF frame */
+ af_params->dwell_time = cpu_to_le32(P2P_AF_MED_DWELL_TIME);
+ break;
+ case P2P_PAF_GON_CONF:
+ /* If we reached till GO Neg confirmation reset the filter */
+ brcmf_dbg(TRACE, "P2P: GO_NEG_PHASE status cleared\n");
+ clear_bit(BRCMF_P2P_STATUS_GO_NEG_PHASE, &p2p->status);
+ /* turn on mpc again if go nego is done */
+ config_af_params->mpc_onoff = 1;
+ /* minimize dwell time */
+ af_params->dwell_time = cpu_to_le32(P2P_AF_MIN_DWELL_TIME);
+ config_af_params->extra_listen = false;
+ break;
+ case P2P_PAF_INVITE_REQ:
+ config_af_params->search_channel = true;
+ p2p->next_af_subtype = act_frm->subtype + 1;
+ /* increase dwell time */
+ af_params->dwell_time = cpu_to_le32(P2P_AF_MED_DWELL_TIME);
+ break;
+ case P2P_PAF_INVITE_RSP:
+ /* minimize dwell time */
+ af_params->dwell_time = cpu_to_le32(P2P_AF_MIN_DWELL_TIME);
+ config_af_params->extra_listen = false;
+ break;
+ case P2P_PAF_DEVDIS_REQ:
+ config_af_params->search_channel = true;
+ p2p->next_af_subtype = act_frm->subtype + 1;
+ /* maximize dwell time to wait for RESP frame */
+ af_params->dwell_time = cpu_to_le32(P2P_AF_LONG_DWELL_TIME);
+ break;
+ case P2P_PAF_DEVDIS_RSP:
+ /* minimize dwell time */
+ af_params->dwell_time = cpu_to_le32(P2P_AF_MIN_DWELL_TIME);
+ config_af_params->extra_listen = false;
+ break;
+ case P2P_PAF_PROVDIS_REQ:
+ ie_len = le16_to_cpu(action_frame->len) -
+ offsetof(struct brcmf_p2p_pub_act_frame, elts);
+ if (cfg80211_get_p2p_attr(&act_frm->elts[0], ie_len,
+ IEEE80211_P2P_ATTR_GROUP_ID,
+ NULL, 0) < 0)
+ config_af_params->search_channel = true;
+ config_af_params->mpc_onoff = 0;
+ p2p->next_af_subtype = act_frm->subtype + 1;
+ /* increase dwell time to wait for RESP frame */
+ af_params->dwell_time = cpu_to_le32(P2P_AF_MED_DWELL_TIME);
+ break;
+ case P2P_PAF_PROVDIS_RSP:
+ /* wpa_supplicant send go nego req right after prov disc */
+ p2p->next_af_subtype = P2P_PAF_GON_REQ;
+ /* increase dwell time to MED level */
+ af_params->dwell_time = cpu_to_le32(P2P_AF_MED_DWELL_TIME);
+ config_af_params->extra_listen = false;
+ break;
+ default:
+ brcmf_err("Unknown p2p pub act frame subtype: %d\n",
+ act_frm->subtype);
+ err = -EINVAL;
+ }
+ return err;
+}
+
+/**
+ * brcmf_p2p_send_action_frame() - send action frame .
+ *
+ * @cfg: driver private data for cfg80211 interface.
+ * @ndev: net device to transmit on.
+ * @af_params: configuration data for action frame.
+ */
+bool brcmf_p2p_send_action_frame(struct brcmf_cfg80211_info *cfg,
+ struct net_device *ndev,
+ struct brcmf_fil_af_params_le *af_params)
+{
+ struct brcmf_p2p_info *p2p = &cfg->p2p;
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ struct brcmf_fil_action_frame_le *action_frame;
+ struct brcmf_config_af_params config_af_params;
+ struct afx_hdl *afx_hdl = &p2p->afx_hdl;
+ u16 action_frame_len;
+ bool ack = false;
+ u8 category;
+ u8 action;
+ s32 tx_retry;
+ s32 extra_listen_time;
+ uint delta_ms;
+
+ action_frame = &af_params->action_frame;
+ action_frame_len = le16_to_cpu(action_frame->len);
+
+ brcmf_p2p_print_actframe(true, action_frame->data, action_frame_len);
+
+ /* Add the default dwell time. Dwell time to stay off-channel */
+ /* to wait for a response action frame after transmitting an */
+ /* GO Negotiation action frame */
+ af_params->dwell_time = cpu_to_le32(P2P_AF_DWELL_TIME);
+
+ category = action_frame->data[DOT11_ACTION_CAT_OFF];
+ action = action_frame->data[DOT11_ACTION_ACT_OFF];
+
+ /* initialize variables */
+ p2p->next_af_subtype = P2P_PAF_SUBTYPE_INVALID;
+ p2p->gon_req_action = false;
+
+ /* config parameters */
+ config_af_params.mpc_onoff = -1;
+ config_af_params.search_channel = false;
+ config_af_params.extra_listen = false;
+
+ if (brcmf_p2p_is_pub_action(action_frame->data, action_frame_len)) {
+ /* p2p public action frame process */
+ if (brcmf_p2p_pub_af_tx(cfg, af_params, &config_af_params)) {
+ /* Just send unknown subtype frame with */
+ /* default parameters. */
+ brcmf_err("P2P Public action frame, unknown subtype.\n");
+ }
+ } else if (brcmf_p2p_is_gas_action(action_frame->data,
+ action_frame_len)) {
+ /* service discovery process */
+ if (action == P2PSD_ACTION_ID_GAS_IREQ ||
+ action == P2PSD_ACTION_ID_GAS_CREQ) {
+ /* configure service discovery query frame */
+ config_af_params.search_channel = true;
+
+ /* save next af suptype to cancel */
+ /* remaining dwell time */
+ p2p->next_af_subtype = action + 1;
+
+ af_params->dwell_time =
+ cpu_to_le32(P2P_AF_MED_DWELL_TIME);
+ } else if (action == P2PSD_ACTION_ID_GAS_IRESP ||
+ action == P2PSD_ACTION_ID_GAS_CRESP) {
+ /* configure service discovery response frame */
+ af_params->dwell_time =
+ cpu_to_le32(P2P_AF_MIN_DWELL_TIME);
+ } else {
+ brcmf_err("Unknown action type: %d\n", action);
+ goto exit;
+ }
+ } else if (brcmf_p2p_is_p2p_action(action_frame->data,
+ action_frame_len)) {
+ /* do not configure anything. it will be */
+ /* sent with a default configuration */
+ } else {
+ brcmf_err("Unknown Frame: category 0x%x, action 0x%x\n",
+ category, action);
+ return false;
+ }
+
+ /* if connecting on primary iface, sleep for a while before sending
+ * af tx for VSDB
+ */
+ if (test_bit(BRCMF_VIF_STATUS_CONNECTING,
+ &p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->sme_state))
+ msleep(50);
+
+ /* if scan is ongoing, abort current scan. */
+ if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status))
+ brcmf_abort_scanning(cfg);
+
+ memcpy(afx_hdl->tx_dst_addr, action_frame->da, ETH_ALEN);
+
+ /* To make sure to send successfully action frame, turn off mpc */
+ if (config_af_params.mpc_onoff == 0)
+ brcmf_set_mpc(ifp, 0);
+
+ /* set status and destination address before sending af */
+ if (p2p->next_af_subtype != P2P_PAF_SUBTYPE_INVALID) {
+ /* set status to cancel the remained dwell time in rx process */
+ set_bit(BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME, &p2p->status);
+ }
+
+ p2p->af_sent_channel = 0;
+ set_bit(BRCMF_P2P_STATUS_SENDING_ACT_FRAME, &p2p->status);
+ /* validate channel and p2p ies */
+ if (config_af_params.search_channel &&
+ IS_P2P_SOCIAL_CHANNEL(le32_to_cpu(af_params->channel)) &&
+ p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif->saved_ie.probe_req_ie_len) {
+ afx_hdl = &p2p->afx_hdl;
+ afx_hdl->peer_listen_chan = le32_to_cpu(af_params->channel);
+
+ if (brcmf_p2p_af_searching_channel(p2p) ==
+ P2P_INVALID_CHANNEL) {
+ brcmf_err("Couldn't find peer's channel.\n");
+ goto exit;
+ }
+
+ /* Abort scan even for VSDB scenarios. Scan gets aborted in
+ * firmware but after the check of piggyback algorithm. To take
+ * care of current piggback algo, lets abort the scan here
+ * itself.
+ */
+ brcmf_notify_escan_complete(cfg, ifp, true, true);
+
+ /* update channel */
+ af_params->channel = cpu_to_le32(afx_hdl->peer_chan);
+ }
+
+ tx_retry = 0;
+ while (!p2p->block_gon_req_tx &&
+ (ack == false) && (tx_retry < P2P_AF_TX_MAX_RETRY)) {
+ ack = !brcmf_p2p_tx_action_frame(p2p, af_params);
+ tx_retry++;
+ }
+ if (ack == false) {
+ brcmf_err("Failed to send Action Frame(retry %d)\n", tx_retry);
+ clear_bit(BRCMF_P2P_STATUS_GO_NEG_PHASE, &p2p->status);
+ }
+
+exit:
+ clear_bit(BRCMF_P2P_STATUS_SENDING_ACT_FRAME, &p2p->status);
+
+ /* WAR: sometimes dongle does not keep the dwell time of 'actframe'.
+ * if we coundn't get the next action response frame and dongle does
+ * not keep the dwell time, go to listen state again to get next action
+ * response frame.
+ */
+ if (ack && config_af_params.extra_listen && !p2p->block_gon_req_tx &&
+ test_bit(BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME, &p2p->status) &&
+ p2p->af_sent_channel == afx_hdl->my_listen_chan) {
+ delta_ms = jiffies_to_msecs(jiffies - p2p->af_tx_sent_jiffies);
+ if (le32_to_cpu(af_params->dwell_time) > delta_ms)
+ extra_listen_time = le32_to_cpu(af_params->dwell_time) -
+ delta_ms;
+ else
+ extra_listen_time = 0;
+ if (extra_listen_time > 50) {
+ set_bit(BRCMF_P2P_STATUS_WAITING_NEXT_AF_LISTEN,
+ &p2p->status);
+ brcmf_dbg(INFO, "Wait more time! actual af time:%d, calculated extra listen:%d\n",
+ le32_to_cpu(af_params->dwell_time),
+ extra_listen_time);
+ extra_listen_time += 100;
+ if (!brcmf_p2p_discover_listen(p2p,
+ p2p->af_sent_channel,
+ extra_listen_time)) {
+ unsigned long duration;
+
+ extra_listen_time += 100;
+ duration = msecs_to_jiffies(extra_listen_time);
+ wait_for_completion_timeout(&p2p->wait_next_af,
+ duration);
+ }
+ clear_bit(BRCMF_P2P_STATUS_WAITING_NEXT_AF_LISTEN,
+ &p2p->status);
+ }
+ }
+
+ if (p2p->block_gon_req_tx) {
+ /* if ack is true, supplicant will wait more time(100ms).
+ * so we will return it as a success to get more time .
+ */
+ p2p->block_gon_req_tx = false;
+ ack = true;
+ }
+
+ clear_bit(BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME, &p2p->status);
+ /* if all done, turn mpc on again */
+ if (config_af_params.mpc_onoff == 1)
+ brcmf_set_mpc(ifp, 1);
+
+ return ack;
+}
+
+/**
+ * brcmf_p2p_notify_rx_mgmt_p2p_probereq() - Event handler for p2p probe req.
+ *
+ * @ifp: interface pointer for which event was received.
+ * @e: even message.
+ * @data: payload of event message (probe request).
+ */
+s32 brcmf_p2p_notify_rx_mgmt_p2p_probereq(struct brcmf_if *ifp,
+ const struct brcmf_event_msg *e,
+ void *data)
+{
+ struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
+ struct brcmf_p2p_info *p2p = &cfg->p2p;
+ struct afx_hdl *afx_hdl = &p2p->afx_hdl;
+ struct brcmf_cfg80211_vif *vif = ifp->vif;
+ struct brcmf_rx_mgmt_data *rxframe = (struct brcmf_rx_mgmt_data *)data;
+ u16 chanspec = be16_to_cpu(rxframe->chanspec);
+ struct brcmu_chan ch;
+ u8 *mgmt_frame;
+ u32 mgmt_frame_len;
+ s32 freq;
+ u16 mgmt_type;
+
+ brcmf_dbg(INFO, "Enter: event %d reason %d\n", e->event_code,
+ e->reason);
+
+ if (e->datalen < sizeof(*rxframe)) {
+ brcmf_dbg(SCAN, "Event data to small. Ignore\n");
+ return 0;
+ }
+
+ ch.chspec = be16_to_cpu(rxframe->chanspec);
+ cfg->d11inf.decchspec(&ch);
+
+ if (test_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL, &p2p->status) &&
+ (ether_addr_equal(afx_hdl->tx_dst_addr, e->addr))) {
+ afx_hdl->peer_chan = ch.control_ch_num;
+ brcmf_dbg(INFO, "PROBE REQUEST: Peer found, channel=%d\n",
+ afx_hdl->peer_chan);
+ complete(&afx_hdl->act_frm_scan);
+ }
+
+ /* Firmware sends us two proberesponses for each idx one. At the */
+ /* moment anything but bsscfgidx 0 is passed up to supplicant */
+ if (e->bsscfgidx == 0)
+ return 0;
+
+ /* Filter any P2P probe reqs arriving during the GO-NEG Phase */
+ if (test_bit(BRCMF_P2P_STATUS_GO_NEG_PHASE, &p2p->status)) {
+ brcmf_dbg(INFO, "Filtering P2P probe_req in GO-NEG phase\n");
+ return 0;
+ }
+
+ /* Check if wpa_supplicant has registered for this frame */
+ brcmf_dbg(INFO, "vif->mgmt_rx_reg %04x\n", vif->mgmt_rx_reg);
+ mgmt_type = (IEEE80211_STYPE_PROBE_REQ & IEEE80211_FCTL_STYPE) >> 4;
+ if ((vif->mgmt_rx_reg & BIT(mgmt_type)) == 0)
+ return 0;
+
+ mgmt_frame = (u8 *)(rxframe + 1);
+ mgmt_frame_len = e->datalen - sizeof(*rxframe);
+ freq = ieee80211_channel_to_frequency(ch.control_ch_num,
+ ch.band == BRCMU_CHAN_BAND_2G ?
+ NL80211_BAND_2GHZ :
+ NL80211_BAND_5GHZ);
+
+ cfg80211_rx_mgmt(&vif->wdev, freq, 0, mgmt_frame, mgmt_frame_len, 0);
+
+ brcmf_dbg(INFO, "mgmt_frame_len (%d) , e->datalen (%d), chanspec (%04x), freq (%d)\n",
+ mgmt_frame_len, e->datalen, chanspec, freq);
+
+ return 0;
+}
+
+
+/**
+ * brcmf_p2p_get_current_chanspec() - Get current operation channel.
+ *
+ * @p2p: P2P specific data.
+ * @chanspec: chanspec to be returned.
+ */
+static void brcmf_p2p_get_current_chanspec(struct brcmf_p2p_info *p2p,
+ u16 *chanspec)
+{
+ struct brcmf_if *ifp;
+ u8 mac_addr[ETH_ALEN];
+ struct brcmu_chan ch;
+ struct brcmf_bss_info_le *bi;
+ u8 *buf;
+
+ ifp = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp;
+
+ if (brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSSID, mac_addr,
+ ETH_ALEN) == 0) {
+ buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
+ if (buf != NULL) {
+ *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);
+ if (brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO,
+ buf, WL_BSS_INFO_MAX) == 0) {
+ bi = (struct brcmf_bss_info_le *)(buf + 4);
+ *chanspec = le16_to_cpu(bi->chanspec);
+ kfree(buf);
+ return;
+ }
+ kfree(buf);
+ }
+ }
+ /* Use default channel for P2P */
+ ch.chnum = BRCMF_P2P_TEMP_CHAN;
+ ch.bw = BRCMU_CHAN_BW_20;
+ p2p->cfg->d11inf.encchspec(&ch);
+ *chanspec = ch.chspec;
+}
+
+/**
+ * Change a P2P Role.
+ * Parameters:
+ * @mac: MAC address of the BSS to change a role
+ * Returns 0 if success.
+ */
+int brcmf_p2p_ifchange(struct brcmf_cfg80211_info *cfg,
+ enum brcmf_fil_p2p_if_types if_type)
+{
+ struct brcmf_p2p_info *p2p = &cfg->p2p;
+ struct brcmf_cfg80211_vif *vif;
+ struct brcmf_fil_p2p_if_le if_request;
+ s32 err;
+ u16 chanspec;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ vif = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif;
+ if (!vif) {
+ brcmf_err("vif for P2PAPI_BSSCFG_PRIMARY does not exist\n");
+ return -EPERM;
+ }
+ brcmf_notify_escan_complete(cfg, vif->ifp, true, true);
+ vif = p2p->bss_idx[P2PAPI_BSSCFG_CONNECTION].vif;
+ if (!vif) {
+ brcmf_err("vif for P2PAPI_BSSCFG_CONNECTION does not exist\n");
+ return -EPERM;
+ }
+ brcmf_set_mpc(vif->ifp, 0);
+
+ /* In concurrency case, STA may be already associated in a particular */
+ /* channel. so retrieve the current channel of primary interface and */
+ /* then start the virtual interface on that. */
+ brcmf_p2p_get_current_chanspec(p2p, &chanspec);
+
+ if_request.type = cpu_to_le16((u16)if_type);
+ if_request.chspec = cpu_to_le16(chanspec);
+ memcpy(if_request.addr, p2p->int_addr, sizeof(if_request.addr));
+
+ brcmf_cfg80211_arm_vif_event(cfg, vif);
+ err = brcmf_fil_iovar_data_set(vif->ifp, "p2p_ifupd", &if_request,
+ sizeof(if_request));
+ if (err) {
+ brcmf_err("p2p_ifupd FAILED, err=%d\n", err);
+ brcmf_cfg80211_arm_vif_event(cfg, NULL);
+ return err;
+ }
+ err = brcmf_cfg80211_wait_vif_event(cfg, BRCMF_E_IF_CHANGE,
+ BRCMF_VIF_EVENT_TIMEOUT);
+ brcmf_cfg80211_arm_vif_event(cfg, NULL);
+ if (!err) {
+ brcmf_err("No BRCMF_E_IF_CHANGE event received\n");
+ return -EIO;
+ }
+
+ err = brcmf_fil_cmd_int_set(vif->ifp, BRCMF_C_SET_SCB_TIMEOUT,
+ BRCMF_SCB_TIMEOUT_VALUE);
+
+ return err;
+}
+
+static int brcmf_p2p_request_p2p_if(struct brcmf_p2p_info *p2p,
+ struct brcmf_if *ifp, u8 ea[ETH_ALEN],
+ enum brcmf_fil_p2p_if_types iftype)
+{
+ struct brcmf_fil_p2p_if_le if_request;
+ int err;
+ u16 chanspec;
+
+ /* we need a default channel */
+ brcmf_p2p_get_current_chanspec(p2p, &chanspec);
+
+ /* fill the firmware request */
+ memcpy(if_request.addr, ea, ETH_ALEN);
+ if_request.type = cpu_to_le16((u16)iftype);
+ if_request.chspec = cpu_to_le16(chanspec);
+
+ err = brcmf_fil_iovar_data_set(ifp, "p2p_ifadd", &if_request,
+ sizeof(if_request));
+
+ return err;
+}
+
+static int brcmf_p2p_disable_p2p_if(struct brcmf_cfg80211_vif *vif)
+{
+ struct brcmf_cfg80211_info *cfg = wdev_to_cfg(&vif->wdev);
+ struct net_device *pri_ndev = cfg_to_ndev(cfg);
+ struct brcmf_if *ifp = netdev_priv(pri_ndev);
+ u8 *addr = vif->wdev.netdev->dev_addr;
+
+ return brcmf_fil_iovar_data_set(ifp, "p2p_ifdis", addr, ETH_ALEN);
+}
+
+static int brcmf_p2p_release_p2p_if(struct brcmf_cfg80211_vif *vif)
+{
+ struct brcmf_cfg80211_info *cfg = wdev_to_cfg(&vif->wdev);
+ struct net_device *pri_ndev = cfg_to_ndev(cfg);
+ struct brcmf_if *ifp = netdev_priv(pri_ndev);
+ u8 *addr = vif->wdev.netdev->dev_addr;
+
+ return brcmf_fil_iovar_data_set(ifp, "p2p_ifdel", addr, ETH_ALEN);
+}
+
+/**
+ * brcmf_p2p_create_p2pdev() - create a P2P_DEVICE virtual interface.
+ *
+ * @p2p: P2P specific data.
+ * @wiphy: wiphy device of new interface.
+ * @addr: mac address for this new interface.
+ */
+static struct wireless_dev *brcmf_p2p_create_p2pdev(struct brcmf_p2p_info *p2p,
+ struct wiphy *wiphy,
+ u8 *addr)
+{
+ struct brcmf_cfg80211_vif *p2p_vif;
+ struct brcmf_if *p2p_ifp;
+ struct brcmf_if *pri_ifp;
+ int err;
+ u32 bsscfgidx;
+
+ if (p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif)
+ return ERR_PTR(-ENOSPC);
+
+ p2p_vif = brcmf_alloc_vif(p2p->cfg, NL80211_IFTYPE_P2P_DEVICE);
+ if (IS_ERR(p2p_vif)) {
+ brcmf_err("could not create discovery vif\n");
+ return (struct wireless_dev *)p2p_vif;
+ }
+
+ pri_ifp = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp;
+ brcmf_p2p_generate_bss_mac(p2p, addr);
+ brcmf_p2p_set_firmware(pri_ifp, p2p->dev_addr);
+
+ brcmf_cfg80211_arm_vif_event(p2p->cfg, p2p_vif);
+ brcmf_fweh_p2pdev_setup(pri_ifp, true);
+
+ /* Initialize P2P Discovery in the firmware */
+ err = brcmf_fil_iovar_int_set(pri_ifp, "p2p_disc", 1);
+ if (err < 0) {
+ brcmf_err("set p2p_disc error\n");
+ brcmf_fweh_p2pdev_setup(pri_ifp, false);
+ brcmf_cfg80211_arm_vif_event(p2p->cfg, NULL);
+ goto fail;
+ }
+
+ /* wait for firmware event */
+ err = brcmf_cfg80211_wait_vif_event(p2p->cfg, BRCMF_E_IF_ADD,
+ BRCMF_VIF_EVENT_TIMEOUT);
+ brcmf_cfg80211_arm_vif_event(p2p->cfg, NULL);
+ brcmf_fweh_p2pdev_setup(pri_ifp, false);
+ if (!err) {
+ brcmf_err("timeout occurred\n");
+ err = -EIO;
+ goto fail;
+ }
+
+ /* discovery interface created */
+ p2p_ifp = p2p_vif->ifp;
+ p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif = p2p_vif;
+ memcpy(p2p_ifp->mac_addr, p2p->dev_addr, ETH_ALEN);
+ memcpy(&p2p_vif->wdev.address, p2p->dev_addr, sizeof(p2p->dev_addr));
+
+ /* verify bsscfg index for P2P discovery */
+ err = brcmf_fil_iovar_int_get(pri_ifp, "p2p_dev", &bsscfgidx);
+ if (err < 0) {
+ brcmf_err("retrieving discover bsscfg index failed\n");
+ goto fail;
+ }
+
+ WARN_ON(p2p_ifp->bsscfgidx != bsscfgidx);
+
+ init_completion(&p2p->send_af_done);
+ INIT_WORK(&p2p->afx_hdl.afx_work, brcmf_p2p_afx_handler);
+ init_completion(&p2p->afx_hdl.act_frm_scan);
+ init_completion(&p2p->wait_next_af);
+
+ return &p2p_vif->wdev;
+
+fail:
+ brcmf_free_vif(p2p_vif);
+ return ERR_PTR(err);
+}
+
+/**
+ * brcmf_p2p_add_vif() - create a new P2P virtual interface.
+ *
+ * @wiphy: wiphy device of new interface.
+ * @name: name of the new interface.
+ * @name_assign_type: origin of the interface name
+ * @type: nl80211 interface type.
+ * @params: contains mac address for P2P device.
+ */
+struct wireless_dev *brcmf_p2p_add_vif(struct wiphy *wiphy, const char *name,
+ unsigned char name_assign_type,
+ enum nl80211_iftype type,
+ struct vif_params *params)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
+ struct brcmf_cfg80211_vif *vif;
+ enum brcmf_fil_p2p_if_types iftype;
+ int err;
+
+ if (brcmf_cfg80211_vif_event_armed(cfg))
+ return ERR_PTR(-EBUSY);
+
+ brcmf_dbg(INFO, "adding vif \"%s\" (type=%d)\n", name, type);
+
+ switch (type) {
+ case NL80211_IFTYPE_P2P_CLIENT:
+ iftype = BRCMF_FIL_P2P_IF_CLIENT;
+ break;
+ case NL80211_IFTYPE_P2P_GO:
+ iftype = BRCMF_FIL_P2P_IF_GO;
+ break;
+ case NL80211_IFTYPE_P2P_DEVICE:
+ return brcmf_p2p_create_p2pdev(&cfg->p2p, wiphy,
+ params->macaddr);
+ default:
+ return ERR_PTR(-EOPNOTSUPP);
+ }
+
+ vif = brcmf_alloc_vif(cfg, type);
+ if (IS_ERR(vif))
+ return (struct wireless_dev *)vif;
+ brcmf_cfg80211_arm_vif_event(cfg, vif);
+
+ err = brcmf_p2p_request_p2p_if(&cfg->p2p, ifp, cfg->p2p.int_addr,
+ iftype);
+ if (err) {
+ brcmf_cfg80211_arm_vif_event(cfg, NULL);
+ goto fail;
+ }
+
+ /* wait for firmware event */
+ err = brcmf_cfg80211_wait_vif_event(cfg, BRCMF_E_IF_ADD,
+ BRCMF_VIF_EVENT_TIMEOUT);
+ brcmf_cfg80211_arm_vif_event(cfg, NULL);
+ if (!err) {
+ brcmf_err("timeout occurred\n");
+ err = -EIO;
+ goto fail;
+ }
+
+ /* interface created in firmware */
+ ifp = vif->ifp;
+ if (!ifp) {
+ brcmf_err("no if pointer provided\n");
+ err = -ENOENT;
+ goto fail;
+ }
+
+ strncpy(ifp->ndev->name, name, sizeof(ifp->ndev->name) - 1);
+#if LINUX_VERSION_IS_GEQ(3,17,0)
+ ifp->ndev->name_assign_type = name_assign_type;
+#endif /* >= 3.17.0 */
+ err = brcmf_net_attach(ifp, true);
+ if (err) {
+ brcmf_err("Registering netdevice failed\n");
+ goto fail;
+ }
+
+ cfg->p2p.bss_idx[P2PAPI_BSSCFG_CONNECTION].vif = vif;
+ /* Disable firmware roaming for P2P interface */
+ brcmf_fil_iovar_int_set(ifp, "roam_off", 1);
+ if (iftype == BRCMF_FIL_P2P_IF_GO) {
+ /* set station timeout for p2p */
+ brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCB_TIMEOUT,
+ BRCMF_SCB_TIMEOUT_VALUE);
+ }
+ return &ifp->vif->wdev;
+
+fail:
+ brcmf_free_vif(vif);
+ return ERR_PTR(err);
+}
+
+/**
+ * brcmf_p2p_del_vif() - delete a P2P virtual interface.
+ *
+ * @wiphy: wiphy device of interface.
+ * @wdev: wireless device of interface.
+ */
+int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
+ struct brcmf_p2p_info *p2p = &cfg->p2p;
+ struct brcmf_cfg80211_vif *vif;
+ enum nl80211_iftype iftype;
+ bool wait_for_disable = false;
+ int err;
+
+ brcmf_dbg(TRACE, "delete P2P vif\n");
+ vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
+
+ iftype = vif->wdev.iftype;
+ brcmf_cfg80211_arm_vif_event(cfg, vif);
+ switch (iftype) {
+ case NL80211_IFTYPE_P2P_CLIENT:
+ if (test_bit(BRCMF_VIF_STATUS_DISCONNECTING, &vif->sme_state))
+ wait_for_disable = true;
+ break;
+
+ case NL80211_IFTYPE_P2P_GO:
+ if (!brcmf_p2p_disable_p2p_if(vif))
+ wait_for_disable = true;
+ break;
+
+ case NL80211_IFTYPE_P2P_DEVICE:
+ if (!p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif)
+ return 0;
+ brcmf_p2p_cancel_remain_on_channel(vif->ifp);
+ brcmf_p2p_deinit_discovery(p2p);
+ break;
+
+ default:
+ return -ENOTSUPP;
+ }
+
+ clear_bit(BRCMF_P2P_STATUS_GO_NEG_PHASE, &p2p->status);
+ brcmf_dbg(INFO, "P2P: GO_NEG_PHASE status cleared\n");
+
+ if (wait_for_disable)
+ wait_for_completion_timeout(&cfg->vif_disabled,
+ BRCMF_P2P_DISABLE_TIMEOUT);
+
+ err = 0;
+ if (iftype != NL80211_IFTYPE_P2P_DEVICE) {
+ brcmf_vif_clear_mgmt_ies(vif);
+ err = brcmf_p2p_release_p2p_if(vif);
+ }
+ if (!err) {
+ /* wait for firmware event */
+ err = brcmf_cfg80211_wait_vif_event(cfg, BRCMF_E_IF_DEL,
+ BRCMF_VIF_EVENT_TIMEOUT);
+ if (!err)
+ err = -EIO;
+ else
+ err = 0;
+ }
+ brcmf_remove_interface(vif->ifp, true);
+
+ brcmf_cfg80211_arm_vif_event(cfg, NULL);
+ if (iftype != NL80211_IFTYPE_P2P_DEVICE)
+ p2p->bss_idx[P2PAPI_BSSCFG_CONNECTION].vif = NULL;
+
+ return err;
+}
+
+void brcmf_p2p_ifp_removed(struct brcmf_if *ifp, bool rtnl_locked)
+{
+ struct brcmf_cfg80211_info *cfg;
+ struct brcmf_cfg80211_vif *vif;
+
+ brcmf_dbg(INFO, "P2P: device interface removed\n");
+ vif = ifp->vif;
+ cfg = wdev_to_cfg(&vif->wdev);
+ cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif = NULL;
+ if (!rtnl_locked)
+ rtnl_lock();
+ cfg80211_unregister_wdev(&vif->wdev);
+ if (!rtnl_locked)
+ rtnl_unlock();
+ brcmf_free_vif(vif);
+}
+
+int brcmf_p2p_start_device(struct wiphy *wiphy, struct wireless_dev *wdev)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct brcmf_p2p_info *p2p = &cfg->p2p;
+ struct brcmf_cfg80211_vif *vif;
+ int err;
+
+ vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
+ mutex_lock(&cfg->usr_sync);
+ err = brcmf_p2p_enable_discovery(p2p);
+ if (!err)
+ set_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state);
+ mutex_unlock(&cfg->usr_sync);
+ return err;
+}
+
+void brcmf_p2p_stop_device(struct wiphy *wiphy, struct wireless_dev *wdev)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct brcmf_p2p_info *p2p = &cfg->p2p;
+ struct brcmf_cfg80211_vif *vif;
+
+ vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
+ /* This call can be result of the unregister_wdev call. In that case
+ * we dont want to do anything anymore. Just return. The config vif
+ * will have been cleared at this point.
+ */
+ if (p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif == vif) {
+ mutex_lock(&cfg->usr_sync);
+ /* Set the discovery state to SCAN */
+ (void)brcmf_p2p_set_discover_state(vif->ifp,
+ WL_P2P_DISC_ST_SCAN, 0, 0);
+ brcmf_abort_scanning(cfg);
+ clear_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state);
+ mutex_unlock(&cfg->usr_sync);
+ }
+}
+
+/**
+ * brcmf_p2p_attach() - attach for P2P.
+ *
+ * @cfg: driver private data for cfg80211 interface.
+ * @p2pdev_forced: create p2p device interface at attach.
+ */
+s32 brcmf_p2p_attach(struct brcmf_cfg80211_info *cfg, bool p2pdev_forced)
+{
+ struct brcmf_p2p_info *p2p;
+ struct brcmf_if *pri_ifp;
+ s32 err = 0;
+ void *err_ptr;
+
+ p2p = &cfg->p2p;
+ p2p->cfg = cfg;
+
+ pri_ifp = brcmf_get_ifp(cfg->pub, 0);
+ p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif = pri_ifp->vif;
+
+ if (p2pdev_forced) {
+ err_ptr = brcmf_p2p_create_p2pdev(p2p, NULL, NULL);
+ if (IS_ERR(err_ptr)) {
+ brcmf_err("P2P device creation failed.\n");
+ err = PTR_ERR(err_ptr);
+ }
+ } else {
+ p2p->p2pdev_dynamically = true;
+ }
+ return err;
+}
+
+/**
+ * brcmf_p2p_detach() - detach P2P.
+ *
+ * @p2p: P2P specific data.
+ */
+void brcmf_p2p_detach(struct brcmf_p2p_info *p2p)
+{
+ struct brcmf_cfg80211_vif *vif;
+
+ vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
+ if (vif != NULL) {
+ brcmf_p2p_cancel_remain_on_channel(vif->ifp);
+ brcmf_p2p_deinit_discovery(p2p);
+ brcmf_remove_interface(vif->ifp, false);
+ }
+ /* just set it all to zero */
+ memset(p2p, 0, sizeof(*p2p));
+}
+
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h
new file mode 100644
index 0000000..0e8b34d
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 2012 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#ifndef WL_CFGP2P_H_
+#define WL_CFGP2P_H_
+
+#include <net/cfg80211.h>
+
+struct brcmf_cfg80211_info;
+
+/**
+ * enum p2p_bss_type - different type of BSS configurations.
+ *
+ * @P2PAPI_BSSCFG_PRIMARY: maps to driver's primary bsscfg.
+ * @P2PAPI_BSSCFG_DEVICE: maps to driver's P2P device discovery bsscfg.
+ * @P2PAPI_BSSCFG_CONNECTION: maps to driver's P2P connection bsscfg.
+ * @P2PAPI_BSSCFG_MAX: used for range checking.
+ */
+enum p2p_bss_type {
+ P2PAPI_BSSCFG_PRIMARY, /* maps to driver's primary bsscfg */
+ P2PAPI_BSSCFG_DEVICE, /* maps to driver's P2P device discovery bsscfg */
+ P2PAPI_BSSCFG_CONNECTION, /* maps to driver's P2P connection bsscfg */
+ P2PAPI_BSSCFG_MAX
+};
+
+/**
+ * struct p2p_bss - peer-to-peer bss related information.
+ *
+ * @vif: virtual interface of this P2P bss.
+ * @private_data: TBD
+ */
+struct p2p_bss {
+ struct brcmf_cfg80211_vif *vif;
+ void *private_data;
+};
+
+/**
+ * enum brcmf_p2p_status - P2P specific dongle status.
+ *
+ * @BRCMF_P2P_STATUS_IF_ADD: peer-to-peer vif add sent to dongle.
+ * @BRCMF_P2P_STATUS_IF_DEL: NOT-USED?
+ * @BRCMF_P2P_STATUS_IF_DELETING: peer-to-peer vif delete sent to dongle.
+ * @BRCMF_P2P_STATUS_IF_CHANGING: peer-to-peer vif change sent to dongle.
+ * @BRCMF_P2P_STATUS_IF_CHANGED: peer-to-peer vif change completed on dongle.
+ * @BRCMF_P2P_STATUS_ACTION_TX_COMPLETED: action frame tx completed.
+ * @BRCMF_P2P_STATUS_ACTION_TX_NOACK: action frame tx not acked.
+ * @BRCMF_P2P_STATUS_GO_NEG_PHASE: P2P GO negotiation ongoing.
+ * @BRCMF_P2P_STATUS_DISCOVER_LISTEN: P2P listen, remaining on channel.
+ * @BRCMF_P2P_STATUS_SENDING_ACT_FRAME: In the process of sending action frame.
+ * @BRCMF_P2P_STATUS_WAITING_NEXT_AF_LISTEN: extra listen time for af tx.
+ * @BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME: waiting for action frame response.
+ * @BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL: search channel for AF active.
+ */
+enum brcmf_p2p_status {
+ BRCMF_P2P_STATUS_ENABLED,
+ BRCMF_P2P_STATUS_IF_ADD,
+ BRCMF_P2P_STATUS_IF_DEL,
+ BRCMF_P2P_STATUS_IF_DELETING,
+ BRCMF_P2P_STATUS_IF_CHANGING,
+ BRCMF_P2P_STATUS_IF_CHANGED,
+ BRCMF_P2P_STATUS_ACTION_TX_COMPLETED,
+ BRCMF_P2P_STATUS_ACTION_TX_NOACK,
+ BRCMF_P2P_STATUS_GO_NEG_PHASE,
+ BRCMF_P2P_STATUS_DISCOVER_LISTEN,
+ BRCMF_P2P_STATUS_SENDING_ACT_FRAME,
+ BRCMF_P2P_STATUS_WAITING_NEXT_AF_LISTEN,
+ BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME,
+ BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL
+};
+
+/**
+ * struct afx_hdl - action frame off channel storage.
+ *
+ * @afx_work: worker thread for searching channel
+ * @act_frm_scan: thread synchronizing struct.
+ * @is_active: channel searching active.
+ * @peer_chan: current channel.
+ * @is_listen: sets mode for afx worker.
+ * @my_listen_chan: this peers listen channel.
+ * @peer_listen_chan: remote peers listen channel.
+ * @tx_dst_addr: mac address where tx af should be sent to.
+ */
+struct afx_hdl {
+ struct work_struct afx_work;
+ struct completion act_frm_scan;
+ bool is_active;
+ s32 peer_chan;
+ bool is_listen;
+ u16 my_listen_chan;
+ u16 peer_listen_chan;
+ u8 tx_dst_addr[ETH_ALEN];
+};
+
+/**
+ * struct brcmf_p2p_info - p2p specific driver information.
+ *
+ * @cfg: driver private data for cfg80211 interface.
+ * @status: status of P2P (see enum brcmf_p2p_status).
+ * @dev_addr: P2P device address.
+ * @int_addr: P2P interface address.
+ * @bss_idx: informate for P2P bss types.
+ * @listen_timer: timer for @WL_P2P_DISC_ST_LISTEN discover state.
+ * @listen_channel: channel for @WL_P2P_DISC_ST_LISTEN discover state.
+ * @remain_on_channel: contains copy of struct used by cfg80211.
+ * @remain_on_channel_cookie: cookie counter for remain on channel cmd
+ * @next_af_subtype: expected action frame subtype.
+ * @send_af_done: indication that action frame tx is complete.
+ * @afx_hdl: action frame search handler info.
+ * @af_sent_channel: channel action frame is sent.
+ * @af_tx_sent_jiffies: jiffies time when af tx was transmitted.
+ * @wait_next_af: thread synchronizing struct.
+ * @gon_req_action: about to send go negotiation requets frame.
+ * @block_gon_req_tx: drop tx go negotiation requets frame.
+ * @p2pdev_dynamically: is p2p device if created by module param or supplicant.
+ */
+struct brcmf_p2p_info {
+ struct brcmf_cfg80211_info *cfg;
+ unsigned long status;
+ u8 dev_addr[ETH_ALEN];
+ u8 int_addr[ETH_ALEN];
+ struct p2p_bss bss_idx[P2PAPI_BSSCFG_MAX];
+ struct timer_list listen_timer;
+ u8 listen_channel;
+ struct ieee80211_channel remain_on_channel;
+ u32 remain_on_channel_cookie;
+ u8 next_af_subtype;
+ struct completion send_af_done;
+ struct afx_hdl afx_hdl;
+ u32 af_sent_channel;
+ unsigned long af_tx_sent_jiffies;
+ struct completion wait_next_af;
+ bool gon_req_action;
+ bool block_gon_req_tx;
+ bool p2pdev_dynamically;
+};
+
+s32 brcmf_p2p_attach(struct brcmf_cfg80211_info *cfg, bool p2pdev_forced);
+void brcmf_p2p_detach(struct brcmf_p2p_info *p2p);
+struct wireless_dev *brcmf_p2p_add_vif(struct wiphy *wiphy, const char *name,
+ unsigned char name_assign_type,
+ enum nl80211_iftype type,
+ struct vif_params *params);
+int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev);
+int brcmf_p2p_ifchange(struct brcmf_cfg80211_info *cfg,
+ enum brcmf_fil_p2p_if_types if_type);
+void brcmf_p2p_ifp_removed(struct brcmf_if *ifp, bool rtnl_locked);
+int brcmf_p2p_start_device(struct wiphy *wiphy, struct wireless_dev *wdev);
+void brcmf_p2p_stop_device(struct wiphy *wiphy, struct wireless_dev *wdev);
+int brcmf_p2p_scan_prep(struct wiphy *wiphy,
+ struct cfg80211_scan_request *request,
+ struct brcmf_cfg80211_vif *vif);
+int brcmf_p2p_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev,
+ struct ieee80211_channel *channel,
+ unsigned int duration, u64 *cookie);
+int brcmf_p2p_notify_listen_complete(struct brcmf_if *ifp,
+ const struct brcmf_event_msg *e,
+ void *data);
+void brcmf_p2p_cancel_remain_on_channel(struct brcmf_if *ifp);
+int brcmf_p2p_notify_action_frame_rx(struct brcmf_if *ifp,
+ const struct brcmf_event_msg *e,
+ void *data);
+int brcmf_p2p_notify_action_tx_complete(struct brcmf_if *ifp,
+ const struct brcmf_event_msg *e,
+ void *data);
+bool brcmf_p2p_send_action_frame(struct brcmf_cfg80211_info *cfg,
+ struct net_device *ndev,
+ struct brcmf_fil_af_params_le *af_params);
+bool brcmf_p2p_scan_finding_common_channel(struct brcmf_cfg80211_info *cfg,
+ struct brcmf_bss_info_le *bi);
+s32 brcmf_p2p_notify_rx_mgmt_p2p_probereq(struct brcmf_if *ifp,
+ const struct brcmf_event_msg *e,
+ void *data);
+#endif /* WL_CFGP2P_H_ */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
new file mode 100644
index 0000000..b370ee7
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
@@ -0,0 +1,2026 @@
+/* Copyright (c) 2014 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/firmware.h>
+#include <linux/pci.h>
+#include <linux/vmalloc.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/bcma/bcma.h>
+#include <linux/sched.h>
+#include <asm/unaligned.h>
+
+#include <soc.h>
+#include <chipcommon.h>
+#include <brcmu_utils.h>
+#include <brcmu_wifi.h>
+#include <brcm_hw_ids.h>
+
+#include "debug.h"
+#include "bus.h"
+#include "commonring.h"
+#include "msgbuf.h"
+#include "pcie.h"
+#include "firmware.h"
+#include "chip.h"
+#include "core.h"
+#include "common.h"
+
+
+enum brcmf_pcie_state {
+ BRCMFMAC_PCIE_STATE_DOWN,
+ BRCMFMAC_PCIE_STATE_UP
+};
+
+BRCMF_FW_NVRAM_DEF(43602, "brcmfmac43602-pcie.bin", "brcmfmac43602-pcie.txt");
+BRCMF_FW_NVRAM_DEF(4350, "brcmfmac4350-pcie.bin", "brcmfmac4350-pcie.txt");
+BRCMF_FW_NVRAM_DEF(4350C, "brcmfmac4350c2-pcie.bin", "brcmfmac4350c2-pcie.txt");
+BRCMF_FW_NVRAM_DEF(4356, "brcmfmac4356-pcie.bin", "brcmfmac4356-pcie.txt");
+BRCMF_FW_NVRAM_DEF(43570, "brcmfmac43570-pcie.bin", "brcmfmac43570-pcie.txt");
+BRCMF_FW_NVRAM_DEF(4358, "brcmfmac4358-pcie.bin", "brcmfmac4358-pcie.txt");
+BRCMF_FW_NVRAM_DEF(4359, "brcmfmac4359-pcie.bin", "brcmfmac4359-pcie.txt");
+BRCMF_FW_NVRAM_DEF(4365B, "brcmfmac4365b-pcie.bin", "brcmfmac4365b-pcie.txt");
+BRCMF_FW_NVRAM_DEF(4365C, "brcmfmac4365c-pcie.bin", "brcmfmac4365c-pcie.txt");
+BRCMF_FW_NVRAM_DEF(4366B, "brcmfmac4366b-pcie.bin", "brcmfmac4366b-pcie.txt");
+BRCMF_FW_NVRAM_DEF(4366C, "brcmfmac4366c-pcie.bin", "brcmfmac4366c-pcie.txt");
+BRCMF_FW_NVRAM_DEF(4371, "brcmfmac4371-pcie.bin", "brcmfmac4371-pcie.txt");
+
+static struct brcmf_firmware_mapping brcmf_pcie_fwnames[] = {
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43602_CHIP_ID, 0xFFFFFFFF, 43602),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43465_CHIP_ID, 0xFFFFFFF0, 4366C),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4350_CHIP_ID, 0x000000FF, 4350C),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4350_CHIP_ID, 0xFFFFFF00, 4350),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43525_CHIP_ID, 0xFFFFFFF0, 4365C),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4356_CHIP_ID, 0xFFFFFFFF, 4356),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43567_CHIP_ID, 0xFFFFFFFF, 43570),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43569_CHIP_ID, 0xFFFFFFFF, 43570),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43570_CHIP_ID, 0xFFFFFFFF, 43570),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4358_CHIP_ID, 0xFFFFFFFF, 4358),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4359_CHIP_ID, 0xFFFFFFFF, 4359),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4365_CHIP_ID, 0x0000000F, 4365B),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4365_CHIP_ID, 0xFFFFFFF0, 4365C),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4366_CHIP_ID, 0x0000000F, 4366B),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4366_CHIP_ID, 0xFFFFFFF0, 4366C),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4371_CHIP_ID, 0xFFFFFFFF, 4371),
+};
+
+#define BRCMF_PCIE_FW_UP_TIMEOUT 2000 /* msec */
+
+#define BRCMF_PCIE_REG_MAP_SIZE (32 * 1024)
+
+/* backplane addres space accessed by BAR0 */
+#define BRCMF_PCIE_BAR0_WINDOW 0x80
+#define BRCMF_PCIE_BAR0_REG_SIZE 0x1000
+#define BRCMF_PCIE_BAR0_WRAPPERBASE 0x70
+
+#define BRCMF_PCIE_BAR0_WRAPBASE_DMP_OFFSET 0x1000
+#define BRCMF_PCIE_BARO_PCIE_ENUM_OFFSET 0x2000
+
+#define BRCMF_PCIE_ARMCR4REG_BANKIDX 0x40
+#define BRCMF_PCIE_ARMCR4REG_BANKPDA 0x4C
+
+#define BRCMF_PCIE_REG_INTSTATUS 0x90
+#define BRCMF_PCIE_REG_INTMASK 0x94
+#define BRCMF_PCIE_REG_SBMBX 0x98
+
+#define BRCMF_PCIE_REG_LINK_STATUS_CTRL 0xBC
+
+#define BRCMF_PCIE_PCIE2REG_INTMASK 0x24
+#define BRCMF_PCIE_PCIE2REG_MAILBOXINT 0x48
+#define BRCMF_PCIE_PCIE2REG_MAILBOXMASK 0x4C
+#define BRCMF_PCIE_PCIE2REG_CONFIGADDR 0x120
+#define BRCMF_PCIE_PCIE2REG_CONFIGDATA 0x124
+#define BRCMF_PCIE_PCIE2REG_H2D_MAILBOX 0x140
+
+#define BRCMF_PCIE2_INTA 0x01
+#define BRCMF_PCIE2_INTB 0x02
+
+#define BRCMF_PCIE_INT_0 0x01
+#define BRCMF_PCIE_INT_1 0x02
+#define BRCMF_PCIE_INT_DEF (BRCMF_PCIE_INT_0 | \
+ BRCMF_PCIE_INT_1)
+
+#define BRCMF_PCIE_MB_INT_FN0_0 0x0100
+#define BRCMF_PCIE_MB_INT_FN0_1 0x0200
+#define BRCMF_PCIE_MB_INT_D2H0_DB0 0x10000
+#define BRCMF_PCIE_MB_INT_D2H0_DB1 0x20000
+#define BRCMF_PCIE_MB_INT_D2H1_DB0 0x40000
+#define BRCMF_PCIE_MB_INT_D2H1_DB1 0x80000
+#define BRCMF_PCIE_MB_INT_D2H2_DB0 0x100000
+#define BRCMF_PCIE_MB_INT_D2H2_DB1 0x200000
+#define BRCMF_PCIE_MB_INT_D2H3_DB0 0x400000
+#define BRCMF_PCIE_MB_INT_D2H3_DB1 0x800000
+
+#define BRCMF_PCIE_MB_INT_D2H_DB (BRCMF_PCIE_MB_INT_D2H0_DB0 | \
+ BRCMF_PCIE_MB_INT_D2H0_DB1 | \
+ BRCMF_PCIE_MB_INT_D2H1_DB0 | \
+ BRCMF_PCIE_MB_INT_D2H1_DB1 | \
+ BRCMF_PCIE_MB_INT_D2H2_DB0 | \
+ BRCMF_PCIE_MB_INT_D2H2_DB1 | \
+ BRCMF_PCIE_MB_INT_D2H3_DB0 | \
+ BRCMF_PCIE_MB_INT_D2H3_DB1)
+
+#define BRCMF_PCIE_MIN_SHARED_VERSION 5
+#define BRCMF_PCIE_MAX_SHARED_VERSION 6
+#define BRCMF_PCIE_SHARED_VERSION_MASK 0x00FF
+#define BRCMF_PCIE_SHARED_DMA_INDEX 0x10000
+#define BRCMF_PCIE_SHARED_DMA_2B_IDX 0x100000
+
+#define BRCMF_PCIE_FLAGS_HTOD_SPLIT 0x4000
+#define BRCMF_PCIE_FLAGS_DTOH_SPLIT 0x8000
+
+#define BRCMF_SHARED_MAX_RXBUFPOST_OFFSET 34
+#define BRCMF_SHARED_RING_BASE_OFFSET 52
+#define BRCMF_SHARED_RX_DATAOFFSET_OFFSET 36
+#define BRCMF_SHARED_CONSOLE_ADDR_OFFSET 20
+#define BRCMF_SHARED_HTOD_MB_DATA_ADDR_OFFSET 40
+#define BRCMF_SHARED_DTOH_MB_DATA_ADDR_OFFSET 44
+#define BRCMF_SHARED_RING_INFO_ADDR_OFFSET 48
+#define BRCMF_SHARED_DMA_SCRATCH_LEN_OFFSET 52
+#define BRCMF_SHARED_DMA_SCRATCH_ADDR_OFFSET 56
+#define BRCMF_SHARED_DMA_RINGUPD_LEN_OFFSET 64
+#define BRCMF_SHARED_DMA_RINGUPD_ADDR_OFFSET 68
+
+#define BRCMF_RING_H2D_RING_COUNT_OFFSET 0
+#define BRCMF_RING_D2H_RING_COUNT_OFFSET 1
+#define BRCMF_RING_H2D_RING_MEM_OFFSET 4
+#define BRCMF_RING_H2D_RING_STATE_OFFSET 8
+
+#define BRCMF_RING_MEM_BASE_ADDR_OFFSET 8
+#define BRCMF_RING_MAX_ITEM_OFFSET 4
+#define BRCMF_RING_LEN_ITEMS_OFFSET 6
+#define BRCMF_RING_MEM_SZ 16
+#define BRCMF_RING_STATE_SZ 8
+
+#define BRCMF_DEF_MAX_RXBUFPOST 255
+
+#define BRCMF_CONSOLE_BUFADDR_OFFSET 8
+#define BRCMF_CONSOLE_BUFSIZE_OFFSET 12
+#define BRCMF_CONSOLE_WRITEIDX_OFFSET 16
+
+#define BRCMF_DMA_D2H_SCRATCH_BUF_LEN 8
+#define BRCMF_DMA_D2H_RINGUPD_BUF_LEN 1024
+
+#define BRCMF_D2H_DEV_D3_ACK 0x00000001
+#define BRCMF_D2H_DEV_DS_ENTER_REQ 0x00000002
+#define BRCMF_D2H_DEV_DS_EXIT_NOTE 0x00000004
+
+#define BRCMF_H2D_HOST_D3_INFORM 0x00000001
+#define BRCMF_H2D_HOST_DS_ACK 0x00000002
+#define BRCMF_H2D_HOST_D0_INFORM_IN_USE 0x00000008
+#define BRCMF_H2D_HOST_D0_INFORM 0x00000010
+
+#define BRCMF_PCIE_MBDATA_TIMEOUT msecs_to_jiffies(2000)
+
+#define BRCMF_PCIE_CFGREG_STATUS_CMD 0x4
+#define BRCMF_PCIE_CFGREG_PM_CSR 0x4C
+#define BRCMF_PCIE_CFGREG_MSI_CAP 0x58
+#define BRCMF_PCIE_CFGREG_MSI_ADDR_L 0x5C
+#define BRCMF_PCIE_CFGREG_MSI_ADDR_H 0x60
+#define BRCMF_PCIE_CFGREG_MSI_DATA 0x64
+#define BRCMF_PCIE_CFGREG_LINK_STATUS_CTRL 0xBC
+#define BRCMF_PCIE_CFGREG_LINK_STATUS_CTRL2 0xDC
+#define BRCMF_PCIE_CFGREG_RBAR_CTRL 0x228
+#define BRCMF_PCIE_CFGREG_PML1_SUB_CTRL1 0x248
+#define BRCMF_PCIE_CFGREG_REG_BAR2_CONFIG 0x4E0
+#define BRCMF_PCIE_CFGREG_REG_BAR3_CONFIG 0x4F4
+#define BRCMF_PCIE_LINK_STATUS_CTRL_ASPM_ENAB 3
+
+/* Magic number at a magic location to find RAM size */
+#define BRCMF_RAMSIZE_MAGIC 0x534d4152 /* SMAR */
+#define BRCMF_RAMSIZE_OFFSET 0x6c
+
+
+struct brcmf_pcie_console {
+ u32 base_addr;
+ u32 buf_addr;
+ u32 bufsize;
+ u32 read_idx;
+ u8 log_str[256];
+ u8 log_idx;
+};
+
+struct brcmf_pcie_shared_info {
+ u32 tcm_base_address;
+ u32 flags;
+ struct brcmf_pcie_ringbuf *commonrings[BRCMF_NROF_COMMON_MSGRINGS];
+ struct brcmf_pcie_ringbuf *flowrings;
+ u16 max_rxbufpost;
+ u16 max_flowrings;
+ u16 max_submissionrings;
+ u16 max_completionrings;
+ u32 rx_dataoffset;
+ u32 htod_mb_data_addr;
+ u32 dtoh_mb_data_addr;
+ u32 ring_info_addr;
+ struct brcmf_pcie_console console;
+ void *scratch;
+ dma_addr_t scratch_dmahandle;
+ void *ringupd;
+ dma_addr_t ringupd_dmahandle;
+ u8 version;
+};
+
+struct brcmf_pcie_core_info {
+ u32 base;
+ u32 wrapbase;
+};
+
+struct brcmf_pciedev_info {
+ enum brcmf_pcie_state state;
+ bool in_irq;
+ struct pci_dev *pdev;
+ char fw_name[BRCMF_FW_NAME_LEN];
+ char nvram_name[BRCMF_FW_NAME_LEN];
+ void __iomem *regs;
+ void __iomem *tcm;
+ u32 ram_base;
+ u32 ram_size;
+ struct brcmf_chip *ci;
+ u32 coreid;
+ struct brcmf_pcie_shared_info shared;
+ wait_queue_head_t mbdata_resp_wait;
+ bool mbdata_completed;
+ bool irq_allocated;
+ bool wowl_enabled;
+ u8 dma_idx_sz;
+ void *idxbuf;
+ u32 idxbuf_sz;
+ dma_addr_t idxbuf_dmahandle;
+ u16 (*read_ptr)(struct brcmf_pciedev_info *devinfo, u32 mem_offset);
+ void (*write_ptr)(struct brcmf_pciedev_info *devinfo, u32 mem_offset,
+ u16 value);
+ struct brcmf_mp_device *settings;
+};
+
+struct brcmf_pcie_ringbuf {
+ struct brcmf_commonring commonring;
+ dma_addr_t dma_handle;
+ u32 w_idx_addr;
+ u32 r_idx_addr;
+ struct brcmf_pciedev_info *devinfo;
+ u8 id;
+};
+
+/**
+ * struct brcmf_pcie_dhi_ringinfo - dongle/host interface shared ring info
+ *
+ * @ringmem: dongle memory pointer to ring memory location
+ * @h2d_w_idx_ptr: h2d ring write indices dongle memory pointers
+ * @h2d_r_idx_ptr: h2d ring read indices dongle memory pointers
+ * @d2h_w_idx_ptr: d2h ring write indices dongle memory pointers
+ * @d2h_r_idx_ptr: d2h ring read indices dongle memory pointers
+ * @h2d_w_idx_hostaddr: h2d ring write indices host memory pointers
+ * @h2d_r_idx_hostaddr: h2d ring read indices host memory pointers
+ * @d2h_w_idx_hostaddr: d2h ring write indices host memory pointers
+ * @d2h_r_idx_hostaddr: d2h ring reaD indices host memory pointers
+ * @max_flowrings: maximum number of tx flow rings supported.
+ * @max_submissionrings: maximum number of submission rings(h2d) supported.
+ * @max_completionrings: maximum number of completion rings(d2h) supported.
+ */
+struct brcmf_pcie_dhi_ringinfo {
+ __le32 ringmem;
+ __le32 h2d_w_idx_ptr;
+ __le32 h2d_r_idx_ptr;
+ __le32 d2h_w_idx_ptr;
+ __le32 d2h_r_idx_ptr;
+ struct msgbuf_buf_addr h2d_w_idx_hostaddr;
+ struct msgbuf_buf_addr h2d_r_idx_hostaddr;
+ struct msgbuf_buf_addr d2h_w_idx_hostaddr;
+ struct msgbuf_buf_addr d2h_r_idx_hostaddr;
+ __le16 max_flowrings;
+ __le16 max_submissionrings;
+ __le16 max_completionrings;
+};
+
+static const u32 brcmf_ring_max_item[BRCMF_NROF_COMMON_MSGRINGS] = {
+ BRCMF_H2D_MSGRING_CONTROL_SUBMIT_MAX_ITEM,
+ BRCMF_H2D_MSGRING_RXPOST_SUBMIT_MAX_ITEM,
+ BRCMF_D2H_MSGRING_CONTROL_COMPLETE_MAX_ITEM,
+ BRCMF_D2H_MSGRING_TX_COMPLETE_MAX_ITEM,
+ BRCMF_D2H_MSGRING_RX_COMPLETE_MAX_ITEM
+};
+
+static const u32 brcmf_ring_itemsize[BRCMF_NROF_COMMON_MSGRINGS] = {
+ BRCMF_H2D_MSGRING_CONTROL_SUBMIT_ITEMSIZE,
+ BRCMF_H2D_MSGRING_RXPOST_SUBMIT_ITEMSIZE,
+ BRCMF_D2H_MSGRING_CONTROL_COMPLETE_ITEMSIZE,
+ BRCMF_D2H_MSGRING_TX_COMPLETE_ITEMSIZE,
+ BRCMF_D2H_MSGRING_RX_COMPLETE_ITEMSIZE
+};
+
+
+static u32
+brcmf_pcie_read_reg32(struct brcmf_pciedev_info *devinfo, u32 reg_offset)
+{
+ void __iomem *address = devinfo->regs + reg_offset;
+
+ return (ioread32(address));
+}
+
+
+static void
+brcmf_pcie_write_reg32(struct brcmf_pciedev_info *devinfo, u32 reg_offset,
+ u32 value)
+{
+ void __iomem *address = devinfo->regs + reg_offset;
+
+ iowrite32(value, address);
+}
+
+
+static u8
+brcmf_pcie_read_tcm8(struct brcmf_pciedev_info *devinfo, u32 mem_offset)
+{
+ void __iomem *address = devinfo->tcm + mem_offset;
+
+ return (ioread8(address));
+}
+
+
+static u16
+brcmf_pcie_read_tcm16(struct brcmf_pciedev_info *devinfo, u32 mem_offset)
+{
+ void __iomem *address = devinfo->tcm + mem_offset;
+
+ return (ioread16(address));
+}
+
+
+static void
+brcmf_pcie_write_tcm16(struct brcmf_pciedev_info *devinfo, u32 mem_offset,
+ u16 value)
+{
+ void __iomem *address = devinfo->tcm + mem_offset;
+
+ iowrite16(value, address);
+}
+
+
+static u16
+brcmf_pcie_read_idx(struct brcmf_pciedev_info *devinfo, u32 mem_offset)
+{
+ u16 *address = devinfo->idxbuf + mem_offset;
+
+ return (*(address));
+}
+
+
+static void
+brcmf_pcie_write_idx(struct brcmf_pciedev_info *devinfo, u32 mem_offset,
+ u16 value)
+{
+ u16 *address = devinfo->idxbuf + mem_offset;
+
+ *(address) = value;
+}
+
+
+static u32
+brcmf_pcie_read_tcm32(struct brcmf_pciedev_info *devinfo, u32 mem_offset)
+{
+ void __iomem *address = devinfo->tcm + mem_offset;
+
+ return (ioread32(address));
+}
+
+
+static void
+brcmf_pcie_write_tcm32(struct brcmf_pciedev_info *devinfo, u32 mem_offset,
+ u32 value)
+{
+ void __iomem *address = devinfo->tcm + mem_offset;
+
+ iowrite32(value, address);
+}
+
+
+static u32
+brcmf_pcie_read_ram32(struct brcmf_pciedev_info *devinfo, u32 mem_offset)
+{
+ void __iomem *addr = devinfo->tcm + devinfo->ci->rambase + mem_offset;
+
+ return (ioread32(addr));
+}
+
+
+static void
+brcmf_pcie_write_ram32(struct brcmf_pciedev_info *devinfo, u32 mem_offset,
+ u32 value)
+{
+ void __iomem *addr = devinfo->tcm + devinfo->ci->rambase + mem_offset;
+
+ iowrite32(value, addr);
+}
+
+
+static void
+brcmf_pcie_copy_mem_todev(struct brcmf_pciedev_info *devinfo, u32 mem_offset,
+ void *srcaddr, u32 len)
+{
+ void __iomem *address = devinfo->tcm + mem_offset;
+ __le32 *src32;
+ __le16 *src16;
+ u8 *src8;
+
+ if (((ulong)address & 4) || ((ulong)srcaddr & 4) || (len & 4)) {
+ if (((ulong)address & 2) || ((ulong)srcaddr & 2) || (len & 2)) {
+ src8 = (u8 *)srcaddr;
+ while (len) {
+ iowrite8(*src8, address);
+ address++;
+ src8++;
+ len--;
+ }
+ } else {
+ len = len / 2;
+ src16 = (__le16 *)srcaddr;
+ while (len) {
+ iowrite16(le16_to_cpu(*src16), address);
+ address += 2;
+ src16++;
+ len--;
+ }
+ }
+ } else {
+ len = len / 4;
+ src32 = (__le32 *)srcaddr;
+ while (len) {
+ iowrite32(le32_to_cpu(*src32), address);
+ address += 4;
+ src32++;
+ len--;
+ }
+ }
+}
+
+
+static void
+brcmf_pcie_copy_dev_tomem(struct brcmf_pciedev_info *devinfo, u32 mem_offset,
+ void *dstaddr, u32 len)
+{
+ void __iomem *address = devinfo->tcm + mem_offset;
+ __le32 *dst32;
+ __le16 *dst16;
+ u8 *dst8;
+
+ if (((ulong)address & 4) || ((ulong)dstaddr & 4) || (len & 4)) {
+ if (((ulong)address & 2) || ((ulong)dstaddr & 2) || (len & 2)) {
+ dst8 = (u8 *)dstaddr;
+ while (len) {
+ *dst8 = ioread8(address);
+ address++;
+ dst8++;
+ len--;
+ }
+ } else {
+ len = len / 2;
+ dst16 = (__le16 *)dstaddr;
+ while (len) {
+ *dst16 = cpu_to_le16(ioread16(address));
+ address += 2;
+ dst16++;
+ len--;
+ }
+ }
+ } else {
+ len = len / 4;
+ dst32 = (__le32 *)dstaddr;
+ while (len) {
+ *dst32 = cpu_to_le32(ioread32(address));
+ address += 4;
+ dst32++;
+ len--;
+ }
+ }
+}
+
+
+#define WRITECC32(devinfo, reg, value) brcmf_pcie_write_reg32(devinfo, \
+ CHIPCREGOFFS(reg), value)
+
+
+static void
+brcmf_pcie_select_core(struct brcmf_pciedev_info *devinfo, u16 coreid)
+{
+ const struct pci_dev *pdev = devinfo->pdev;
+ struct brcmf_core *core;
+ u32 bar0_win;
+
+ core = brcmf_chip_get_core(devinfo->ci, coreid);
+ if (core) {
+ bar0_win = core->base;
+ pci_write_config_dword(pdev, BRCMF_PCIE_BAR0_WINDOW, bar0_win);
+ if (pci_read_config_dword(pdev, BRCMF_PCIE_BAR0_WINDOW,
+ &bar0_win) == 0) {
+ if (bar0_win != core->base) {
+ bar0_win = core->base;
+ pci_write_config_dword(pdev,
+ BRCMF_PCIE_BAR0_WINDOW,
+ bar0_win);
+ }
+ }
+ } else {
+ brcmf_err("Unsupported core selected %x\n", coreid);
+ }
+}
+
+
+static void brcmf_pcie_reset_device(struct brcmf_pciedev_info *devinfo)
+{
+ struct brcmf_core *core;
+ u16 cfg_offset[] = { BRCMF_PCIE_CFGREG_STATUS_CMD,
+ BRCMF_PCIE_CFGREG_PM_CSR,
+ BRCMF_PCIE_CFGREG_MSI_CAP,
+ BRCMF_PCIE_CFGREG_MSI_ADDR_L,
+ BRCMF_PCIE_CFGREG_MSI_ADDR_H,
+ BRCMF_PCIE_CFGREG_MSI_DATA,
+ BRCMF_PCIE_CFGREG_LINK_STATUS_CTRL2,
+ BRCMF_PCIE_CFGREG_RBAR_CTRL,
+ BRCMF_PCIE_CFGREG_PML1_SUB_CTRL1,
+ BRCMF_PCIE_CFGREG_REG_BAR2_CONFIG,
+ BRCMF_PCIE_CFGREG_REG_BAR3_CONFIG };
+ u32 i;
+ u32 val;
+ u32 lsc;
+
+ if (!devinfo->ci)
+ return;
+
+ /* Disable ASPM */
+ brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2);
+ pci_read_config_dword(devinfo->pdev, BRCMF_PCIE_REG_LINK_STATUS_CTRL,
+ &lsc);
+ val = lsc & (~BRCMF_PCIE_LINK_STATUS_CTRL_ASPM_ENAB);
+ pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_LINK_STATUS_CTRL,
+ val);
+
+ /* Watchdog reset */
+ brcmf_pcie_select_core(devinfo, BCMA_CORE_CHIPCOMMON);
+ WRITECC32(devinfo, watchdog, 4);
+ msleep(100);
+
+ /* Restore ASPM */
+ brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2);
+ pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_LINK_STATUS_CTRL,
+ lsc);
+
+ core = brcmf_chip_get_core(devinfo->ci, BCMA_CORE_PCIE2);
+ if (core->rev <= 13) {
+ for (i = 0; i < ARRAY_SIZE(cfg_offset); i++) {
+ brcmf_pcie_write_reg32(devinfo,
+ BRCMF_PCIE_PCIE2REG_CONFIGADDR,
+ cfg_offset[i]);
+ val = brcmf_pcie_read_reg32(devinfo,
+ BRCMF_PCIE_PCIE2REG_CONFIGDATA);
+ brcmf_dbg(PCIE, "config offset 0x%04x, value 0x%04x\n",
+ cfg_offset[i], val);
+ brcmf_pcie_write_reg32(devinfo,
+ BRCMF_PCIE_PCIE2REG_CONFIGDATA,
+ val);
+ }
+ }
+}
+
+
+static void brcmf_pcie_attach(struct brcmf_pciedev_info *devinfo)
+{
+ u32 config;
+
+ /* BAR1 window may not be sized properly */
+ brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2);
+ brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGADDR, 0x4e0);
+ config = brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGDATA);
+ brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGDATA, config);
+
+ device_wakeup_enable(&devinfo->pdev->dev);
+}
+
+
+static int brcmf_pcie_enter_download_state(struct brcmf_pciedev_info *devinfo)
+{
+ if (devinfo->ci->chip == BRCM_CC_43602_CHIP_ID) {
+ brcmf_pcie_select_core(devinfo, BCMA_CORE_ARM_CR4);
+ brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_ARMCR4REG_BANKIDX,
+ 5);
+ brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_ARMCR4REG_BANKPDA,
+ 0);
+ brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_ARMCR4REG_BANKIDX,
+ 7);
+ brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_ARMCR4REG_BANKPDA,
+ 0);
+ }
+ return 0;
+}
+
+
+static int brcmf_pcie_exit_download_state(struct brcmf_pciedev_info *devinfo,
+ u32 resetintr)
+{
+ struct brcmf_core *core;
+
+ if (devinfo->ci->chip == BRCM_CC_43602_CHIP_ID) {
+ core = brcmf_chip_get_core(devinfo->ci, BCMA_CORE_INTERNAL_MEM);
+ brcmf_chip_resetcore(core, 0, 0, 0);
+ }
+
+ if (!brcmf_chip_set_active(devinfo->ci, resetintr))
+ return -EINVAL;
+ return 0;
+}
+
+
+static int
+brcmf_pcie_send_mb_data(struct brcmf_pciedev_info *devinfo, u32 htod_mb_data)
+{
+ struct brcmf_pcie_shared_info *shared;
+ u32 addr;
+ u32 cur_htod_mb_data;
+ u32 i;
+
+ shared = &devinfo->shared;
+ addr = shared->htod_mb_data_addr;
+ cur_htod_mb_data = brcmf_pcie_read_tcm32(devinfo, addr);
+
+ if (cur_htod_mb_data != 0)
+ brcmf_dbg(PCIE, "MB transaction is already pending 0x%04x\n",
+ cur_htod_mb_data);
+
+ i = 0;
+ while (cur_htod_mb_data != 0) {
+ msleep(10);
+ i++;
+ if (i > 100)
+ return -EIO;
+ cur_htod_mb_data = brcmf_pcie_read_tcm32(devinfo, addr);
+ }
+
+ brcmf_pcie_write_tcm32(devinfo, addr, htod_mb_data);
+ pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_SBMBX, 1);
+ pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_SBMBX, 1);
+
+ return 0;
+}
+
+
+static void brcmf_pcie_handle_mb_data(struct brcmf_pciedev_info *devinfo)
+{
+ struct brcmf_pcie_shared_info *shared;
+ u32 addr;
+ u32 dtoh_mb_data;
+
+ shared = &devinfo->shared;
+ addr = shared->dtoh_mb_data_addr;
+ dtoh_mb_data = brcmf_pcie_read_tcm32(devinfo, addr);
+
+ if (!dtoh_mb_data)
+ return;
+
+ brcmf_pcie_write_tcm32(devinfo, addr, 0);
+
+ brcmf_dbg(PCIE, "D2H_MB_DATA: 0x%04x\n", dtoh_mb_data);
+ if (dtoh_mb_data & BRCMF_D2H_DEV_DS_ENTER_REQ) {
+ brcmf_dbg(PCIE, "D2H_MB_DATA: DEEP SLEEP REQ\n");
+ brcmf_pcie_send_mb_data(devinfo, BRCMF_H2D_HOST_DS_ACK);
+ brcmf_dbg(PCIE, "D2H_MB_DATA: sent DEEP SLEEP ACK\n");
+ }
+ if (dtoh_mb_data & BRCMF_D2H_DEV_DS_EXIT_NOTE)
+ brcmf_dbg(PCIE, "D2H_MB_DATA: DEEP SLEEP EXIT\n");
+ if (dtoh_mb_data & BRCMF_D2H_DEV_D3_ACK) {
+ brcmf_dbg(PCIE, "D2H_MB_DATA: D3 ACK\n");
+ devinfo->mbdata_completed = true;
+ wake_up(&devinfo->mbdata_resp_wait);
+ }
+}
+
+
+static void brcmf_pcie_bus_console_init(struct brcmf_pciedev_info *devinfo)
+{
+ struct brcmf_pcie_shared_info *shared;
+ struct brcmf_pcie_console *console;
+ u32 addr;
+
+ shared = &devinfo->shared;
+ console = &shared->console;
+ addr = shared->tcm_base_address + BRCMF_SHARED_CONSOLE_ADDR_OFFSET;
+ console->base_addr = brcmf_pcie_read_tcm32(devinfo, addr);
+
+ addr = console->base_addr + BRCMF_CONSOLE_BUFADDR_OFFSET;
+ console->buf_addr = brcmf_pcie_read_tcm32(devinfo, addr);
+ addr = console->base_addr + BRCMF_CONSOLE_BUFSIZE_OFFSET;
+ console->bufsize = brcmf_pcie_read_tcm32(devinfo, addr);
+
+ brcmf_dbg(FWCON, "Console: base %x, buf %x, size %d\n",
+ console->base_addr, console->buf_addr, console->bufsize);
+}
+
+
+static void brcmf_pcie_bus_console_read(struct brcmf_pciedev_info *devinfo)
+{
+ struct brcmf_pcie_console *console;
+ u32 addr;
+ u8 ch;
+ u32 newidx;
+
+ if (!BRCMF_FWCON_ON())
+ return;
+
+ console = &devinfo->shared.console;
+ addr = console->base_addr + BRCMF_CONSOLE_WRITEIDX_OFFSET;
+ newidx = brcmf_pcie_read_tcm32(devinfo, addr);
+ while (newidx != console->read_idx) {
+ addr = console->buf_addr + console->read_idx;
+ ch = brcmf_pcie_read_tcm8(devinfo, addr);
+ console->read_idx++;
+ if (console->read_idx == console->bufsize)
+ console->read_idx = 0;
+ if (ch == '\r')
+ continue;
+ console->log_str[console->log_idx] = ch;
+ console->log_idx++;
+ if ((ch != '\n') &&
+ (console->log_idx == (sizeof(console->log_str) - 2))) {
+ ch = '\n';
+ console->log_str[console->log_idx] = ch;
+ console->log_idx++;
+ }
+ if (ch == '\n') {
+ console->log_str[console->log_idx] = 0;
+ pr_debug("CONSOLE: %s", console->log_str);
+ console->log_idx = 0;
+ }
+ }
+}
+
+
+static void brcmf_pcie_intr_disable(struct brcmf_pciedev_info *devinfo)
+{
+ brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXMASK, 0);
+}
+
+
+static void brcmf_pcie_intr_enable(struct brcmf_pciedev_info *devinfo)
+{
+ brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXMASK,
+ BRCMF_PCIE_MB_INT_D2H_DB |
+ BRCMF_PCIE_MB_INT_FN0_0 |
+ BRCMF_PCIE_MB_INT_FN0_1);
+}
+
+
+static irqreturn_t brcmf_pcie_quick_check_isr(int irq, void *arg)
+{
+ struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)arg;
+
+ if (brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT)) {
+ brcmf_pcie_intr_disable(devinfo);
+ brcmf_dbg(PCIE, "Enter\n");
+ return IRQ_WAKE_THREAD;
+ }
+ return IRQ_NONE;
+}
+
+
+static irqreturn_t brcmf_pcie_isr_thread(int irq, void *arg)
+{
+ struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)arg;
+ u32 status;
+
+ devinfo->in_irq = true;
+ status = brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT);
+ brcmf_dbg(PCIE, "Enter %x\n", status);
+ if (status) {
+ brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT,
+ status);
+ if (status & (BRCMF_PCIE_MB_INT_FN0_0 |
+ BRCMF_PCIE_MB_INT_FN0_1))
+ brcmf_pcie_handle_mb_data(devinfo);
+ if (status & BRCMF_PCIE_MB_INT_D2H_DB) {
+ if (devinfo->state == BRCMFMAC_PCIE_STATE_UP)
+ brcmf_proto_msgbuf_rx_trigger(
+ &devinfo->pdev->dev);
+ }
+ }
+ brcmf_pcie_bus_console_read(devinfo);
+ if (devinfo->state == BRCMFMAC_PCIE_STATE_UP)
+ brcmf_pcie_intr_enable(devinfo);
+ devinfo->in_irq = false;
+ return IRQ_HANDLED;
+}
+
+
+static int brcmf_pcie_request_irq(struct brcmf_pciedev_info *devinfo)
+{
+ struct pci_dev *pdev;
+
+ pdev = devinfo->pdev;
+
+ brcmf_pcie_intr_disable(devinfo);
+
+ brcmf_dbg(PCIE, "Enter\n");
+
+ pci_enable_msi(pdev);
+ if (request_threaded_irq(pdev->irq, brcmf_pcie_quick_check_isr,
+ brcmf_pcie_isr_thread, IRQF_SHARED,
+ "brcmf_pcie_intr", devinfo)) {
+ pci_disable_msi(pdev);
+ brcmf_err("Failed to request IRQ %d\n", pdev->irq);
+ return -EIO;
+ }
+ devinfo->irq_allocated = true;
+ return 0;
+}
+
+
+static void brcmf_pcie_release_irq(struct brcmf_pciedev_info *devinfo)
+{
+ struct pci_dev *pdev;
+ u32 status;
+ u32 count;
+
+ if (!devinfo->irq_allocated)
+ return;
+
+ pdev = devinfo->pdev;
+
+ brcmf_pcie_intr_disable(devinfo);
+ free_irq(pdev->irq, devinfo);
+ pci_disable_msi(pdev);
+
+ msleep(50);
+ count = 0;
+ while ((devinfo->in_irq) && (count < 20)) {
+ msleep(50);
+ count++;
+ }
+ if (devinfo->in_irq)
+ brcmf_err("Still in IRQ (processing) !!!\n");
+
+ status = brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT);
+ brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT, status);
+
+ devinfo->irq_allocated = false;
+}
+
+
+static int brcmf_pcie_ring_mb_write_rptr(void *ctx)
+{
+ struct brcmf_pcie_ringbuf *ring = (struct brcmf_pcie_ringbuf *)ctx;
+ struct brcmf_pciedev_info *devinfo = ring->devinfo;
+ struct brcmf_commonring *commonring = &ring->commonring;
+
+ if (devinfo->state != BRCMFMAC_PCIE_STATE_UP)
+ return -EIO;
+
+ brcmf_dbg(PCIE, "W r_ptr %d (%d), ring %d\n", commonring->r_ptr,
+ commonring->w_ptr, ring->id);
+
+ devinfo->write_ptr(devinfo, ring->r_idx_addr, commonring->r_ptr);
+
+ return 0;
+}
+
+
+static int brcmf_pcie_ring_mb_write_wptr(void *ctx)
+{
+ struct brcmf_pcie_ringbuf *ring = (struct brcmf_pcie_ringbuf *)ctx;
+ struct brcmf_pciedev_info *devinfo = ring->devinfo;
+ struct brcmf_commonring *commonring = &ring->commonring;
+
+ if (devinfo->state != BRCMFMAC_PCIE_STATE_UP)
+ return -EIO;
+
+ brcmf_dbg(PCIE, "W w_ptr %d (%d), ring %d\n", commonring->w_ptr,
+ commonring->r_ptr, ring->id);
+
+ devinfo->write_ptr(devinfo, ring->w_idx_addr, commonring->w_ptr);
+
+ return 0;
+}
+
+
+static int brcmf_pcie_ring_mb_ring_bell(void *ctx)
+{
+ struct brcmf_pcie_ringbuf *ring = (struct brcmf_pcie_ringbuf *)ctx;
+ struct brcmf_pciedev_info *devinfo = ring->devinfo;
+
+ if (devinfo->state != BRCMFMAC_PCIE_STATE_UP)
+ return -EIO;
+
+ brcmf_dbg(PCIE, "RING !\n");
+ /* Any arbitrary value will do, lets use 1 */
+ brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_H2D_MAILBOX, 1);
+
+ return 0;
+}
+
+
+static int brcmf_pcie_ring_mb_update_rptr(void *ctx)
+{
+ struct brcmf_pcie_ringbuf *ring = (struct brcmf_pcie_ringbuf *)ctx;
+ struct brcmf_pciedev_info *devinfo = ring->devinfo;
+ struct brcmf_commonring *commonring = &ring->commonring;
+
+ if (devinfo->state != BRCMFMAC_PCIE_STATE_UP)
+ return -EIO;
+
+ commonring->r_ptr = devinfo->read_ptr(devinfo, ring->r_idx_addr);
+
+ brcmf_dbg(PCIE, "R r_ptr %d (%d), ring %d\n", commonring->r_ptr,
+ commonring->w_ptr, ring->id);
+
+ return 0;
+}
+
+
+static int brcmf_pcie_ring_mb_update_wptr(void *ctx)
+{
+ struct brcmf_pcie_ringbuf *ring = (struct brcmf_pcie_ringbuf *)ctx;
+ struct brcmf_pciedev_info *devinfo = ring->devinfo;
+ struct brcmf_commonring *commonring = &ring->commonring;
+
+ if (devinfo->state != BRCMFMAC_PCIE_STATE_UP)
+ return -EIO;
+
+ commonring->w_ptr = devinfo->read_ptr(devinfo, ring->w_idx_addr);
+
+ brcmf_dbg(PCIE, "R w_ptr %d (%d), ring %d\n", commonring->w_ptr,
+ commonring->r_ptr, ring->id);
+
+ return 0;
+}
+
+
+static void *
+brcmf_pcie_init_dmabuffer_for_device(struct brcmf_pciedev_info *devinfo,
+ u32 size, u32 tcm_dma_phys_addr,
+ dma_addr_t *dma_handle)
+{
+ void *ring;
+ u64 address;
+
+ ring = dma_alloc_coherent(&devinfo->pdev->dev, size, dma_handle,
+ GFP_KERNEL);
+ if (!ring)
+ return NULL;
+
+ address = (u64)*dma_handle;
+ brcmf_pcie_write_tcm32(devinfo, tcm_dma_phys_addr,
+ address & 0xffffffff);
+ brcmf_pcie_write_tcm32(devinfo, tcm_dma_phys_addr + 4, address >> 32);
+
+ memset(ring, 0, size);
+
+ return (ring);
+}
+
+
+static struct brcmf_pcie_ringbuf *
+brcmf_pcie_alloc_dma_and_ring(struct brcmf_pciedev_info *devinfo, u32 ring_id,
+ u32 tcm_ring_phys_addr)
+{
+ void *dma_buf;
+ dma_addr_t dma_handle;
+ struct brcmf_pcie_ringbuf *ring;
+ u32 size;
+ u32 addr;
+
+ size = brcmf_ring_max_item[ring_id] * brcmf_ring_itemsize[ring_id];
+ dma_buf = brcmf_pcie_init_dmabuffer_for_device(devinfo, size,
+ tcm_ring_phys_addr + BRCMF_RING_MEM_BASE_ADDR_OFFSET,
+ &dma_handle);
+ if (!dma_buf)
+ return NULL;
+
+ addr = tcm_ring_phys_addr + BRCMF_RING_MAX_ITEM_OFFSET;
+ brcmf_pcie_write_tcm16(devinfo, addr, brcmf_ring_max_item[ring_id]);
+ addr = tcm_ring_phys_addr + BRCMF_RING_LEN_ITEMS_OFFSET;
+ brcmf_pcie_write_tcm16(devinfo, addr, brcmf_ring_itemsize[ring_id]);
+
+ ring = kzalloc(sizeof(*ring), GFP_KERNEL);
+ if (!ring) {
+ dma_free_coherent(&devinfo->pdev->dev, size, dma_buf,
+ dma_handle);
+ return NULL;
+ }
+ brcmf_commonring_config(&ring->commonring, brcmf_ring_max_item[ring_id],
+ brcmf_ring_itemsize[ring_id], dma_buf);
+ ring->dma_handle = dma_handle;
+ ring->devinfo = devinfo;
+ brcmf_commonring_register_cb(&ring->commonring,
+ brcmf_pcie_ring_mb_ring_bell,
+ brcmf_pcie_ring_mb_update_rptr,
+ brcmf_pcie_ring_mb_update_wptr,
+ brcmf_pcie_ring_mb_write_rptr,
+ brcmf_pcie_ring_mb_write_wptr, ring);
+
+ return (ring);
+}
+
+
+static void brcmf_pcie_release_ringbuffer(struct device *dev,
+ struct brcmf_pcie_ringbuf *ring)
+{
+ void *dma_buf;
+ u32 size;
+
+ if (!ring)
+ return;
+
+ dma_buf = ring->commonring.buf_addr;
+ if (dma_buf) {
+ size = ring->commonring.depth * ring->commonring.item_len;
+ dma_free_coherent(dev, size, dma_buf, ring->dma_handle);
+ }
+ kfree(ring);
+}
+
+
+static void brcmf_pcie_release_ringbuffers(struct brcmf_pciedev_info *devinfo)
+{
+ u32 i;
+
+ for (i = 0; i < BRCMF_NROF_COMMON_MSGRINGS; i++) {
+ brcmf_pcie_release_ringbuffer(&devinfo->pdev->dev,
+ devinfo->shared.commonrings[i]);
+ devinfo->shared.commonrings[i] = NULL;
+ }
+ kfree(devinfo->shared.flowrings);
+ devinfo->shared.flowrings = NULL;
+ if (devinfo->idxbuf) {
+ dma_free_coherent(&devinfo->pdev->dev,
+ devinfo->idxbuf_sz,
+ devinfo->idxbuf,
+ devinfo->idxbuf_dmahandle);
+ devinfo->idxbuf = NULL;
+ }
+}
+
+
+static int brcmf_pcie_init_ringbuffers(struct brcmf_pciedev_info *devinfo)
+{
+ struct brcmf_pcie_ringbuf *ring;
+ struct brcmf_pcie_ringbuf *rings;
+ u32 d2h_w_idx_ptr;
+ u32 d2h_r_idx_ptr;
+ u32 h2d_w_idx_ptr;
+ u32 h2d_r_idx_ptr;
+ u32 ring_mem_ptr;
+ u32 i;
+ u64 address;
+ u32 bufsz;
+ u8 idx_offset;
+ struct brcmf_pcie_dhi_ringinfo ringinfo;
+ u16 max_flowrings;
+ u16 max_submissionrings;
+ u16 max_completionrings;
+
+ memcpy_fromio(&ringinfo, devinfo->tcm + devinfo->shared.ring_info_addr,
+ sizeof(ringinfo));
+ if (devinfo->shared.version >= 6) {
+ max_submissionrings = le16_to_cpu(ringinfo.max_submissionrings);
+ max_flowrings = le16_to_cpu(ringinfo.max_flowrings);
+ max_completionrings = le16_to_cpu(ringinfo.max_completionrings);
+ } else {
+ max_submissionrings = le16_to_cpu(ringinfo.max_flowrings);
+ max_flowrings = max_submissionrings -
+ BRCMF_NROF_H2D_COMMON_MSGRINGS;
+ max_completionrings = BRCMF_NROF_D2H_COMMON_MSGRINGS;
+ }
+
+ if (devinfo->dma_idx_sz != 0) {
+ bufsz = (max_submissionrings + max_completionrings) *
+ devinfo->dma_idx_sz * 2;
+ devinfo->idxbuf = dma_alloc_coherent(&devinfo->pdev->dev, bufsz,
+ &devinfo->idxbuf_dmahandle,
+ GFP_KERNEL);
+ if (!devinfo->idxbuf)
+ devinfo->dma_idx_sz = 0;
+ }
+
+ if (devinfo->dma_idx_sz == 0) {
+ d2h_w_idx_ptr = le32_to_cpu(ringinfo.d2h_w_idx_ptr);
+ d2h_r_idx_ptr = le32_to_cpu(ringinfo.d2h_r_idx_ptr);
+ h2d_w_idx_ptr = le32_to_cpu(ringinfo.h2d_w_idx_ptr);
+ h2d_r_idx_ptr = le32_to_cpu(ringinfo.h2d_r_idx_ptr);
+ idx_offset = sizeof(u32);
+ devinfo->write_ptr = brcmf_pcie_write_tcm16;
+ devinfo->read_ptr = brcmf_pcie_read_tcm16;
+ brcmf_dbg(PCIE, "Using TCM indices\n");
+ } else {
+ memset(devinfo->idxbuf, 0, bufsz);
+ devinfo->idxbuf_sz = bufsz;
+ idx_offset = devinfo->dma_idx_sz;
+ devinfo->write_ptr = brcmf_pcie_write_idx;
+ devinfo->read_ptr = brcmf_pcie_read_idx;
+
+ h2d_w_idx_ptr = 0;
+ address = (u64)devinfo->idxbuf_dmahandle;
+ ringinfo.h2d_w_idx_hostaddr.low_addr =
+ cpu_to_le32(address & 0xffffffff);
+ ringinfo.h2d_w_idx_hostaddr.high_addr =
+ cpu_to_le32(address >> 32);
+
+ h2d_r_idx_ptr = h2d_w_idx_ptr +
+ max_submissionrings * idx_offset;
+ address += max_submissionrings * idx_offset;
+ ringinfo.h2d_r_idx_hostaddr.low_addr =
+ cpu_to_le32(address & 0xffffffff);
+ ringinfo.h2d_r_idx_hostaddr.high_addr =
+ cpu_to_le32(address >> 32);
+
+ d2h_w_idx_ptr = h2d_r_idx_ptr +
+ max_submissionrings * idx_offset;
+ address += max_submissionrings * idx_offset;
+ ringinfo.d2h_w_idx_hostaddr.low_addr =
+ cpu_to_le32(address & 0xffffffff);
+ ringinfo.d2h_w_idx_hostaddr.high_addr =
+ cpu_to_le32(address >> 32);
+
+ d2h_r_idx_ptr = d2h_w_idx_ptr +
+ max_completionrings * idx_offset;
+ address += max_completionrings * idx_offset;
+ ringinfo.d2h_r_idx_hostaddr.low_addr =
+ cpu_to_le32(address & 0xffffffff);
+ ringinfo.d2h_r_idx_hostaddr.high_addr =
+ cpu_to_le32(address >> 32);
+
+ memcpy_toio(devinfo->tcm + devinfo->shared.ring_info_addr,
+ &ringinfo, sizeof(ringinfo));
+ brcmf_dbg(PCIE, "Using host memory indices\n");
+ }
+
+ ring_mem_ptr = le32_to_cpu(ringinfo.ringmem);
+
+ for (i = 0; i < BRCMF_NROF_H2D_COMMON_MSGRINGS; i++) {
+ ring = brcmf_pcie_alloc_dma_and_ring(devinfo, i, ring_mem_ptr);
+ if (!ring)
+ goto fail;
+ ring->w_idx_addr = h2d_w_idx_ptr;
+ ring->r_idx_addr = h2d_r_idx_ptr;
+ ring->id = i;
+ devinfo->shared.commonrings[i] = ring;
+
+ h2d_w_idx_ptr += idx_offset;
+ h2d_r_idx_ptr += idx_offset;
+ ring_mem_ptr += BRCMF_RING_MEM_SZ;
+ }
+
+ for (i = BRCMF_NROF_H2D_COMMON_MSGRINGS;
+ i < BRCMF_NROF_COMMON_MSGRINGS; i++) {
+ ring = brcmf_pcie_alloc_dma_and_ring(devinfo, i, ring_mem_ptr);
+ if (!ring)
+ goto fail;
+ ring->w_idx_addr = d2h_w_idx_ptr;
+ ring->r_idx_addr = d2h_r_idx_ptr;
+ ring->id = i;
+ devinfo->shared.commonrings[i] = ring;
+
+ d2h_w_idx_ptr += idx_offset;
+ d2h_r_idx_ptr += idx_offset;
+ ring_mem_ptr += BRCMF_RING_MEM_SZ;
+ }
+
+ devinfo->shared.max_flowrings = max_flowrings;
+ devinfo->shared.max_submissionrings = max_submissionrings;
+ devinfo->shared.max_completionrings = max_completionrings;
+ rings = kcalloc(max_flowrings, sizeof(*ring), GFP_KERNEL);
+ if (!rings)
+ goto fail;
+
+ brcmf_dbg(PCIE, "Nr of flowrings is %d\n", max_flowrings);
+
+ for (i = 0; i < max_flowrings; i++) {
+ ring = &rings[i];
+ ring->devinfo = devinfo;
+ ring->id = i + BRCMF_H2D_MSGRING_FLOWRING_IDSTART;
+ brcmf_commonring_register_cb(&ring->commonring,
+ brcmf_pcie_ring_mb_ring_bell,
+ brcmf_pcie_ring_mb_update_rptr,
+ brcmf_pcie_ring_mb_update_wptr,
+ brcmf_pcie_ring_mb_write_rptr,
+ brcmf_pcie_ring_mb_write_wptr,
+ ring);
+ ring->w_idx_addr = h2d_w_idx_ptr;
+ ring->r_idx_addr = h2d_r_idx_ptr;
+ h2d_w_idx_ptr += idx_offset;
+ h2d_r_idx_ptr += idx_offset;
+ }
+ devinfo->shared.flowrings = rings;
+
+ return 0;
+
+fail:
+ brcmf_err("Allocating ring buffers failed\n");
+ brcmf_pcie_release_ringbuffers(devinfo);
+ return -ENOMEM;
+}
+
+
+static void
+brcmf_pcie_release_scratchbuffers(struct brcmf_pciedev_info *devinfo)
+{
+ if (devinfo->shared.scratch)
+ dma_free_coherent(&devinfo->pdev->dev,
+ BRCMF_DMA_D2H_SCRATCH_BUF_LEN,
+ devinfo->shared.scratch,
+ devinfo->shared.scratch_dmahandle);
+ if (devinfo->shared.ringupd)
+ dma_free_coherent(&devinfo->pdev->dev,
+ BRCMF_DMA_D2H_RINGUPD_BUF_LEN,
+ devinfo->shared.ringupd,
+ devinfo->shared.ringupd_dmahandle);
+}
+
+static int brcmf_pcie_init_scratchbuffers(struct brcmf_pciedev_info *devinfo)
+{
+ u64 address;
+ u32 addr;
+
+ devinfo->shared.scratch = dma_alloc_coherent(&devinfo->pdev->dev,
+ BRCMF_DMA_D2H_SCRATCH_BUF_LEN,
+ &devinfo->shared.scratch_dmahandle, GFP_KERNEL);
+ if (!devinfo->shared.scratch)
+ goto fail;
+
+ memset(devinfo->shared.scratch, 0, BRCMF_DMA_D2H_SCRATCH_BUF_LEN);
+
+ addr = devinfo->shared.tcm_base_address +
+ BRCMF_SHARED_DMA_SCRATCH_ADDR_OFFSET;
+ address = (u64)devinfo->shared.scratch_dmahandle;
+ brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff);
+ brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32);
+ addr = devinfo->shared.tcm_base_address +
+ BRCMF_SHARED_DMA_SCRATCH_LEN_OFFSET;
+ brcmf_pcie_write_tcm32(devinfo, addr, BRCMF_DMA_D2H_SCRATCH_BUF_LEN);
+
+ devinfo->shared.ringupd = dma_alloc_coherent(&devinfo->pdev->dev,
+ BRCMF_DMA_D2H_RINGUPD_BUF_LEN,
+ &devinfo->shared.ringupd_dmahandle, GFP_KERNEL);
+ if (!devinfo->shared.ringupd)
+ goto fail;
+
+ memset(devinfo->shared.ringupd, 0, BRCMF_DMA_D2H_RINGUPD_BUF_LEN);
+
+ addr = devinfo->shared.tcm_base_address +
+ BRCMF_SHARED_DMA_RINGUPD_ADDR_OFFSET;
+ address = (u64)devinfo->shared.ringupd_dmahandle;
+ brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff);
+ brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32);
+ addr = devinfo->shared.tcm_base_address +
+ BRCMF_SHARED_DMA_RINGUPD_LEN_OFFSET;
+ brcmf_pcie_write_tcm32(devinfo, addr, BRCMF_DMA_D2H_RINGUPD_BUF_LEN);
+ return 0;
+
+fail:
+ brcmf_err("Allocating scratch buffers failed\n");
+ brcmf_pcie_release_scratchbuffers(devinfo);
+ return -ENOMEM;
+}
+
+
+static void brcmf_pcie_down(struct device *dev)
+{
+}
+
+
+static int brcmf_pcie_tx(struct device *dev, struct sk_buff *skb)
+{
+ return 0;
+}
+
+
+static int brcmf_pcie_tx_ctlpkt(struct device *dev, unsigned char *msg,
+ uint len)
+{
+ return 0;
+}
+
+
+static int brcmf_pcie_rx_ctlpkt(struct device *dev, unsigned char *msg,
+ uint len)
+{
+ return 0;
+}
+
+
+static void brcmf_pcie_wowl_config(struct device *dev, bool enabled)
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+ struct brcmf_pciedev *buspub = bus_if->bus_priv.pcie;
+ struct brcmf_pciedev_info *devinfo = buspub->devinfo;
+
+ brcmf_dbg(PCIE, "Configuring WOWL, enabled=%d\n", enabled);
+ devinfo->wowl_enabled = enabled;
+}
+
+
+static size_t brcmf_pcie_get_ramsize(struct device *dev)
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+ struct brcmf_pciedev *buspub = bus_if->bus_priv.pcie;
+ struct brcmf_pciedev_info *devinfo = buspub->devinfo;
+
+ return devinfo->ci->ramsize - devinfo->ci->srsize;
+}
+
+
+static int brcmf_pcie_get_memdump(struct device *dev, void *data, size_t len)
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+ struct brcmf_pciedev *buspub = bus_if->bus_priv.pcie;
+ struct brcmf_pciedev_info *devinfo = buspub->devinfo;
+
+ brcmf_dbg(PCIE, "dump at 0x%08X: len=%zu\n", devinfo->ci->rambase, len);
+ brcmf_pcie_copy_dev_tomem(devinfo, devinfo->ci->rambase, data, len);
+ return 0;
+}
+
+static int brcmf_pcie_get_fwname(struct device *dev, u32 chip, u32 chiprev,
+ u8 *fw_name)
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+ struct brcmf_pciedev *buspub = bus_if->bus_priv.pcie;
+ struct brcmf_pciedev_info *devinfo = buspub->devinfo;
+ int ret = 0;
+
+ if (devinfo->fw_name[0] != '\0')
+ strlcpy(fw_name, devinfo->fw_name, BRCMF_FW_NAME_LEN);
+ else
+ ret = brcmf_fw_map_chip_to_name(chip, chiprev,
+ brcmf_pcie_fwnames,
+ ARRAY_SIZE(brcmf_pcie_fwnames),
+ fw_name, NULL);
+
+ return ret;
+}
+
+static const struct brcmf_bus_ops brcmf_pcie_bus_ops = {
+ .txdata = brcmf_pcie_tx,
+ .stop = brcmf_pcie_down,
+ .txctl = brcmf_pcie_tx_ctlpkt,
+ .rxctl = brcmf_pcie_rx_ctlpkt,
+ .wowl_config = brcmf_pcie_wowl_config,
+ .get_ramsize = brcmf_pcie_get_ramsize,
+ .get_memdump = brcmf_pcie_get_memdump,
+ .get_fwname = brcmf_pcie_get_fwname,
+};
+
+
+static void
+brcmf_pcie_adjust_ramsize(struct brcmf_pciedev_info *devinfo, u8 *data,
+ u32 data_len)
+{
+ __le32 *field;
+ u32 newsize;
+
+ if (data_len < BRCMF_RAMSIZE_OFFSET + 8)
+ return;
+
+ field = (__le32 *)&data[BRCMF_RAMSIZE_OFFSET];
+ if (le32_to_cpup(field) != BRCMF_RAMSIZE_MAGIC)
+ return;
+ field++;
+ newsize = le32_to_cpup(field);
+
+ brcmf_dbg(PCIE, "Found ramsize info in FW, adjusting to 0x%x\n",
+ newsize);
+ devinfo->ci->ramsize = newsize;
+}
+
+
+static int
+brcmf_pcie_init_share_ram_info(struct brcmf_pciedev_info *devinfo,
+ u32 sharedram_addr)
+{
+ struct brcmf_pcie_shared_info *shared;
+ u32 addr;
+
+ shared = &devinfo->shared;
+ shared->tcm_base_address = sharedram_addr;
+
+ shared->flags = brcmf_pcie_read_tcm32(devinfo, sharedram_addr);
+ shared->version = (u8)(shared->flags & BRCMF_PCIE_SHARED_VERSION_MASK);
+ brcmf_dbg(PCIE, "PCIe protocol version %d\n", shared->version);
+ if ((shared->version > BRCMF_PCIE_MAX_SHARED_VERSION) ||
+ (shared->version < BRCMF_PCIE_MIN_SHARED_VERSION)) {
+ brcmf_err("Unsupported PCIE version %d\n", shared->version);
+ return -EINVAL;
+ }
+
+ /* check firmware support dma indicies */
+ if (shared->flags & BRCMF_PCIE_SHARED_DMA_INDEX) {
+ if (shared->flags & BRCMF_PCIE_SHARED_DMA_2B_IDX)
+ devinfo->dma_idx_sz = sizeof(u16);
+ else
+ devinfo->dma_idx_sz = sizeof(u32);
+ }
+
+ addr = sharedram_addr + BRCMF_SHARED_MAX_RXBUFPOST_OFFSET;
+ shared->max_rxbufpost = brcmf_pcie_read_tcm16(devinfo, addr);
+ if (shared->max_rxbufpost == 0)
+ shared->max_rxbufpost = BRCMF_DEF_MAX_RXBUFPOST;
+
+ addr = sharedram_addr + BRCMF_SHARED_RX_DATAOFFSET_OFFSET;
+ shared->rx_dataoffset = brcmf_pcie_read_tcm32(devinfo, addr);
+
+ addr = sharedram_addr + BRCMF_SHARED_HTOD_MB_DATA_ADDR_OFFSET;
+ shared->htod_mb_data_addr = brcmf_pcie_read_tcm32(devinfo, addr);
+
+ addr = sharedram_addr + BRCMF_SHARED_DTOH_MB_DATA_ADDR_OFFSET;
+ shared->dtoh_mb_data_addr = brcmf_pcie_read_tcm32(devinfo, addr);
+
+ addr = sharedram_addr + BRCMF_SHARED_RING_INFO_ADDR_OFFSET;
+ shared->ring_info_addr = brcmf_pcie_read_tcm32(devinfo, addr);
+
+ brcmf_dbg(PCIE, "max rx buf post %d, rx dataoffset %d\n",
+ shared->max_rxbufpost, shared->rx_dataoffset);
+
+ brcmf_pcie_bus_console_init(devinfo);
+
+ return 0;
+}
+
+
+static int brcmf_pcie_download_fw_nvram(struct brcmf_pciedev_info *devinfo,
+ const struct firmware *fw, void *nvram,
+ u32 nvram_len)
+{
+ u32 sharedram_addr;
+ u32 sharedram_addr_written;
+ u32 loop_counter;
+ int err;
+ u32 address;
+ u32 resetintr;
+
+ brcmf_dbg(PCIE, "Halt ARM.\n");
+ err = brcmf_pcie_enter_download_state(devinfo);
+ if (err)
+ return err;
+
+ brcmf_dbg(PCIE, "Download FW %s\n", devinfo->fw_name);
+ brcmf_pcie_copy_mem_todev(devinfo, devinfo->ci->rambase,
+ (void *)fw->data, fw->size);
+
+ resetintr = get_unaligned_le32(fw->data);
+ release_firmware(fw);
+
+ /* reset last 4 bytes of RAM address. to be used for shared
+ * area. This identifies when FW is running
+ */
+ brcmf_pcie_write_ram32(devinfo, devinfo->ci->ramsize - 4, 0);
+
+ if (nvram) {
+ brcmf_dbg(PCIE, "Download NVRAM %s\n", devinfo->nvram_name);
+ address = devinfo->ci->rambase + devinfo->ci->ramsize -
+ nvram_len;
+ brcmf_pcie_copy_mem_todev(devinfo, address, nvram, nvram_len);
+ brcmf_fw_nvram_free(nvram);
+ } else {
+ brcmf_dbg(PCIE, "No matching NVRAM file found %s\n",
+ devinfo->nvram_name);
+ }
+
+ sharedram_addr_written = brcmf_pcie_read_ram32(devinfo,
+ devinfo->ci->ramsize -
+ 4);
+ brcmf_dbg(PCIE, "Bring ARM in running state\n");
+ err = brcmf_pcie_exit_download_state(devinfo, resetintr);
+ if (err)
+ return err;
+
+ brcmf_dbg(PCIE, "Wait for FW init\n");
+ sharedram_addr = sharedram_addr_written;
+ loop_counter = BRCMF_PCIE_FW_UP_TIMEOUT / 50;
+ while ((sharedram_addr == sharedram_addr_written) && (loop_counter)) {
+ msleep(50);
+ sharedram_addr = brcmf_pcie_read_ram32(devinfo,
+ devinfo->ci->ramsize -
+ 4);
+ loop_counter--;
+ }
+ if (sharedram_addr == sharedram_addr_written) {
+ brcmf_err("FW failed to initialize\n");
+ return -ENODEV;
+ }
+ brcmf_dbg(PCIE, "Shared RAM addr: 0x%08x\n", sharedram_addr);
+
+ return (brcmf_pcie_init_share_ram_info(devinfo, sharedram_addr));
+}
+
+
+static int brcmf_pcie_get_resource(struct brcmf_pciedev_info *devinfo)
+{
+ struct pci_dev *pdev;
+ int err;
+ phys_addr_t bar0_addr, bar1_addr;
+ ulong bar1_size;
+
+ pdev = devinfo->pdev;
+
+ err = pci_enable_device(pdev);
+ if (err) {
+ brcmf_err("pci_enable_device failed err=%d\n", err);
+ return err;
+ }
+
+ pci_set_master(pdev);
+
+ /* Bar-0 mapped address */
+ bar0_addr = pci_resource_start(pdev, 0);
+ /* Bar-1 mapped address */
+ bar1_addr = pci_resource_start(pdev, 2);
+ /* read Bar-1 mapped memory range */
+ bar1_size = pci_resource_len(pdev, 2);
+ if ((bar1_size == 0) || (bar1_addr == 0)) {
+ brcmf_err("BAR1 Not enabled, device size=%ld, addr=%#016llx\n",
+ bar1_size, (unsigned long long)bar1_addr);
+ return -EINVAL;
+ }
+
+ devinfo->regs = ioremap_nocache(bar0_addr, BRCMF_PCIE_REG_MAP_SIZE);
+ devinfo->tcm = ioremap_nocache(bar1_addr, bar1_size);
+
+ if (!devinfo->regs || !devinfo->tcm) {
+ brcmf_err("ioremap() failed (%p,%p)\n", devinfo->regs,
+ devinfo->tcm);
+ return -EINVAL;
+ }
+ brcmf_dbg(PCIE, "Phys addr : reg space = %p base addr %#016llx\n",
+ devinfo->regs, (unsigned long long)bar0_addr);
+ brcmf_dbg(PCIE, "Phys addr : mem space = %p base addr %#016llx size 0x%x\n",
+ devinfo->tcm, (unsigned long long)bar1_addr,
+ (unsigned int)bar1_size);
+
+ return 0;
+}
+
+
+static void brcmf_pcie_release_resource(struct brcmf_pciedev_info *devinfo)
+{
+ if (devinfo->tcm)
+ iounmap(devinfo->tcm);
+ if (devinfo->regs)
+ iounmap(devinfo->regs);
+
+ pci_disable_device(devinfo->pdev);
+}
+
+
+static int brcmf_pcie_attach_bus(struct brcmf_pciedev_info *devinfo)
+{
+ int ret;
+
+ /* Attach to the common driver interface */
+ ret = brcmf_attach(&devinfo->pdev->dev, devinfo->settings);
+ if (ret) {
+ brcmf_err("brcmf_attach failed\n");
+ } else {
+ ret = brcmf_bus_started(&devinfo->pdev->dev);
+ if (ret)
+ brcmf_err("dongle is not responding\n");
+ }
+
+ return ret;
+}
+
+
+static u32 brcmf_pcie_buscore_prep_addr(const struct pci_dev *pdev, u32 addr)
+{
+ u32 ret_addr;
+
+ ret_addr = addr & (BRCMF_PCIE_BAR0_REG_SIZE - 1);
+ addr &= ~(BRCMF_PCIE_BAR0_REG_SIZE - 1);
+ pci_write_config_dword(pdev, BRCMF_PCIE_BAR0_WINDOW, addr);
+
+ return ret_addr;
+}
+
+
+static u32 brcmf_pcie_buscore_read32(void *ctx, u32 addr)
+{
+ struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)ctx;
+
+ addr = brcmf_pcie_buscore_prep_addr(devinfo->pdev, addr);
+ return brcmf_pcie_read_reg32(devinfo, addr);
+}
+
+
+static void brcmf_pcie_buscore_write32(void *ctx, u32 addr, u32 value)
+{
+ struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)ctx;
+
+ addr = brcmf_pcie_buscore_prep_addr(devinfo->pdev, addr);
+ brcmf_pcie_write_reg32(devinfo, addr, value);
+}
+
+
+static int brcmf_pcie_buscoreprep(void *ctx)
+{
+ return brcmf_pcie_get_resource(ctx);
+}
+
+
+static int brcmf_pcie_buscore_reset(void *ctx, struct brcmf_chip *chip)
+{
+ struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)ctx;
+ u32 val;
+
+ devinfo->ci = chip;
+ brcmf_pcie_reset_device(devinfo);
+
+ val = brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT);
+ if (val != 0xffffffff)
+ brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT,
+ val);
+
+ return 0;
+}
+
+
+static void brcmf_pcie_buscore_activate(void *ctx, struct brcmf_chip *chip,
+ u32 rstvec)
+{
+ struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)ctx;
+
+ brcmf_pcie_write_tcm32(devinfo, 0, rstvec);
+}
+
+
+static const struct brcmf_buscore_ops brcmf_pcie_buscore_ops = {
+ .prepare = brcmf_pcie_buscoreprep,
+ .reset = brcmf_pcie_buscore_reset,
+ .activate = brcmf_pcie_buscore_activate,
+ .read32 = brcmf_pcie_buscore_read32,
+ .write32 = brcmf_pcie_buscore_write32,
+};
+
+static void brcmf_pcie_setup(struct device *dev, int ret,
+ const struct firmware *fw,
+ void *nvram, u32 nvram_len)
+{
+ struct brcmf_bus *bus;
+ struct brcmf_pciedev *pcie_bus_dev;
+ struct brcmf_pciedev_info *devinfo;
+ struct brcmf_commonring **flowrings;
+ u32 i;
+
+ /* check firmware loading result */
+ if (ret)
+ goto fail;
+
+ bus = dev_get_drvdata(dev);
+ pcie_bus_dev = bus->bus_priv.pcie;
+ devinfo = pcie_bus_dev->devinfo;
+ brcmf_pcie_attach(devinfo);
+
+ /* Some of the firmwares have the size of the memory of the device
+ * defined inside the firmware. This is because part of the memory in
+ * the device is shared and the devision is determined by FW. Parse
+ * the firmware and adjust the chip memory size now.
+ */
+ brcmf_pcie_adjust_ramsize(devinfo, (u8 *)fw->data, fw->size);
+
+ ret = brcmf_pcie_download_fw_nvram(devinfo, fw, nvram, nvram_len);
+ if (ret)
+ goto fail;
+
+ devinfo->state = BRCMFMAC_PCIE_STATE_UP;
+
+ ret = brcmf_pcie_init_ringbuffers(devinfo);
+ if (ret)
+ goto fail;
+
+ ret = brcmf_pcie_init_scratchbuffers(devinfo);
+ if (ret)
+ goto fail;
+
+ brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2);
+ ret = brcmf_pcie_request_irq(devinfo);
+ if (ret)
+ goto fail;
+
+ /* hook the commonrings in the bus structure. */
+ for (i = 0; i < BRCMF_NROF_COMMON_MSGRINGS; i++)
+ bus->msgbuf->commonrings[i] =
+ &devinfo->shared.commonrings[i]->commonring;
+
+ flowrings = kcalloc(devinfo->shared.max_flowrings, sizeof(*flowrings),
+ GFP_KERNEL);
+ if (!flowrings)
+ goto fail;
+
+ for (i = 0; i < devinfo->shared.max_flowrings; i++)
+ flowrings[i] = &devinfo->shared.flowrings[i].commonring;
+ bus->msgbuf->flowrings = flowrings;
+
+ bus->msgbuf->rx_dataoffset = devinfo->shared.rx_dataoffset;
+ bus->msgbuf->max_rxbufpost = devinfo->shared.max_rxbufpost;
+ bus->msgbuf->max_flowrings = devinfo->shared.max_flowrings;
+
+ init_waitqueue_head(&devinfo->mbdata_resp_wait);
+
+ brcmf_pcie_intr_enable(devinfo);
+ if (brcmf_pcie_attach_bus(devinfo) == 0)
+ return;
+
+ brcmf_pcie_bus_console_read(devinfo);
+
+fail:
+ device_release_driver(dev);
+}
+
+static int
+brcmf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ int ret;
+ struct brcmf_pciedev_info *devinfo;
+ struct brcmf_pciedev *pcie_bus_dev;
+ struct brcmf_bus *bus;
+ u16 domain_nr;
+ u16 bus_nr;
+
+ domain_nr = pci_domain_nr(pdev->bus) + 1;
+ bus_nr = pdev->bus->number;
+ brcmf_dbg(PCIE, "Enter %x:%x (%d/%d)\n", pdev->vendor, pdev->device,
+ domain_nr, bus_nr);
+
+ ret = -ENOMEM;
+ devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);
+ if (devinfo == NULL)
+ return ret;
+
+ devinfo->pdev = pdev;
+ pcie_bus_dev = NULL;
+ devinfo->ci = brcmf_chip_attach(devinfo, &brcmf_pcie_buscore_ops);
+ if (IS_ERR(devinfo->ci)) {
+ ret = PTR_ERR(devinfo->ci);
+ devinfo->ci = NULL;
+ goto fail;
+ }
+
+ pcie_bus_dev = kzalloc(sizeof(*pcie_bus_dev), GFP_KERNEL);
+ if (pcie_bus_dev == NULL) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ devinfo->settings = brcmf_get_module_param(&devinfo->pdev->dev,
+ BRCMF_BUSTYPE_PCIE,
+ devinfo->ci->chip,
+ devinfo->ci->chiprev);
+ if (!devinfo->settings) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ bus = kzalloc(sizeof(*bus), GFP_KERNEL);
+ if (!bus) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+ bus->msgbuf = kzalloc(sizeof(*bus->msgbuf), GFP_KERNEL);
+ if (!bus->msgbuf) {
+ ret = -ENOMEM;
+ kfree(bus);
+ goto fail;
+ }
+
+ /* hook it all together. */
+ pcie_bus_dev->devinfo = devinfo;
+ pcie_bus_dev->bus = bus;
+ bus->dev = &pdev->dev;
+ bus->bus_priv.pcie = pcie_bus_dev;
+ bus->ops = &brcmf_pcie_bus_ops;
+ bus->proto_type = BRCMF_PROTO_MSGBUF;
+ bus->chip = devinfo->coreid;
+ bus->wowl_supported = pci_pme_capable(pdev, PCI_D3hot);
+ dev_set_drvdata(&pdev->dev, bus);
+
+ ret = brcmf_fw_map_chip_to_name(devinfo->ci->chip, devinfo->ci->chiprev,
+ brcmf_pcie_fwnames,
+ ARRAY_SIZE(brcmf_pcie_fwnames),
+ devinfo->fw_name, devinfo->nvram_name);
+ if (ret)
+ goto fail_bus;
+
+ ret = brcmf_fw_get_firmwares_pcie(bus->dev, BRCMF_FW_REQUEST_NVRAM |
+ BRCMF_FW_REQ_NV_OPTIONAL,
+ devinfo->fw_name, devinfo->nvram_name,
+ brcmf_pcie_setup, domain_nr, bus_nr);
+ if (ret == 0)
+ return 0;
+fail_bus:
+ kfree(bus->msgbuf);
+ kfree(bus);
+fail:
+ brcmf_err("failed %x:%x\n", pdev->vendor, pdev->device);
+ brcmf_pcie_release_resource(devinfo);
+ if (devinfo->ci)
+ brcmf_chip_detach(devinfo->ci);
+ if (devinfo->settings)
+ brcmf_release_module_param(devinfo->settings);
+ kfree(pcie_bus_dev);
+ kfree(devinfo);
+ return ret;
+}
+
+
+static void
+brcmf_pcie_remove(struct pci_dev *pdev)
+{
+ struct brcmf_pciedev_info *devinfo;
+ struct brcmf_bus *bus;
+
+ brcmf_dbg(PCIE, "Enter\n");
+
+ bus = dev_get_drvdata(&pdev->dev);
+ if (bus == NULL)
+ return;
+
+ devinfo = bus->bus_priv.pcie->devinfo;
+
+ devinfo->state = BRCMFMAC_PCIE_STATE_DOWN;
+ if (devinfo->ci)
+ brcmf_pcie_intr_disable(devinfo);
+
+ brcmf_detach(&pdev->dev);
+
+ kfree(bus->bus_priv.pcie);
+ kfree(bus->msgbuf->flowrings);
+ kfree(bus->msgbuf);
+ kfree(bus);
+
+ brcmf_pcie_release_irq(devinfo);
+ brcmf_pcie_release_scratchbuffers(devinfo);
+ brcmf_pcie_release_ringbuffers(devinfo);
+ brcmf_pcie_reset_device(devinfo);
+ brcmf_pcie_release_resource(devinfo);
+
+ if (devinfo->ci)
+ brcmf_chip_detach(devinfo->ci);
+ if (devinfo->settings)
+ brcmf_release_module_param(devinfo->settings);
+
+ kfree(devinfo);
+ dev_set_drvdata(&pdev->dev, NULL);
+}
+
+
+#ifdef CONFIG_PM
+
+
+static int brcmf_pcie_pm_enter_D3(struct device *dev)
+{
+ struct brcmf_pciedev_info *devinfo;
+ struct brcmf_bus *bus;
+
+ brcmf_dbg(PCIE, "Enter\n");
+
+ bus = dev_get_drvdata(dev);
+ devinfo = bus->bus_priv.pcie->devinfo;
+
+ brcmf_bus_change_state(bus, BRCMF_BUS_DOWN);
+
+ devinfo->mbdata_completed = false;
+ brcmf_pcie_send_mb_data(devinfo, BRCMF_H2D_HOST_D3_INFORM);
+
+ wait_event_timeout(devinfo->mbdata_resp_wait, devinfo->mbdata_completed,
+ BRCMF_PCIE_MBDATA_TIMEOUT);
+ if (!devinfo->mbdata_completed) {
+ brcmf_err("Timeout on response for entering D3 substate\n");
+ brcmf_bus_change_state(bus, BRCMF_BUS_UP);
+ return -EIO;
+ }
+
+ devinfo->state = BRCMFMAC_PCIE_STATE_DOWN;
+
+ return 0;
+}
+
+
+static int brcmf_pcie_pm_leave_D3(struct device *dev)
+{
+ struct brcmf_pciedev_info *devinfo;
+ struct brcmf_bus *bus;
+ struct pci_dev *pdev;
+ int err;
+
+ brcmf_dbg(PCIE, "Enter\n");
+
+ bus = dev_get_drvdata(dev);
+ devinfo = bus->bus_priv.pcie->devinfo;
+ brcmf_dbg(PCIE, "Enter, dev=%p, bus=%p\n", dev, bus);
+
+ /* Check if device is still up and running, if so we are ready */
+ if (brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_INTMASK) != 0) {
+ brcmf_dbg(PCIE, "Try to wakeup device....\n");
+ if (brcmf_pcie_send_mb_data(devinfo, BRCMF_H2D_HOST_D0_INFORM))
+ goto cleanup;
+ brcmf_dbg(PCIE, "Hot resume, continue....\n");
+ devinfo->state = BRCMFMAC_PCIE_STATE_UP;
+ brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2);
+ brcmf_bus_change_state(bus, BRCMF_BUS_UP);
+ brcmf_pcie_intr_enable(devinfo);
+ return 0;
+ }
+
+cleanup:
+ brcmf_chip_detach(devinfo->ci);
+ devinfo->ci = NULL;
+ pdev = devinfo->pdev;
+ brcmf_pcie_remove(pdev);
+
+ err = brcmf_pcie_probe(pdev, NULL);
+ if (err)
+ brcmf_err("probe after resume failed, err=%d\n", err);
+
+ return err;
+}
+
+
+static const struct dev_pm_ops brcmf_pciedrvr_pm = {
+ .suspend = brcmf_pcie_pm_enter_D3,
+ .resume = brcmf_pcie_pm_leave_D3,
+ .freeze = brcmf_pcie_pm_enter_D3,
+ .restore = brcmf_pcie_pm_leave_D3,
+};
+
+
+#endif /* CONFIG_PM */
+
+
+#define BRCMF_PCIE_DEVICE(dev_id) { BRCM_PCIE_VENDOR_ID_BROADCOM, dev_id,\
+ PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_OTHER << 8, 0xffff00, 0 }
+#define BRCMF_PCIE_DEVICE_SUB(dev_id, subvend, subdev) { \
+ BRCM_PCIE_VENDOR_ID_BROADCOM, dev_id,\
+ subvend, subdev, PCI_CLASS_NETWORK_OTHER << 8, 0xffff00, 0 }
+
+static struct pci_device_id brcmf_pcie_devid_table[] = {
+ BRCMF_PCIE_DEVICE(BRCM_PCIE_4350_DEVICE_ID),
+ BRCMF_PCIE_DEVICE(BRCM_PCIE_4356_DEVICE_ID),
+ BRCMF_PCIE_DEVICE(BRCM_PCIE_43567_DEVICE_ID),
+ BRCMF_PCIE_DEVICE(BRCM_PCIE_43570_DEVICE_ID),
+ BRCMF_PCIE_DEVICE(BRCM_PCIE_4358_DEVICE_ID),
+ BRCMF_PCIE_DEVICE(BRCM_PCIE_4359_DEVICE_ID),
+ BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_DEVICE_ID),
+ BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_2G_DEVICE_ID),
+ BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_5G_DEVICE_ID),
+ BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_RAW_DEVICE_ID),
+ BRCMF_PCIE_DEVICE(BRCM_PCIE_4365_DEVICE_ID),
+ BRCMF_PCIE_DEVICE(BRCM_PCIE_4365_2G_DEVICE_ID),
+ BRCMF_PCIE_DEVICE(BRCM_PCIE_4365_5G_DEVICE_ID),
+ BRCMF_PCIE_DEVICE_SUB(0x4365, BRCM_PCIE_VENDOR_ID_BROADCOM, 0x4365),
+ BRCMF_PCIE_DEVICE(BRCM_PCIE_4366_DEVICE_ID),
+ BRCMF_PCIE_DEVICE(BRCM_PCIE_4366_2G_DEVICE_ID),
+ BRCMF_PCIE_DEVICE(BRCM_PCIE_4366_5G_DEVICE_ID),
+ BRCMF_PCIE_DEVICE(BRCM_PCIE_4371_DEVICE_ID),
+ { /* end: all zeroes */ }
+};
+
+
+MODULE_DEVICE_TABLE(pci, brcmf_pcie_devid_table);
+
+
+static struct pci_driver brcmf_pciedrvr = {
+ .node = {},
+ .name = KBUILD_MODNAME,
+ .id_table = brcmf_pcie_devid_table,
+ .probe = brcmf_pcie_probe,
+ .remove = brcmf_pcie_remove,
+#ifdef CONFIG_PM
+ .driver.pm = &brcmf_pciedrvr_pm,
+#endif
+};
+
+
+void brcmf_pcie_register(void)
+{
+ int err;
+
+ brcmf_dbg(PCIE, "Enter\n");
+ err = pci_register_driver(&brcmf_pciedrvr);
+ if (err)
+ brcmf_err("PCIE driver registration failed, err=%d\n", err);
+}
+
+
+void brcmf_pcie_exit(void)
+{
+ brcmf_dbg(PCIE, "Enter\n");
+ pci_unregister_driver(&brcmf_pciedrvr);
+}
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.h
new file mode 100644
index 0000000..6edaaf8
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.h
@@ -0,0 +1,29 @@
+/* Copyright (c) 2014 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#ifndef BRCMFMAC_PCIE_H
+#define BRCMFMAC_PCIE_H
+
+
+struct brcmf_pciedev {
+ struct brcmf_bus *bus;
+ struct brcmf_pciedev_info *devinfo;
+};
+
+
+void brcmf_pcie_exit(void);
+void brcmf_pcie_register(void);
+
+
+#endif /* BRCMFMAC_PCIE_H */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c
new file mode 100644
index 0000000..6c3bde8
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c
@@ -0,0 +1,241 @@
+/*
+ * Copyright (c) 2016 Broadcom
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <linux/netdevice.h>
+#include <net/cfg80211.h>
+
+#include "core.h"
+#include "debug.h"
+#include "fwil.h"
+#include "fwil_types.h"
+#include "cfg80211.h"
+#include "pno.h"
+
+#define BRCMF_PNO_VERSION 2
+#define BRCMF_PNO_REPEAT 4
+#define BRCMF_PNO_FREQ_EXPO_MAX 3
+#define BRCMF_PNO_IMMEDIATE_SCAN_BIT 3
+#define BRCMF_PNO_ENABLE_BD_SCAN_BIT 5
+#define BRCMF_PNO_ENABLE_ADAPTSCAN_BIT 6
+#define BRCMF_PNO_REPORT_SEPARATELY_BIT 11
+#define BRCMF_PNO_SCAN_INCOMPLETE 0
+#define BRCMF_PNO_WPA_AUTH_ANY 0xFFFFFFFF
+#define BRCMF_PNO_HIDDEN_BIT 2
+#define BRCMF_PNO_SCHED_SCAN_PERIOD 30
+
+static int brcmf_pno_channel_config(struct brcmf_if *ifp,
+ struct brcmf_pno_config_le *cfg)
+{
+ cfg->reporttype = 0;
+ cfg->flags = 0;
+
+ return brcmf_fil_iovar_data_set(ifp, "pfn_cfg", cfg, sizeof(*cfg));
+}
+
+static int brcmf_pno_config(struct brcmf_if *ifp, u32 scan_freq,
+ u32 mscan, u32 bestn)
+{
+ struct brcmf_pno_param_le pfn_param;
+ u16 flags;
+ u32 pfnmem;
+ s32 err;
+
+ memset(&pfn_param, 0, sizeof(pfn_param));
+ pfn_param.version = cpu_to_le32(BRCMF_PNO_VERSION);
+
+ /* set extra pno params */
+ flags = BIT(BRCMF_PNO_IMMEDIATE_SCAN_BIT) |
+ BIT(BRCMF_PNO_REPORT_SEPARATELY_BIT) |
+ BIT(BRCMF_PNO_ENABLE_ADAPTSCAN_BIT);
+ pfn_param.repeat = BRCMF_PNO_REPEAT;
+ pfn_param.exp = BRCMF_PNO_FREQ_EXPO_MAX;
+
+ /* set up pno scan fr */
+ if (scan_freq < BRCMF_PNO_SCHED_SCAN_MIN_PERIOD) {
+ brcmf_dbg(SCAN, "scan period too small, using minimum\n");
+ scan_freq = BRCMF_PNO_SCHED_SCAN_MIN_PERIOD;
+ }
+ pfn_param.scan_freq = cpu_to_le32(scan_freq);
+
+ if (mscan) {
+ pfnmem = bestn;
+
+ /* set bestn in firmware */
+ err = brcmf_fil_iovar_int_set(ifp, "pfnmem", pfnmem);
+ if (err < 0) {
+ brcmf_err("failed to set pfnmem\n");
+ goto exit;
+ }
+ /* get max mscan which the firmware supports */
+ err = brcmf_fil_iovar_int_get(ifp, "pfnmem", &pfnmem);
+ if (err < 0) {
+ brcmf_err("failed to get pfnmem\n");
+ goto exit;
+ }
+ mscan = min_t(u32, mscan, pfnmem);
+ pfn_param.mscan = mscan;
+ pfn_param.bestn = bestn;
+ flags |= BIT(BRCMF_PNO_ENABLE_BD_SCAN_BIT);
+ brcmf_dbg(INFO, "mscan=%d, bestn=%d\n", mscan, bestn);
+ }
+
+ pfn_param.flags = cpu_to_le16(flags);
+ err = brcmf_fil_iovar_data_set(ifp, "pfn_set", &pfn_param,
+ sizeof(pfn_param));
+ if (err)
+ brcmf_err("pfn_set failed, err=%d\n", err);
+
+exit:
+ return err;
+}
+
+static int brcmf_pno_set_random(struct brcmf_if *ifp, u8 *mac_addr,
+ u8 *mac_mask)
+{
+ struct brcmf_pno_macaddr_le pfn_mac;
+ int err, i;
+
+ pfn_mac.version = BRCMF_PFN_MACADDR_CFG_VER;
+ pfn_mac.flags = BRCMF_PFN_MAC_OUI_ONLY | BRCMF_PFN_SET_MAC_UNASSOC;
+
+ memcpy(pfn_mac.mac, mac_addr, ETH_ALEN);
+ for (i = 0; i < ETH_ALEN; i++) {
+ pfn_mac.mac[i] &= mac_mask[i];
+ pfn_mac.mac[i] |= get_random_int() & ~(mac_mask[i]);
+ }
+ /* Clear multi bit */
+ pfn_mac.mac[0] &= 0xFE;
+ /* Set locally administered */
+ pfn_mac.mac[0] |= 0x02;
+
+ err = brcmf_fil_iovar_data_set(ifp, "pfn_macaddr", &pfn_mac,
+ sizeof(pfn_mac));
+ if (err)
+ brcmf_err("pfn_macaddr failed, err=%d\n", err);
+
+ return err;
+}
+
+static int brcmf_pno_add_ssid(struct brcmf_if *ifp, struct cfg80211_ssid *ssid,
+ bool active)
+{
+ struct brcmf_pno_net_param_le pfn;
+
+ pfn.auth = cpu_to_le32(WLAN_AUTH_OPEN);
+ pfn.wpa_auth = cpu_to_le32(BRCMF_PNO_WPA_AUTH_ANY);
+ pfn.wsec = cpu_to_le32(0);
+ pfn.infra = cpu_to_le32(1);
+ pfn.flags = 0;
+ if (active)
+ pfn.flags = cpu_to_le32(1 << BRCMF_PNO_HIDDEN_BIT);
+ pfn.ssid.SSID_len = cpu_to_le32(ssid->ssid_len);
+ memcpy(pfn.ssid.SSID, ssid->ssid, ssid->ssid_len);
+ return brcmf_fil_iovar_data_set(ifp, "pfn_add", &pfn, sizeof(pfn));
+}
+
+static bool brcmf_is_ssid_active(struct cfg80211_ssid *ssid,
+ struct cfg80211_sched_scan_request *req)
+{
+ int i;
+
+ if (!ssid || !req->ssids || !req->n_ssids)
+ return false;
+
+ for (i = 0; i < req->n_ssids; i++) {
+ if (ssid->ssid_len == req->ssids[i].ssid_len) {
+ if (!strncmp(ssid->ssid, req->ssids[i].ssid,
+ ssid->ssid_len))
+ return true;
+ }
+ }
+ return false;
+}
+
+int brcmf_pno_clean(struct brcmf_if *ifp)
+{
+ int ret;
+
+ /* Disable pfn */
+ ret = brcmf_fil_iovar_int_set(ifp, "pfn", 0);
+ if (ret == 0) {
+ /* clear pfn */
+ ret = brcmf_fil_iovar_data_set(ifp, "pfnclear", NULL, 0);
+ }
+ if (ret < 0)
+ brcmf_err("failed code %d\n", ret);
+
+ return ret;
+}
+
+int brcmf_pno_start_sched_scan(struct brcmf_if *ifp,
+ struct cfg80211_sched_scan_request *req)
+{
+ struct brcmf_pno_config_le pno_cfg;
+ struct cfg80211_ssid *ssid;
+ u16 chan;
+ int i, ret;
+
+ /* clean up everything */
+ ret = brcmf_pno_clean(ifp);
+ if (ret < 0) {
+ brcmf_err("failed error=%d\n", ret);
+ return ret;
+ }
+
+ /* configure pno */
+ ret = brcmf_pno_config(ifp, req->scan_plans[0].interval, 0, 0);
+ if (ret < 0)
+ return ret;
+
+ /* configure random mac */
+ if (req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) {
+ ret = brcmf_pno_set_random(ifp, req->mac_addr,
+ req->mac_addr_mask);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* configure channels to use */
+ for (i = 0; i < req->n_channels; i++) {
+ chan = req->channels[i]->hw_value;
+ pno_cfg.channel_list[i] = cpu_to_le16(chan);
+ }
+ if (req->n_channels) {
+ pno_cfg.channel_num = cpu_to_le32(req->n_channels);
+ brcmf_pno_channel_config(ifp, &pno_cfg);
+ }
+
+ /* configure each match set */
+ for (i = 0; i < req->n_match_sets; i++) {
+ ssid = &req->match_sets[i].ssid;
+ if (!ssid->ssid_len) {
+ brcmf_err("skip broadcast ssid\n");
+ continue;
+ }
+
+ ret = brcmf_pno_add_ssid(ifp, ssid,
+ brcmf_is_ssid_active(ssid, req));
+ if (ret < 0)
+ brcmf_dbg(SCAN, ">>> PNO filter %s for ssid (%s)\n",
+ ret == 0 ? "set" : "failed", ssid->ssid);
+ }
+ /* Enable the PNO */
+ ret = brcmf_fil_iovar_int_set(ifp, "pfn", 1);
+ if (ret < 0)
+ brcmf_err("PNO enable failed!! ret=%d\n", ret);
+
+ return ret;
+}
+
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.h
new file mode 100644
index 0000000..bae55b2
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2016 Broadcom
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#ifndef _BRCMF_PNO_H
+#define _BRCMF_PNO_H
+
+#define BRCMF_PNO_SCAN_COMPLETE 1
+#define BRCMF_PNO_MAX_PFN_COUNT 16
+#define BRCMF_PNO_SCHED_SCAN_MIN_PERIOD 10
+#define BRCMF_PNO_SCHED_SCAN_MAX_PERIOD 508
+
+/**
+ * brcmf_pno_clean - disable and clear pno in firmware.
+ *
+ * @ifp: interface object used.
+ */
+int brcmf_pno_clean(struct brcmf_if *ifp);
+
+/**
+ * brcmf_pno_start_sched_scan - initiate scheduled scan on device.
+ *
+ * @ifp: interface object used.
+ * @req: configuration parameters for scheduled scan.
+ */
+int brcmf_pno_start_sched_scan(struct brcmf_if *ifp,
+ struct cfg80211_sched_scan_request *req);
+
+#endif /* _BRCMF_PNO_H */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.c
new file mode 100644
index 0000000..d26ff21
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.c
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2013 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+
+ #include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/netdevice.h>
+
+#include <brcmu_wifi.h>
+#include "core.h"
+#include "bus.h"
+#include "debug.h"
+#include "proto.h"
+#include "bcdc.h"
+#include "msgbuf.h"
+
+
+int brcmf_proto_attach(struct brcmf_pub *drvr)
+{
+ struct brcmf_proto *proto;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ proto = kzalloc(sizeof(*proto), GFP_ATOMIC);
+ if (!proto)
+ goto fail;
+
+ drvr->proto = proto;
+
+ if (drvr->bus_if->proto_type == BRCMF_PROTO_BCDC) {
+ if (brcmf_proto_bcdc_attach(drvr))
+ goto fail;
+ } else if (drvr->bus_if->proto_type == BRCMF_PROTO_MSGBUF) {
+ if (brcmf_proto_msgbuf_attach(drvr))
+ goto fail;
+ } else {
+ brcmf_err("Unsupported proto type %d\n",
+ drvr->bus_if->proto_type);
+ goto fail;
+ }
+ if (!proto->tx_queue_data || (proto->hdrpull == NULL) ||
+ (proto->query_dcmd == NULL) || (proto->set_dcmd == NULL) ||
+ (proto->configure_addr_mode == NULL) ||
+ (proto->delete_peer == NULL) || (proto->add_tdls_peer == NULL)) {
+ brcmf_err("Not all proto handlers have been installed\n");
+ goto fail;
+ }
+ return 0;
+
+fail:
+ kfree(proto);
+ drvr->proto = NULL;
+ return -ENOMEM;
+}
+
+void brcmf_proto_detach(struct brcmf_pub *drvr)
+{
+ brcmf_dbg(TRACE, "Enter\n");
+
+ if (drvr->proto) {
+ if (drvr->bus_if->proto_type == BRCMF_PROTO_BCDC)
+ brcmf_proto_bcdc_detach(drvr);
+ else if (drvr->bus_if->proto_type == BRCMF_PROTO_MSGBUF)
+ brcmf_proto_msgbuf_detach(drvr);
+ kfree(drvr->proto);
+ drvr->proto = NULL;
+ }
+}
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h
new file mode 100644
index 0000000..2404f8a
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2013 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#ifndef BRCMFMAC_PROTO_H
+#define BRCMFMAC_PROTO_H
+
+
+enum proto_addr_mode {
+ ADDR_INDIRECT = 0,
+ ADDR_DIRECT
+};
+
+struct brcmf_skb_reorder_data {
+ u8 *reorder;
+};
+
+struct brcmf_proto {
+ int (*hdrpull)(struct brcmf_pub *drvr, bool do_fws,
+ struct sk_buff *skb, struct brcmf_if **ifp);
+ int (*query_dcmd)(struct brcmf_pub *drvr, int ifidx, uint cmd,
+ void *buf, uint len);
+ int (*set_dcmd)(struct brcmf_pub *drvr, int ifidx, uint cmd, void *buf,
+ uint len);
+ int (*tx_queue_data)(struct brcmf_pub *drvr, int ifidx,
+ struct sk_buff *skb);
+ int (*txdata)(struct brcmf_pub *drvr, int ifidx, u8 offset,
+ struct sk_buff *skb);
+ void (*configure_addr_mode)(struct brcmf_pub *drvr, int ifidx,
+ enum proto_addr_mode addr_mode);
+ void (*delete_peer)(struct brcmf_pub *drvr, int ifidx,
+ u8 peer[ETH_ALEN]);
+ void (*add_tdls_peer)(struct brcmf_pub *drvr, int ifidx,
+ u8 peer[ETH_ALEN]);
+ void (*rxreorder)(struct brcmf_if *ifp, struct sk_buff *skb);
+ void (*add_if)(struct brcmf_if *ifp);
+ void (*del_if)(struct brcmf_if *ifp);
+ void (*reset_if)(struct brcmf_if *ifp);
+ int (*init_done)(struct brcmf_pub *drvr);
+ void *pd;
+};
+
+
+int brcmf_proto_attach(struct brcmf_pub *drvr);
+void brcmf_proto_detach(struct brcmf_pub *drvr);
+
+static inline int brcmf_proto_hdrpull(struct brcmf_pub *drvr, bool do_fws,
+ struct sk_buff *skb,
+ struct brcmf_if **ifp)
+{
+ struct brcmf_if *tmp = NULL;
+
+ /* assure protocol is always called with
+ * non-null initialized pointer.
+ */
+ if (ifp)
+ *ifp = NULL;
+ else
+ ifp = &tmp;
+ return drvr->proto->hdrpull(drvr, do_fws, skb, ifp);
+}
+static inline int brcmf_proto_query_dcmd(struct brcmf_pub *drvr, int ifidx,
+ uint cmd, void *buf, uint len)
+{
+ return drvr->proto->query_dcmd(drvr, ifidx, cmd, buf, len);
+}
+static inline int brcmf_proto_set_dcmd(struct brcmf_pub *drvr, int ifidx,
+ uint cmd, void *buf, uint len)
+{
+ return drvr->proto->set_dcmd(drvr, ifidx, cmd, buf, len);
+}
+
+static inline int brcmf_proto_tx_queue_data(struct brcmf_pub *drvr, int ifidx,
+ struct sk_buff *skb)
+{
+ return drvr->proto->tx_queue_data(drvr, ifidx, skb);
+}
+
+static inline int brcmf_proto_txdata(struct brcmf_pub *drvr, int ifidx,
+ u8 offset, struct sk_buff *skb)
+{
+ return drvr->proto->txdata(drvr, ifidx, offset, skb);
+}
+static inline void
+brcmf_proto_configure_addr_mode(struct brcmf_pub *drvr, int ifidx,
+ enum proto_addr_mode addr_mode)
+{
+ drvr->proto->configure_addr_mode(drvr, ifidx, addr_mode);
+}
+static inline void
+brcmf_proto_delete_peer(struct brcmf_pub *drvr, int ifidx, u8 peer[ETH_ALEN])
+{
+ drvr->proto->delete_peer(drvr, ifidx, peer);
+}
+static inline void
+brcmf_proto_add_tdls_peer(struct brcmf_pub *drvr, int ifidx, u8 peer[ETH_ALEN])
+{
+ drvr->proto->add_tdls_peer(drvr, ifidx, peer);
+}
+static inline bool brcmf_proto_is_reorder_skb(struct sk_buff *skb)
+{
+ struct brcmf_skb_reorder_data *rd;
+
+ rd = (struct brcmf_skb_reorder_data *)skb->cb;
+ return !!rd->reorder;
+}
+
+static inline void
+brcmf_proto_rxreorder(struct brcmf_if *ifp, struct sk_buff *skb)
+{
+ ifp->drvr->proto->rxreorder(ifp, skb);
+}
+
+static inline void
+brcmf_proto_add_if(struct brcmf_pub *drvr, struct brcmf_if *ifp)
+{
+ if (!drvr->proto->add_if)
+ return;
+ drvr->proto->add_if(ifp);
+}
+
+static inline void
+brcmf_proto_del_if(struct brcmf_pub *drvr, struct brcmf_if *ifp)
+{
+ if (!drvr->proto->del_if)
+ return;
+ drvr->proto->del_if(ifp);
+}
+
+static inline void
+brcmf_proto_reset_if(struct brcmf_pub *drvr, struct brcmf_if *ifp)
+{
+ if (!drvr->proto->reset_if)
+ return;
+ drvr->proto->reset_if(ifp);
+}
+
+static inline int
+brcmf_proto_init_done(struct brcmf_pub *drvr)
+{
+ if (!drvr->proto->init_done)
+ return 0;
+ return drvr->proto->init_done(drvr);
+}
+
+#endif /* BRCMFMAC_PROTO_H */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
new file mode 100644
index 0000000..3c3e7eb
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
@@ -0,0 +1,4443 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/types.h>
+#include <linux/atomic.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/printk.h>
+#include <linux/pci_ids.h>
+#include <linux/netdevice.h>
+#include <linux/interrupt.h>
+#include <linux/sched/signal.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/sdio_ids.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/card.h>
+#include <linux/semaphore.h>
+#include <linux/firmware.h>
+#include <linux/module.h>
+#include <linux/bcma/bcma.h>
+#include <linux/debugfs.h>
+#include <linux/vmalloc.h>
+#include <asm/unaligned.h>
+#include <defs.h>
+#include <brcmu_wifi.h>
+#include <brcmu_utils.h>
+#include <brcm_hw_ids.h>
+#include <soc.h>
+#include "sdio.h"
+#include "chip.h"
+#include "firmware.h"
+#include "core.h"
+#include "common.h"
+#include "bcdc.h"
+
+#define DCMD_RESP_TIMEOUT msecs_to_jiffies(2500)
+#define CTL_DONE_TIMEOUT msecs_to_jiffies(2500)
+
+#define DEFAULT_F2_WATERMARK 0x8
+#define CY_4373_F2_WATERMARK 0x40
+
+#define OVERFLOW_BLKSZ512_WM 96
+#define OVERFLOW_BLKSZ512_MES 80
+
+#ifdef DEBUG
+
+#define BRCMF_TRAP_INFO_SIZE 80
+
+#define CBUF_LEN (128)
+
+/* Device console log buffer state */
+#define CONSOLE_BUFFER_MAX 2024
+
+struct rte_log_le {
+ __le32 buf; /* Can't be pointer on (64-bit) hosts */
+ __le32 buf_size;
+ __le32 idx;
+ char *_buf_compat; /* Redundant pointer for backward compat. */
+};
+
+struct rte_console {
+ /* Virtual UART
+ * When there is no UART (e.g. Quickturn),
+ * the host should write a complete
+ * input line directly into cbuf and then write
+ * the length into vcons_in.
+ * This may also be used when there is a real UART
+ * (at risk of conflicting with
+ * the real UART). vcons_out is currently unused.
+ */
+ uint vcons_in;
+ uint vcons_out;
+
+ /* Output (logging) buffer
+ * Console output is written to a ring buffer log_buf at index log_idx.
+ * The host may read the output when it sees log_idx advance.
+ * Output will be lost if the output wraps around faster than the host
+ * polls.
+ */
+ struct rte_log_le log_le;
+
+ /* Console input line buffer
+ * Characters are read one at a time into cbuf
+ * until <CR> is received, then
+ * the buffer is processed as a command line.
+ * Also used for virtual UART.
+ */
+ uint cbuf_idx;
+ char cbuf[CBUF_LEN];
+};
+
+#endif /* DEBUG */
+#include <chipcommon.h>
+
+#include "bus.h"
+#include "debug.h"
+#include "tracepoint.h"
+
+#define TXQLEN 2048 /* bulk tx queue length */
+#define TXHI (TXQLEN - 256) /* turn on flow control above TXHI */
+#define TXLOW (TXHI - 256) /* turn off flow control below TXLOW */
+#define PRIOMASK 7
+
+#define TXRETRIES 2 /* # of retries for tx frames */
+
+#define BRCMF_RXBOUND 50 /* Default for max rx frames in
+ one scheduling */
+
+#define BRCMF_TXBOUND 20 /* Default for max tx frames in
+ one scheduling */
+
+#define BRCMF_TXMINMAX 1 /* Max tx frames if rx still pending */
+
+#define MEMBLOCK 2048 /* Block size used for downloading
+ of dongle image */
+#define MAX_DATA_BUF (32 * 1024) /* Must be large enough to hold
+ biggest possible glom */
+
+#define BRCMF_FIRSTREAD (1 << 6)
+
+#define BRCMF_CONSOLE 10 /* watchdog interval to poll console */
+
+/* SBSDIO_DEVICE_CTL */
+
+/* 1: device will assert busy signal when receiving CMD53 */
+#define SBSDIO_DEVCTL_SETBUSY 0x01
+/* 1: assertion of sdio interrupt is synchronous to the sdio clock */
+#define SBSDIO_DEVCTL_SPI_INTR_SYNC 0x02
+/* 1: mask all interrupts to host except the chipActive (rev 8) */
+#define SBSDIO_DEVCTL_CA_INT_ONLY 0x04
+/* 1: isolate internal sdio signals, put external pads in tri-state; requires
+ * sdio bus power cycle to clear (rev 9) */
+#define SBSDIO_DEVCTL_PADS_ISO 0x08
+/* 1: enable F2 Watermark */
+#define SBSDIO_DEVCTL_F2WM_ENAB 0x10
+/* Force SD->SB reset mapping (rev 11) */
+#define SBSDIO_DEVCTL_SB_RST_CTL 0x30
+/* Determined by CoreControl bit */
+#define SBSDIO_DEVCTL_RST_CORECTL 0x00
+/* Force backplane reset */
+#define SBSDIO_DEVCTL_RST_BPRESET 0x10
+/* Force no backplane reset */
+#define SBSDIO_DEVCTL_RST_NOBPRESET 0x20
+
+/* direct(mapped) cis space */
+
+/* MAPPED common CIS address */
+#define SBSDIO_CIS_BASE_COMMON 0x1000
+/* maximum bytes in one CIS */
+#define SBSDIO_CIS_SIZE_LIMIT 0x200
+/* cis offset addr is < 17 bits */
+#define SBSDIO_CIS_OFT_ADDR_MASK 0x1FFFF
+
+/* manfid tuple length, include tuple, link bytes */
+#define SBSDIO_CIS_MANFID_TUPLE_LEN 6
+
+#define CORE_BUS_REG(base, field) \
+ (base + offsetof(struct sdpcmd_regs, field))
+
+/* SDIO function 1 register CHIPCLKCSR */
+/* Force ALP request to backplane */
+#define SBSDIO_FORCE_ALP 0x01
+/* Force HT request to backplane */
+#define SBSDIO_FORCE_HT 0x02
+/* Force ILP request to backplane */
+#define SBSDIO_FORCE_ILP 0x04
+/* Make ALP ready (power up xtal) */
+#define SBSDIO_ALP_AVAIL_REQ 0x08
+/* Make HT ready (power up PLL) */
+#define SBSDIO_HT_AVAIL_REQ 0x10
+/* Squelch clock requests from HW */
+#define SBSDIO_FORCE_HW_CLKREQ_OFF 0x20
+/* Status: ALP is ready */
+#define SBSDIO_ALP_AVAIL 0x40
+/* Status: HT is ready */
+#define SBSDIO_HT_AVAIL 0x80
+#define SBSDIO_CSR_MASK 0x1F
+#define SBSDIO_AVBITS (SBSDIO_HT_AVAIL | SBSDIO_ALP_AVAIL)
+#define SBSDIO_ALPAV(regval) ((regval) & SBSDIO_AVBITS)
+#define SBSDIO_HTAV(regval) (((regval) & SBSDIO_AVBITS) == SBSDIO_AVBITS)
+#define SBSDIO_ALPONLY(regval) (SBSDIO_ALPAV(regval) && !SBSDIO_HTAV(regval))
+#define SBSDIO_CLKAV(regval, alponly) \
+ (SBSDIO_ALPAV(regval) && (alponly ? 1 : SBSDIO_HTAV(regval)))
+
+/* intstatus */
+#define I_SMB_SW0 (1 << 0) /* To SB Mail S/W interrupt 0 */
+#define I_SMB_SW1 (1 << 1) /* To SB Mail S/W interrupt 1 */
+#define I_SMB_SW2 (1 << 2) /* To SB Mail S/W interrupt 2 */
+#define I_SMB_SW3 (1 << 3) /* To SB Mail S/W interrupt 3 */
+#define I_SMB_SW_MASK 0x0000000f /* To SB Mail S/W interrupts mask */
+#define I_SMB_SW_SHIFT 0 /* To SB Mail S/W interrupts shift */
+#define I_HMB_SW0 (1 << 4) /* To Host Mail S/W interrupt 0 */
+#define I_HMB_SW1 (1 << 5) /* To Host Mail S/W interrupt 1 */
+#define I_HMB_SW2 (1 << 6) /* To Host Mail S/W interrupt 2 */
+#define I_HMB_SW3 (1 << 7) /* To Host Mail S/W interrupt 3 */
+#define I_HMB_SW_MASK 0x000000f0 /* To Host Mail S/W interrupts mask */
+#define I_HMB_SW_SHIFT 4 /* To Host Mail S/W interrupts shift */
+#define I_WR_OOSYNC (1 << 8) /* Write Frame Out Of Sync */
+#define I_RD_OOSYNC (1 << 9) /* Read Frame Out Of Sync */
+#define I_PC (1 << 10) /* descriptor error */
+#define I_PD (1 << 11) /* data error */
+#define I_DE (1 << 12) /* Descriptor protocol Error */
+#define I_RU (1 << 13) /* Receive descriptor Underflow */
+#define I_RO (1 << 14) /* Receive fifo Overflow */
+#define I_XU (1 << 15) /* Transmit fifo Underflow */
+#define I_RI (1 << 16) /* Receive Interrupt */
+#define I_BUSPWR (1 << 17) /* SDIO Bus Power Change (rev 9) */
+#define I_XMTDATA_AVAIL (1 << 23) /* bits in fifo */
+#define I_XI (1 << 24) /* Transmit Interrupt */
+#define I_RF_TERM (1 << 25) /* Read Frame Terminate */
+#define I_WF_TERM (1 << 26) /* Write Frame Terminate */
+#define I_PCMCIA_XU (1 << 27) /* PCMCIA Transmit FIFO Underflow */
+#define I_SBINT (1 << 28) /* sbintstatus Interrupt */
+#define I_CHIPACTIVE (1 << 29) /* chip from doze to active state */
+#define I_SRESET (1 << 30) /* CCCR RES interrupt */
+#define I_IOE2 (1U << 31) /* CCCR IOE2 Bit Changed */
+#define I_ERRORS (I_PC | I_PD | I_DE | I_RU | I_RO | I_XU)
+#define I_DMA (I_RI | I_XI | I_ERRORS)
+
+/* corecontrol */
+#define CC_CISRDY (1 << 0) /* CIS Ready */
+#define CC_BPRESEN (1 << 1) /* CCCR RES signal */
+#define CC_F2RDY (1 << 2) /* set CCCR IOR2 bit */
+#define CC_CLRPADSISO (1 << 3) /* clear SDIO pads isolation */
+#define CC_XMTDATAAVAIL_MODE (1 << 4)
+#define CC_XMTDATAAVAIL_CTRL (1 << 5)
+
+/* SDA_FRAMECTRL */
+#define SFC_RF_TERM (1 << 0) /* Read Frame Terminate */
+#define SFC_WF_TERM (1 << 1) /* Write Frame Terminate */
+#define SFC_CRC4WOOS (1 << 2) /* CRC error for write out of sync */
+#define SFC_ABORTALL (1 << 3) /* Abort all in-progress frames */
+
+/*
+ * Software allocation of To SB Mailbox resources
+ */
+
+/* tosbmailbox bits corresponding to intstatus bits */
+#define SMB_NAK (1 << 0) /* Frame NAK */
+#define SMB_INT_ACK (1 << 1) /* Host Interrupt ACK */
+#define SMB_USE_OOB (1 << 2) /* Use OOB Wakeup */
+#define SMB_DEV_INT (1 << 3) /* Miscellaneous Interrupt */
+
+/* tosbmailboxdata */
+#define SMB_DATA_VERSION_SHIFT 16 /* host protocol version */
+
+/*
+ * Software allocation of To Host Mailbox resources
+ */
+
+/* intstatus bits */
+#define I_HMB_FC_STATE I_HMB_SW0 /* Flow Control State */
+#define I_HMB_FC_CHANGE I_HMB_SW1 /* Flow Control State Changed */
+#define I_HMB_FRAME_IND I_HMB_SW2 /* Frame Indication */
+#define I_HMB_HOST_INT I_HMB_SW3 /* Miscellaneous Interrupt */
+
+/* tohostmailboxdata */
+#define HMB_DATA_NAKHANDLED 1 /* retransmit NAK'd frame */
+#define HMB_DATA_DEVREADY 2 /* talk to host after enable */
+#define HMB_DATA_FC 4 /* per prio flowcontrol update flag */
+#define HMB_DATA_FWREADY 8 /* fw ready for protocol activity */
+
+#define HMB_DATA_FCDATA_MASK 0xff000000
+#define HMB_DATA_FCDATA_SHIFT 24
+
+#define HMB_DATA_VERSION_MASK 0x00ff0000
+#define HMB_DATA_VERSION_SHIFT 16
+
+/*
+ * Software-defined protocol header
+ */
+
+/* Current protocol version */
+#define SDPCM_PROT_VERSION 4
+
+/*
+ * Shared structure between dongle and the host.
+ * The structure contains pointers to trap or assert information.
+ */
+#define SDPCM_SHARED_VERSION 0x0003
+#define SDPCM_SHARED_VERSION_MASK 0x00FF
+#define SDPCM_SHARED_ASSERT_BUILT 0x0100
+#define SDPCM_SHARED_ASSERT 0x0200
+#define SDPCM_SHARED_TRAP 0x0400
+
+/* Space for header read, limit for data packets */
+#define MAX_HDR_READ (1 << 6)
+#define MAX_RX_DATASZ 2048
+
+/* Bump up limit on waiting for HT to account for first startup;
+ * if the image is doing a CRC calculation before programming the PMU
+ * for HT availability, it could take a couple hundred ms more, so
+ * max out at a 1 second (1000000us).
+ */
+#undef PMU_MAX_TRANSITION_DLY
+#define PMU_MAX_TRANSITION_DLY 1000000
+
+/* Value for ChipClockCSR during initial setup */
+#define BRCMF_INIT_CLKCTL1 (SBSDIO_FORCE_HW_CLKREQ_OFF | \
+ SBSDIO_ALP_AVAIL_REQ)
+
+/* Flags for SDH calls */
+#define F2SYNC (SDIO_REQ_4BYTE | SDIO_REQ_FIXED)
+
+#define BRCMF_IDLE_ACTIVE 0 /* Do not request any SD clock change
+ * when idle
+ */
+#define BRCMF_IDLE_INTERVAL 1
+
+#define KSO_WAIT_US 50
+#define MAX_KSO_ATTEMPTS (PMU_MAX_TRANSITION_DLY/KSO_WAIT_US)
+#define BRCMF_SDIO_MAX_ACCESS_ERRORS 5
+
+/*
+ * Conversion of 802.1D priority to precedence level
+ */
+static uint prio2prec(u32 prio)
+{
+ return (prio == PRIO_8021D_NONE || prio == PRIO_8021D_BE) ?
+ (prio^2) : prio;
+}
+
+#ifdef DEBUG
+/* Device console log buffer state */
+struct brcmf_console {
+ uint count; /* Poll interval msec counter */
+ uint log_addr; /* Log struct address (fixed) */
+ struct rte_log_le log_le; /* Log struct (host copy) */
+ uint bufsize; /* Size of log buffer */
+ u8 *buf; /* Log buffer (host copy) */
+ uint last; /* Last buffer read index */
+};
+
+struct brcmf_trap_info {
+ __le32 type;
+ __le32 epc;
+ __le32 cpsr;
+ __le32 spsr;
+ __le32 r0; /* a1 */
+ __le32 r1; /* a2 */
+ __le32 r2; /* a3 */
+ __le32 r3; /* a4 */
+ __le32 r4; /* v1 */
+ __le32 r5; /* v2 */
+ __le32 r6; /* v3 */
+ __le32 r7; /* v4 */
+ __le32 r8; /* v5 */
+ __le32 r9; /* sb/v6 */
+ __le32 r10; /* sl/v7 */
+ __le32 r11; /* fp/v8 */
+ __le32 r12; /* ip */
+ __le32 r13; /* sp */
+ __le32 r14; /* lr */
+ __le32 pc; /* r15 */
+};
+#endif /* DEBUG */
+
+struct sdpcm_shared {
+ u32 flags;
+ u32 trap_addr;
+ u32 assert_exp_addr;
+ u32 assert_file_addr;
+ u32 assert_line;
+ u32 console_addr; /* Address of struct rte_console */
+ u32 msgtrace_addr;
+ u8 tag[32];
+ u32 brpt_addr;
+};
+
+struct sdpcm_shared_le {
+ __le32 flags;
+ __le32 trap_addr;
+ __le32 assert_exp_addr;
+ __le32 assert_file_addr;
+ __le32 assert_line;
+ __le32 console_addr; /* Address of struct rte_console */
+ __le32 msgtrace_addr;
+ u8 tag[32];
+ __le32 brpt_addr;
+};
+
+/* dongle SDIO bus specific header info */
+struct brcmf_sdio_hdrinfo {
+ u8 seq_num;
+ u8 channel;
+ u16 len;
+ u16 len_left;
+ u16 len_nxtfrm;
+ u8 dat_offset;
+ bool lastfrm;
+ u16 tail_pad;
+};
+
+/*
+ * hold counter variables
+ */
+struct brcmf_sdio_count {
+ uint intrcount; /* Count of device interrupt callbacks */
+ uint lastintrs; /* Count as of last watchdog timer */
+ uint pollcnt; /* Count of active polls */
+ uint regfails; /* Count of R_REG failures */
+ uint tx_sderrs; /* Count of tx attempts with sd errors */
+ uint fcqueued; /* Tx packets that got queued */
+ uint rxrtx; /* Count of rtx requests (NAK to dongle) */
+ uint rx_toolong; /* Receive frames too long to receive */
+ uint rxc_errors; /* SDIO errors when reading control frames */
+ uint rx_hdrfail; /* SDIO errors on header reads */
+ uint rx_badhdr; /* Bad received headers (roosync?) */
+ uint rx_badseq; /* Mismatched rx sequence number */
+ uint fc_rcvd; /* Number of flow-control events received */
+ uint fc_xoff; /* Number which turned on flow-control */
+ uint fc_xon; /* Number which turned off flow-control */
+ uint rxglomfail; /* Failed deglom attempts */
+ uint rxglomframes; /* Number of glom frames (superframes) */
+ uint rxglompkts; /* Number of packets from glom frames */
+ uint f2rxhdrs; /* Number of header reads */
+ uint f2rxdata; /* Number of frame data reads */
+ uint f2txdata; /* Number of f2 frame writes */
+ uint f1regdata; /* Number of f1 register accesses */
+ uint tickcnt; /* Number of watchdog been schedule */
+ ulong tx_ctlerrs; /* Err of sending ctrl frames */
+ ulong tx_ctlpkts; /* Ctrl frames sent to dongle */
+ ulong rx_ctlerrs; /* Err of processing rx ctrl frames */
+ ulong rx_ctlpkts; /* Ctrl frames processed from dongle */
+ ulong rx_readahead_cnt; /* packets where header read-ahead was used */
+};
+
+/* misc chip info needed by some of the routines */
+/* Private data for SDIO bus interaction */
+struct brcmf_sdio {
+ struct brcmf_sdio_dev *sdiodev; /* sdio device handler */
+ struct brcmf_chip *ci; /* Chip info struct */
+
+ u32 hostintmask; /* Copy of Host Interrupt Mask */
+ atomic_t intstatus; /* Intstatus bits (events) pending */
+ atomic_t fcstate; /* State of dongle flow-control */
+
+ uint blocksize; /* Block size of SDIO transfers */
+ uint roundup; /* Max roundup limit */
+
+ struct pktq txq; /* Queue length used for flow-control */
+ u8 flowcontrol; /* per prio flow control bitmask */
+ u8 tx_seq; /* Transmit sequence number (next) */
+ u8 tx_max; /* Maximum transmit sequence allowed */
+
+ u8 *hdrbuf; /* buffer for handling rx frame */
+ u8 *rxhdr; /* Header of current rx frame (in hdrbuf) */
+ u8 rx_seq; /* Receive sequence number (expected) */
+ struct brcmf_sdio_hdrinfo cur_read;
+ /* info of current read frame */
+ bool rxskip; /* Skip receive (awaiting NAK ACK) */
+ bool rxpending; /* Data frame pending in dongle */
+
+ uint rxbound; /* Rx frames to read before resched */
+ uint txbound; /* Tx frames to send before resched */
+ uint txminmax;
+
+ struct sk_buff *glomd; /* Packet containing glomming descriptor */
+ struct sk_buff_head glom; /* Packet list for glommed superframe */
+
+ u8 *rxbuf; /* Buffer for receiving control packets */
+ uint rxblen; /* Allocated length of rxbuf */
+ u8 *rxctl; /* Aligned pointer into rxbuf */
+ u8 *rxctl_orig; /* pointer for freeing rxctl */
+ uint rxlen; /* Length of valid data in buffer */
+ spinlock_t rxctl_lock; /* protection lock for ctrl frame resources */
+
+ u8 sdpcm_ver; /* Bus protocol reported by dongle */
+
+ bool intr; /* Use interrupts */
+ bool poll; /* Use polling */
+ atomic_t ipend; /* Device interrupt is pending */
+ uint spurious; /* Count of spurious interrupts */
+ uint pollrate; /* Ticks between device polls */
+ uint polltick; /* Tick counter */
+
+#ifdef DEBUG
+ uint console_interval;
+ struct brcmf_console console; /* Console output polling support */
+ uint console_addr; /* Console address from shared struct */
+#endif /* DEBUG */
+
+ uint clkstate; /* State of sd and backplane clock(s) */
+ s32 idletime; /* Control for activity timeout */
+ s32 idlecount; /* Activity timeout counter */
+ s32 idleclock; /* How to set bus driver when idle */
+ bool rxflow_mode; /* Rx flow control mode */
+ bool rxflow; /* Is rx flow control on */
+ bool alp_only; /* Don't use HT clock (ALP only) */
+
+ u8 *ctrl_frame_buf;
+ u16 ctrl_frame_len;
+ bool ctrl_frame_stat;
+ int ctrl_frame_err;
+
+ spinlock_t txq_lock; /* protect bus->txq */
+ wait_queue_head_t ctrl_wait;
+ wait_queue_head_t dcmd_resp_wait;
+
+ struct timer_list timer;
+ struct completion watchdog_wait;
+ struct task_struct *watchdog_tsk;
+ bool wd_active;
+
+ struct workqueue_struct *brcmf_wq;
+ struct work_struct datawork;
+ bool dpc_triggered;
+ bool dpc_running;
+
+ bool txoff; /* Transmit flow-controlled */
+ struct brcmf_sdio_count sdcnt;
+ bool sr_enabled; /* SaveRestore enabled */
+ bool sleeping;
+
+ u8 tx_hdrlen; /* sdio bus header length for tx packet */
+ bool txglom; /* host tx glomming enable flag */
+ u16 head_align; /* buffer pointer alignment */
+ u16 sgentry_align; /* scatter-gather buffer alignment */
+};
+
+/* clkstate */
+#define CLK_NONE 0
+#define CLK_SDONLY 1
+#define CLK_PENDING 2
+#define CLK_AVAIL 3
+
+#ifdef DEBUG
+static int qcount[NUMPRIO];
+#endif /* DEBUG */
+
+#define DEFAULT_SDIO_DRIVE_STRENGTH 6 /* in milliamps */
+
+#define RETRYCHAN(chan) ((chan) == SDPCM_EVENT_CHANNEL)
+
+/* Limit on rounding up frames */
+static const uint max_roundup = 512;
+
+extern u8 g_dump_packet;
+extern u8 g_wakeup_irq;
+
+#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
+#define ALIGNMENT 8
+#else
+#define ALIGNMENT 4
+#endif
+
+enum brcmf_sdio_frmtype {
+ BRCMF_SDIO_FT_NORMAL,
+ BRCMF_SDIO_FT_SUPER,
+ BRCMF_SDIO_FT_SUB,
+};
+
+#define SDIOD_DRVSTR_KEY(chip, pmu) (((chip) << 16) | (pmu))
+
+/* SDIO Pad drive strength to select value mappings */
+struct sdiod_drive_str {
+ u8 strength; /* Pad Drive Strength in mA */
+ u8 sel; /* Chip-specific select value */
+};
+
+/* SDIO Drive Strength to sel value table for PMU Rev 11 (1.8V) */
+static const struct sdiod_drive_str sdiod_drvstr_tab1_1v8[] = {
+ {32, 0x6},
+ {26, 0x7},
+ {22, 0x4},
+ {16, 0x5},
+ {12, 0x2},
+ {8, 0x3},
+ {4, 0x0},
+ {0, 0x1}
+};
+
+/* SDIO Drive Strength to sel value table for PMU Rev 13 (1.8v) */
+static const struct sdiod_drive_str sdiod_drive_strength_tab5_1v8[] = {
+ {6, 0x7},
+ {5, 0x6},
+ {4, 0x5},
+ {3, 0x4},
+ {2, 0x2},
+ {1, 0x1},
+ {0, 0x0}
+};
+
+/* SDIO Drive Strength to sel value table for PMU Rev 17 (1.8v) */
+static const struct sdiod_drive_str sdiod_drvstr_tab6_1v8[] = {
+ {3, 0x3},
+ {2, 0x2},
+ {1, 0x1},
+ {0, 0x0} };
+
+/* SDIO Drive Strength to sel value table for 43143 PMU Rev 17 (3.3V) */
+static const struct sdiod_drive_str sdiod_drvstr_tab2_3v3[] = {
+ {16, 0x7},
+ {12, 0x5},
+ {8, 0x3},
+ {4, 0x1}
+};
+
+BRCMF_FW_NVRAM_DEF(43143, "brcmfmac43143-sdio.bin", "brcmfmac43143-sdio.txt");
+BRCMF_FW_NVRAM_DEF(43241B0, "brcmfmac43241b0-sdio.bin",
+ "brcmfmac43241b0-sdio.txt");
+BRCMF_FW_NVRAM_DEF(43241B4, "brcmfmac43241b4-sdio.bin",
+ "brcmfmac43241b4-sdio.txt");
+BRCMF_FW_NVRAM_DEF(43241B5, "brcmfmac43241b5-sdio.bin",
+ "brcmfmac43241b5-sdio.txt");
+BRCMF_FW_NVRAM_DEF(4329, "brcmfmac4329-sdio.bin", "brcmfmac4329-sdio.txt");
+BRCMF_FW_NVRAM_DEF(4330, "brcmfmac4330-sdio.bin", "brcmfmac4330-sdio.txt");
+BRCMF_FW_NVRAM_DEF(4334, "brcmfmac4334-sdio.bin", "brcmfmac4334-sdio.txt");
+BRCMF_FW_NVRAM_DEF(43340, "brcmfmac43340-sdio.bin", "brcmfmac43340-sdio.txt");
+BRCMF_FW_NVRAM_DEF(4335, "brcmfmac4335-sdio.bin", "brcmfmac4335-sdio.txt");
+BRCMF_FW_NVRAM_DEF(43362, "brcmfmac43362-sdio.bin", "brcmfmac43362-sdio.txt");
+BRCMF_FW_NVRAM_DEF(4339, "brcmfmac4339-sdio.bin", "brcmfmac4339-sdio.txt");
+BRCMF_FW_NVRAM_DEF(43430, "brcmfmac43430-sdio.bin", "brcmfmac43430-sdio.txt");
+BRCMF_FW_NVRAM_DEF(43455, "brcmfmac43455-sdio.bin", "brcmfmac43455-sdio.txt");
+BRCMF_FW_NVRAM_DEF(4354, "brcmfmac4354-sdio.bin", "brcmfmac4354-sdio.txt");
+BRCMF_FW_NVRAM_DEF(4356, "brcmfmac4356-sdio.bin", "brcmfmac4356-sdio.txt");
+BRCMF_FW_NVRAM_DEF(4373, "brcmfmac4373-sdio.bin", "brcmfmac4373-sdio.txt");
+BRCMF_FW_NVRAM_DEF(43012, "brcmfmac43012-sdio.bin", "brcmfmac43012-sdio.txt");
+
+static struct brcmf_firmware_mapping brcmf_sdio_fwnames[] = {
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43143_CHIP_ID, 0xFFFFFFFF, 43143),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43241_CHIP_ID, 0x0000001F, 43241B0),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43241_CHIP_ID, 0x00000020, 43241B4),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43241_CHIP_ID, 0xFFFFFFC0, 43241B5),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4329_CHIP_ID, 0xFFFFFFFF, 4329),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4330_CHIP_ID, 0xFFFFFFFF, 4330),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4334_CHIP_ID, 0xFFFFFFFF, 4334),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43340_CHIP_ID, 0xFFFFFFFF, 43340),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43341_CHIP_ID, 0xFFFFFFFF, 43340),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4335_CHIP_ID, 0xFFFFFFFF, 4335),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43362_CHIP_ID, 0xFFFFFFFE, 43362),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4339_CHIP_ID, 0xFFFFFFFF, 4339),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43430_CHIP_ID, 0xFFFFFFFF, 43430),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4345_CHIP_ID, 0xFFFFFFC0, 43455),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4354_CHIP_ID, 0xFFFFFFFF, 4354),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4356_CHIP_ID, 0xFFFFFFFF, 4356),
+ BRCMF_FW_NVRAM_ENTRY(CY_CC_4373_CHIP_ID, 0xFFFFFFFF, 4373),
+ BRCMF_FW_NVRAM_ENTRY(CY_CC_43012_CHIP_ID, 0xFFFFFFFF, 43012)
+};
+
+static void pkt_align(struct sk_buff *p, int len, int align)
+{
+ uint datalign;
+ datalign = (unsigned long)(p->data);
+ datalign = roundup(datalign, (align)) - datalign;
+ if (datalign)
+ skb_pull(p, datalign);
+ __skb_trim(p, len);
+}
+
+/* To check if there's window offered */
+static bool data_ok(struct brcmf_sdio *bus)
+{
+ return (u8)(bus->tx_max - bus->tx_seq) != 0 &&
+ ((u8)(bus->tx_max - bus->tx_seq) & 0x80) == 0;
+}
+
+/*
+ * Reads a register in the SDIO hardware block. This block occupies a series of
+ * adresses on the 32 bit backplane bus.
+ */
+static int r_sdreg32(struct brcmf_sdio *bus, u32 *regvar, u32 offset)
+{
+ struct brcmf_core *core;
+ int ret;
+
+ core = brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV);
+ *regvar = brcmf_sdiod_regrl(bus->sdiodev, core->base + offset, &ret);
+
+ return ret;
+}
+
+static int w_sdreg32(struct brcmf_sdio *bus, u32 regval, u32 reg_offset)
+{
+ struct brcmf_core *core;
+ int ret;
+
+ core = brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV);
+ brcmf_sdiod_regwl(bus->sdiodev, core->base + reg_offset, regval, &ret);
+
+ return ret;
+}
+
+static int
+brcmf_sdio_kso_control(struct brcmf_sdio *bus, bool on)
+{
+ u8 wr_val = 0, rd_val, cmp_val, bmask;
+ int err = 0;
+ int err_cnt = 0;
+ int try_cnt = 0;
+
+ brcmf_dbg(TRACE, "Enter: on=%d\n", on);
+
+ wr_val = (on << SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT);
+ /* 1st KSO write goes to AOS wake up core if device is asleep */
+ brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR,
+ wr_val, &err);
+
+ /* In case of 43012 chip, the chip could go down immediately after
+ * KSO bit is cleared. So the further reads of KSO register could
+ * fail. Thereby just bailing out immediately after clearing KSO
+ * bit, to avoid polling of KSO bit.
+ */
+ if (!on && (bus->ci->chip == CY_CC_43012_CHIP_ID)) {
+ return err;
+ }
+
+ if (on) {
+ /* device WAKEUP through KSO:
+ * write bit 0 & read back until
+ * both bits 0 (kso bit) & 1 (dev on status) are set
+ */
+ cmp_val = SBSDIO_FUNC1_SLEEPCSR_KSO_MASK |
+ SBSDIO_FUNC1_SLEEPCSR_DEVON_MASK;
+ bmask = cmp_val;
+ usleep_range(2000, 3000);
+ } else {
+ /* Put device to sleep, turn off KSO */
+ cmp_val = 0;
+ /* only check for bit0, bit1(dev on status) may not
+ * get cleared right away
+ */
+ bmask = SBSDIO_FUNC1_SLEEPCSR_KSO_MASK;
+ }
+
+ do {
+ /* reliable KSO bit set/clr:
+ * the sdiod sleep write access is synced to PMU 32khz clk
+ * just one write attempt may fail,
+ * read it back until it matches written value
+ */
+ rd_val = brcmf_sdiod_regrb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR,
+ &err);
+ if (!err) {
+ if ((rd_val & bmask) == cmp_val)
+ break;
+ err_cnt = 0;
+ }
+ /* bail out upon subsequent access errors */
+ if (err && (err_cnt++ > BRCMF_SDIO_MAX_ACCESS_ERRORS))
+ break;
+ udelay(KSO_WAIT_US);
+ brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR,
+ wr_val, &err);
+ } while (try_cnt++ < MAX_KSO_ATTEMPTS);
+
+ if (try_cnt > 2)
+ brcmf_dbg(SDIO, "try_cnt=%d rd_val=0x%x err=%d\n", try_cnt,
+ rd_val, err);
+
+ if (try_cnt > MAX_KSO_ATTEMPTS)
+ brcmf_err("max tries: rd_val=0x%x err=%d\n", rd_val, err);
+
+ return err;
+}
+
+#define HOSTINTMASK (I_HMB_SW_MASK | I_CHIPACTIVE)
+
+/* Turn backplane clock on or off */
+static int brcmf_sdio_htclk(struct brcmf_sdio *bus, bool on, bool pendok)
+{
+ int err;
+ u8 clkctl, clkreq, devctl;
+ unsigned long timeout;
+
+ brcmf_dbg(SDIO, "Enter\n");
+
+ clkctl = 0;
+
+ if (bus->sr_enabled) {
+ bus->clkstate = (on ? CLK_AVAIL : CLK_SDONLY);
+ return 0;
+ }
+
+ if (on) {
+ /* Request HT Avail */
+ clkreq =
+ bus->alp_only ? SBSDIO_ALP_AVAIL_REQ : SBSDIO_HT_AVAIL_REQ;
+
+ brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
+ clkreq, &err);
+ if (err) {
+ brcmf_err("HT Avail request error: %d\n", err);
+ return -EBADE;
+ }
+
+ /* Check current status */
+ clkctl = brcmf_sdiod_regrb(bus->sdiodev,
+ SBSDIO_FUNC1_CHIPCLKCSR, &err);
+ if (err) {
+ brcmf_err("HT Avail read error: %d\n", err);
+ return -EBADE;
+ }
+
+ /* Go to pending and await interrupt if appropriate */
+ if (!SBSDIO_CLKAV(clkctl, bus->alp_only) && pendok) {
+ /* Allow only clock-available interrupt */
+ devctl = brcmf_sdiod_regrb(bus->sdiodev,
+ SBSDIO_DEVICE_CTL, &err);
+ if (err) {
+ brcmf_err("Devctl error setting CA: %d\n",
+ err);
+ return -EBADE;
+ }
+
+ devctl |= SBSDIO_DEVCTL_CA_INT_ONLY;
+ brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL,
+ devctl, &err);
+ brcmf_dbg(SDIO, "CLKCTL: set PENDING\n");
+ bus->clkstate = CLK_PENDING;
+
+ return 0;
+ } else if (bus->clkstate == CLK_PENDING) {
+ /* Cancel CA-only interrupt filter */
+ devctl = brcmf_sdiod_regrb(bus->sdiodev,
+ SBSDIO_DEVICE_CTL, &err);
+ devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY;
+ brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL,
+ devctl, &err);
+ }
+
+ /* Otherwise, wait here (polling) for HT Avail */
+ timeout = jiffies +
+ msecs_to_jiffies(PMU_MAX_TRANSITION_DLY/1000);
+ while (!SBSDIO_CLKAV(clkctl, bus->alp_only)) {
+ clkctl = brcmf_sdiod_regrb(bus->sdiodev,
+ SBSDIO_FUNC1_CHIPCLKCSR,
+ &err);
+ if (time_after(jiffies, timeout))
+ break;
+ else
+ usleep_range(5000, 10000);
+ }
+ if (err) {
+ brcmf_err("HT Avail request error: %d\n", err);
+ return -EBADE;
+ }
+ if (!SBSDIO_CLKAV(clkctl, bus->alp_only)) {
+ brcmf_err("HT Avail timeout (%d): clkctl 0x%02x\n",
+ PMU_MAX_TRANSITION_DLY, clkctl);
+ return -EBADE;
+ }
+
+ /* Mark clock available */
+ bus->clkstate = CLK_AVAIL;
+ brcmf_dbg(SDIO, "CLKCTL: turned ON\n");
+
+#if defined(DEBUG)
+ if (!bus->alp_only) {
+ if (SBSDIO_ALPONLY(clkctl))
+ brcmf_err("HT Clock should be on\n");
+ }
+#endif /* defined (DEBUG) */
+
+ } else {
+ clkreq = 0;
+
+ if (bus->clkstate == CLK_PENDING) {
+ /* Cancel CA-only interrupt filter */
+ devctl = brcmf_sdiod_regrb(bus->sdiodev,
+ SBSDIO_DEVICE_CTL, &err);
+ devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY;
+ brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL,
+ devctl, &err);
+ }
+
+ bus->clkstate = CLK_SDONLY;
+ brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
+ clkreq, &err);
+ brcmf_dbg(SDIO, "CLKCTL: turned OFF\n");
+ if (err) {
+ brcmf_err("Failed access turning clock off: %d\n",
+ err);
+ return -EBADE;
+ }
+ }
+ return 0;
+}
+
+/* Change idle/active SD state */
+static int brcmf_sdio_sdclk(struct brcmf_sdio *bus, bool on)
+{
+ brcmf_dbg(SDIO, "Enter\n");
+
+ if (on)
+ bus->clkstate = CLK_SDONLY;
+ else
+ bus->clkstate = CLK_NONE;
+
+ return 0;
+}
+
+/* Transition SD and backplane clock readiness */
+static int brcmf_sdio_clkctl(struct brcmf_sdio *bus, uint target, bool pendok)
+{
+#ifdef DEBUG
+ uint oldstate = bus->clkstate;
+#endif /* DEBUG */
+
+ brcmf_dbg(SDIO, "Enter\n");
+
+ /* Early exit if we're already there */
+ if (bus->clkstate == target)
+ return 0;
+
+ switch (target) {
+ case CLK_AVAIL:
+ /* Make sure SD clock is available */
+ if (bus->clkstate == CLK_NONE)
+ brcmf_sdio_sdclk(bus, true);
+ /* Now request HT Avail on the backplane */
+ brcmf_sdio_htclk(bus, true, pendok);
+ break;
+
+ case CLK_SDONLY:
+ /* Remove HT request, or bring up SD clock */
+ if (bus->clkstate == CLK_NONE)
+ brcmf_sdio_sdclk(bus, true);
+ else if (bus->clkstate == CLK_AVAIL)
+ brcmf_sdio_htclk(bus, false, false);
+ else
+ brcmf_err("request for %d -> %d\n",
+ bus->clkstate, target);
+ break;
+
+ case CLK_NONE:
+ /* Make sure to remove HT request */
+ if (bus->clkstate == CLK_AVAIL)
+ brcmf_sdio_htclk(bus, false, false);
+ /* Now remove the SD clock */
+ brcmf_sdio_sdclk(bus, false);
+ break;
+ }
+#ifdef DEBUG
+ brcmf_dbg(SDIO, "%d -> %d\n", oldstate, bus->clkstate);
+#endif /* DEBUG */
+
+ return 0;
+}
+
+static int
+brcmf_sdio_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok)
+{
+ int err = 0;
+ u8 clkcsr;
+
+ brcmf_dbg(SDIO, "Enter: request %s currently %s\n",
+ (sleep ? "SLEEP" : "WAKE"),
+ (bus->sleeping ? "SLEEP" : "WAKE"));
+
+ /* If SR is enabled control bus state with KSO */
+ if (bus->sr_enabled) {
+ /* Done if we're already in the requested state */
+ if (sleep == bus->sleeping)
+ goto end;
+
+ /* Going to sleep */
+ if (sleep) {
+ clkcsr = brcmf_sdiod_regrb(bus->sdiodev,
+ SBSDIO_FUNC1_CHIPCLKCSR,
+ &err);
+ if ((clkcsr & SBSDIO_CSR_MASK) == 0) {
+ brcmf_dbg(SDIO, "no clock, set ALP\n");
+ brcmf_sdiod_regwb(bus->sdiodev,
+ SBSDIO_FUNC1_CHIPCLKCSR,
+ SBSDIO_ALP_AVAIL_REQ, &err);
+ }
+ err = brcmf_sdio_kso_control(bus, false);
+ } else {
+ err = brcmf_sdio_kso_control(bus, true);
+ }
+ if (err) {
+ brcmf_err("error while changing bus sleep state %d\n",
+ err);
+ goto done;
+ }
+ }
+
+end:
+ /* control clocks */
+ if (sleep) {
+ if (!bus->sr_enabled)
+ brcmf_sdio_clkctl(bus, CLK_NONE, pendok);
+ } else {
+ brcmf_sdio_clkctl(bus, CLK_AVAIL, pendok);
+ brcmf_sdio_wd_timer(bus, true);
+ }
+ bus->sleeping = sleep;
+ brcmf_dbg(SDIO, "new state %s\n",
+ (sleep ? "SLEEP" : "WAKE"));
+done:
+ brcmf_dbg(SDIO, "Exit: err=%d\n", err);
+ return err;
+
+}
+
+#ifdef DEBUG
+static inline bool brcmf_sdio_valid_shared_address(u32 addr)
+{
+ return !(addr == 0 || ((~addr >> 16) & 0xffff) == (addr & 0xffff));
+}
+
+static int brcmf_sdio_readshared(struct brcmf_sdio *bus,
+ struct sdpcm_shared *sh)
+{
+ u32 addr = 0;
+ int rv;
+ u32 shaddr = 0;
+ struct sdpcm_shared_le sh_le;
+ __le32 addr_le;
+
+ sdio_claim_host(bus->sdiodev->func[1]);
+ brcmf_sdio_bus_sleep(bus, false, false);
+
+ /*
+ * Read last word in socram to determine
+ * address of sdpcm_shared structure
+ */
+ shaddr = bus->ci->rambase + bus->ci->ramsize - 4;
+ if (!bus->ci->rambase && brcmf_chip_sr_capable(bus->ci))
+ shaddr -= bus->ci->srsize;
+ rv = brcmf_sdiod_ramrw(bus->sdiodev, false, shaddr,
+ (u8 *)&addr_le, 4);
+ if (rv < 0)
+ goto fail;
+
+ /*
+ * Check if addr is valid.
+ * NVRAM length at the end of memory should have been overwritten.
+ */
+ addr = le32_to_cpu(addr_le);
+ if (!brcmf_sdio_valid_shared_address(addr)) {
+ brcmf_err("invalid sdpcm_shared address 0x%08X\n", addr);
+ rv = -EINVAL;
+ goto fail;
+ }
+
+ brcmf_dbg(INFO, "sdpcm_shared address 0x%08X\n", addr);
+
+ /* Read hndrte_shared structure */
+ rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, (u8 *)&sh_le,
+ sizeof(struct sdpcm_shared_le));
+ if (rv < 0)
+ goto fail;
+
+ sdio_release_host(bus->sdiodev->func[1]);
+
+ /* Endianness */
+ sh->flags = le32_to_cpu(sh_le.flags);
+ sh->trap_addr = le32_to_cpu(sh_le.trap_addr);
+ sh->assert_exp_addr = le32_to_cpu(sh_le.assert_exp_addr);
+ sh->assert_file_addr = le32_to_cpu(sh_le.assert_file_addr);
+ sh->assert_line = le32_to_cpu(sh_le.assert_line);
+ sh->console_addr = le32_to_cpu(sh_le.console_addr);
+ sh->msgtrace_addr = le32_to_cpu(sh_le.msgtrace_addr);
+
+ if ((sh->flags & SDPCM_SHARED_VERSION_MASK) > SDPCM_SHARED_VERSION) {
+ brcmf_err("sdpcm shared version unsupported: dhd %d dongle %d\n",
+ SDPCM_SHARED_VERSION,
+ sh->flags & SDPCM_SHARED_VERSION_MASK);
+ return -EPROTO;
+ }
+ return 0;
+
+fail:
+ brcmf_err("unable to obtain sdpcm_shared info: rv=%d (addr=0x%x)\n",
+ rv, addr);
+ sdio_release_host(bus->sdiodev->func[1]);
+ return rv;
+}
+
+static void brcmf_sdio_get_console_addr(struct brcmf_sdio *bus)
+{
+ struct sdpcm_shared sh;
+
+ if (brcmf_sdio_readshared(bus, &sh) == 0)
+ bus->console_addr = sh.console_addr;
+}
+#else
+static void brcmf_sdio_get_console_addr(struct brcmf_sdio *bus)
+{
+}
+#endif /* DEBUG */
+
+static u32 brcmf_sdio_hostmail(struct brcmf_sdio *bus)
+{
+ u32 intstatus = 0;
+ u32 hmb_data;
+ u8 fcbits;
+ int ret;
+
+ brcmf_dbg(SDIO, "Enter\n");
+
+ /* Read mailbox data and ack that we did so */
+ ret = r_sdreg32(bus, &hmb_data,
+ offsetof(struct sdpcmd_regs, tohostmailboxdata));
+
+ if (ret == 0)
+ w_sdreg32(bus, SMB_INT_ACK,
+ offsetof(struct sdpcmd_regs, tosbmailbox));
+ bus->sdcnt.f1regdata += 2;
+
+ /* Dongle recomposed rx frames, accept them again */
+ if (hmb_data & HMB_DATA_NAKHANDLED) {
+ brcmf_dbg(SDIO, "Dongle reports NAK handled, expect rtx of %d\n",
+ bus->rx_seq);
+ if (!bus->rxskip)
+ brcmf_err("unexpected NAKHANDLED!\n");
+
+ bus->rxskip = false;
+ intstatus |= I_HMB_FRAME_IND;
+ }
+
+ /*
+ * DEVREADY does not occur with gSPI.
+ */
+ if (hmb_data & (HMB_DATA_DEVREADY | HMB_DATA_FWREADY)) {
+ bus->sdpcm_ver =
+ (hmb_data & HMB_DATA_VERSION_MASK) >>
+ HMB_DATA_VERSION_SHIFT;
+ if (bus->sdpcm_ver != SDPCM_PROT_VERSION)
+ brcmf_err("Version mismatch, dongle reports %d, "
+ "expecting %d\n",
+ bus->sdpcm_ver, SDPCM_PROT_VERSION);
+ else
+ brcmf_dbg(SDIO, "Dongle ready, protocol version %d\n",
+ bus->sdpcm_ver);
+
+ /*
+ * Retrieve console state address now that firmware should have
+ * updated it.
+ */
+ brcmf_sdio_get_console_addr(bus);
+ }
+
+ /*
+ * Flow Control has been moved into the RX headers and this out of band
+ * method isn't used any more.
+ * remaining backward compatible with older dongles.
+ */
+ if (hmb_data & HMB_DATA_FC) {
+ fcbits = (hmb_data & HMB_DATA_FCDATA_MASK) >>
+ HMB_DATA_FCDATA_SHIFT;
+
+ if (fcbits & ~bus->flowcontrol)
+ bus->sdcnt.fc_xoff++;
+
+ if (bus->flowcontrol & ~fcbits)
+ bus->sdcnt.fc_xon++;
+
+ bus->sdcnt.fc_rcvd++;
+ bus->flowcontrol = fcbits;
+ }
+
+ /* Shouldn't be any others */
+ if (hmb_data & ~(HMB_DATA_DEVREADY |
+ HMB_DATA_NAKHANDLED |
+ HMB_DATA_FC |
+ HMB_DATA_FWREADY |
+ HMB_DATA_FCDATA_MASK | HMB_DATA_VERSION_MASK))
+ brcmf_err("Unknown mailbox data content: 0x%02x\n",
+ hmb_data);
+
+ return intstatus;
+}
+
+static void brcmf_sdio_rxfail(struct brcmf_sdio *bus, bool abort, bool rtx)
+{
+ uint retries = 0;
+ u16 lastrbc;
+ u8 hi, lo;
+ int err;
+
+ brcmf_err("%sterminate frame%s\n",
+ abort ? "abort command, " : "",
+ rtx ? ", send NAK" : "");
+
+ if (abort)
+ brcmf_sdiod_abort(bus->sdiodev, SDIO_FUNC_2);
+
+ brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL,
+ SFC_RF_TERM, &err);
+ bus->sdcnt.f1regdata++;
+
+ /* Wait until the packet has been flushed (device/FIFO stable) */
+ for (lastrbc = retries = 0xffff; retries > 0; retries--) {
+ hi = brcmf_sdiod_regrb(bus->sdiodev,
+ SBSDIO_FUNC1_RFRAMEBCHI, &err);
+ lo = brcmf_sdiod_regrb(bus->sdiodev,
+ SBSDIO_FUNC1_RFRAMEBCLO, &err);
+ bus->sdcnt.f1regdata += 2;
+
+ if ((hi == 0) && (lo == 0))
+ break;
+
+ if ((hi > (lastrbc >> 8)) && (lo > (lastrbc & 0x00ff))) {
+ brcmf_err("count growing: last 0x%04x now 0x%04x\n",
+ lastrbc, (hi << 8) + lo);
+ }
+ lastrbc = (hi << 8) + lo;
+ }
+
+ if (!retries)
+ brcmf_err("count never zeroed: last 0x%04x\n", lastrbc);
+ else
+ brcmf_dbg(SDIO, "flush took %d iterations\n", 0xffff - retries);
+
+ if (rtx) {
+ bus->sdcnt.rxrtx++;
+ err = w_sdreg32(bus, SMB_NAK,
+ offsetof(struct sdpcmd_regs, tosbmailbox));
+
+ bus->sdcnt.f1regdata++;
+ if (err == 0)
+ bus->rxskip = true;
+ }
+
+ /* Clear partial in any case */
+ bus->cur_read.len = 0;
+}
+
+static void brcmf_sdio_txfail(struct brcmf_sdio *bus)
+{
+ struct brcmf_sdio_dev *sdiodev = bus->sdiodev;
+ u8 i, hi, lo;
+
+ /* On failure, abort the command and terminate the frame */
+ brcmf_err("sdio error, abort command and terminate frame\n");
+ bus->sdcnt.tx_sderrs++;
+
+ brcmf_sdiod_abort(sdiodev, SDIO_FUNC_2);
+ brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_FRAMECTRL, SFC_WF_TERM, NULL);
+ bus->sdcnt.f1regdata++;
+
+ for (i = 0; i < 3; i++) {
+ hi = brcmf_sdiod_regrb(sdiodev, SBSDIO_FUNC1_WFRAMEBCHI, NULL);
+ lo = brcmf_sdiod_regrb(sdiodev, SBSDIO_FUNC1_WFRAMEBCLO, NULL);
+ bus->sdcnt.f1regdata += 2;
+ if ((hi == 0) && (lo == 0))
+ break;
+ }
+}
+
+/* return total length of buffer chain */
+static uint brcmf_sdio_glom_len(struct brcmf_sdio *bus)
+{
+ struct sk_buff *p;
+ uint total;
+
+ total = 0;
+ skb_queue_walk(&bus->glom, p)
+ total += p->len;
+ return total;
+}
+
+static void brcmf_sdio_free_glom(struct brcmf_sdio *bus)
+{
+ struct sk_buff *cur, *next;
+
+ skb_queue_walk_safe(&bus->glom, cur, next) {
+ skb_unlink(cur, &bus->glom);
+ brcmu_pkt_buf_free_skb(cur);
+ }
+}
+
+/**
+ * brcmfmac sdio bus specific header
+ * This is the lowest layer header wrapped on the packets transmitted between
+ * host and WiFi dongle which contains information needed for SDIO core and
+ * firmware
+ *
+ * It consists of 3 parts: hardware header, hardware extension header and
+ * software header
+ * hardware header (frame tag) - 4 bytes
+ * Byte 0~1: Frame length
+ * Byte 2~3: Checksum, bit-wise inverse of frame length
+ * hardware extension header - 8 bytes
+ * Tx glom mode only, N/A for Rx or normal Tx
+ * Byte 0~1: Packet length excluding hw frame tag
+ * Byte 2: Reserved
+ * Byte 3: Frame flags, bit 0: last frame indication
+ * Byte 4~5: Reserved
+ * Byte 6~7: Tail padding length
+ * software header - 8 bytes
+ * Byte 0: Rx/Tx sequence number
+ * Byte 1: 4 MSB Channel number, 4 LSB arbitrary flag
+ * Byte 2: Length of next data frame, reserved for Tx
+ * Byte 3: Data offset
+ * Byte 4: Flow control bits, reserved for Tx
+ * Byte 5: Maximum Sequence number allowed by firmware for Tx, N/A for Tx packet
+ * Byte 6~7: Reserved
+ */
+#define SDPCM_HWHDR_LEN 4
+#define SDPCM_HWEXT_LEN 8
+#define SDPCM_SWHDR_LEN 8
+#define SDPCM_HDRLEN (SDPCM_HWHDR_LEN + SDPCM_SWHDR_LEN)
+/* software header */
+#define SDPCM_SEQ_MASK 0x000000ff
+#define SDPCM_SEQ_WRAP 256
+#define SDPCM_CHANNEL_MASK 0x00000f00
+#define SDPCM_CHANNEL_SHIFT 8
+#define SDPCM_CONTROL_CHANNEL 0 /* Control */
+#define SDPCM_EVENT_CHANNEL 1 /* Asyc Event Indication */
+#define SDPCM_DATA_CHANNEL 2 /* Data Xmit/Recv */
+#define SDPCM_GLOM_CHANNEL 3 /* Coalesced packets */
+#define SDPCM_TEST_CHANNEL 15 /* Test/debug packets */
+#define SDPCM_GLOMDESC(p) (((u8 *)p)[1] & 0x80)
+#define SDPCM_NEXTLEN_MASK 0x00ff0000
+#define SDPCM_NEXTLEN_SHIFT 16
+#define SDPCM_DOFFSET_MASK 0xff000000
+#define SDPCM_DOFFSET_SHIFT 24
+#define SDPCM_FCMASK_MASK 0x000000ff
+#define SDPCM_WINDOW_MASK 0x0000ff00
+#define SDPCM_WINDOW_SHIFT 8
+
+static inline u8 brcmf_sdio_getdatoffset(u8 *swheader)
+{
+ u32 hdrvalue;
+ hdrvalue = *(u32 *)swheader;
+ return (u8)((hdrvalue & SDPCM_DOFFSET_MASK) >> SDPCM_DOFFSET_SHIFT);
+}
+
+static inline bool brcmf_sdio_fromevntchan(u8 *swheader)
+{
+ u32 hdrvalue;
+ u8 ret;
+
+ hdrvalue = *(u32 *)swheader;
+ ret = (u8)((hdrvalue & SDPCM_CHANNEL_MASK) >> SDPCM_CHANNEL_SHIFT);
+
+ return (ret == SDPCM_EVENT_CHANNEL);
+}
+
+static int brcmf_sdio_hdparse(struct brcmf_sdio *bus, u8 *header,
+ struct brcmf_sdio_hdrinfo *rd,
+ enum brcmf_sdio_frmtype type)
+{
+ u16 len, checksum;
+ u8 rx_seq, fc, tx_seq_max;
+ u32 swheader;
+
+ trace_brcmf_sdpcm_hdr(SDPCM_RX, header);
+
+ /* hw header */
+ len = get_unaligned_le16(header);
+ checksum = get_unaligned_le16(header + sizeof(u16));
+ /* All zero means no more to read */
+ if (!(len | checksum)) {
+ bus->rxpending = false;
+ return -ENODATA;
+ }
+ if ((u16)(~(len ^ checksum))) {
+ brcmf_err("HW header checksum error\n");
+ bus->sdcnt.rx_badhdr++;
+ brcmf_sdio_rxfail(bus, false, false);
+ return -EIO;
+ }
+ if (len < SDPCM_HDRLEN) {
+ brcmf_err("HW header length error\n");
+ return -EPROTO;
+ }
+ if (type == BRCMF_SDIO_FT_SUPER &&
+ (roundup(len, bus->blocksize) != rd->len)) {
+ brcmf_err("HW superframe header length error\n");
+ return -EPROTO;
+ }
+ if (type == BRCMF_SDIO_FT_SUB && len > rd->len) {
+ brcmf_err("HW subframe header length error\n");
+ return -EPROTO;
+ }
+ rd->len = len;
+
+ /* software header */
+ header += SDPCM_HWHDR_LEN;
+ swheader = le32_to_cpu(*(__le32 *)header);
+ if (type == BRCMF_SDIO_FT_SUPER && SDPCM_GLOMDESC(header)) {
+ brcmf_err("Glom descriptor found in superframe head\n");
+ rd->len = 0;
+ return -EINVAL;
+ }
+ rx_seq = (u8)(swheader & SDPCM_SEQ_MASK);
+ rd->channel = (swheader & SDPCM_CHANNEL_MASK) >> SDPCM_CHANNEL_SHIFT;
+ if (len > MAX_RX_DATASZ && rd->channel != SDPCM_CONTROL_CHANNEL &&
+ type != BRCMF_SDIO_FT_SUPER) {
+ brcmf_err("HW header length too long\n");
+ bus->sdcnt.rx_toolong++;
+ brcmf_sdio_rxfail(bus, false, false);
+ rd->len = 0;
+ return -EPROTO;
+ }
+ if (type == BRCMF_SDIO_FT_SUPER && rd->channel != SDPCM_GLOM_CHANNEL) {
+ brcmf_err("Wrong channel for superframe\n");
+ rd->len = 0;
+ return -EINVAL;
+ }
+ if (type == BRCMF_SDIO_FT_SUB && rd->channel != SDPCM_DATA_CHANNEL &&
+ rd->channel != SDPCM_EVENT_CHANNEL) {
+ brcmf_err("Wrong channel for subframe\n");
+ rd->len = 0;
+ return -EINVAL;
+ }
+ rd->dat_offset = brcmf_sdio_getdatoffset(header);
+ if (rd->dat_offset < SDPCM_HDRLEN || rd->dat_offset > rd->len) {
+ brcmf_err("seq %d: bad data offset\n", rx_seq);
+ bus->sdcnt.rx_badhdr++;
+ brcmf_sdio_rxfail(bus, false, false);
+ rd->len = 0;
+ return -ENXIO;
+ }
+ if (rd->seq_num != rx_seq) {
+ brcmf_dbg(SDIO, "seq %d, expected %d\n", rx_seq, rd->seq_num);
+ bus->sdcnt.rx_badseq++;
+ rd->seq_num = rx_seq;
+ }
+ /* no need to check the reset for subframe */
+ if (type == BRCMF_SDIO_FT_SUB)
+ return 0;
+ rd->len_nxtfrm = (swheader & SDPCM_NEXTLEN_MASK) >> SDPCM_NEXTLEN_SHIFT;
+ if (rd->len_nxtfrm << 4 > MAX_RX_DATASZ) {
+ /* only warm for NON glom packet */
+ if (rd->channel != SDPCM_GLOM_CHANNEL)
+ brcmf_err("seq %d: next length error\n", rx_seq);
+ rd->len_nxtfrm = 0;
+ }
+ swheader = le32_to_cpu(*(__le32 *)(header + 4));
+ fc = swheader & SDPCM_FCMASK_MASK;
+ if (bus->flowcontrol != fc) {
+ if (~bus->flowcontrol & fc)
+ bus->sdcnt.fc_xoff++;
+ if (bus->flowcontrol & ~fc)
+ bus->sdcnt.fc_xon++;
+ bus->sdcnt.fc_rcvd++;
+ bus->flowcontrol = fc;
+ }
+ tx_seq_max = (swheader & SDPCM_WINDOW_MASK) >> SDPCM_WINDOW_SHIFT;
+ if ((u8)(tx_seq_max - bus->tx_seq) > 0x40) {
+ brcmf_err("seq %d: max tx seq number error\n", rx_seq);
+ tx_seq_max = bus->tx_seq + 2;
+ }
+ bus->tx_max = tx_seq_max;
+
+ return 0;
+}
+
+static inline void brcmf_sdio_update_hwhdr(u8 *header, u16 frm_length)
+{
+ *(__le16 *)header = cpu_to_le16(frm_length);
+ *(((__le16 *)header) + 1) = cpu_to_le16(~frm_length);
+}
+
+static void brcmf_sdio_hdpack(struct brcmf_sdio *bus, u8 *header,
+ struct brcmf_sdio_hdrinfo *hd_info)
+{
+ u32 hdrval;
+ u8 hdr_offset;
+
+ brcmf_sdio_update_hwhdr(header, hd_info->len);
+ hdr_offset = SDPCM_HWHDR_LEN;
+
+ if (bus->txglom) {
+ hdrval = (hd_info->len - hdr_offset) | (hd_info->lastfrm << 24);
+ *((__le32 *)(header + hdr_offset)) = cpu_to_le32(hdrval);
+ hdrval = (u16)hd_info->tail_pad << 16;
+ *(((__le32 *)(header + hdr_offset)) + 1) = cpu_to_le32(hdrval);
+ hdr_offset += SDPCM_HWEXT_LEN;
+ }
+
+ hdrval = hd_info->seq_num;
+ hdrval |= (hd_info->channel << SDPCM_CHANNEL_SHIFT) &
+ SDPCM_CHANNEL_MASK;
+ hdrval |= (hd_info->dat_offset << SDPCM_DOFFSET_SHIFT) &
+ SDPCM_DOFFSET_MASK;
+ *((__le32 *)(header + hdr_offset)) = cpu_to_le32(hdrval);
+ *(((__le32 *)(header + hdr_offset)) + 1) = 0;
+ trace_brcmf_sdpcm_hdr(SDPCM_TX + !!(bus->txglom), header);
+}
+
+static u8 brcmf_sdio_rxglom(struct brcmf_sdio *bus, u8 rxseq)
+{
+ u16 dlen, totlen;
+ u8 *dptr, num = 0;
+ u16 sublen;
+ struct sk_buff *pfirst, *pnext;
+
+ int errcode;
+ u8 doff, sfdoff;
+
+ struct brcmf_sdio_hdrinfo rd_new;
+
+ /* If packets, issue read(s) and send up packet chain */
+ /* Return sequence numbers consumed? */
+
+ brcmf_dbg(SDIO, "start: glomd %p glom %p\n",
+ bus->glomd, skb_peek(&bus->glom));
+
+ /* If there's a descriptor, generate the packet chain */
+ if (bus->glomd) {
+ pfirst = pnext = NULL;
+ dlen = (u16) (bus->glomd->len);
+ dptr = bus->glomd->data;
+ if (!dlen || (dlen & 1)) {
+ brcmf_err("bad glomd len(%d), ignore descriptor\n",
+ dlen);
+ dlen = 0;
+ }
+
+ for (totlen = num = 0; dlen; num++) {
+ /* Get (and move past) next length */
+ sublen = get_unaligned_le16(dptr);
+ dlen -= sizeof(u16);
+ dptr += sizeof(u16);
+ if ((sublen < SDPCM_HDRLEN) ||
+ ((num == 0) && (sublen < (2 * SDPCM_HDRLEN)))) {
+ brcmf_err("descriptor len %d bad: %d\n",
+ num, sublen);
+ pnext = NULL;
+ break;
+ }
+ if (sublen % bus->sgentry_align) {
+ brcmf_err("sublen %d not multiple of %d\n",
+ sublen, bus->sgentry_align);
+ }
+ totlen += sublen;
+
+ /* For last frame, adjust read len so total
+ is a block multiple */
+ if (!dlen) {
+ sublen +=
+ (roundup(totlen, bus->blocksize) - totlen);
+ totlen = roundup(totlen, bus->blocksize);
+ }
+
+ /* Allocate/chain packet for next subframe */
+ pnext = brcmu_pkt_buf_get_skb(sublen + bus->sgentry_align);
+ if (pnext == NULL) {
+ brcmf_err("bcm_pkt_buf_get_skb failed, num %d len %d\n",
+ num, sublen);
+ break;
+ }
+ skb_queue_tail(&bus->glom, pnext);
+
+ /* Adhere to start alignment requirements */
+ pkt_align(pnext, sublen, bus->sgentry_align);
+ }
+
+ /* If all allocations succeeded, save packet chain
+ in bus structure */
+ if (pnext) {
+ brcmf_dbg(GLOM, "allocated %d-byte packet chain for %d subframes\n",
+ totlen, num);
+ if (BRCMF_GLOM_ON() && bus->cur_read.len &&
+ totlen != bus->cur_read.len) {
+ brcmf_dbg(GLOM, "glomdesc mismatch: nextlen %d glomdesc %d rxseq %d\n",
+ bus->cur_read.len, totlen, rxseq);
+ }
+ pfirst = pnext = NULL;
+ } else {
+ brcmf_sdio_free_glom(bus);
+ num = 0;
+ }
+
+ /* Done with descriptor packet */
+ brcmu_pkt_buf_free_skb(bus->glomd);
+ bus->glomd = NULL;
+ bus->cur_read.len = 0;
+ }
+
+ /* Ok -- either we just generated a packet chain,
+ or had one from before */
+ if (!skb_queue_empty(&bus->glom)) {
+ if (BRCMF_GLOM_ON()) {
+ brcmf_dbg(GLOM, "try superframe read, packet chain:\n");
+ skb_queue_walk(&bus->glom, pnext) {
+ brcmf_dbg(GLOM, " %p: %p len 0x%04x (%d)\n",
+ pnext, (u8 *) (pnext->data),
+ pnext->len, pnext->len);
+ }
+ }
+
+ pfirst = skb_peek(&bus->glom);
+ dlen = (u16) brcmf_sdio_glom_len(bus);
+
+ /* Do an SDIO read for the superframe. Configurable iovar to
+ * read directly into the chained packet, or allocate a large
+ * packet and and copy into the chain.
+ */
+ sdio_claim_host(bus->sdiodev->func[1]);
+ errcode = brcmf_sdiod_recv_chain(bus->sdiodev,
+ &bus->glom, dlen);
+ sdio_release_host(bus->sdiodev->func[1]);
+ bus->sdcnt.f2rxdata++;
+
+ /* On failure, kill the superframe */
+ if (errcode < 0) {
+ brcmf_err("glom read of %d bytes failed: %d\n",
+ dlen, errcode);
+
+ sdio_claim_host(bus->sdiodev->func[1]);
+ brcmf_sdio_rxfail(bus, true, false);
+ bus->sdcnt.rxglomfail++;
+ brcmf_sdio_free_glom(bus);
+ sdio_release_host(bus->sdiodev->func[1]);
+ return 0;
+ }
+
+ brcmf_dbg_hex_dump(BRCMF_GLOM_ON(),
+ pfirst->data, min_t(int, pfirst->len, 48),
+ "SUPERFRAME:\n");
+
+ rd_new.seq_num = rxseq;
+ rd_new.len = dlen;
+ sdio_claim_host(bus->sdiodev->func[1]);
+ errcode = brcmf_sdio_hdparse(bus, pfirst->data, &rd_new,
+ BRCMF_SDIO_FT_SUPER);
+ sdio_release_host(bus->sdiodev->func[1]);
+ bus->cur_read.len = rd_new.len_nxtfrm << 4;
+
+ /* Remove superframe header, remember offset */
+ skb_pull(pfirst, rd_new.dat_offset);
+ sfdoff = rd_new.dat_offset;
+ num = 0;
+
+ /* Validate all the subframe headers */
+ skb_queue_walk(&bus->glom, pnext) {
+ /* leave when invalid subframe is found */
+ if (errcode)
+ break;
+
+ rd_new.len = pnext->len;
+ rd_new.seq_num = rxseq++;
+ sdio_claim_host(bus->sdiodev->func[1]);
+ errcode = brcmf_sdio_hdparse(bus, pnext->data, &rd_new,
+ BRCMF_SDIO_FT_SUB);
+ sdio_release_host(bus->sdiodev->func[1]);
+ brcmf_dbg_hex_dump(BRCMF_GLOM_ON(),
+ pnext->data, 32, "subframe:\n");
+
+ num++;
+ }
+
+ if (errcode) {
+ /* Terminate frame on error */
+ sdio_claim_host(bus->sdiodev->func[1]);
+ brcmf_sdio_rxfail(bus, true, false);
+ bus->sdcnt.rxglomfail++;
+ brcmf_sdio_free_glom(bus);
+ sdio_release_host(bus->sdiodev->func[1]);
+ bus->cur_read.len = 0;
+ return 0;
+ }
+
+ /* Basic SD framing looks ok - process each packet (header) */
+
+ skb_queue_walk_safe(&bus->glom, pfirst, pnext) {
+ dptr = (u8 *) (pfirst->data);
+ sublen = get_unaligned_le16(dptr);
+ doff = brcmf_sdio_getdatoffset(&dptr[SDPCM_HWHDR_LEN]);
+
+ brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_DATA_ON(),
+ dptr, pfirst->len,
+ "Rx Subframe Data:\n");
+
+ __skb_trim(pfirst, sublen);
+ skb_pull(pfirst, doff);
+
+ if (pfirst->len == 0) {
+ skb_unlink(pfirst, &bus->glom);
+ brcmu_pkt_buf_free_skb(pfirst);
+ continue;
+ }
+
+ brcmf_dbg_hex_dump(BRCMF_GLOM_ON(),
+ pfirst->data,
+ min_t(int, pfirst->len, 32),
+ "subframe %d to stack, %p (%p/%d) nxt/lnk %p/%p\n",
+ bus->glom.qlen, pfirst, pfirst->data,
+ pfirst->len, pfirst->next,
+ pfirst->prev);
+ skb_unlink(pfirst, &bus->glom);
+ if (brcmf_sdio_fromevntchan(&dptr[SDPCM_HWHDR_LEN]))
+ brcmf_rx_event(bus->sdiodev->dev, pfirst);
+ else
+ brcmf_rx_frame(bus->sdiodev->dev, pfirst,
+ false);
+ bus->sdcnt.rxglompkts++;
+ }
+
+ bus->sdcnt.rxglomframes++;
+ }
+ return num;
+}
+
+static int brcmf_sdio_dcmd_resp_wait(struct brcmf_sdio *bus, uint *condition,
+ bool *pending)
+{
+ DECLARE_WAITQUEUE(wait, current);
+ int timeout = DCMD_RESP_TIMEOUT;
+
+ /* Wait until control frame is available */
+ add_wait_queue(&bus->dcmd_resp_wait, &wait);
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ while (!(*condition) && (!signal_pending(current) && timeout))
+ timeout = schedule_timeout(timeout);
+
+ if (signal_pending(current))
+ *pending = true;
+
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&bus->dcmd_resp_wait, &wait);
+
+ return timeout;
+}
+
+static int brcmf_sdio_dcmd_resp_wake(struct brcmf_sdio *bus)
+{
+ wake_up_interruptible(&bus->dcmd_resp_wait);
+
+ return 0;
+}
+static void
+brcmf_sdio_read_control(struct brcmf_sdio *bus, u8 *hdr, uint len, uint doff)
+{
+ uint rdlen, pad;
+ u8 *buf = NULL, *rbuf;
+ int sdret;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ if (bus->rxblen)
+ buf = vzalloc(bus->rxblen);
+ if (!buf)
+ goto done;
+
+ rbuf = bus->rxbuf;
+ pad = ((unsigned long)rbuf % bus->head_align);
+ if (pad)
+ rbuf += (bus->head_align - pad);
+
+ /* Copy the already-read portion over */
+ memcpy(buf, hdr, BRCMF_FIRSTREAD);
+ if (len <= BRCMF_FIRSTREAD)
+ goto gotpkt;
+
+ /* Raise rdlen to next SDIO block to avoid tail command */
+ rdlen = len - BRCMF_FIRSTREAD;
+ if (bus->roundup && bus->blocksize && (rdlen > bus->blocksize)) {
+ pad = bus->blocksize - (rdlen % bus->blocksize);
+ if ((pad <= bus->roundup) && (pad < bus->blocksize) &&
+ ((len + pad) < bus->sdiodev->bus_if->maxctl))
+ rdlen += pad;
+ } else if (rdlen % bus->head_align) {
+ rdlen += bus->head_align - (rdlen % bus->head_align);
+ }
+
+ /* Drop if the read is too big or it exceeds our maximum */
+ if ((rdlen + BRCMF_FIRSTREAD) > bus->sdiodev->bus_if->maxctl) {
+ brcmf_err("%d-byte control read exceeds %d-byte buffer\n",
+ rdlen, bus->sdiodev->bus_if->maxctl);
+ brcmf_sdio_rxfail(bus, false, false);
+ goto done;
+ }
+
+ if ((len - doff) > bus->sdiodev->bus_if->maxctl) {
+ brcmf_err("%d-byte ctl frame (%d-byte ctl data) exceeds %d-byte limit\n",
+ len, len - doff, bus->sdiodev->bus_if->maxctl);
+ bus->sdcnt.rx_toolong++;
+ brcmf_sdio_rxfail(bus, false, false);
+ goto done;
+ }
+
+ /* Read remain of frame body */
+ sdret = brcmf_sdiod_recv_buf(bus->sdiodev, rbuf, rdlen);
+ bus->sdcnt.f2rxdata++;
+
+ /* Control frame failures need retransmission */
+ if (sdret < 0) {
+ brcmf_err("read %d control bytes failed: %d\n",
+ rdlen, sdret);
+ bus->sdcnt.rxc_errors++;
+ brcmf_sdio_rxfail(bus, true, true);
+ goto done;
+ } else
+ memcpy(buf + BRCMF_FIRSTREAD, rbuf, rdlen);
+
+gotpkt:
+
+ brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_CTL_ON(),
+ buf, len, "RxCtrl:\n");
+
+ /* Point to valid data and indicate its length */
+ spin_lock_bh(&bus->rxctl_lock);
+ if (bus->rxctl) {
+ brcmf_err("last control frame is being processed.\n");
+ spin_unlock_bh(&bus->rxctl_lock);
+ vfree(buf);
+ goto done;
+ }
+ bus->rxctl = buf + doff;
+ bus->rxctl_orig = buf;
+ bus->rxlen = len - doff;
+ spin_unlock_bh(&bus->rxctl_lock);
+
+done:
+ /* Awake any waiters */
+ brcmf_sdio_dcmd_resp_wake(bus);
+}
+
+/* Pad read to blocksize for efficiency */
+static void brcmf_sdio_pad(struct brcmf_sdio *bus, u16 *pad, u16 *rdlen)
+{
+ if (bus->roundup && bus->blocksize && *rdlen > bus->blocksize) {
+ *pad = bus->blocksize - (*rdlen % bus->blocksize);
+ if (*pad <= bus->roundup && *pad < bus->blocksize &&
+ *rdlen + *pad + BRCMF_FIRSTREAD < MAX_RX_DATASZ)
+ *rdlen += *pad;
+ } else if (*rdlen % bus->head_align) {
+ *rdlen += bus->head_align - (*rdlen % bus->head_align);
+ }
+}
+
+static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes)
+{
+ struct sk_buff *pkt; /* Packet for event or data frames */
+ u16 pad; /* Number of pad bytes to read */
+ uint rxleft = 0; /* Remaining number of frames allowed */
+ int ret; /* Return code from calls */
+ uint rxcount = 0; /* Total frames read */
+ struct brcmf_sdio_hdrinfo *rd = &bus->cur_read, rd_new;
+ u8 head_read = 0;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ /* Not finished unless we encounter no more frames indication */
+ bus->rxpending = true;
+
+ for (rd->seq_num = bus->rx_seq, rxleft = maxframes;
+ !bus->rxskip && rxleft && bus->sdiodev->state == BRCMF_SDIOD_DATA;
+ rd->seq_num++, rxleft--) {
+
+ /* Handle glomming separately */
+ if (bus->glomd || !skb_queue_empty(&bus->glom)) {
+ u8 cnt;
+ brcmf_dbg(GLOM, "calling rxglom: glomd %p, glom %p\n",
+ bus->glomd, skb_peek(&bus->glom));
+ cnt = brcmf_sdio_rxglom(bus, rd->seq_num);
+ brcmf_dbg(GLOM, "rxglom returned %d\n", cnt);
+ rd->seq_num += cnt - 1;
+ rxleft = (rxleft > cnt) ? (rxleft - cnt) : 1;
+ continue;
+ }
+
+ rd->len_left = rd->len;
+ /* read header first for unknow frame length */
+ sdio_claim_host(bus->sdiodev->func[1]);
+ if (!rd->len) {
+ ret = brcmf_sdiod_recv_buf(bus->sdiodev,
+ bus->rxhdr, BRCMF_FIRSTREAD);
+ bus->sdcnt.f2rxhdrs++;
+ if (ret < 0) {
+ brcmf_err("RXHEADER FAILED: %d\n",
+ ret);
+ bus->sdcnt.rx_hdrfail++;
+ brcmf_sdio_rxfail(bus, true, true);
+ sdio_release_host(bus->sdiodev->func[1]);
+ continue;
+ }
+
+ brcmf_dbg_hex_dump(BRCMF_BYTES_ON() || BRCMF_HDRS_ON(),
+ bus->rxhdr, SDPCM_HDRLEN,
+ "RxHdr:\n");
+
+ if (brcmf_sdio_hdparse(bus, bus->rxhdr, rd,
+ BRCMF_SDIO_FT_NORMAL)) {
+ sdio_release_host(bus->sdiodev->func[1]);
+ if (!bus->rxpending)
+ break;
+ else
+ continue;
+ }
+
+ if (rd->channel == SDPCM_CONTROL_CHANNEL) {
+ brcmf_sdio_read_control(bus, bus->rxhdr,
+ rd->len,
+ rd->dat_offset);
+ /* prepare the descriptor for the next read */
+ rd->len = rd->len_nxtfrm << 4;
+ rd->len_nxtfrm = 0;
+ /* treat all packet as event if we don't know */
+ rd->channel = SDPCM_EVENT_CHANNEL;
+ sdio_release_host(bus->sdiodev->func[1]);
+ continue;
+ }
+ rd->len_left = rd->len > BRCMF_FIRSTREAD ?
+ rd->len - BRCMF_FIRSTREAD : 0;
+ head_read = BRCMF_FIRSTREAD;
+ }
+
+ brcmf_sdio_pad(bus, &pad, &rd->len_left);
+
+ pkt = brcmu_pkt_buf_get_skb(rd->len_left + head_read +
+ bus->head_align);
+ if (!pkt) {
+ /* Give up on data, request rtx of events */
+ brcmf_err("brcmu_pkt_buf_get_skb failed\n");
+ brcmf_sdio_rxfail(bus, false,
+ RETRYCHAN(rd->channel));
+ sdio_release_host(bus->sdiodev->func[1]);
+ continue;
+ }
+ skb_pull(pkt, head_read);
+ pkt_align(pkt, rd->len_left, bus->head_align);
+
+ ret = brcmf_sdiod_recv_pkt(bus->sdiodev, pkt);
+ bus->sdcnt.f2rxdata++;
+ sdio_release_host(bus->sdiodev->func[1]);
+
+ if (ret < 0) {
+ brcmf_err("read %d bytes from channel %d failed: %d\n",
+ rd->len, rd->channel, ret);
+ brcmu_pkt_buf_free_skb(pkt);
+ sdio_claim_host(bus->sdiodev->func[1]);
+ brcmf_sdio_rxfail(bus, true,
+ RETRYCHAN(rd->channel));
+ sdio_release_host(bus->sdiodev->func[1]);
+ continue;
+ }
+
+ if (head_read) {
+ skb_push(pkt, head_read);
+ memcpy(pkt->data, bus->rxhdr, head_read);
+ head_read = 0;
+ } else {
+ memcpy(bus->rxhdr, pkt->data, SDPCM_HDRLEN);
+ rd_new.seq_num = rd->seq_num;
+ sdio_claim_host(bus->sdiodev->func[1]);
+ if (brcmf_sdio_hdparse(bus, bus->rxhdr, &rd_new,
+ BRCMF_SDIO_FT_NORMAL)) {
+ rd->len = 0;
+ brcmu_pkt_buf_free_skb(pkt);
+ }
+ bus->sdcnt.rx_readahead_cnt++;
+ if (rd->len != roundup(rd_new.len, 16)) {
+ brcmf_err("frame length mismatch:read %d, should be %d\n",
+ rd->len,
+ roundup(rd_new.len, 16) >> 4);
+ rd->len = 0;
+ brcmf_sdio_rxfail(bus, true, true);
+ sdio_release_host(bus->sdiodev->func[1]);
+ brcmu_pkt_buf_free_skb(pkt);
+ continue;
+ }
+ sdio_release_host(bus->sdiodev->func[1]);
+ rd->len_nxtfrm = rd_new.len_nxtfrm;
+ rd->channel = rd_new.channel;
+ rd->dat_offset = rd_new.dat_offset;
+
+ brcmf_dbg_hex_dump(!(BRCMF_BYTES_ON() &&
+ BRCMF_DATA_ON()) &&
+ BRCMF_HDRS_ON(),
+ bus->rxhdr, SDPCM_HDRLEN,
+ "RxHdr:\n");
+
+ if (rd_new.channel == SDPCM_CONTROL_CHANNEL) {
+ brcmf_err("readahead on control packet %d?\n",
+ rd_new.seq_num);
+ /* Force retry w/normal header read */
+ rd->len = 0;
+ sdio_claim_host(bus->sdiodev->func[1]);
+ brcmf_sdio_rxfail(bus, false, true);
+ sdio_release_host(bus->sdiodev->func[1]);
+ brcmu_pkt_buf_free_skb(pkt);
+ continue;
+ }
+ }
+
+ brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_DATA_ON(),
+ pkt->data, rd->len, "Rx Data:\n");
+
+ /* Save superframe descriptor and allocate packet frame */
+ if (rd->channel == SDPCM_GLOM_CHANNEL) {
+ if (SDPCM_GLOMDESC(&bus->rxhdr[SDPCM_HWHDR_LEN])) {
+ brcmf_dbg(GLOM, "glom descriptor, %d bytes:\n",
+ rd->len);
+ brcmf_dbg_hex_dump(BRCMF_GLOM_ON(),
+ pkt->data, rd->len,
+ "Glom Data:\n");
+ __skb_trim(pkt, rd->len);
+ skb_pull(pkt, SDPCM_HDRLEN);
+ bus->glomd = pkt;
+ } else {
+ brcmf_err("%s: glom superframe w/o "
+ "descriptor!\n", __func__);
+ sdio_claim_host(bus->sdiodev->func[1]);
+ brcmf_sdio_rxfail(bus, false, false);
+ sdio_release_host(bus->sdiodev->func[1]);
+ }
+ /* prepare the descriptor for the next read */
+ rd->len = rd->len_nxtfrm << 4;
+ rd->len_nxtfrm = 0;
+ /* treat all packet as event if we don't know */
+ rd->channel = SDPCM_EVENT_CHANNEL;
+ continue;
+ }
+
+ /* Fill in packet len and prio, deliver upward */
+ __skb_trim(pkt, rd->len);
+ skb_pull(pkt, rd->dat_offset);
+
+ if (pkt->len == 0)
+ brcmu_pkt_buf_free_skb(pkt);
+ else if (rd->channel == SDPCM_EVENT_CHANNEL)
+ brcmf_rx_event(bus->sdiodev->dev, pkt);
+ else
+ brcmf_rx_frame(bus->sdiodev->dev, pkt,
+ false);
+
+ /* prepare the descriptor for the next read */
+ rd->len = rd->len_nxtfrm << 4;
+ rd->len_nxtfrm = 0;
+ /* treat all packet as event if we don't know */
+ rd->channel = SDPCM_EVENT_CHANNEL;
+ }
+
+ rxcount = maxframes - rxleft;
+ /* Message if we hit the limit */
+ if (!rxleft)
+ brcmf_dbg(DATA, "hit rx limit of %d frames\n", maxframes);
+ else
+ brcmf_dbg(DATA, "processed %d frames\n", rxcount);
+ /* Back off rxseq if awaiting rtx, update rx_seq */
+ if (bus->rxskip)
+ rd->seq_num--;
+ bus->rx_seq = rd->seq_num;
+
+ return rxcount;
+}
+
+static void
+brcmf_sdio_wait_event_wakeup(struct brcmf_sdio *bus)
+{
+ wake_up_interruptible(&bus->ctrl_wait);
+ return;
+}
+
+static int brcmf_sdio_txpkt_hdalign(struct brcmf_sdio *bus, struct sk_buff *pkt)
+{
+ u16 head_pad;
+ u8 *dat_buf;
+
+ dat_buf = (u8 *)(pkt->data);
+
+ /* Check head padding */
+ head_pad = ((unsigned long)dat_buf % bus->head_align);
+ if (head_pad) {
+ if (skb_headroom(pkt) < head_pad) {
+ bus->sdiodev->bus_if->tx_realloc++;
+ head_pad = 0;
+ if (skb_cow(pkt, head_pad))
+ return -ENOMEM;
+ }
+ skb_push(pkt, head_pad);
+ dat_buf = (u8 *)(pkt->data);
+ memset(dat_buf, 0, head_pad + bus->tx_hdrlen);
+ }
+ return head_pad;
+}
+
+/**
+ * struct brcmf_skbuff_cb reserves first two bytes in sk_buff::cb for
+ * bus layer usage.
+ */
+/* flag marking a dummy skb added for DMA alignment requirement */
+#define ALIGN_SKB_FLAG 0x8000
+/* bit mask of data length chopped from the previous packet */
+#define ALIGN_SKB_CHOP_LEN_MASK 0x7fff
+
+static int brcmf_sdio_txpkt_prep_sg(struct brcmf_sdio *bus,
+ struct sk_buff_head *pktq,
+ struct sk_buff *pkt, u16 total_len)
+{
+ struct brcmf_sdio_dev *sdiodev;
+ struct sk_buff *pkt_pad;
+ u16 tail_pad, tail_chop, chain_pad;
+ unsigned int blksize;
+ bool lastfrm;
+ int ntail, ret;
+
+ sdiodev = bus->sdiodev;
+ blksize = sdiodev->func[SDIO_FUNC_2]->cur_blksize;
+ /* sg entry alignment should be a divisor of block size */
+ WARN_ON(blksize % bus->sgentry_align);
+
+ /* Check tail padding */
+ lastfrm = skb_queue_is_last(pktq, pkt);
+ tail_pad = 0;
+ tail_chop = pkt->len % bus->sgentry_align;
+ if (tail_chop)
+ tail_pad = bus->sgentry_align - tail_chop;
+ chain_pad = (total_len + tail_pad) % blksize;
+ if (lastfrm && chain_pad)
+ tail_pad += blksize - chain_pad;
+ if (skb_tailroom(pkt) < tail_pad && pkt->len > blksize) {
+ pkt_pad = brcmu_pkt_buf_get_skb(tail_pad + tail_chop +
+ bus->head_align);
+ if (pkt_pad == NULL)
+ return -ENOMEM;
+ ret = brcmf_sdio_txpkt_hdalign(bus, pkt_pad);
+ if (unlikely(ret < 0)) {
+ kfree_skb(pkt_pad);
+ return ret;
+ }
+ memcpy(pkt_pad->data,
+ pkt->data + pkt->len - tail_chop,
+ tail_chop);
+ *(u16 *)(pkt_pad->cb) = ALIGN_SKB_FLAG + tail_chop;
+ skb_trim(pkt, pkt->len - tail_chop);
+ skb_trim(pkt_pad, tail_pad + tail_chop);
+ __skb_queue_after(pktq, pkt, pkt_pad);
+ } else {
+ ntail = pkt->data_len + tail_pad -
+ (pkt->end - pkt->tail);
+ if (skb_cloned(pkt) || ntail > 0)
+ if (pskb_expand_head(pkt, 0, ntail, GFP_ATOMIC))
+ return -ENOMEM;
+ if (skb_linearize(pkt))
+ return -ENOMEM;
+ __skb_put(pkt, tail_pad);
+ }
+
+ return tail_pad;
+}
+
+/**
+ * brcmf_sdio_txpkt_prep - packet preparation for transmit
+ * @bus: brcmf_sdio structure pointer
+ * @pktq: packet list pointer
+ * @chan: virtual channel to transmit the packet
+ *
+ * Processes to be applied to the packet
+ * - Align data buffer pointer
+ * - Align data buffer length
+ * - Prepare header
+ * Return: negative value if there is error
+ */
+static int
+brcmf_sdio_txpkt_prep(struct brcmf_sdio *bus, struct sk_buff_head *pktq,
+ uint chan)
+{
+ u16 head_pad, total_len;
+ struct sk_buff *pkt_next;
+ u8 txseq;
+ int ret;
+ struct brcmf_sdio_hdrinfo hd_info = {0};
+
+ txseq = bus->tx_seq;
+ total_len = 0;
+ skb_queue_walk(pktq, pkt_next) {
+ /* alignment packet inserted in previous
+ * loop cycle can be skipped as it is
+ * already properly aligned and does not
+ * need an sdpcm header.
+ */
+ if (*(u16 *)(pkt_next->cb) & ALIGN_SKB_FLAG)
+ continue;
+
+ /* align packet data pointer */
+ ret = brcmf_sdio_txpkt_hdalign(bus, pkt_next);
+ if (ret < 0)
+ return ret;
+ head_pad = (u16)ret;
+ if (head_pad)
+ memset(pkt_next->data + bus->tx_hdrlen, 0, head_pad);
+
+ total_len += pkt_next->len;
+
+ hd_info.len = pkt_next->len;
+ hd_info.lastfrm = skb_queue_is_last(pktq, pkt_next);
+ if (bus->txglom && pktq->qlen > 1) {
+ ret = brcmf_sdio_txpkt_prep_sg(bus, pktq,
+ pkt_next, total_len);
+ if (ret < 0)
+ return ret;
+ hd_info.tail_pad = (u16)ret;
+ total_len += (u16)ret;
+ }
+
+ hd_info.channel = chan;
+ hd_info.dat_offset = head_pad + bus->tx_hdrlen;
+ hd_info.seq_num = txseq++;
+
+ /* Now fill the header */
+ brcmf_sdio_hdpack(bus, pkt_next->data, &hd_info);
+
+ if (BRCMF_BYTES_ON() &&
+ ((BRCMF_CTL_ON() && chan == SDPCM_CONTROL_CHANNEL) ||
+ (BRCMF_DATA_ON() && chan != SDPCM_CONTROL_CHANNEL)))
+ brcmf_dbg_hex_dump(true, pkt_next->data, hd_info.len,
+ "Tx Frame:\n");
+ else if (BRCMF_HDRS_ON())
+ brcmf_dbg_hex_dump(true, pkt_next->data,
+ head_pad + bus->tx_hdrlen,
+ "Tx Header:\n");
+ }
+ /* Hardware length tag of the first packet should be total
+ * length of the chain (including padding)
+ */
+ if (bus->txglom)
+ brcmf_sdio_update_hwhdr(pktq->next->data, total_len);
+ return 0;
+}
+
+/**
+ * brcmf_sdio_txpkt_postp - packet post processing for transmit
+ * @bus: brcmf_sdio structure pointer
+ * @pktq: packet list pointer
+ *
+ * Processes to be applied to the packet
+ * - Remove head padding
+ * - Remove tail padding
+ */
+static void
+brcmf_sdio_txpkt_postp(struct brcmf_sdio *bus, struct sk_buff_head *pktq)
+{
+ u8 *hdr;
+ u32 dat_offset;
+ u16 tail_pad;
+ u16 dummy_flags, chop_len;
+ struct sk_buff *pkt_next, *tmp, *pkt_prev;
+
+ skb_queue_walk_safe(pktq, pkt_next, tmp) {
+ dummy_flags = *(u16 *)(pkt_next->cb);
+ if (dummy_flags & ALIGN_SKB_FLAG) {
+ chop_len = dummy_flags & ALIGN_SKB_CHOP_LEN_MASK;
+ if (chop_len) {
+ pkt_prev = pkt_next->prev;
+ skb_put(pkt_prev, chop_len);
+ }
+ __skb_unlink(pkt_next, pktq);
+ brcmu_pkt_buf_free_skb(pkt_next);
+ } else {
+ hdr = pkt_next->data + bus->tx_hdrlen - SDPCM_SWHDR_LEN;
+ dat_offset = le32_to_cpu(*(__le32 *)hdr);
+ dat_offset = (dat_offset & SDPCM_DOFFSET_MASK) >>
+ SDPCM_DOFFSET_SHIFT;
+ skb_pull(pkt_next, dat_offset);
+ if (bus->txglom) {
+ tail_pad = le16_to_cpu(*(__le16 *)(hdr - 2));
+ skb_trim(pkt_next, pkt_next->len - tail_pad);
+ }
+ }
+ }
+}
+
+/* Writes a HW/SW header into the packet and sends it. */
+/* Assumes: (a) header space already there, (b) caller holds lock */
+static int brcmf_sdio_txpkt(struct brcmf_sdio *bus, struct sk_buff_head *pktq,
+ uint chan)
+{
+ int ret;
+ struct sk_buff *pkt_next, *tmp;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ ret = brcmf_sdio_txpkt_prep(bus, pktq, chan);
+ if (ret)
+ goto done;
+
+ sdio_claim_host(bus->sdiodev->func[1]);
+ ret = brcmf_sdiod_send_pkt(bus->sdiodev, pktq);
+ bus->sdcnt.f2txdata++;
+
+ if (ret < 0)
+ brcmf_sdio_txfail(bus);
+
+ sdio_release_host(bus->sdiodev->func[1]);
+
+done:
+ brcmf_sdio_txpkt_postp(bus, pktq);
+ if (ret == 0)
+ bus->tx_seq = (bus->tx_seq + pktq->qlen) % SDPCM_SEQ_WRAP;
+ skb_queue_walk_safe(pktq, pkt_next, tmp) {
+ __skb_unlink(pkt_next, pktq);
+ brcmf_proto_bcdc_txcomplete(bus->sdiodev->dev, pkt_next,
+ ret == 0);
+ }
+ return ret;
+}
+
+static uint brcmf_sdio_sendfromq(struct brcmf_sdio *bus, uint maxframes)
+{
+ struct sk_buff *pkt;
+ struct sk_buff_head pktq;
+ u32 intstatus = 0;
+ int ret = 0, prec_out, i;
+ uint cnt = 0;
+ u8 tx_prec_map, pkt_num;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ tx_prec_map = ~bus->flowcontrol;
+
+ /* Send frames until the limit or some other event */
+ for (cnt = 0; (cnt < maxframes) && data_ok(bus);) {
+ pkt_num = 1;
+ if (bus->txglom)
+ pkt_num = min_t(u8, bus->tx_max - bus->tx_seq,
+ bus->sdiodev->txglomsz);
+ pkt_num = min_t(u32, pkt_num,
+ brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol));
+ __skb_queue_head_init(&pktq);
+ spin_lock_bh(&bus->txq_lock);
+ for (i = 0; i < pkt_num; i++) {
+ pkt = brcmu_pktq_mdeq(&bus->txq, tx_prec_map,
+ &prec_out);
+ if (pkt == NULL)
+ break;
+ __skb_queue_tail(&pktq, pkt);
+ }
+ spin_unlock_bh(&bus->txq_lock);
+ if (i == 0)
+ break;
+
+ ret = brcmf_sdio_txpkt(bus, &pktq, SDPCM_DATA_CHANNEL);
+
+ cnt += i;
+
+ /* In poll mode, need to check for other events */
+ if (!bus->intr) {
+ /* Check device status, signal pending interrupt */
+ sdio_claim_host(bus->sdiodev->func[1]);
+ ret = r_sdreg32(bus, &intstatus,
+ offsetof(struct sdpcmd_regs,
+ intstatus));
+ sdio_release_host(bus->sdiodev->func[1]);
+ bus->sdcnt.f2txdata++;
+ if (ret != 0)
+ break;
+ if (intstatus & bus->hostintmask)
+ atomic_set(&bus->ipend, 1);
+ }
+ }
+
+ /* Deflow-control stack if needed */
+ if ((bus->sdiodev->state == BRCMF_SDIOD_DATA) &&
+ bus->txoff && (pktq_len(&bus->txq) < TXLOW)) {
+ bus->txoff = false;
+ brcmf_proto_bcdc_txflowblock(bus->sdiodev->dev, false);
+ }
+
+ return cnt;
+}
+
+static int brcmf_sdio_tx_ctrlframe(struct brcmf_sdio *bus, u8 *frame, u16 len)
+{
+ u8 doff;
+ u16 pad;
+ uint retries = 0;
+ struct brcmf_sdio_hdrinfo hd_info = {0};
+ int ret;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ /* Back the pointer to make room for bus header */
+ frame -= bus->tx_hdrlen;
+ len += bus->tx_hdrlen;
+
+ /* Add alignment padding (optional for ctl frames) */
+ doff = ((unsigned long)frame % bus->head_align);
+ if (doff) {
+ frame -= doff;
+ len += doff;
+ memset(frame + bus->tx_hdrlen, 0, doff);
+ }
+
+ /* Round send length to next SDIO block */
+ pad = 0;
+ if (bus->roundup && bus->blocksize && (len > bus->blocksize)) {
+ pad = bus->blocksize - (len % bus->blocksize);
+ if ((pad > bus->roundup) || (pad >= bus->blocksize))
+ pad = 0;
+ } else if (len % bus->head_align) {
+ pad = bus->head_align - (len % bus->head_align);
+ }
+ len += pad;
+
+ hd_info.len = len - pad;
+ hd_info.channel = SDPCM_CONTROL_CHANNEL;
+ hd_info.dat_offset = doff + bus->tx_hdrlen;
+ hd_info.seq_num = bus->tx_seq;
+ hd_info.lastfrm = true;
+ hd_info.tail_pad = pad;
+ brcmf_sdio_hdpack(bus, frame, &hd_info);
+
+ if (bus->txglom)
+ brcmf_sdio_update_hwhdr(frame, len);
+
+ brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_CTL_ON(),
+ frame, len, "Tx Frame:\n");
+ brcmf_dbg_hex_dump(!(BRCMF_BYTES_ON() && BRCMF_CTL_ON()) &&
+ BRCMF_HDRS_ON(),
+ frame, min_t(u16, len, 16), "TxHdr:\n");
+
+ do {
+ ret = brcmf_sdiod_send_buf(bus->sdiodev, frame, len);
+
+ if (ret < 0)
+ brcmf_sdio_txfail(bus);
+ else
+ bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQ_WRAP;
+ } while (ret < 0 && retries++ < TXRETRIES);
+
+ return ret;
+}
+
+static void brcmf_sdio_bus_stop(struct device *dev)
+{
+ u32 local_hostintmask;
+ u8 saveclk;
+ int err;
+ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+ struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
+ struct brcmf_sdio *bus = sdiodev->bus;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ if (bus->watchdog_tsk) {
+ send_sig(SIGTERM, bus->watchdog_tsk, 1);
+ kthread_stop(bus->watchdog_tsk);
+ bus->watchdog_tsk = NULL;
+ }
+
+ if (sdiodev->state != BRCMF_SDIOD_NOMEDIUM) {
+ sdio_claim_host(sdiodev->func[1]);
+
+ /* Enable clock for device interrupts */
+ brcmf_sdio_bus_sleep(bus, false, false);
+
+ /* Disable and clear interrupts at the chip level also */
+ w_sdreg32(bus, 0, offsetof(struct sdpcmd_regs, hostintmask));
+ local_hostintmask = bus->hostintmask;
+ bus->hostintmask = 0;
+
+ /* Force backplane clocks to assure F2 interrupt propagates */
+ saveclk = brcmf_sdiod_regrb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
+ &err);
+ if (!err) {
+ if (bus->ci->chip == CY_CC_43012_CHIP_ID) {
+ brcmf_sdiod_regwb(sdiodev,
+ SBSDIO_FUNC1_CHIPCLKCSR,
+ (saveclk |
+ SBSDIO_HT_AVAIL_REQ),
+ &err);
+ } else {
+ brcmf_sdiod_regwb(sdiodev,
+ SBSDIO_FUNC1_CHIPCLKCSR,
+ (saveclk | SBSDIO_FORCE_HT),
+ &err);
+ }
+ }
+ if (err)
+ brcmf_err("Failed to force clock for F2: err %d\n",
+ err);
+
+ /* Turn off the bus (F2), free any pending packets */
+ brcmf_dbg(INTR, "disable SDIO interrupts\n");
+ sdio_disable_func(sdiodev->func[SDIO_FUNC_2]);
+
+ /* Clear any pending interrupts now that F2 is disabled */
+ w_sdreg32(bus, local_hostintmask,
+ offsetof(struct sdpcmd_regs, intstatus));
+
+ sdio_release_host(sdiodev->func[1]);
+ }
+ /* Clear the data packet queues */
+ brcmu_pktq_flush(&bus->txq, true, NULL, NULL);
+
+ /* Clear any held glomming stuff */
+ brcmu_pkt_buf_free_skb(bus->glomd);
+ brcmf_sdio_free_glom(bus);
+
+ /* Clear rx control and wake any waiters */
+ spin_lock_bh(&bus->rxctl_lock);
+ bus->rxlen = 0;
+ spin_unlock_bh(&bus->rxctl_lock);
+ brcmf_sdio_dcmd_resp_wake(bus);
+
+ /* Reset some F2 state stuff */
+ bus->rxskip = false;
+ bus->tx_seq = bus->rx_seq = 0;
+}
+
+static inline void brcmf_sdio_clrintr(struct brcmf_sdio *bus)
+{
+ struct brcmf_sdio_dev *sdiodev;
+ unsigned long flags;
+
+ sdiodev = bus->sdiodev;
+ if (sdiodev->oob_irq_requested) {
+ spin_lock_irqsave(&sdiodev->irq_en_lock, flags);
+ if (!sdiodev->irq_en && !atomic_read(&bus->ipend)) {
+ enable_irq(sdiodev->settings->bus.sdio.oob_irq_nr);
+ sdiodev->irq_en = true;
+ }
+ spin_unlock_irqrestore(&sdiodev->irq_en_lock, flags);
+ }
+}
+
+static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus)
+{
+ struct brcmf_core *buscore;
+ u32 addr;
+ unsigned long val;
+ int ret;
+
+ buscore = brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV);
+ addr = buscore->base + offsetof(struct sdpcmd_regs, intstatus);
+
+ val = brcmf_sdiod_regrl(bus->sdiodev, addr, &ret);
+ bus->sdcnt.f1regdata++;
+ if (ret != 0)
+ return ret;
+
+ val &= bus->hostintmask;
+ atomic_set(&bus->fcstate, !!(val & I_HMB_FC_STATE));
+
+ /* Clear interrupts */
+ if (val) {
+ brcmf_sdiod_regwl(bus->sdiodev, addr, val, &ret);
+ bus->sdcnt.f1regdata++;
+ atomic_or(val, &bus->intstatus);
+ }
+
+ return ret;
+}
+
+static void brcmf_sdio_dpc(struct brcmf_sdio *bus)
+{
+ u32 newstatus = 0;
+ unsigned long intstatus;
+ uint txlimit = bus->txbound; /* Tx frames to send before resched */
+ uint framecnt; /* Temporary counter of tx/rx frames */
+ int err = 0;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ sdio_claim_host(bus->sdiodev->func[1]);
+
+ /* If waiting for HTAVAIL, check status */
+ if (!bus->sr_enabled && bus->clkstate == CLK_PENDING) {
+ u8 clkctl, devctl = 0;
+
+#ifdef DEBUG
+ /* Check for inconsistent device control */
+ devctl = brcmf_sdiod_regrb(bus->sdiodev,
+ SBSDIO_DEVICE_CTL, &err);
+#endif /* DEBUG */
+
+ /* Read CSR, if clock on switch to AVAIL, else ignore */
+ clkctl = brcmf_sdiod_regrb(bus->sdiodev,
+ SBSDIO_FUNC1_CHIPCLKCSR, &err);
+
+ brcmf_dbg(SDIO, "DPC: PENDING, devctl 0x%02x clkctl 0x%02x\n",
+ devctl, clkctl);
+
+ if (SBSDIO_HTAV(clkctl)) {
+ devctl = brcmf_sdiod_regrb(bus->sdiodev,
+ SBSDIO_DEVICE_CTL, &err);
+ devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY;
+ brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL,
+ devctl, &err);
+ bus->clkstate = CLK_AVAIL;
+ }
+ }
+
+ /* Make sure backplane clock is on */
+ brcmf_sdio_bus_sleep(bus, false, true);
+
+ /* Pending interrupt indicates new device status */
+ if (atomic_read(&bus->ipend) > 0) {
+ atomic_set(&bus->ipend, 0);
+ err = brcmf_sdio_intr_rstatus(bus);
+ }
+
+ /* Start with leftover status bits */
+ intstatus = atomic_xchg(&bus->intstatus, 0);
+
+ /* Handle flow-control change: read new state in case our ack
+ * crossed another change interrupt. If change still set, assume
+ * FC ON for safety, let next loop through do the debounce.
+ */
+ if (intstatus & I_HMB_FC_CHANGE) {
+ intstatus &= ~I_HMB_FC_CHANGE;
+ err = w_sdreg32(bus, I_HMB_FC_CHANGE,
+ offsetof(struct sdpcmd_regs, intstatus));
+
+ err = r_sdreg32(bus, &newstatus,
+ offsetof(struct sdpcmd_regs, intstatus));
+ bus->sdcnt.f1regdata += 2;
+ atomic_set(&bus->fcstate,
+ !!(newstatus & (I_HMB_FC_STATE | I_HMB_FC_CHANGE)));
+ intstatus |= (newstatus & bus->hostintmask);
+ }
+
+ /* Handle host mailbox indication */
+ if (intstatus & I_HMB_HOST_INT) {
+ intstatus &= ~I_HMB_HOST_INT;
+ intstatus |= brcmf_sdio_hostmail(bus);
+ }
+
+ sdio_release_host(bus->sdiodev->func[1]);
+
+ /* Generally don't ask for these, can get CRC errors... */
+ if (intstatus & I_WR_OOSYNC) {
+ brcmf_err("Dongle reports WR_OOSYNC\n");
+ intstatus &= ~I_WR_OOSYNC;
+ }
+
+ if (intstatus & I_RD_OOSYNC) {
+ brcmf_err("Dongle reports RD_OOSYNC\n");
+ intstatus &= ~I_RD_OOSYNC;
+ }
+
+ if (intstatus & I_SBINT) {
+ brcmf_err("Dongle reports SBINT\n");
+ intstatus &= ~I_SBINT;
+ }
+
+ /* Would be active due to wake-wlan in gSPI */
+ if (intstatus & I_CHIPACTIVE) {
+ brcmf_dbg(INFO, "Dongle reports CHIPACTIVE\n");
+ intstatus &= ~I_CHIPACTIVE;
+ }
+
+ /* Ignore frame indications if rxskip is set */
+ if (bus->rxskip)
+ intstatus &= ~I_HMB_FRAME_IND;
+
+ /* On frame indication, read available frames */
+ if ((intstatus & I_HMB_FRAME_IND) && (bus->clkstate == CLK_AVAIL)) {
+ brcmf_sdio_readframes(bus, bus->rxbound);
+ if (!bus->rxpending)
+ intstatus &= ~I_HMB_FRAME_IND;
+ }
+
+ /* Keep still-pending events for next scheduling */
+ if (intstatus)
+ atomic_or(intstatus, &bus->intstatus);
+
+ brcmf_sdio_clrintr(bus);
+
+ if (bus->ctrl_frame_stat && (bus->clkstate == CLK_AVAIL) &&
+ data_ok(bus)) {
+ sdio_claim_host(bus->sdiodev->func[1]);
+ if (bus->ctrl_frame_stat) {
+ err = brcmf_sdio_tx_ctrlframe(bus, bus->ctrl_frame_buf,
+ bus->ctrl_frame_len);
+ bus->ctrl_frame_err = err;
+ wmb();
+ bus->ctrl_frame_stat = false;
+ }
+ sdio_release_host(bus->sdiodev->func[1]);
+ brcmf_sdio_wait_event_wakeup(bus);
+ }
+ /* Send queued frames (limit 1 if rx may still be pending) */
+ if ((bus->clkstate == CLK_AVAIL) && !atomic_read(&bus->fcstate) &&
+ brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) && txlimit &&
+ data_ok(bus)) {
+ framecnt = bus->rxpending ? min(txlimit, bus->txminmax) :
+ txlimit;
+ brcmf_sdio_sendfromq(bus, framecnt);
+ }
+
+ if ((bus->sdiodev->state != BRCMF_SDIOD_DATA) || (err != 0)) {
+ brcmf_err("failed backplane access over SDIO, halting operation\n");
+ atomic_set(&bus->intstatus, 0);
+ if (bus->ctrl_frame_stat) {
+ sdio_claim_host(bus->sdiodev->func[1]);
+ if (bus->ctrl_frame_stat) {
+ bus->ctrl_frame_err = -ENODEV;
+ wmb();
+ bus->ctrl_frame_stat = false;
+ brcmf_sdio_wait_event_wakeup(bus);
+ }
+ sdio_release_host(bus->sdiodev->func[1]);
+ }
+ } else if (atomic_read(&bus->intstatus) ||
+ atomic_read(&bus->ipend) > 0 ||
+ (!atomic_read(&bus->fcstate) &&
+ brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) &&
+ data_ok(bus))) {
+ bus->dpc_triggered = true;
+ }
+}
+
+static struct pktq *brcmf_sdio_bus_gettxq(struct device *dev)
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+ struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
+ struct brcmf_sdio *bus = sdiodev->bus;
+
+ return &bus->txq;
+}
+
+static bool brcmf_sdio_prec_enq(struct pktq *q, struct sk_buff *pkt, int prec)
+{
+ struct sk_buff *p;
+ int eprec = -1; /* precedence to evict from */
+
+ /* Fast case, precedence queue is not full and we are also not
+ * exceeding total queue length
+ */
+ if (!pktq_pfull(q, prec) && !pktq_full(q)) {
+ brcmu_pktq_penq(q, prec, pkt);
+ return true;
+ }
+
+ /* Determine precedence from which to evict packet, if any */
+ if (pktq_pfull(q, prec)) {
+ eprec = prec;
+ } else if (pktq_full(q)) {
+ p = brcmu_pktq_peek_tail(q, &eprec);
+ if (eprec > prec)
+ return false;
+ }
+
+ /* Evict if needed */
+ if (eprec >= 0) {
+ /* Detect queueing to unconfigured precedence */
+ if (eprec == prec)
+ return false; /* refuse newer (incoming) packet */
+ /* Evict packet according to discard policy */
+ p = brcmu_pktq_pdeq_tail(q, eprec);
+ if (p == NULL)
+ brcmf_err("brcmu_pktq_pdeq_tail() failed\n");
+ brcmu_pkt_buf_free_skb(p);
+ }
+
+ /* Enqueue */
+ p = brcmu_pktq_penq(q, prec, pkt);
+ if (p == NULL)
+ brcmf_err("brcmu_pktq_penq() failed\n");
+
+ return p != NULL;
+}
+
+static int brcmf_sdio_bus_txdata(struct device *dev, struct sk_buff *pkt)
+{
+ int ret = -EBADE;
+ uint prec;
+ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+ struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
+ struct brcmf_sdio *bus = sdiodev->bus;
+
+ brcmf_dbg(TRACE, "Enter: pkt: data %p len %d\n", pkt->data, pkt->len);
+ if (sdiodev->state != BRCMF_SDIOD_DATA)
+ return -EIO;
+
+ /* Add space for the header */
+ skb_push(pkt, bus->tx_hdrlen);
+ /* precondition: IS_ALIGNED((unsigned long)(pkt->data), 2) */
+
+ prec = prio2prec((pkt->priority & PRIOMASK));
+
+ /* Check for existing queue, current flow-control,
+ pending event, or pending clock */
+ brcmf_dbg(TRACE, "deferring pktq len %d\n", pktq_len(&bus->txq));
+ bus->sdcnt.fcqueued++;
+
+ /* Priority based enq */
+ spin_lock_bh(&bus->txq_lock);
+ /* reset bus_flags in packet cb */
+ *(u16 *)(pkt->cb) = 0;
+ if (!brcmf_sdio_prec_enq(&bus->txq, pkt, prec)) {
+ skb_pull(pkt, bus->tx_hdrlen);
+ brcmf_err("out of bus->txq !!!\n");
+ ret = -ENOSR;
+ } else {
+ ret = 0;
+ }
+
+ if (pktq_len(&bus->txq) >= TXHI) {
+ bus->txoff = true;
+ brcmf_proto_bcdc_txflowblock(dev, true);
+ }
+ spin_unlock_bh(&bus->txq_lock);
+
+#ifdef DEBUG
+ if (pktq_plen(&bus->txq, prec) > qcount[prec])
+ qcount[prec] = pktq_plen(&bus->txq, prec);
+#endif
+
+ brcmf_sdio_trigger_dpc(bus);
+ return ret;
+}
+
+#ifdef DEBUG
+#define CONSOLE_LINE_MAX 192
+
+static int brcmf_sdio_readconsole(struct brcmf_sdio *bus)
+{
+ struct brcmf_console *c = &bus->console;
+ u8 line[CONSOLE_LINE_MAX], ch;
+ u32 n, idx, addr;
+ int rv;
+
+ /* Don't do anything until FWREADY updates console address */
+ if (bus->console_addr == 0)
+ return 0;
+
+ /* Read console log struct */
+ addr = bus->console_addr + offsetof(struct rte_console, log_le);
+ rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, (u8 *)&c->log_le,
+ sizeof(c->log_le));
+ if (rv < 0)
+ return rv;
+
+ /* Allocate console buffer (one time only) */
+ if (c->buf == NULL) {
+ c->bufsize = le32_to_cpu(c->log_le.buf_size);
+ c->buf = kmalloc(c->bufsize, GFP_ATOMIC);
+ if (c->buf == NULL)
+ return -ENOMEM;
+ }
+
+ idx = le32_to_cpu(c->log_le.idx);
+
+ /* Protect against corrupt value */
+ if (idx > c->bufsize)
+ return -EBADE;
+
+ /* Skip reading the console buffer if the index pointer
+ has not moved */
+ if (idx == c->last)
+ return 0;
+
+ /* Read the console buffer */
+ addr = le32_to_cpu(c->log_le.buf);
+ rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, c->buf, c->bufsize);
+ if (rv < 0)
+ return rv;
+
+ while (c->last != idx) {
+ for (n = 0; n < CONSOLE_LINE_MAX - 2; n++) {
+ if (c->last == idx) {
+ /* This would output a partial line.
+ * Instead, back up
+ * the buffer pointer and output this
+ * line next time around.
+ */
+ if (c->last >= n)
+ c->last -= n;
+ else
+ c->last = c->bufsize - n;
+ goto break2;
+ }
+ ch = c->buf[c->last];
+ c->last = (c->last + 1) % c->bufsize;
+ if (ch == '\n')
+ break;
+ line[n] = ch;
+ }
+
+ if (n > 0) {
+ if (line[n - 1] == '\r')
+ n--;
+ line[n] = 0;
+ pr_debug("CONSOLE: %s\n", line);
+ }
+ }
+break2:
+
+ return 0;
+}
+#endif /* DEBUG */
+
+static int
+brcmf_sdio_bus_txctl(struct device *dev, unsigned char *msg, uint msglen)
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+ struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
+ struct brcmf_sdio *bus = sdiodev->bus;
+ int ret;
+
+ brcmf_dbg(TRACE, "Enter\n");
+ if (sdiodev->state != BRCMF_SDIOD_DATA)
+ return -EIO;
+
+ /* Send from dpc */
+ bus->ctrl_frame_buf = msg;
+ bus->ctrl_frame_len = msglen;
+ wmb();
+ bus->ctrl_frame_stat = true;
+
+ brcmf_sdio_trigger_dpc(bus);
+ wait_event_interruptible_timeout(bus->ctrl_wait, !bus->ctrl_frame_stat,
+ CTL_DONE_TIMEOUT);
+ ret = 0;
+ if (bus->ctrl_frame_stat) {
+ sdio_claim_host(bus->sdiodev->func[1]);
+ if (bus->ctrl_frame_stat) {
+ brcmf_dbg(SDIO, "ctrl_frame timeout\n");
+ bus->ctrl_frame_stat = false;
+ ret = -ETIMEDOUT;
+ }
+ sdio_release_host(bus->sdiodev->func[1]);
+ }
+ if (!ret) {
+ brcmf_dbg(SDIO, "ctrl_frame complete, err=%d\n",
+ bus->ctrl_frame_err);
+ rmb();
+ ret = bus->ctrl_frame_err;
+ }
+
+ if (ret)
+ bus->sdcnt.tx_ctlerrs++;
+ else
+ bus->sdcnt.tx_ctlpkts++;
+
+ return ret;
+}
+
+#ifdef DEBUG
+static int brcmf_sdio_dump_console(struct seq_file *seq, struct brcmf_sdio *bus,
+ struct sdpcm_shared *sh)
+{
+ u32 addr, console_ptr, console_size, console_index;
+ char *conbuf = NULL;
+ __le32 sh_val;
+ int rv;
+
+ /* obtain console information from device memory */
+ addr = sh->console_addr + offsetof(struct rte_console, log_le);
+ rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr,
+ (u8 *)&sh_val, sizeof(u32));
+ if (rv < 0)
+ return rv;
+ console_ptr = le32_to_cpu(sh_val);
+
+ addr = sh->console_addr + offsetof(struct rte_console, log_le.buf_size);
+ rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr,
+ (u8 *)&sh_val, sizeof(u32));
+ if (rv < 0)
+ return rv;
+ console_size = le32_to_cpu(sh_val);
+
+ addr = sh->console_addr + offsetof(struct rte_console, log_le.idx);
+ rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr,
+ (u8 *)&sh_val, sizeof(u32));
+ if (rv < 0)
+ return rv;
+ console_index = le32_to_cpu(sh_val);
+
+ /* allocate buffer for console data */
+ if (console_size <= CONSOLE_BUFFER_MAX)
+ conbuf = vzalloc(console_size+1);
+
+ if (!conbuf)
+ return -ENOMEM;
+
+ /* obtain the console data from device */
+ conbuf[console_size] = '\0';
+ rv = brcmf_sdiod_ramrw(bus->sdiodev, false, console_ptr, (u8 *)conbuf,
+ console_size);
+ if (rv < 0)
+ goto done;
+
+ rv = seq_write(seq, conbuf + console_index,
+ console_size - console_index);
+ if (rv < 0)
+ goto done;
+
+ if (console_index > 0)
+ rv = seq_write(seq, conbuf, console_index - 1);
+
+done:
+ vfree(conbuf);
+ return rv;
+}
+
+static int brcmf_sdio_trap_info(struct seq_file *seq, struct brcmf_sdio *bus,
+ struct sdpcm_shared *sh)
+{
+ int error;
+ struct brcmf_trap_info tr;
+
+ if ((sh->flags & SDPCM_SHARED_TRAP) == 0) {
+ brcmf_dbg(INFO, "no trap in firmware\n");
+ return 0;
+ }
+
+ error = brcmf_sdiod_ramrw(bus->sdiodev, false, sh->trap_addr, (u8 *)&tr,
+ sizeof(struct brcmf_trap_info));
+ if (error < 0)
+ return error;
+
+ seq_printf(seq,
+ "dongle trap info: type 0x%x @ epc 0x%08x\n"
+ " cpsr 0x%08x spsr 0x%08x sp 0x%08x\n"
+ " lr 0x%08x pc 0x%08x offset 0x%x\n"
+ " r0 0x%08x r1 0x%08x r2 0x%08x r3 0x%08x\n"
+ " r4 0x%08x r5 0x%08x r6 0x%08x r7 0x%08x\n",
+ le32_to_cpu(tr.type), le32_to_cpu(tr.epc),
+ le32_to_cpu(tr.cpsr), le32_to_cpu(tr.spsr),
+ le32_to_cpu(tr.r13), le32_to_cpu(tr.r14),
+ le32_to_cpu(tr.pc), sh->trap_addr,
+ le32_to_cpu(tr.r0), le32_to_cpu(tr.r1),
+ le32_to_cpu(tr.r2), le32_to_cpu(tr.r3),
+ le32_to_cpu(tr.r4), le32_to_cpu(tr.r5),
+ le32_to_cpu(tr.r6), le32_to_cpu(tr.r7));
+
+ return 0;
+}
+
+static int brcmf_sdio_assert_info(struct seq_file *seq, struct brcmf_sdio *bus,
+ struct sdpcm_shared *sh)
+{
+ int error = 0;
+ char file[80] = "?";
+ char expr[80] = "<???>";
+
+ if ((sh->flags & SDPCM_SHARED_ASSERT_BUILT) == 0) {
+ brcmf_dbg(INFO, "firmware not built with -assert\n");
+ return 0;
+ } else if ((sh->flags & SDPCM_SHARED_ASSERT) == 0) {
+ brcmf_dbg(INFO, "no assert in dongle\n");
+ return 0;
+ }
+
+ sdio_claim_host(bus->sdiodev->func[1]);
+ if (sh->assert_file_addr != 0) {
+ error = brcmf_sdiod_ramrw(bus->sdiodev, false,
+ sh->assert_file_addr, (u8 *)file, 80);
+ if (error < 0)
+ return error;
+ }
+ if (sh->assert_exp_addr != 0) {
+ error = brcmf_sdiod_ramrw(bus->sdiodev, false,
+ sh->assert_exp_addr, (u8 *)expr, 80);
+ if (error < 0)
+ return error;
+ }
+ sdio_release_host(bus->sdiodev->func[1]);
+
+ seq_printf(seq, "dongle assert: %s:%d: assert(%s)\n",
+ file, sh->assert_line, expr);
+ return 0;
+}
+
+static int brcmf_sdio_checkdied(struct brcmf_sdio *bus)
+{
+ int error;
+ struct sdpcm_shared sh;
+
+ error = brcmf_sdio_readshared(bus, &sh);
+
+ if (error < 0)
+ return error;
+
+ if ((sh.flags & SDPCM_SHARED_ASSERT_BUILT) == 0)
+ brcmf_dbg(INFO, "firmware not built with -assert\n");
+ else if (sh.flags & SDPCM_SHARED_ASSERT)
+ brcmf_err("assertion in dongle\n");
+
+ if (sh.flags & SDPCM_SHARED_TRAP)
+ brcmf_err("firmware trap in dongle\n");
+
+ return 0;
+}
+
+static int brcmf_sdio_died_dump(struct seq_file *seq, struct brcmf_sdio *bus)
+{
+ int error = 0;
+ struct sdpcm_shared sh;
+
+ error = brcmf_sdio_readshared(bus, &sh);
+ if (error < 0)
+ goto done;
+
+ error = brcmf_sdio_assert_info(seq, bus, &sh);
+ if (error < 0)
+ goto done;
+
+ error = brcmf_sdio_trap_info(seq, bus, &sh);
+ if (error < 0)
+ goto done;
+
+ error = brcmf_sdio_dump_console(seq, bus, &sh);
+
+done:
+ return error;
+}
+
+static int brcmf_sdio_forensic_read(struct seq_file *seq, void *data)
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(seq->private);
+ struct brcmf_sdio *bus = bus_if->bus_priv.sdio->bus;
+
+ return brcmf_sdio_died_dump(seq, bus);
+}
+
+static int brcmf_debugfs_sdio_count_read(struct seq_file *seq, void *data)
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(seq->private);
+ struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
+ struct brcmf_sdio_count *sdcnt = &sdiodev->bus->sdcnt;
+
+ seq_printf(seq,
+ "intrcount: %u\nlastintrs: %u\n"
+ "pollcnt: %u\nregfails: %u\n"
+ "tx_sderrs: %u\nfcqueued: %u\n"
+ "rxrtx: %u\nrx_toolong: %u\n"
+ "rxc_errors: %u\nrx_hdrfail: %u\n"
+ "rx_badhdr: %u\nrx_badseq: %u\n"
+ "fc_rcvd: %u\nfc_xoff: %u\n"
+ "fc_xon: %u\nrxglomfail: %u\n"
+ "rxglomframes: %u\nrxglompkts: %u\n"
+ "f2rxhdrs: %u\nf2rxdata: %u\n"
+ "f2txdata: %u\nf1regdata: %u\n"
+ "tickcnt: %u\ntx_ctlerrs: %lu\n"
+ "tx_ctlpkts: %lu\nrx_ctlerrs: %lu\n"
+ "rx_ctlpkts: %lu\nrx_readahead: %lu\n",
+ sdcnt->intrcount, sdcnt->lastintrs,
+ sdcnt->pollcnt, sdcnt->regfails,
+ sdcnt->tx_sderrs, sdcnt->fcqueued,
+ sdcnt->rxrtx, sdcnt->rx_toolong,
+ sdcnt->rxc_errors, sdcnt->rx_hdrfail,
+ sdcnt->rx_badhdr, sdcnt->rx_badseq,
+ sdcnt->fc_rcvd, sdcnt->fc_xoff,
+ sdcnt->fc_xon, sdcnt->rxglomfail,
+ sdcnt->rxglomframes, sdcnt->rxglompkts,
+ sdcnt->f2rxhdrs, sdcnt->f2rxdata,
+ sdcnt->f2txdata, sdcnt->f1regdata,
+ sdcnt->tickcnt, sdcnt->tx_ctlerrs,
+ sdcnt->tx_ctlpkts, sdcnt->rx_ctlerrs,
+ sdcnt->rx_ctlpkts, sdcnt->rx_readahead_cnt);
+
+ return 0;
+}
+
+static void brcmf_sdio_debugfs_create(struct brcmf_sdio *bus)
+{
+ struct brcmf_pub *drvr = bus->sdiodev->bus_if->drvr;
+ struct dentry *dentry = brcmf_debugfs_get_devdir(drvr);
+
+ if (IS_ERR_OR_NULL(dentry))
+ return;
+
+ bus->console_interval = BRCMF_CONSOLE;
+
+ brcmf_debugfs_add_entry(drvr, "forensics", brcmf_sdio_forensic_read);
+ brcmf_debugfs_add_entry(drvr, "counters",
+ brcmf_debugfs_sdio_count_read);
+ debugfs_create_u32("console_interval", 0644, dentry,
+ &bus->console_interval);
+}
+#else
+static int brcmf_sdio_checkdied(struct brcmf_sdio *bus)
+{
+ return 0;
+}
+
+static void brcmf_sdio_debugfs_create(struct brcmf_sdio *bus)
+{
+}
+#endif /* DEBUG */
+
+static int
+brcmf_sdio_bus_rxctl(struct device *dev, unsigned char *msg, uint msglen)
+{
+ int timeleft;
+ uint rxlen = 0;
+ bool pending;
+ u8 *buf;
+ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+ struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
+ struct brcmf_sdio *bus = sdiodev->bus;
+
+ brcmf_dbg(TRACE, "Enter\n");
+ if (sdiodev->state != BRCMF_SDIOD_DATA)
+ return -EIO;
+
+ /* Wait until control frame is available */
+ timeleft = brcmf_sdio_dcmd_resp_wait(bus, &bus->rxlen, &pending);
+
+ spin_lock_bh(&bus->rxctl_lock);
+ rxlen = bus->rxlen;
+ memcpy(msg, bus->rxctl, min(msglen, rxlen));
+ bus->rxctl = NULL;
+ buf = bus->rxctl_orig;
+ bus->rxctl_orig = NULL;
+ bus->rxlen = 0;
+ spin_unlock_bh(&bus->rxctl_lock);
+ vfree(buf);
+
+ if (rxlen) {
+ brcmf_dbg(CTL, "resumed on rxctl frame, got %d expected %d\n",
+ rxlen, msglen);
+ } else if (timeleft == 0) {
+ brcmf_err("resumed on timeout\n");
+ brcmf_sdio_checkdied(bus);
+ } else if (pending) {
+ brcmf_dbg(CTL, "cancelled\n");
+ return -ERESTARTSYS;
+ } else {
+ brcmf_dbg(CTL, "resumed for unknown reason?\n");
+ brcmf_sdio_checkdied(bus);
+ }
+
+ if (rxlen)
+ bus->sdcnt.rx_ctlpkts++;
+ else
+ bus->sdcnt.rx_ctlerrs++;
+
+ return rxlen ? (int)rxlen : -ETIMEDOUT;
+}
+
+#ifdef DEBUG
+static bool
+brcmf_sdio_verifymemory(struct brcmf_sdio_dev *sdiodev, u32 ram_addr,
+ u8 *ram_data, uint ram_sz)
+{
+ char *ram_cmp;
+ int err;
+ bool ret = true;
+ int address;
+ int offset;
+ int len;
+
+ /* read back and verify */
+ brcmf_dbg(INFO, "Compare RAM dl & ul at 0x%08x; size=%d\n", ram_addr,
+ ram_sz);
+ ram_cmp = kmalloc(MEMBLOCK, GFP_KERNEL);
+ /* do not proceed while no memory but */
+ if (!ram_cmp)
+ return true;
+
+ address = ram_addr;
+ offset = 0;
+ while (offset < ram_sz) {
+ len = ((offset + MEMBLOCK) < ram_sz) ? MEMBLOCK :
+ ram_sz - offset;
+ err = brcmf_sdiod_ramrw(sdiodev, false, address, ram_cmp, len);
+ if (err) {
+ brcmf_err("error %d on reading %d membytes at 0x%08x\n",
+ err, len, address);
+ ret = false;
+ break;
+ } else if (memcmp(ram_cmp, &ram_data[offset], len)) {
+ brcmf_err("Downloaded RAM image is corrupted, block offset is %d, len is %d\n",
+ offset, len);
+ ret = false;
+ break;
+ }
+ offset += len;
+ address += len;
+ }
+
+ kfree(ram_cmp);
+
+ return ret;
+}
+#else /* DEBUG */
+static bool
+brcmf_sdio_verifymemory(struct brcmf_sdio_dev *sdiodev, u32 ram_addr,
+ u8 *ram_data, uint ram_sz)
+{
+ return true;
+}
+#endif /* DEBUG */
+
+static int brcmf_sdio_download_code_file(struct brcmf_sdio *bus,
+ const struct firmware *fw)
+{
+ int err;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ err = brcmf_sdiod_ramrw(bus->sdiodev, true, bus->ci->rambase,
+ (u8 *)fw->data, fw->size);
+ if (err)
+ brcmf_err("error %d on writing %d membytes at 0x%08x\n",
+ err, (int)fw->size, bus->ci->rambase);
+ else if (!brcmf_sdio_verifymemory(bus->sdiodev, bus->ci->rambase,
+ (u8 *)fw->data, fw->size))
+ err = -EIO;
+
+ return err;
+}
+
+static int brcmf_sdio_download_nvram(struct brcmf_sdio *bus,
+ void *vars, u32 varsz)
+{
+ int address;
+ int err;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ address = bus->ci->ramsize - varsz + bus->ci->rambase;
+ err = brcmf_sdiod_ramrw(bus->sdiodev, true, address, vars, varsz);
+ if (err)
+ brcmf_err("error %d on writing %d nvram bytes at 0x%08x\n",
+ err, varsz, address);
+ else if (!brcmf_sdio_verifymemory(bus->sdiodev, address, vars, varsz))
+ err = -EIO;
+
+ return err;
+}
+
+static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus,
+ const struct firmware *fw,
+ void *nvram, u32 nvlen)
+{
+ int bcmerror;
+ u32 rstvec;
+
+ sdio_claim_host(bus->sdiodev->func[1]);
+ brcmf_sdio_clkctl(bus, CLK_AVAIL, false);
+
+ rstvec = get_unaligned_le32(fw->data);
+ brcmf_dbg(SDIO, "firmware rstvec: %x\n", rstvec);
+
+ bcmerror = brcmf_sdio_download_code_file(bus, fw);
+ release_firmware(fw);
+ if (bcmerror) {
+ brcmf_err("dongle image file download failed\n");
+ brcmf_fw_nvram_free(nvram);
+ goto err;
+ }
+
+ bcmerror = brcmf_sdio_download_nvram(bus, nvram, nvlen);
+ brcmf_fw_nvram_free(nvram);
+ if (bcmerror) {
+ brcmf_err("dongle nvram file download failed\n");
+ goto err;
+ }
+
+ /* Take arm out of reset */
+ if (!brcmf_chip_set_active(bus->ci, rstvec)) {
+ brcmf_err("error getting out of ARM core reset\n");
+ goto err;
+ }
+
+err:
+ brcmf_sdio_clkctl(bus, CLK_SDONLY, false);
+ sdio_release_host(bus->sdiodev->func[1]);
+ return bcmerror;
+}
+
+static void brcmf_sdio_sr_init(struct brcmf_sdio *bus)
+{
+ int err = 0;
+ u8 val;
+ u8 wakeupctrl;
+ u8 cardcap;
+ u8 chipclkcsr;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ if (bus->ci->chip == CY_CC_43012_CHIP_ID) {
+ wakeupctrl = SBSDIO_FUNC1_WCTRL_ALPWAIT_SHIFT;
+ cardcap = SDIO_CCCR_BRCM_CARDCAP_CMD_NODEC;
+ chipclkcsr = SBSDIO_HT_AVAIL_REQ;
+ } else {
+ wakeupctrl = SBSDIO_FUNC1_WCTRL_HTWAIT_SHIFT;
+ cardcap = (SDIO_CCCR_BRCM_CARDCAP_CMD14_SUPPORT |
+ SDIO_CCCR_BRCM_CARDCAP_CMD14_EXT);
+ chipclkcsr = SBSDIO_FORCE_HT;
+ }
+
+ val = brcmf_sdiod_regrb(bus->sdiodev, SBSDIO_FUNC1_WAKEUPCTRL, &err);
+ if (err) {
+ brcmf_err("error reading SBSDIO_FUNC1_WAKEUPCTRL\n");
+ return;
+ }
+ val |= 1 << wakeupctrl;
+ brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_WAKEUPCTRL, val, &err);
+ if (err) {
+ brcmf_err("error writing SBSDIO_FUNC1_WAKEUPCTRL\n");
+ return;
+ }
+ brcmf_sdiod_regwb(bus->sdiodev, SDIO_CCCR_BRCM_CARDCAP,
+ cardcap,
+ &err);
+ if (err) {
+ brcmf_err("error writing SDIO_CCCR_BRCM_CARDCAP\n");
+ return;
+ }
+ brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
+ chipclkcsr, &err);
+ if (err) {
+ brcmf_err("error writing SBSDIO_FUNC1_CHIPCLKCSR\n");
+ return;
+ }
+
+ /* set flag */
+ bus->sr_enabled = true;
+ brcmf_dbg(INFO, "SR enabled\n");
+}
+
+/* enable KSO bit */
+static int brcmf_sdio_kso_init(struct brcmf_sdio *bus)
+{
+ u8 val;
+ int err = 0;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ /* KSO bit added in SDIO core rev 12 */
+ if (brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV)->rev < 12)
+ return 0;
+
+ val = brcmf_sdiod_regrb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, &err);
+ if (err) {
+ brcmf_err("error reading SBSDIO_FUNC1_SLEEPCSR\n");
+ return err;
+ }
+
+ if (!(val & SBSDIO_FUNC1_SLEEPCSR_KSO_MASK)) {
+ val |= (SBSDIO_FUNC1_SLEEPCSR_KSO_EN <<
+ SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT);
+ brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR,
+ val, &err);
+ if (err) {
+ brcmf_err("error writing SBSDIO_FUNC1_SLEEPCSR\n");
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+
+static int brcmf_sdio_bus_preinit(struct device *dev)
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+ struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
+ struct brcmf_sdio *bus = sdiodev->bus;
+ uint pad_size;
+ u32 value;
+ int err;
+
+ /* the commands below use the terms tx and rx from
+ * a device perspective, ie. bus:txglom affects the
+ * bus transfers from device to host.
+ */
+ if (brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV)->rev < 12) {
+ /* for sdio core rev < 12, disable txgloming */
+ value = 0;
+ err = brcmf_iovar_data_set(dev, "bus:txglom", &value,
+ sizeof(u32));
+ } else {
+ /* otherwise, set txglomalign */
+ value = sdiodev->settings->bus.sdio.sd_sgentry_align;
+ /* SDIO ADMA requires at least 32 bit alignment */
+ value = max_t(u32, value, ALIGNMENT);
+ err = brcmf_iovar_data_set(dev, "bus:txglomalign", &value,
+ sizeof(u32));
+ }
+
+ if (err < 0)
+ goto done;
+
+ bus->tx_hdrlen = SDPCM_HWHDR_LEN + SDPCM_SWHDR_LEN;
+ if (sdiodev->sg_support) {
+ bus->txglom = false;
+ value = 1;
+ pad_size = bus->sdiodev->func[2]->cur_blksize << 1;
+ err = brcmf_iovar_data_set(bus->sdiodev->dev, "bus:rxglom",
+ &value, sizeof(u32));
+ if (err < 0) {
+ /* bus:rxglom is allowed to fail */
+ err = 0;
+ } else {
+ bus->txglom = true;
+ bus->tx_hdrlen += SDPCM_HWEXT_LEN;
+ }
+ }
+ brcmf_bus_add_txhdrlen(bus->sdiodev->dev, bus->tx_hdrlen);
+
+done:
+ return err;
+}
+
+static size_t brcmf_sdio_bus_get_ramsize(struct device *dev)
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+ struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
+ struct brcmf_sdio *bus = sdiodev->bus;
+
+ return bus->ci->ramsize - bus->ci->srsize;
+}
+
+static int brcmf_sdio_bus_get_memdump(struct device *dev, void *data,
+ size_t mem_size)
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+ struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
+ struct brcmf_sdio *bus = sdiodev->bus;
+ int err;
+ int address;
+ int offset;
+ int len;
+
+ brcmf_dbg(INFO, "dump at 0x%08x: size=%zu\n", bus->ci->rambase,
+ mem_size);
+
+ address = bus->ci->rambase;
+ offset = err = 0;
+ sdio_claim_host(sdiodev->func[1]);
+ while (offset < mem_size) {
+ len = ((offset + MEMBLOCK) < mem_size) ? MEMBLOCK :
+ mem_size - offset;
+ err = brcmf_sdiod_ramrw(sdiodev, false, address, data, len);
+ if (err) {
+ brcmf_err("error %d on reading %d membytes at 0x%08x\n",
+ err, len, address);
+ goto done;
+ }
+ data += len;
+ offset += len;
+ address += len;
+ }
+
+done:
+ sdio_release_host(sdiodev->func[1]);
+ return err;
+}
+
+void brcmf_sdio_trigger_dpc(struct brcmf_sdio *bus)
+{
+ if (!bus->dpc_triggered) {
+ bus->dpc_triggered = true;
+ queue_work(bus->brcmf_wq, &bus->datawork);
+ }
+}
+
+void brcmf_sdio_isr(struct brcmf_sdio *bus)
+{
+ brcmf_dbg(TRACE, "Enter\n");
+
+ if (!bus) {
+ brcmf_err("bus is null pointer, exiting\n");
+ return;
+ }
+
+ /* Count the interrupt call */
+ bus->sdcnt.intrcount++;
+ if (in_interrupt())
+ atomic_set(&bus->ipend, 1);
+ else
+ if (brcmf_sdio_intr_rstatus(bus)) {
+ brcmf_err("failed backplane access\n");
+ }
+
+ /* Disable additional interrupts (is this needed now)? */
+ if (!bus->intr)
+ brcmf_err("isr w/o interrupt configured!\n");
+
+ if (unlikely(g_wakeup_irq)) {
+ /* SAPPHIRE-10643 - at this point we know the wake
+ * filters are installed and thus this is a packet
+ * we care about. In case we are suspending, abort
+ * with a pm_wakeup_event.
+ */
+ brcmf_info("Received wakeup irq\n");
+ g_wakeup_irq = 0;
+ g_dump_packet = 1;
+ pm_wakeup_event(bus->sdiodev->dev, 0);
+ }
+
+ bus->dpc_triggered = true;
+ queue_work(bus->brcmf_wq, &bus->datawork);
+}
+
+static void brcmf_sdio_bus_watchdog(struct brcmf_sdio *bus)
+{
+ brcmf_dbg(TIMER, "Enter\n");
+
+ /* Poll period: check device if appropriate. */
+ if (!bus->sr_enabled &&
+ bus->poll && (++bus->polltick >= bus->pollrate)) {
+ u32 intstatus = 0;
+
+ /* Reset poll tick */
+ bus->polltick = 0;
+
+ /* Check device if no interrupts */
+ if (!bus->intr ||
+ (bus->sdcnt.intrcount == bus->sdcnt.lastintrs)) {
+
+ if (!bus->dpc_triggered) {
+ u8 devpend;
+
+ sdio_claim_host(bus->sdiodev->func[1]);
+ devpend = brcmf_sdiod_regrb(bus->sdiodev,
+ SDIO_CCCR_INTx,
+ NULL);
+ sdio_release_host(bus->sdiodev->func[1]);
+ intstatus = devpend & (INTR_STATUS_FUNC1 |
+ INTR_STATUS_FUNC2);
+ }
+
+ /* If there is something, make like the ISR and
+ schedule the DPC */
+ if (intstatus) {
+ bus->sdcnt.pollcnt++;
+ atomic_set(&bus->ipend, 1);
+
+ bus->dpc_triggered = true;
+ queue_work(bus->brcmf_wq, &bus->datawork);
+ }
+ }
+
+ /* Update interrupt tracking */
+ bus->sdcnt.lastintrs = bus->sdcnt.intrcount;
+ }
+#ifdef DEBUG
+ /* Poll for console output periodically */
+ if (bus->sdiodev->state == BRCMF_SDIOD_DATA && BRCMF_FWCON_ON() &&
+ bus->console_interval != 0) {
+ bus->console.count += jiffies_to_msecs(BRCMF_WD_POLL);
+ if (bus->console.count >= bus->console_interval) {
+ bus->console.count -= bus->console_interval;
+ sdio_claim_host(bus->sdiodev->func[1]);
+ /* Make sure backplane clock is on */
+ brcmf_sdio_bus_sleep(bus, false, false);
+ if (brcmf_sdio_readconsole(bus) < 0)
+ /* stop on error */
+ bus->console_interval = 0;
+ sdio_release_host(bus->sdiodev->func[1]);
+ }
+ }
+#endif /* DEBUG */
+
+ /* On idle timeout clear activity flag and/or turn off clock */
+ if (!bus->dpc_triggered) {
+ rmb();
+ if ((!bus->dpc_running) && (bus->idletime > 0) &&
+ (bus->clkstate == CLK_AVAIL)) {
+ bus->idlecount++;
+ if (bus->idlecount > bus->idletime) {
+ brcmf_dbg(SDIO, "idle\n");
+ sdio_claim_host(bus->sdiodev->func[1]);
+ brcmf_sdio_wd_timer(bus, false);
+ bus->idlecount = 0;
+ brcmf_sdio_bus_sleep(bus, true, false);
+ sdio_release_host(bus->sdiodev->func[1]);
+ }
+ } else {
+ bus->idlecount = 0;
+ }
+ } else {
+ bus->idlecount = 0;
+ }
+}
+
+static void brcmf_sdio_dataworker(struct work_struct *work)
+{
+ struct brcmf_sdio *bus = container_of(work, struct brcmf_sdio,
+ datawork);
+
+ bus->dpc_running = true;
+ wmb();
+ while (ACCESS_ONCE(bus->dpc_triggered)) {
+ bus->dpc_triggered = false;
+ brcmf_sdio_dpc(bus);
+ bus->idlecount = 0;
+ }
+ bus->dpc_running = false;
+ if (brcmf_sdiod_freezing(bus->sdiodev)) {
+ brcmf_sdiod_change_state(bus->sdiodev, BRCMF_SDIOD_DOWN);
+ brcmf_sdiod_try_freeze(bus->sdiodev);
+ brcmf_sdiod_change_state(bus->sdiodev, BRCMF_SDIOD_DATA);
+ }
+}
+
+static void
+brcmf_sdio_drivestrengthinit(struct brcmf_sdio_dev *sdiodev,
+ struct brcmf_chip *ci, u32 drivestrength)
+{
+ const struct sdiod_drive_str *str_tab = NULL;
+ u32 str_mask;
+ u32 str_shift;
+ u32 i;
+ u32 drivestrength_sel = 0;
+ u32 cc_data_temp;
+ u32 addr;
+
+ if (!(ci->cc_caps & CC_CAP_PMU))
+ return;
+
+ switch (SDIOD_DRVSTR_KEY(ci->chip, ci->pmurev)) {
+ case SDIOD_DRVSTR_KEY(BRCM_CC_4330_CHIP_ID, 12):
+ str_tab = sdiod_drvstr_tab1_1v8;
+ str_mask = 0x00003800;
+ str_shift = 11;
+ break;
+ case SDIOD_DRVSTR_KEY(BRCM_CC_4334_CHIP_ID, 17):
+ str_tab = sdiod_drvstr_tab6_1v8;
+ str_mask = 0x00001800;
+ str_shift = 11;
+ break;
+ case SDIOD_DRVSTR_KEY(BRCM_CC_43143_CHIP_ID, 17):
+ /* note: 43143 does not support tristate */
+ i = ARRAY_SIZE(sdiod_drvstr_tab2_3v3) - 1;
+ if (drivestrength >= sdiod_drvstr_tab2_3v3[i].strength) {
+ str_tab = sdiod_drvstr_tab2_3v3;
+ str_mask = 0x00000007;
+ str_shift = 0;
+ } else
+ brcmf_err("Invalid SDIO Drive strength for chip %s, strength=%d\n",
+ ci->name, drivestrength);
+ break;
+ case SDIOD_DRVSTR_KEY(BRCM_CC_43362_CHIP_ID, 13):
+ str_tab = sdiod_drive_strength_tab5_1v8;
+ str_mask = 0x00003800;
+ str_shift = 11;
+ break;
+ default:
+ brcmf_dbg(INFO, "No SDIO driver strength init needed for chip %s rev %d pmurev %d\n",
+ ci->name, ci->chiprev, ci->pmurev);
+ break;
+ }
+
+ if (str_tab != NULL) {
+ struct brcmf_core *pmu = brcmf_chip_get_pmu(ci);
+
+ for (i = 0; str_tab[i].strength != 0; i++) {
+ if (drivestrength >= str_tab[i].strength) {
+ drivestrength_sel = str_tab[i].sel;
+ break;
+ }
+ }
+ addr = CORE_CC_REG(pmu->base, chipcontrol_addr);
+ brcmf_sdiod_regwl(sdiodev, addr, 1, NULL);
+ cc_data_temp = brcmf_sdiod_regrl(sdiodev, addr, NULL);
+ cc_data_temp &= ~str_mask;
+ drivestrength_sel <<= str_shift;
+ cc_data_temp |= drivestrength_sel;
+ brcmf_sdiod_regwl(sdiodev, addr, cc_data_temp, NULL);
+
+ brcmf_dbg(INFO, "SDIO: %d mA (req=%d mA) drive strength selected, set to 0x%08x\n",
+ str_tab[i].strength, drivestrength, cc_data_temp);
+ }
+}
+
+static int brcmf_sdio_buscoreprep(void *ctx)
+{
+ struct brcmf_sdio_dev *sdiodev = ctx;
+ int err = 0;
+ u8 clkval, clkset;
+
+ /* Try forcing SDIO core to do ALPAvail request only */
+ clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_ALP_AVAIL_REQ;
+ brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err);
+ if (err) {
+ brcmf_err("error writing for HT off\n");
+ return err;
+ }
+
+ /* If register supported, wait for ALPAvail and then force ALP */
+ /* This may take up to 15 milliseconds */
+ clkval = brcmf_sdiod_regrb(sdiodev,
+ SBSDIO_FUNC1_CHIPCLKCSR, NULL);
+
+ if ((clkval & ~SBSDIO_AVBITS) != clkset) {
+ brcmf_err("ChipClkCSR access: wrote 0x%02x read 0x%02x\n",
+ clkset, clkval);
+ return -EACCES;
+ }
+
+ SPINWAIT(((clkval = brcmf_sdiod_regrb(sdiodev,
+ SBSDIO_FUNC1_CHIPCLKCSR, NULL)),
+ !SBSDIO_ALPAV(clkval)),
+ PMU_MAX_TRANSITION_DLY);
+ if (!SBSDIO_ALPAV(clkval)) {
+ brcmf_err("timeout on ALPAV wait, clkval 0x%02x\n",
+ clkval);
+ return -EBUSY;
+ }
+
+ clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_FORCE_ALP;
+ brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err);
+ udelay(65);
+
+ /* Also, disable the extra SDIO pull-ups */
+ brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_SDIOPULLUP, 0, NULL);
+
+ return 0;
+}
+
+static void brcmf_sdio_buscore_activate(void *ctx, struct brcmf_chip *chip,
+ u32 rstvec)
+{
+ struct brcmf_sdio_dev *sdiodev = ctx;
+ struct brcmf_core *core;
+ u32 reg_addr;
+
+ /* clear all interrupts */
+ core = brcmf_chip_get_core(chip, BCMA_CORE_SDIO_DEV);
+ reg_addr = core->base + offsetof(struct sdpcmd_regs, intstatus);
+ brcmf_sdiod_regwl(sdiodev, reg_addr, 0xFFFFFFFF, NULL);
+
+ if (rstvec)
+ /* Write reset vector to address 0 */
+ brcmf_sdiod_ramrw(sdiodev, true, 0, (void *)&rstvec,
+ sizeof(rstvec));
+}
+
+static u32 brcmf_sdio_buscore_read32(void *ctx, u32 addr)
+{
+ struct brcmf_sdio_dev *sdiodev = ctx;
+ u32 val, rev;
+
+ val = brcmf_sdiod_regrl(sdiodev, addr, NULL);
+ if ((sdiodev->func[0]->device == SDIO_DEVICE_ID_BROADCOM_4335_4339 ||
+ sdiodev->func[0]->device == SDIO_DEVICE_ID_BROADCOM_4339) &&
+ addr == CORE_CC_REG(SI_ENUM_BASE, chipid)) {
+ rev = (val & CID_REV_MASK) >> CID_REV_SHIFT;
+ if (rev >= 2) {
+ val &= ~CID_ID_MASK;
+ val |= BRCM_CC_4339_CHIP_ID;
+ }
+ }
+ return val;
+}
+
+static void brcmf_sdio_buscore_write32(void *ctx, u32 addr, u32 val)
+{
+ struct brcmf_sdio_dev *sdiodev = ctx;
+
+ brcmf_sdiod_regwl(sdiodev, addr, val, NULL);
+}
+
+static const struct brcmf_buscore_ops brcmf_sdio_buscore_ops = {
+ .prepare = brcmf_sdio_buscoreprep,
+ .activate = brcmf_sdio_buscore_activate,
+ .read32 = brcmf_sdio_buscore_read32,
+ .write32 = brcmf_sdio_buscore_write32,
+};
+
+static bool
+brcmf_sdio_probe_attach(struct brcmf_sdio *bus)
+{
+ struct brcmf_sdio_dev *sdiodev;
+ u8 clkctl = 0;
+ int err = 0;
+ int reg_addr;
+ u32 reg_val;
+ u32 drivestrength;
+
+ sdiodev = bus->sdiodev;
+ sdio_claim_host(sdiodev->func[1]);
+
+ pr_debug("F1 signature read @0x18000000=0x%4x\n",
+ brcmf_sdiod_regrl(sdiodev, SI_ENUM_BASE, NULL));
+
+ /*
+ * Force PLL off until brcmf_chip_attach()
+ * programs PLL control regs
+ */
+
+ brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
+ BRCMF_INIT_CLKCTL1, &err);
+ if (!err)
+ clkctl = brcmf_sdiod_regrb(sdiodev,
+ SBSDIO_FUNC1_CHIPCLKCSR, &err);
+
+ if (err || ((clkctl & ~SBSDIO_AVBITS) != BRCMF_INIT_CLKCTL1)) {
+ brcmf_err("ChipClkCSR access: err %d wrote 0x%02x read 0x%02x\n",
+ err, BRCMF_INIT_CLKCTL1, clkctl);
+ goto fail;
+ }
+
+ bus->ci = brcmf_chip_attach(sdiodev, &brcmf_sdio_buscore_ops);
+ if (IS_ERR(bus->ci)) {
+ brcmf_err("brcmf_chip_attach failed!\n");
+ bus->ci = NULL;
+ goto fail;
+ }
+ sdiodev->settings = brcmf_get_module_param(sdiodev->dev,
+ BRCMF_BUSTYPE_SDIO,
+ bus->ci->chip,
+ bus->ci->chiprev);
+ if (!sdiodev->settings) {
+ brcmf_err("Failed to get device parameters\n");
+ goto fail;
+ }
+ /* platform specific configuration:
+ * alignments must be at least 4 bytes for ADMA
+ */
+ bus->head_align = ALIGNMENT;
+ bus->sgentry_align = ALIGNMENT;
+ if (sdiodev->settings->bus.sdio.sd_head_align > ALIGNMENT)
+ bus->head_align = sdiodev->settings->bus.sdio.sd_head_align;
+ if (sdiodev->settings->bus.sdio.sd_sgentry_align > ALIGNMENT)
+ bus->sgentry_align =
+ sdiodev->settings->bus.sdio.sd_sgentry_align;
+
+ /* allocate scatter-gather table. sg support
+ * will be disabled upon allocation failure.
+ */
+ brcmf_sdiod_sgtable_alloc(sdiodev);
+
+#ifdef CONFIG_PM_SLEEP
+ /* wowl can be supported when KEEP_POWER is true and (WAKE_SDIO_IRQ
+ * is true or when platform data OOB irq is true).
+ */
+ if ((sdio_get_host_pm_caps(sdiodev->func[1]) & MMC_PM_KEEP_POWER) &&
+ ((sdio_get_host_pm_caps(sdiodev->func[1]) & MMC_PM_WAKE_SDIO_IRQ) ||
+ (sdiodev->settings->bus.sdio.oob_irq_supported)))
+ sdiodev->bus_if->wowl_supported = true;
+#endif
+
+ if (brcmf_sdio_kso_init(bus)) {
+ brcmf_err("error enabling KSO\n");
+ goto fail;
+ }
+
+ if (sdiodev->settings->bus.sdio.drive_strength)
+ drivestrength = sdiodev->settings->bus.sdio.drive_strength;
+ else
+ drivestrength = DEFAULT_SDIO_DRIVE_STRENGTH;
+ brcmf_sdio_drivestrengthinit(sdiodev, bus->ci, drivestrength);
+
+ /* Set card control so an SDIO card reset does a WLAN backplane reset */
+ reg_val = brcmf_sdiod_regrb(sdiodev, SDIO_CCCR_BRCM_CARDCTRL, &err);
+ if (err)
+ goto fail;
+
+ reg_val |= SDIO_CCCR_BRCM_CARDCTRL_WLANRESET;
+
+ brcmf_sdiod_regwb(sdiodev, SDIO_CCCR_BRCM_CARDCTRL, reg_val, &err);
+ if (err)
+ goto fail;
+
+ /* set PMUControl so a backplane reset does PMU state reload */
+ reg_addr = CORE_CC_REG(brcmf_chip_get_pmu(bus->ci)->base, pmucontrol);
+ reg_val = brcmf_sdiod_regrl(sdiodev, reg_addr, &err);
+ if (err)
+ goto fail;
+
+ reg_val |= (BCMA_CC_PMU_CTL_RES_RELOAD << BCMA_CC_PMU_CTL_RES_SHIFT);
+
+ brcmf_sdiod_regwl(sdiodev, reg_addr, reg_val, &err);
+ if (err)
+ goto fail;
+
+ sdio_release_host(sdiodev->func[1]);
+
+ brcmu_pktq_init(&bus->txq, (PRIOMASK + 1), TXQLEN);
+
+ /* allocate header buffer */
+ bus->hdrbuf = kzalloc(MAX_HDR_READ + bus->head_align, GFP_KERNEL);
+ if (!bus->hdrbuf)
+ return false;
+ /* Locate an appropriately-aligned portion of hdrbuf */
+ bus->rxhdr = (u8 *) roundup((unsigned long)&bus->hdrbuf[0],
+ bus->head_align);
+
+ /* Set the poll and/or interrupt flags */
+ bus->intr = true;
+ bus->poll = false;
+ if (bus->poll)
+ bus->pollrate = 1;
+
+ return true;
+
+fail:
+ sdio_release_host(sdiodev->func[1]);
+ return false;
+}
+
+static int
+brcmf_sdio_watchdog_thread(void *data)
+{
+ struct brcmf_sdio *bus = (struct brcmf_sdio *)data;
+ int wait;
+
+ allow_signal(SIGTERM);
+ /* Run until signal received */
+ brcmf_sdiod_freezer_count(bus->sdiodev);
+ while (1) {
+ if (kthread_should_stop())
+ break;
+ brcmf_sdiod_freezer_uncount(bus->sdiodev);
+ wait = wait_for_completion_interruptible(&bus->watchdog_wait);
+ brcmf_sdiod_freezer_count(bus->sdiodev);
+ brcmf_sdiod_try_freeze(bus->sdiodev);
+ if (!wait) {
+ brcmf_sdio_bus_watchdog(bus);
+ /* Count the tick for reference */
+ bus->sdcnt.tickcnt++;
+ reinit_completion(&bus->watchdog_wait);
+ } else
+ break;
+ }
+ return 0;
+}
+
+static void
+brcmf_sdio_watchdog(unsigned long data)
+{
+ struct brcmf_sdio *bus = (struct brcmf_sdio *)data;
+
+ if (bus->watchdog_tsk) {
+ complete(&bus->watchdog_wait);
+ /* Reschedule the watchdog */
+ if (bus->wd_active)
+ mod_timer(&bus->timer,
+ jiffies + BRCMF_WD_POLL);
+ }
+}
+
+static int brcmf_sdio_get_fwname(struct device *dev, u32 chip, u32 chiprev,
+ u8 *fw_name)
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+ struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
+ int ret = 0;
+
+ if (sdiodev->fw_name[0] != '\0')
+ strlcpy(fw_name, sdiodev->fw_name, BRCMF_FW_NAME_LEN);
+ else
+ ret = brcmf_fw_map_chip_to_name(chip, chiprev,
+ brcmf_sdio_fwnames,
+ ARRAY_SIZE(brcmf_sdio_fwnames),
+ fw_name, NULL);
+
+ return ret;
+}
+
+static const struct brcmf_bus_ops brcmf_sdio_bus_ops = {
+ .stop = brcmf_sdio_bus_stop,
+ .preinit = brcmf_sdio_bus_preinit,
+ .txdata = brcmf_sdio_bus_txdata,
+ .txctl = brcmf_sdio_bus_txctl,
+ .rxctl = brcmf_sdio_bus_rxctl,
+ .gettxq = brcmf_sdio_bus_gettxq,
+ .wowl_config = brcmf_sdio_wowl_config,
+ .get_ramsize = brcmf_sdio_bus_get_ramsize,
+ .get_memdump = brcmf_sdio_bus_get_memdump,
+ .get_fwname = brcmf_sdio_get_fwname,
+};
+
+static void brcmf_sdio_firmware_callback(struct device *dev, int err,
+ const struct firmware *code,
+ void *nvram, u32 nvram_len)
+{
+ struct brcmf_bus *bus_if;
+ struct brcmf_sdio_dev *sdiodev;
+ struct brcmf_sdio *bus;
+ u8 saveclk;
+ u8 devctl;
+
+ brcmf_dbg(TRACE, "Enter: dev=%s, err=%d\n", dev_name(dev), err);
+ bus_if = dev_get_drvdata(dev);
+ sdiodev = bus_if->bus_priv.sdio;
+ if (err)
+ goto fail;
+
+ if (!bus_if->drvr)
+ return;
+
+ bus = sdiodev->bus;
+
+ /* try to download image and nvram to the dongle */
+ bus->alp_only = true;
+ err = brcmf_sdio_download_firmware(bus, code, nvram, nvram_len);
+ if (err)
+ goto fail;
+ bus->alp_only = false;
+
+ /* Start the watchdog timer */
+ bus->sdcnt.tickcnt = 0;
+ brcmf_sdio_wd_timer(bus, true);
+
+ sdio_claim_host(sdiodev->func[1]);
+
+ /* Make sure backplane clock is on, needed to generate F2 interrupt */
+ brcmf_sdio_clkctl(bus, CLK_AVAIL, false);
+ if (bus->clkstate != CLK_AVAIL)
+ goto release;
+
+ /* Force clocks on backplane to be sure F2 interrupt propagates */
+ saveclk = brcmf_sdiod_regrb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, &err);
+ if (!err) {
+ if (bus->ci->chip == CY_CC_43012_CHIP_ID) {
+ brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
+ (saveclk | SBSDIO_HT_AVAIL_REQ),
+ &err);
+ } else {
+ brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
+ (saveclk | SBSDIO_FORCE_HT), &err);
+ }
+ }
+ if (err) {
+ brcmf_err("Failed to force clock for F2: err %d\n", err);
+ goto release;
+ }
+
+ /* Enable function 2 (frame transfers) */
+ w_sdreg32(bus, SDPCM_PROT_VERSION << SMB_DATA_VERSION_SHIFT,
+ offsetof(struct sdpcmd_regs, tosbmailboxdata));
+ err = sdio_enable_func(sdiodev->func[SDIO_FUNC_2]);
+
+
+ brcmf_dbg(INFO, "enable F2: err=%d\n", err);
+
+ /* If F2 successfully enabled, set core and enable interrupts */
+ if (!err) {
+ /* Set up the interrupt mask and enable interrupts */
+ bus->hostintmask = HOSTINTMASK;
+ w_sdreg32(bus, bus->hostintmask,
+ offsetof(struct sdpcmd_regs, hostintmask));
+ switch (sdiodev->func[0]->device) {
+ case SDIO_DEVICE_ID_CYPRESS_4373:
+ case SDIO_DEVICE_ID_CYPRESS_43012:
+ brcmf_dbg(INFO, "set F2 watermark to 0x%x*4 bytes\n",
+ CY_4373_F2_WATERMARK);
+ brcmf_sdiod_regwb(sdiodev, SBSDIO_WATERMARK, CY_4373_F2_WATERMARK, &err);
+ devctl = brcmf_sdiod_regrb(sdiodev, SBSDIO_DEVICE_CTL, &err);
+ devctl |= SBSDIO_DEVCTL_F2WM_ENAB;
+ brcmf_sdiod_regwb(sdiodev, SBSDIO_DEVICE_CTL, devctl, &err);
+ break;
+ case SDIO_DEVICE_ID_BROADCOM_43340:
+ case SDIO_DEVICE_ID_BROADCOM_43341:
+ case SDIO_DEVICE_ID_BROADCOM_4354:
+ {
+ u8 wm, mes;
+
+ if (bus->blocksize == 512) {
+ wm = OVERFLOW_BLKSZ512_WM;
+ mes = OVERFLOW_BLKSZ512_MES;
+ } else {
+ wm = bus->blocksize/4;
+ mes = bus->blocksize/4;
+ }
+ brcmf_dbg(INFO, "set wm to %d, mes to %d\n", wm, mes);
+
+ brcmf_sdiod_regwb(sdiodev, SBSDIO_WATERMARK, wm, &err);
+
+ devctl = brcmf_sdiod_regrb(sdiodev, SBSDIO_DEVICE_CTL, &err);
+ devctl |= SBSDIO_DEVCTL_F2WM_ENAB;
+ brcmf_sdiod_regwb(sdiodev, SBSDIO_DEVICE_CTL, devctl, &err);
+
+ brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_MESBUSYCTRL,
+ (mes | SBSDIO_MESBUSYCTRL_ENAB), &err);
+ }
+ break;
+ default:
+ brcmf_sdiod_regwb(sdiodev, SBSDIO_WATERMARK, DEFAULT_F2_WATERMARK, &err);
+ break;
+ }
+ } else {
+ /* Disable F2 again */
+ sdio_disable_func(sdiodev->func[SDIO_FUNC_2]);
+ goto release;
+ }
+
+ if (brcmf_chip_sr_capable(bus->ci)) {
+ brcmf_sdio_sr_init(bus);
+ /* Masking the chip active interrupt permanantly */
+ bus->hostintmask &= ~I_CHIPACTIVE;
+ w_sdreg32(bus, bus->hostintmask,
+ offsetof(struct sdpcmd_regs, hostintmask));
+ brcmf_dbg(INFO, "%s: disable I_CHIPACTIVE in hostintmask[0x%08x]\n",
+ __FUNCTION__, bus->hostintmask);
+ } else {
+ /* Restore previous clock setting */
+ brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
+ saveclk, &err);
+ }
+
+ if (err == 0) {
+ /* Allow full data communication using DPC from now on. */
+ brcmf_sdiod_change_state(bus->sdiodev, BRCMF_SDIOD_DATA);
+
+ err = brcmf_sdiod_intr_register(sdiodev);
+ if (err != 0)
+ brcmf_err("intr register failed:%d\n", err);
+ }
+
+ /* If we didn't come up, turn off backplane clock */
+ if (err != 0)
+ brcmf_sdio_clkctl(bus, CLK_NONE, false);
+
+ sdio_release_host(sdiodev->func[1]);
+
+ err = brcmf_bus_started(dev);
+ if (err != 0) {
+ brcmf_err("dongle is not responding\n");
+ goto fail;
+ }
+ return;
+
+release:
+ sdio_release_host(sdiodev->func[1]);
+fail:
+ brcmf_dbg(TRACE, "failed: dev=%s, err=%d\n", dev_name(dev), err);
+ device_release_driver(dev);
+ device_release_driver(&sdiodev->func[2]->dev);
+}
+
+struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev)
+{
+ int ret;
+ struct brcmf_sdio *bus;
+ struct workqueue_struct *wq;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ /* Allocate private bus interface state */
+ bus = kzalloc(sizeof(struct brcmf_sdio), GFP_ATOMIC);
+ if (!bus)
+ goto fail;
+
+ bus->sdiodev = sdiodev;
+ sdiodev->bus = bus;
+ skb_queue_head_init(&bus->glom);
+ bus->txbound = BRCMF_TXBOUND;
+ bus->rxbound = BRCMF_RXBOUND;
+ bus->txminmax = BRCMF_TXMINMAX;
+ bus->tx_seq = SDPCM_SEQ_WRAP - 1;
+
+ /* single-threaded workqueue */
+ wq = alloc_ordered_workqueue("brcmf_wq/%s", WQ_MEM_RECLAIM,
+ dev_name(&sdiodev->func[1]->dev));
+ if (!wq) {
+ brcmf_err("insufficient memory to create txworkqueue\n");
+ goto fail;
+ }
+ brcmf_sdiod_freezer_count(sdiodev);
+ INIT_WORK(&bus->datawork, brcmf_sdio_dataworker);
+ bus->brcmf_wq = wq;
+
+ /* attempt to attach to the dongle */
+ if (!(brcmf_sdio_probe_attach(bus))) {
+ brcmf_err("brcmf_sdio_probe_attach failed\n");
+ goto fail;
+ }
+
+ spin_lock_init(&bus->rxctl_lock);
+ spin_lock_init(&bus->txq_lock);
+ init_waitqueue_head(&bus->ctrl_wait);
+ init_waitqueue_head(&bus->dcmd_resp_wait);
+
+ /* Set up the watchdog timer */
+ init_timer(&bus->timer);
+ bus->timer.data = (unsigned long)bus;
+ bus->timer.function = brcmf_sdio_watchdog;
+
+ /* Initialize watchdog thread */
+ init_completion(&bus->watchdog_wait);
+ bus->watchdog_tsk = kthread_run(brcmf_sdio_watchdog_thread,
+ bus, "brcmf_wdog/%s",
+ dev_name(&sdiodev->func[1]->dev));
+ if (IS_ERR(bus->watchdog_tsk)) {
+ pr_warn("brcmf_watchdog thread failed to start\n");
+ bus->watchdog_tsk = NULL;
+ }
+ /* Initialize DPC thread */
+ bus->dpc_triggered = false;
+ bus->dpc_running = false;
+
+ /* Assign bus interface call back */
+ bus->sdiodev->bus_if->dev = bus->sdiodev->dev;
+ bus->sdiodev->bus_if->ops = &brcmf_sdio_bus_ops;
+ bus->sdiodev->bus_if->chip = bus->ci->chip;
+ bus->sdiodev->bus_if->chiprev = bus->ci->chiprev;
+
+ /* default sdio bus header length for tx packet */
+ bus->tx_hdrlen = SDPCM_HWHDR_LEN + SDPCM_SWHDR_LEN;
+
+ /* Attach to the common layer, reserve hdr space */
+ ret = brcmf_attach(bus->sdiodev->dev, bus->sdiodev->settings);
+ if (ret != 0) {
+ brcmf_err("brcmf_attach failed\n");
+ goto fail;
+ }
+
+ /* allocate scatter-gather table. sg support
+ * will be disabled upon allocation failure.
+ */
+ brcmf_sdiod_sgtable_alloc(bus->sdiodev);
+
+ /* Query the F2 block size, set roundup accordingly */
+ bus->blocksize = bus->sdiodev->func[2]->cur_blksize;
+ bus->roundup = min(max_roundup, bus->blocksize);
+
+ /* Allocate buffers */
+ if (bus->sdiodev->bus_if->maxctl) {
+ bus->sdiodev->bus_if->maxctl += bus->roundup;
+ bus->rxblen =
+ roundup((bus->sdiodev->bus_if->maxctl + SDPCM_HDRLEN),
+ ALIGNMENT) + bus->head_align;
+ bus->rxbuf = kmalloc(bus->rxblen, GFP_ATOMIC);
+ if (!(bus->rxbuf)) {
+ brcmf_err("rxbuf allocation failed\n");
+ goto fail;
+ }
+ }
+
+ sdio_claim_host(bus->sdiodev->func[1]);
+
+ /* Disable F2 to clear any intermediate frame state on the dongle */
+ sdio_disable_func(bus->sdiodev->func[SDIO_FUNC_2]);
+
+ bus->rxflow = false;
+
+ /* Done with backplane-dependent accesses, can drop clock... */
+ brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, 0, NULL);
+
+ sdio_release_host(bus->sdiodev->func[1]);
+
+ /* ...and initialize clock/power states */
+ bus->clkstate = CLK_SDONLY;
+ bus->idletime = BRCMF_IDLE_INTERVAL;
+ bus->idleclock = BRCMF_IDLE_ACTIVE;
+
+ /* SR state */
+ bus->sr_enabled = false;
+
+ brcmf_sdio_debugfs_create(bus);
+ brcmf_dbg(INFO, "completed!!\n");
+
+ ret = brcmf_fw_map_chip_to_name(bus->ci->chip, bus->ci->chiprev,
+ brcmf_sdio_fwnames,
+ ARRAY_SIZE(brcmf_sdio_fwnames),
+ sdiodev->fw_name, sdiodev->nvram_name);
+ if (ret)
+ goto fail;
+
+ ret = brcmf_fw_get_firmwares(sdiodev->dev, BRCMF_FW_REQUEST_NVRAM,
+ sdiodev->fw_name, sdiodev->nvram_name,
+ brcmf_sdio_firmware_callback);
+ if (ret != 0) {
+ brcmf_err("async firmware request failed: %d\n", ret);
+ goto fail;
+ }
+
+ return bus;
+
+fail:
+ brcmf_sdio_remove(bus);
+ return NULL;
+}
+
+/* Detach and free everything */
+void brcmf_sdio_remove(struct brcmf_sdio *bus)
+{
+ brcmf_dbg(TRACE, "Enter\n");
+
+ if (bus) {
+ /* De-register interrupt handler */
+ brcmf_sdiod_intr_unregister(bus->sdiodev);
+
+ brcmf_detach(bus->sdiodev->dev);
+
+ cancel_work_sync(&bus->datawork);
+ if (bus->brcmf_wq)
+ destroy_workqueue(bus->brcmf_wq);
+
+ if (bus->ci) {
+ if (bus->sdiodev->state != BRCMF_SDIOD_NOMEDIUM) {
+ sdio_claim_host(bus->sdiodev->func[1]);
+ brcmf_sdio_wd_timer(bus, false);
+ brcmf_sdio_clkctl(bus, CLK_AVAIL, false);
+ /* Leave the device in state where it is
+ * 'passive'. This is done by resetting all
+ * necessary cores.
+ */
+ msleep(20);
+ brcmf_chip_set_passive(bus->ci);
+ brcmf_sdio_clkctl(bus, CLK_NONE, false);
+ sdio_release_host(bus->sdiodev->func[1]);
+ }
+ brcmf_chip_detach(bus->ci);
+ }
+ if (bus->sdiodev->settings)
+ brcmf_release_module_param(bus->sdiodev->settings);
+
+ kfree(bus->rxbuf);
+ kfree(bus->hdrbuf);
+ kfree(bus);
+ }
+
+ brcmf_dbg(TRACE, "Disconnected\n");
+}
+
+void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, bool active)
+{
+ /* Totally stop the timer */
+ if (!active && bus->wd_active) {
+ del_timer_sync(&bus->timer);
+ bus->wd_active = false;
+ return;
+ }
+
+ /* don't start the wd until fw is loaded */
+ if (bus->sdiodev->state != BRCMF_SDIOD_DATA)
+ return;
+
+ if (active) {
+ if (!bus->wd_active) {
+ /* Create timer again when watchdog period is
+ dynamically changed or in the first instance
+ */
+ bus->timer.expires = jiffies + BRCMF_WD_POLL;
+ add_timer(&bus->timer);
+ bus->wd_active = true;
+ } else {
+ /* Re arm the timer, at last watchdog period */
+ mod_timer(&bus->timer, jiffies + BRCMF_WD_POLL);
+ }
+ }
+}
+
+int brcmf_sdio_sleep(struct brcmf_sdio *bus, bool sleep)
+{
+ int ret;
+
+ sdio_claim_host(bus->sdiodev->func[1]);
+ ret = brcmf_sdio_bus_sleep(bus, sleep, false);
+ sdio_release_host(bus->sdiodev->func[1]);
+
+ return ret;
+}
+
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h
new file mode 100644
index 0000000..05a6636
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h
@@ -0,0 +1,387 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef BRCMFMAC_SDIO_H
+#define BRCMFMAC_SDIO_H
+
+#include <linux/skbuff.h>
+#include <linux/firmware.h>
+#include "firmware.h"
+
+#define SDIO_FUNC_0 0
+#define SDIO_FUNC_1 1
+#define SDIO_FUNC_2 2
+
+#define SDIOD_FBR_SIZE 0x100
+
+/* io_en */
+#define SDIO_FUNC_ENABLE_1 0x02
+#define SDIO_FUNC_ENABLE_2 0x04
+
+/* io_rdys */
+#define SDIO_FUNC_READY_1 0x02
+#define SDIO_FUNC_READY_2 0x04
+
+/* intr_status */
+#define INTR_STATUS_FUNC1 0x2
+#define INTR_STATUS_FUNC2 0x4
+
+/* Maximum number of I/O funcs */
+#define SDIOD_MAX_IOFUNCS 7
+
+/* mask of register map */
+#define REG_F0_REG_MASK 0x7FF
+#define REG_F1_MISC_MASK 0x1FFFF
+
+/* as of sdiod rev 0, supports 3 functions */
+#define SBSDIO_NUM_FUNCTION 3
+
+/* function 0 vendor specific CCCR registers */
+#define SDIO_CCCR_BRCM_CARDCAP 0xf0
+#define SDIO_CCCR_BRCM_CARDCAP_CMD14_SUPPORT 0x02
+#define SDIO_CCCR_BRCM_CARDCAP_CMD14_EXT 0x04
+#define SDIO_CCCR_BRCM_CARDCAP_CMD_NODEC 0x08
+#define SDIO_CCCR_BRCM_CARDCTRL 0xf1
+#define SDIO_CCCR_BRCM_CARDCTRL_WLANRESET 0x02
+#define SDIO_CCCR_BRCM_SEPINT 0xf2
+
+#define SDIO_SEPINT_MASK 0x01
+#define SDIO_SEPINT_OE 0x02
+#define SDIO_SEPINT_ACT_HI 0x04
+
+/* function 1 miscellaneous registers */
+
+/* sprom command and status */
+#define SBSDIO_SPROM_CS 0x10000
+/* sprom info register */
+#define SBSDIO_SPROM_INFO 0x10001
+/* sprom indirect access data byte 0 */
+#define SBSDIO_SPROM_DATA_LOW 0x10002
+/* sprom indirect access data byte 1 */
+#define SBSDIO_SPROM_DATA_HIGH 0x10003
+/* sprom indirect access addr byte 0 */
+#define SBSDIO_SPROM_ADDR_LOW 0x10004
+/* gpio select */
+#define SBSDIO_GPIO_SELECT 0x10005
+/* gpio output */
+#define SBSDIO_GPIO_OUT 0x10006
+/* gpio enable */
+#define SBSDIO_GPIO_EN 0x10007
+/* rev < 7, watermark for sdio device */
+#define SBSDIO_WATERMARK 0x10008
+/* control busy signal generation */
+#define SBSDIO_DEVICE_CTL 0x10009
+
+/* SB Address Window Low (b15) */
+#define SBSDIO_FUNC1_SBADDRLOW 0x1000A
+/* SB Address Window Mid (b23:b16) */
+#define SBSDIO_FUNC1_SBADDRMID 0x1000B
+/* SB Address Window High (b31:b24) */
+#define SBSDIO_FUNC1_SBADDRHIGH 0x1000C
+/* Frame Control (frame term/abort) */
+#define SBSDIO_FUNC1_FRAMECTRL 0x1000D
+/* ChipClockCSR (ALP/HT ctl/status) */
+#define SBSDIO_FUNC1_CHIPCLKCSR 0x1000E
+/* SdioPullUp (on cmd, d0-d2) */
+#define SBSDIO_FUNC1_SDIOPULLUP 0x1000F
+/* Write Frame Byte Count Low */
+#define SBSDIO_FUNC1_WFRAMEBCLO 0x10019
+/* Write Frame Byte Count High */
+#define SBSDIO_FUNC1_WFRAMEBCHI 0x1001A
+/* Read Frame Byte Count Low */
+#define SBSDIO_FUNC1_RFRAMEBCLO 0x1001B
+/* Read Frame Byte Count High */
+#define SBSDIO_FUNC1_RFRAMEBCHI 0x1001C
+/* MesBusyCtl (rev 11) */
+#define SBSDIO_FUNC1_MESBUSYCTRL 0x1001D
+
+/* SBSDIO_MESBUSYCTRL */
+/* When RX FIFO has less entries than this & MBE is set
+ * => busy signal is asserted between data blocks.
+*/
+#define SBSDIO_MESBUSYCTRL_MASK 0x7f
+#define SBSDIO_MESBUSYCTRL_ENAB 0x80 /* Enable busy capability for MES access */
+
+/* Sdio Core Rev 12 */
+#define SBSDIO_FUNC1_WAKEUPCTRL 0x1001E
+#define SBSDIO_FUNC1_WCTRL_ALPWAIT_MASK 0x1
+#define SBSDIO_FUNC1_WCTRL_ALPWAIT_SHIFT 0
+#define SBSDIO_FUNC1_WCTRL_HTWAIT_MASK 0x2
+#define SBSDIO_FUNC1_WCTRL_HTWAIT_SHIFT 1
+#define SBSDIO_FUNC1_SLEEPCSR 0x1001F
+#define SBSDIO_FUNC1_SLEEPCSR_KSO_MASK 0x1
+#define SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT 0
+#define SBSDIO_FUNC1_SLEEPCSR_KSO_EN 1
+#define SBSDIO_FUNC1_SLEEPCSR_DEVON_MASK 0x2
+#define SBSDIO_FUNC1_SLEEPCSR_DEVON_SHIFT 1
+
+#define SBSDIO_FUNC1_MISC_REG_START 0x10000 /* f1 misc register start */
+#define SBSDIO_FUNC1_MISC_REG_LIMIT 0x1001F /* f1 misc register end */
+
+/* function 1 OCP space */
+
+/* sb offset addr is <= 15 bits, 32k */
+#define SBSDIO_SB_OFT_ADDR_MASK 0x07FFF
+#define SBSDIO_SB_OFT_ADDR_LIMIT 0x08000
+/* with b15, maps to 32-bit SB access */
+#define SBSDIO_SB_ACCESS_2_4B_FLAG 0x08000
+
+/* valid bits in SBSDIO_FUNC1_SBADDRxxx regs */
+
+#define SBSDIO_SBADDRLOW_MASK 0x80 /* Valid bits in SBADDRLOW */
+#define SBSDIO_SBADDRMID_MASK 0xff /* Valid bits in SBADDRMID */
+#define SBSDIO_SBADDRHIGH_MASK 0xffU /* Valid bits in SBADDRHIGH */
+/* Address bits from SBADDR regs */
+#define SBSDIO_SBWINDOW_MASK 0xffff8000
+
+#define SDIOH_READ 0 /* Read request */
+#define SDIOH_WRITE 1 /* Write request */
+
+#define SDIOH_DATA_FIX 0 /* Fixed addressing */
+#define SDIOH_DATA_INC 1 /* Incremental addressing */
+
+/* internal return code */
+#define SUCCESS 0
+#define ERROR 1
+
+/* Packet alignment for most efficient SDIO (can change based on platform) */
+#define BRCMF_SDALIGN (1 << 6)
+
+/* watchdog polling interval */
+#define BRCMF_WD_POLL msecs_to_jiffies(10)
+
+/**
+ * enum brcmf_sdiod_state - the state of the bus.
+ *
+ * @BRCMF_SDIOD_DOWN: Device can be accessed, no DPC.
+ * @BRCMF_SDIOD_DATA: Ready for data transfers, DPC enabled.
+ * @BRCMF_SDIOD_NOMEDIUM: No medium access to dongle possible.
+ */
+enum brcmf_sdiod_state {
+ BRCMF_SDIOD_DOWN,
+ BRCMF_SDIOD_DATA,
+ BRCMF_SDIOD_NOMEDIUM
+};
+
+struct brcmf_sdreg {
+ int func;
+ int offset;
+ int value;
+};
+
+struct brcmf_sdio;
+struct brcmf_sdiod_freezer;
+
+struct brcmf_sdio_dev {
+ struct sdio_func *func[SDIO_MAX_FUNCS];
+ u8 num_funcs; /* Supported funcs on client */
+ u32 sbwad; /* Save backplane window address */
+ struct brcmf_sdio *bus;
+ struct device *dev;
+ struct brcmf_bus *bus_if;
+ struct brcmf_mp_device *settings;
+ bool oob_irq_requested;
+ bool sd_irq_requested;
+ bool irq_en; /* irq enable flags */
+ spinlock_t irq_en_lock;
+ bool irq_wake; /* irq wake enable flags */
+ bool sg_support;
+ uint max_request_size;
+ ushort max_segment_count;
+ uint max_segment_size;
+ uint txglomsz;
+ struct sg_table sgtable;
+ char fw_name[BRCMF_FW_NAME_LEN];
+ char nvram_name[BRCMF_FW_NAME_LEN];
+ bool wowl_enabled;
+ enum brcmf_sdiod_state state;
+ struct brcmf_sdiod_freezer *freezer;
+};
+
+/* sdio core registers */
+struct sdpcmd_regs {
+ u32 corecontrol; /* 0x00, rev8 */
+ u32 corestatus; /* rev8 */
+ u32 PAD[1];
+ u32 biststatus; /* rev8 */
+
+ /* PCMCIA access */
+ u16 pcmciamesportaladdr; /* 0x010, rev8 */
+ u16 PAD[1];
+ u16 pcmciamesportalmask; /* rev8 */
+ u16 PAD[1];
+ u16 pcmciawrframebc; /* rev8 */
+ u16 PAD[1];
+ u16 pcmciaunderflowtimer; /* rev8 */
+ u16 PAD[1];
+
+ /* interrupt */
+ u32 intstatus; /* 0x020, rev8 */
+ u32 hostintmask; /* rev8 */
+ u32 intmask; /* rev8 */
+ u32 sbintstatus; /* rev8 */
+ u32 sbintmask; /* rev8 */
+ u32 funcintmask; /* rev4 */
+ u32 PAD[2];
+ u32 tosbmailbox; /* 0x040, rev8 */
+ u32 tohostmailbox; /* rev8 */
+ u32 tosbmailboxdata; /* rev8 */
+ u32 tohostmailboxdata; /* rev8 */
+
+ /* synchronized access to registers in SDIO clock domain */
+ u32 sdioaccess; /* 0x050, rev8 */
+ u32 PAD[3];
+
+ /* PCMCIA frame control */
+ u8 pcmciaframectrl; /* 0x060, rev8 */
+ u8 PAD[3];
+ u8 pcmciawatermark; /* rev8 */
+ u8 PAD[155];
+
+ /* interrupt batching control */
+ u32 intrcvlazy; /* 0x100, rev8 */
+ u32 PAD[3];
+
+ /* counters */
+ u32 cmd52rd; /* 0x110, rev8 */
+ u32 cmd52wr; /* rev8 */
+ u32 cmd53rd; /* rev8 */
+ u32 cmd53wr; /* rev8 */
+ u32 abort; /* rev8 */
+ u32 datacrcerror; /* rev8 */
+ u32 rdoutofsync; /* rev8 */
+ u32 wroutofsync; /* rev8 */
+ u32 writebusy; /* rev8 */
+ u32 readwait; /* rev8 */
+ u32 readterm; /* rev8 */
+ u32 writeterm; /* rev8 */
+ u32 PAD[40];
+ u32 clockctlstatus; /* rev8 */
+ u32 PAD[7];
+
+ u32 PAD[128]; /* DMA engines */
+
+ /* SDIO/PCMCIA CIS region */
+ char cis[512]; /* 0x400-0x5ff, rev6 */
+
+ /* PCMCIA function control registers */
+ char pcmciafcr[256]; /* 0x600-6ff, rev6 */
+ u16 PAD[55];
+
+ /* PCMCIA backplane access */
+ u16 backplanecsr; /* 0x76E, rev6 */
+ u16 backplaneaddr0; /* rev6 */
+ u16 backplaneaddr1; /* rev6 */
+ u16 backplaneaddr2; /* rev6 */
+ u16 backplaneaddr3; /* rev6 */
+ u16 backplanedata0; /* rev6 */
+ u16 backplanedata1; /* rev6 */
+ u16 backplanedata2; /* rev6 */
+ u16 backplanedata3; /* rev6 */
+ u16 PAD[31];
+
+ /* sprom "size" & "blank" info */
+ u16 spromstatus; /* 0x7BE, rev2 */
+ u32 PAD[464];
+
+ u16 PAD[0x80];
+};
+
+/* Register/deregister interrupt handler. */
+int brcmf_sdiod_intr_register(struct brcmf_sdio_dev *sdiodev);
+void brcmf_sdiod_intr_unregister(struct brcmf_sdio_dev *sdiodev);
+
+/* sdio device register access interface */
+u8 brcmf_sdiod_regrb(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret);
+u32 brcmf_sdiod_regrl(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret);
+void brcmf_sdiod_regwb(struct brcmf_sdio_dev *sdiodev, u32 addr, u8 data,
+ int *ret);
+void brcmf_sdiod_regwl(struct brcmf_sdio_dev *sdiodev, u32 addr, u32 data,
+ int *ret);
+
+/* Buffer transfer to/from device (client) core via cmd53.
+ * fn: function number
+ * flags: backplane width, address increment, sync/async
+ * buf: pointer to memory data buffer
+ * nbytes: number of bytes to transfer to/from buf
+ * pkt: pointer to packet associated with buf (if any)
+ * complete: callback function for command completion (async only)
+ * handle: handle for completion callback (first arg in callback)
+ * Returns 0 or error code.
+ * NOTE: Async operation is not currently supported.
+ */
+int brcmf_sdiod_send_pkt(struct brcmf_sdio_dev *sdiodev,
+ struct sk_buff_head *pktq);
+int brcmf_sdiod_send_buf(struct brcmf_sdio_dev *sdiodev, u8 *buf, uint nbytes);
+
+int brcmf_sdiod_recv_pkt(struct brcmf_sdio_dev *sdiodev, struct sk_buff *pkt);
+int brcmf_sdiod_recv_buf(struct brcmf_sdio_dev *sdiodev, u8 *buf, uint nbytes);
+int brcmf_sdiod_recv_chain(struct brcmf_sdio_dev *sdiodev,
+ struct sk_buff_head *pktq, uint totlen);
+
+/* Flags bits */
+
+/* Four-byte target (backplane) width (vs. two-byte) */
+#define SDIO_REQ_4BYTE 0x1
+/* Fixed address (FIFO) (vs. incrementing address) */
+#define SDIO_REQ_FIXED 0x2
+
+/* Read/write to memory block (F1, no FIFO) via CMD53 (sync only).
+ * rw: read or write (0/1)
+ * addr: direct SDIO address
+ * buf: pointer to memory data buffer
+ * nbytes: number of bytes to transfer to/from buf
+ * Returns 0 or error code.
+ */
+int brcmf_sdiod_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address,
+ u8 *data, uint size);
+
+/* Issue an abort to the specified function */
+int brcmf_sdiod_abort(struct brcmf_sdio_dev *sdiodev, uint fn);
+void brcmf_sdiod_sgtable_alloc(struct brcmf_sdio_dev *sdiodev);
+void brcmf_sdiod_change_state(struct brcmf_sdio_dev *sdiodev,
+ enum brcmf_sdiod_state state);
+#ifdef CONFIG_PM_SLEEP
+bool brcmf_sdiod_freezing(struct brcmf_sdio_dev *sdiodev);
+void brcmf_sdiod_try_freeze(struct brcmf_sdio_dev *sdiodev);
+void brcmf_sdiod_freezer_count(struct brcmf_sdio_dev *sdiodev);
+void brcmf_sdiod_freezer_uncount(struct brcmf_sdio_dev *sdiodev);
+#else
+static inline bool brcmf_sdiod_freezing(struct brcmf_sdio_dev *sdiodev)
+{
+ return false;
+}
+static inline void brcmf_sdiod_try_freeze(struct brcmf_sdio_dev *sdiodev)
+{
+}
+static inline void brcmf_sdiod_freezer_count(struct brcmf_sdio_dev *sdiodev)
+{
+}
+static inline void brcmf_sdiod_freezer_uncount(struct brcmf_sdio_dev *sdiodev)
+{
+}
+#endif /* CONFIG_PM_SLEEP */
+
+struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev);
+void brcmf_sdio_remove(struct brcmf_sdio *bus);
+void brcmf_sdio_isr(struct brcmf_sdio *bus);
+
+void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, bool active);
+void brcmf_sdio_wowl_config(struct device *dev, bool enabled);
+int brcmf_sdio_sleep(struct brcmf_sdio *bus, bool sleep);
+void brcmf_sdio_trigger_dpc(struct brcmf_sdio *bus);
+
+#endif /* BRCMFMAC_SDIO_H */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/tracepoint.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/tracepoint.c
new file mode 100644
index 0000000..fe67559
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/tracepoint.c
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2012 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/module.h> /* bug in tracepoint.h, it should include this */
+
+#ifndef __CHECKER__
+#define CREATE_TRACE_POINTS
+#include "tracepoint.h"
+#include "debug.h"
+
+void __brcmf_err(const char *func, const char *fmt, ...)
+{
+ struct va_format vaf = {
+ .fmt = fmt,
+ };
+ va_list args;
+
+ va_start(args, fmt);
+ vaf.va = &args;
+ pr_err("%s: %pV", func, &vaf);
+ trace_brcmf_err(func, &vaf);
+ va_end(args);
+}
+
+#endif
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/tracepoint.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/tracepoint.h
new file mode 100644
index 0000000..ec013fd
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/tracepoint.h
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2013 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#if !defined(BRCMF_TRACEPOINT_H_) || defined(TRACE_HEADER_MULTI_READ)
+#define BRCMF_TRACEPOINT_H_
+
+#include <linux/types.h>
+#include <linux/tracepoint.h>
+
+#ifndef CPTCFG_BRCM_TRACING
+
+#undef TRACE_EVENT
+#define TRACE_EVENT(name, proto, ...) \
+static inline void trace_ ## name(proto) {}
+
+#undef DECLARE_EVENT_CLASS
+#define DECLARE_EVENT_CLASS(...)
+
+#undef DEFINE_EVENT
+#define DEFINE_EVENT(evt_class, name, proto, ...) \
+static inline void trace_ ## name(proto) {}
+
+#endif /* CPTCFG_BRCM_TRACING */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM brcmfmac
+
+#define MAX_MSG_LEN 100
+
+TRACE_EVENT(brcmf_err,
+ TP_PROTO(const char *func, struct va_format *vaf),
+ TP_ARGS(func, vaf),
+ TP_STRUCT__entry(
+ __string(func, func)
+ __dynamic_array(char, msg, MAX_MSG_LEN)
+ ),
+ TP_fast_assign(
+ __assign_str(func, func);
+ WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg),
+ MAX_MSG_LEN, vaf->fmt,
+ *vaf->va) >= MAX_MSG_LEN);
+ ),
+ TP_printk("%s: %s", __get_str(func), __get_str(msg))
+);
+
+TRACE_EVENT(brcmf_dbg,
+ TP_PROTO(u32 level, const char *func, struct va_format *vaf),
+ TP_ARGS(level, func, vaf),
+ TP_STRUCT__entry(
+ __field(u32, level)
+ __string(func, func)
+ __dynamic_array(char, msg, MAX_MSG_LEN)
+ ),
+ TP_fast_assign(
+ __entry->level = level;
+ __assign_str(func, func);
+ WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg),
+ MAX_MSG_LEN, vaf->fmt,
+ *vaf->va) >= MAX_MSG_LEN);
+ ),
+ TP_printk("%s: %s", __get_str(func), __get_str(msg))
+);
+
+TRACE_EVENT(brcmf_hexdump,
+ TP_PROTO(void *data, size_t len),
+ TP_ARGS(data, len),
+ TP_STRUCT__entry(
+ __field(unsigned long, len)
+ __field(unsigned long, addr)
+ __dynamic_array(u8, hdata, len)
+ ),
+ TP_fast_assign(
+ __entry->len = len;
+ __entry->addr = (unsigned long)data;
+ memcpy(__get_dynamic_array(hdata), data, len);
+ ),
+ TP_printk("hexdump [addr=%lx, length=%lu]", __entry->addr, __entry->len)
+);
+
+TRACE_EVENT(brcmf_bcdchdr,
+ TP_PROTO(void *data),
+ TP_ARGS(data),
+ TP_STRUCT__entry(
+ __field(u8, flags)
+ __field(u8, prio)
+ __field(u8, flags2)
+ __field(u32, siglen)
+ __dynamic_array(u8, signal, *((u8 *)data + 3) * 4)
+ ),
+ TP_fast_assign(
+ __entry->flags = *(u8 *)data;
+ __entry->prio = *((u8 *)data + 1);
+ __entry->flags2 = *((u8 *)data + 2);
+ __entry->siglen = *((u8 *)data + 3) * 4;
+ memcpy(__get_dynamic_array(signal),
+ (u8 *)data + 4, __entry->siglen);
+ ),
+ TP_printk("bcdc: prio=%d siglen=%d", __entry->prio, __entry->siglen)
+);
+
+#ifndef SDPCM_RX
+#define SDPCM_RX 0
+#endif
+#ifndef SDPCM_TX
+#define SDPCM_TX 1
+#endif
+#ifndef SDPCM_GLOM
+#define SDPCM_GLOM 2
+#endif
+
+TRACE_EVENT(brcmf_sdpcm_hdr,
+ TP_PROTO(u8 dir, void *data),
+ TP_ARGS(dir, data),
+ TP_STRUCT__entry(
+ __field(u8, dir)
+ __field(u16, len)
+ __dynamic_array(u8, hdr, dir == SDPCM_GLOM ? 20 : 12)
+ ),
+ TP_fast_assign(
+ memcpy(__get_dynamic_array(hdr), data, dir == SDPCM_GLOM ? 20 : 12);
+ __entry->len = *(u8 *)data | (*((u8 *)data + 1) << 8);
+ __entry->dir = dir;
+ ),
+ TP_printk("sdpcm: %s len %u, seq %d",
+ __entry->dir == SDPCM_RX ? "RX" : "TX",
+ __entry->len, ((u8 *)__get_dynamic_array(hdr))[4])
+);
+
+#ifdef CPTCFG_BRCM_TRACING
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE tracepoint
+
+#include <trace/define_trace.h>
+
+#endif /* CPTCFG_BRCM_TRACING */
+
+#endif /* BRCMF_TRACEPOINT_H_ */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c
new file mode 100644
index 0000000..172035c
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c
@@ -0,0 +1,1542 @@
+/*
+ * Copyright (c) 2011 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/firmware.h>
+#include <linux/usb.h>
+#include <linux/vmalloc.h>
+
+#include <brcmu_utils.h>
+#include <brcm_hw_ids.h>
+#include <brcmu_wifi.h>
+#include "bus.h"
+#include "debug.h"
+#include "firmware.h"
+#include "usb.h"
+#include "core.h"
+#include "common.h"
+#include "bcdc.h"
+
+
+#define IOCTL_RESP_TIMEOUT msecs_to_jiffies(2000)
+
+#define BRCMF_USB_RESET_GETVER_SPINWAIT 100 /* in unit of ms */
+#define BRCMF_USB_RESET_GETVER_LOOP_CNT 10
+
+#define BRCMF_POSTBOOT_ID 0xA123 /* ID to detect if dongle
+ has boot up */
+#define BRCMF_USB_NRXQ 50
+#define BRCMF_USB_NTXQ 50
+
+#define BRCMF_USB_CBCTL_WRITE 0
+#define BRCMF_USB_CBCTL_READ 1
+#define BRCMF_USB_MAX_PKT_SIZE 1600
+
+BRCMF_FW_DEF(43143, "brcmfmac43143.bin");
+BRCMF_FW_DEF(43236B, "brcmfmac43236b.bin");
+BRCMF_FW_DEF(43242A, "brcmfmac43242a.bin");
+BRCMF_FW_DEF(43569, "brcmfmac43569.bin");
+BRCMF_FW_DEF(4373, "brcmfmac4373.bin");
+
+static struct brcmf_firmware_mapping brcmf_usb_fwnames[] = {
+ BRCMF_FW_ENTRY(BRCM_CC_43143_CHIP_ID, 0xFFFFFFFF, 43143),
+ BRCMF_FW_ENTRY(BRCM_CC_43235_CHIP_ID, 0x00000008, 43236B),
+ BRCMF_FW_ENTRY(BRCM_CC_43236_CHIP_ID, 0x00000008, 43236B),
+ BRCMF_FW_ENTRY(BRCM_CC_43238_CHIP_ID, 0x00000008, 43236B),
+ BRCMF_FW_ENTRY(BRCM_CC_43242_CHIP_ID, 0xFFFFFFFF, 43242A),
+ BRCMF_FW_ENTRY(BRCM_CC_43566_CHIP_ID, 0xFFFFFFFF, 43569),
+ BRCMF_FW_ENTRY(BRCM_CC_43569_CHIP_ID, 0xFFFFFFFF, 43569),
+ BRCMF_FW_ENTRY(CY_CC_4373_CHIP_ID, 0xFFFFFFFF, 4373)
+};
+
+#define TRX_MAGIC 0x30524448 /* "HDR0" */
+#define TRX_MAX_OFFSET 3 /* Max number of file offsets */
+#define TRX_UNCOMP_IMAGE 0x20 /* Trx holds uncompressed img */
+#define TRX_RDL_CHUNK 1500 /* size of each dl transfer */
+#define TRX_OFFSETS_DLFWLEN_IDX 0
+
+/* Control messages: bRequest values */
+#define DL_GETSTATE 0 /* returns the rdl_state_t struct */
+#define DL_CHECK_CRC 1 /* currently unused */
+#define DL_GO 2 /* execute downloaded image */
+#define DL_START 3 /* initialize dl state */
+#define DL_REBOOT 4 /* reboot the device in 2 seconds */
+#define DL_GETVER 5 /* returns the bootrom_id_t struct */
+#define DL_GO_PROTECTED 6 /* execute the downloaded code and set reset
+ * event to occur in 2 seconds. It is the
+ * responsibility of the downloaded code to
+ * clear this event
+ */
+#define DL_EXEC 7 /* jump to a supplied address */
+#define DL_RESETCFG 8 /* To support single enum on dongle
+ * - Not used by bootloader
+ */
+#define DL_DEFER_RESP_OK 9 /* Potentially defer the response to setup
+ * if resp unavailable
+ */
+
+/* states */
+#define DL_WAITING 0 /* waiting to rx first pkt */
+#define DL_READY 1 /* hdr was good, waiting for more of the
+ * compressed image
+ */
+#define DL_BAD_HDR 2 /* hdr was corrupted */
+#define DL_BAD_CRC 3 /* compressed image was corrupted */
+#define DL_RUNNABLE 4 /* download was successful,waiting for go cmd */
+#define DL_START_FAIL 5 /* failed to initialize correctly */
+#define DL_NVRAM_TOOBIG 6 /* host specified nvram data exceeds DL_NVRAM
+ * value
+ */
+#define DL_IMAGE_TOOBIG 7 /* firmware image too big */
+
+
+struct trx_header_le {
+ __le32 magic; /* "HDR0" */
+ __le32 len; /* Length of file including header */
+ __le32 crc32; /* CRC from flag_version to end of file */
+ __le32 flag_version; /* 0:15 flags, 16:31 version */
+ __le32 offsets[TRX_MAX_OFFSET]; /* Offsets of partitions from start of
+ * header
+ */
+};
+
+struct rdl_state_le {
+ __le32 state;
+ __le32 bytes;
+};
+
+struct bootrom_id_le {
+ __le32 chip; /* Chip id */
+ __le32 chiprev; /* Chip rev */
+ __le32 ramsize; /* Size of RAM */
+ __le32 remapbase; /* Current remap base address */
+ __le32 boardtype; /* Type of board */
+ __le32 boardrev; /* Board revision */
+};
+
+struct brcmf_usb_image {
+ struct list_head list;
+ s8 *fwname;
+ u8 *image;
+ int image_len;
+};
+
+struct brcmf_usbdev_info {
+ struct brcmf_usbdev bus_pub; /* MUST BE FIRST */
+ spinlock_t qlock;
+ struct list_head rx_freeq;
+ struct list_head rx_postq;
+ struct list_head tx_freeq;
+ struct list_head tx_postq;
+ uint rx_pipe, tx_pipe;
+
+ int rx_low_watermark;
+ int tx_low_watermark;
+ int tx_high_watermark;
+ int tx_freecount;
+ bool tx_flowblock;
+ spinlock_t tx_flowblock_lock;
+
+ struct brcmf_usbreq *tx_reqs;
+ struct brcmf_usbreq *rx_reqs;
+
+ char fw_name[BRCMF_FW_NAME_LEN];
+ const u8 *image; /* buffer for combine fw and nvram */
+ int image_len;
+
+ struct usb_device *usbdev;
+ struct device *dev;
+ struct mutex dev_init_lock;
+
+ int ctl_in_pipe, ctl_out_pipe;
+ struct urb *ctl_urb; /* URB for control endpoint */
+ struct usb_ctrlrequest ctl_write;
+ struct usb_ctrlrequest ctl_read;
+ u32 ctl_urb_actual_length;
+ int ctl_urb_status;
+ int ctl_completed;
+ wait_queue_head_t ioctl_resp_wait;
+ ulong ctl_op;
+ u8 ifnum;
+
+ struct urb *bulk_urb; /* used for FW download */
+
+ bool wowl_enabled;
+ struct brcmf_mp_device *settings;
+};
+
+static void brcmf_usb_rx_refill(struct brcmf_usbdev_info *devinfo,
+ struct brcmf_usbreq *req);
+
+static struct brcmf_usbdev *brcmf_usb_get_buspub(struct device *dev)
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+ return bus_if->bus_priv.usb;
+}
+
+static struct brcmf_usbdev_info *brcmf_usb_get_businfo(struct device *dev)
+{
+ return brcmf_usb_get_buspub(dev)->devinfo;
+}
+
+static int brcmf_usb_ioctl_resp_wait(struct brcmf_usbdev_info *devinfo)
+{
+ return wait_event_timeout(devinfo->ioctl_resp_wait,
+ devinfo->ctl_completed, IOCTL_RESP_TIMEOUT);
+}
+
+static void brcmf_usb_ioctl_resp_wake(struct brcmf_usbdev_info *devinfo)
+{
+ wake_up(&devinfo->ioctl_resp_wait);
+}
+
+static void
+brcmf_usb_ctl_complete(struct brcmf_usbdev_info *devinfo, int type, int status)
+{
+ brcmf_dbg(USB, "Enter, status=%d\n", status);
+
+ if (unlikely(devinfo == NULL))
+ return;
+
+ if (type == BRCMF_USB_CBCTL_READ) {
+ if (status == 0)
+ devinfo->bus_pub.stats.rx_ctlpkts++;
+ else
+ devinfo->bus_pub.stats.rx_ctlerrs++;
+ } else if (type == BRCMF_USB_CBCTL_WRITE) {
+ if (status == 0)
+ devinfo->bus_pub.stats.tx_ctlpkts++;
+ else
+ devinfo->bus_pub.stats.tx_ctlerrs++;
+ }
+
+ devinfo->ctl_urb_status = status;
+ devinfo->ctl_completed = true;
+ brcmf_usb_ioctl_resp_wake(devinfo);
+}
+
+static void
+brcmf_usb_ctlread_complete(struct urb *urb)
+{
+ struct brcmf_usbdev_info *devinfo =
+ (struct brcmf_usbdev_info *)urb->context;
+
+ brcmf_dbg(USB, "Enter\n");
+ devinfo->ctl_urb_actual_length = urb->actual_length;
+ brcmf_usb_ctl_complete(devinfo, BRCMF_USB_CBCTL_READ,
+ urb->status);
+}
+
+static void
+brcmf_usb_ctlwrite_complete(struct urb *urb)
+{
+ struct brcmf_usbdev_info *devinfo =
+ (struct brcmf_usbdev_info *)urb->context;
+
+ brcmf_dbg(USB, "Enter\n");
+ brcmf_usb_ctl_complete(devinfo, BRCMF_USB_CBCTL_WRITE,
+ urb->status);
+}
+
+static int
+brcmf_usb_send_ctl(struct brcmf_usbdev_info *devinfo, u8 *buf, int len)
+{
+ int ret;
+ u16 size;
+
+ brcmf_dbg(USB, "Enter\n");
+ if (devinfo == NULL || buf == NULL ||
+ len == 0 || devinfo->ctl_urb == NULL)
+ return -EINVAL;
+
+ size = len;
+ devinfo->ctl_write.wLength = cpu_to_le16p(&size);
+ devinfo->ctl_urb->transfer_buffer_length = size;
+ devinfo->ctl_urb_status = 0;
+ devinfo->ctl_urb_actual_length = 0;
+
+ usb_fill_control_urb(devinfo->ctl_urb,
+ devinfo->usbdev,
+ devinfo->ctl_out_pipe,
+ (unsigned char *) &devinfo->ctl_write,
+ buf, size,
+ (usb_complete_t)brcmf_usb_ctlwrite_complete,
+ devinfo);
+
+ ret = usb_submit_urb(devinfo->ctl_urb, GFP_ATOMIC);
+ if (ret < 0)
+ brcmf_err("usb_submit_urb failed %d\n", ret);
+
+ return ret;
+}
+
+static int
+brcmf_usb_recv_ctl(struct brcmf_usbdev_info *devinfo, u8 *buf, int len)
+{
+ int ret;
+ u16 size;
+
+ brcmf_dbg(USB, "Enter\n");
+ if ((devinfo == NULL) || (buf == NULL) || (len == 0)
+ || (devinfo->ctl_urb == NULL))
+ return -EINVAL;
+
+ size = len;
+ devinfo->ctl_read.wLength = cpu_to_le16p(&size);
+ devinfo->ctl_urb->transfer_buffer_length = size;
+
+ devinfo->ctl_read.bRequestType = USB_DIR_IN
+ | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
+ devinfo->ctl_read.bRequest = 1;
+
+ usb_fill_control_urb(devinfo->ctl_urb,
+ devinfo->usbdev,
+ devinfo->ctl_in_pipe,
+ (unsigned char *) &devinfo->ctl_read,
+ buf, size,
+ (usb_complete_t)brcmf_usb_ctlread_complete,
+ devinfo);
+
+ ret = usb_submit_urb(devinfo->ctl_urb, GFP_ATOMIC);
+ if (ret < 0)
+ brcmf_err("usb_submit_urb failed %d\n", ret);
+
+ return ret;
+}
+
+static int brcmf_usb_tx_ctlpkt(struct device *dev, u8 *buf, u32 len)
+{
+ int err = 0;
+ int timeout = 0;
+ struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev);
+
+ brcmf_dbg(USB, "Enter\n");
+ if (devinfo->bus_pub.state != BRCMFMAC_USB_STATE_UP)
+ return -EIO;
+
+ if (test_and_set_bit(0, &devinfo->ctl_op))
+ return -EIO;
+
+ devinfo->ctl_completed = false;
+ err = brcmf_usb_send_ctl(devinfo, buf, len);
+ if (err) {
+ brcmf_err("fail %d bytes: %d\n", err, len);
+ clear_bit(0, &devinfo->ctl_op);
+ return err;
+ }
+ timeout = brcmf_usb_ioctl_resp_wait(devinfo);
+ clear_bit(0, &devinfo->ctl_op);
+ if (!timeout) {
+ brcmf_err("Txctl wait timed out\n");
+ err = -EIO;
+ }
+ return err;
+}
+
+static int brcmf_usb_rx_ctlpkt(struct device *dev, u8 *buf, u32 len)
+{
+ int err = 0;
+ int timeout = 0;
+ struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev);
+
+ brcmf_dbg(USB, "Enter\n");
+ if (devinfo->bus_pub.state != BRCMFMAC_USB_STATE_UP)
+ return -EIO;
+
+ if (test_and_set_bit(0, &devinfo->ctl_op))
+ return -EIO;
+
+ devinfo->ctl_completed = false;
+ err = brcmf_usb_recv_ctl(devinfo, buf, len);
+ if (err) {
+ brcmf_err("fail %d bytes: %d\n", err, len);
+ clear_bit(0, &devinfo->ctl_op);
+ return err;
+ }
+ timeout = brcmf_usb_ioctl_resp_wait(devinfo);
+ err = devinfo->ctl_urb_status;
+ clear_bit(0, &devinfo->ctl_op);
+ if (!timeout) {
+ brcmf_err("rxctl wait timed out\n");
+ err = -EIO;
+ }
+ if (!err)
+ return devinfo->ctl_urb_actual_length;
+ else
+ return err;
+}
+
+static struct brcmf_usbreq *brcmf_usb_deq(struct brcmf_usbdev_info *devinfo,
+ struct list_head *q, int *counter)
+{
+ unsigned long flags;
+ struct brcmf_usbreq *req;
+ spin_lock_irqsave(&devinfo->qlock, flags);
+ if (list_empty(q)) {
+ spin_unlock_irqrestore(&devinfo->qlock, flags);
+ return NULL;
+ }
+ req = list_entry(q->next, struct brcmf_usbreq, list);
+ list_del_init(q->next);
+ if (counter)
+ (*counter)--;
+ spin_unlock_irqrestore(&devinfo->qlock, flags);
+ return req;
+
+}
+
+static void brcmf_usb_enq(struct brcmf_usbdev_info *devinfo,
+ struct list_head *q, struct brcmf_usbreq *req,
+ int *counter)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&devinfo->qlock, flags);
+ list_add_tail(&req->list, q);
+ if (counter)
+ (*counter)++;
+ spin_unlock_irqrestore(&devinfo->qlock, flags);
+}
+
+static struct brcmf_usbreq *
+brcmf_usbdev_qinit(struct list_head *q, int qsize)
+{
+ int i;
+ struct brcmf_usbreq *req, *reqs;
+
+ reqs = kcalloc(qsize, sizeof(struct brcmf_usbreq), GFP_ATOMIC);
+ if (reqs == NULL)
+ return NULL;
+
+ req = reqs;
+
+ for (i = 0; i < qsize; i++) {
+ req->urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!req->urb)
+ goto fail;
+
+ INIT_LIST_HEAD(&req->list);
+ list_add_tail(&req->list, q);
+ req++;
+ }
+ return reqs;
+fail:
+ brcmf_err("fail!\n");
+ while (!list_empty(q)) {
+ req = list_entry(q->next, struct brcmf_usbreq, list);
+ if (req)
+ usb_free_urb(req->urb);
+ list_del(q->next);
+ }
+ return NULL;
+
+}
+
+static void brcmf_usb_free_q(struct list_head *q, bool pending)
+{
+ struct brcmf_usbreq *req, *next;
+ int i = 0;
+ list_for_each_entry_safe(req, next, q, list) {
+ if (!req->urb) {
+ brcmf_err("bad req\n");
+ break;
+ }
+ i++;
+ if (pending) {
+ usb_kill_urb(req->urb);
+ } else {
+ usb_free_urb(req->urb);
+ list_del_init(&req->list);
+ }
+ }
+}
+
+static void brcmf_usb_del_fromq(struct brcmf_usbdev_info *devinfo,
+ struct brcmf_usbreq *req)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&devinfo->qlock, flags);
+ list_del_init(&req->list);
+ spin_unlock_irqrestore(&devinfo->qlock, flags);
+}
+
+
+static void brcmf_usb_tx_complete(struct urb *urb)
+{
+ struct brcmf_usbreq *req = (struct brcmf_usbreq *)urb->context;
+ struct brcmf_usbdev_info *devinfo = req->devinfo;
+ unsigned long flags;
+
+ brcmf_dbg(USB, "Enter, urb->status=%d, skb=%p\n", urb->status,
+ req->skb);
+ brcmf_usb_del_fromq(devinfo, req);
+
+ brcmf_proto_bcdc_txcomplete(devinfo->dev, req->skb, urb->status == 0);
+ req->skb = NULL;
+ brcmf_usb_enq(devinfo, &devinfo->tx_freeq, req, &devinfo->tx_freecount);
+ spin_lock_irqsave(&devinfo->tx_flowblock_lock, flags);
+ if (devinfo->tx_freecount > devinfo->tx_high_watermark &&
+ devinfo->tx_flowblock) {
+ brcmf_proto_bcdc_txflowblock(devinfo->dev, false);
+ devinfo->tx_flowblock = false;
+ }
+ spin_unlock_irqrestore(&devinfo->tx_flowblock_lock, flags);
+}
+
+static void brcmf_usb_rx_complete(struct urb *urb)
+{
+ struct brcmf_usbreq *req = (struct brcmf_usbreq *)urb->context;
+ struct brcmf_usbdev_info *devinfo = req->devinfo;
+ struct sk_buff *skb;
+
+ brcmf_dbg(USB, "Enter, urb->status=%d\n", urb->status);
+ brcmf_usb_del_fromq(devinfo, req);
+ skb = req->skb;
+ req->skb = NULL;
+
+ /* zero lenght packets indicate usb "failure". Do not refill */
+ if (urb->status != 0 || !urb->actual_length) {
+ brcmu_pkt_buf_free_skb(skb);
+ brcmf_usb_enq(devinfo, &devinfo->rx_freeq, req, NULL);
+ return;
+ }
+
+ if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_UP) {
+ skb_put(skb, urb->actual_length);
+ brcmf_rx_frame(devinfo->dev, skb, true);
+ brcmf_usb_rx_refill(devinfo, req);
+ } else {
+ brcmu_pkt_buf_free_skb(skb);
+ brcmf_usb_enq(devinfo, &devinfo->rx_freeq, req, NULL);
+ }
+ return;
+
+}
+
+static void brcmf_usb_rx_refill(struct brcmf_usbdev_info *devinfo,
+ struct brcmf_usbreq *req)
+{
+ struct sk_buff *skb;
+ int ret;
+
+ if (!req || !devinfo)
+ return;
+
+ skb = dev_alloc_skb(devinfo->bus_pub.bus_mtu);
+ if (!skb) {
+ brcmf_usb_enq(devinfo, &devinfo->rx_freeq, req, NULL);
+ return;
+ }
+ req->skb = skb;
+
+ usb_fill_bulk_urb(req->urb, devinfo->usbdev, devinfo->rx_pipe,
+ skb->data, skb_tailroom(skb), brcmf_usb_rx_complete,
+ req);
+ req->devinfo = devinfo;
+ brcmf_usb_enq(devinfo, &devinfo->rx_postq, req, NULL);
+
+ ret = usb_submit_urb(req->urb, GFP_ATOMIC);
+ if (ret) {
+ brcmf_usb_del_fromq(devinfo, req);
+ brcmu_pkt_buf_free_skb(req->skb);
+ req->skb = NULL;
+ brcmf_usb_enq(devinfo, &devinfo->rx_freeq, req, NULL);
+ }
+ return;
+}
+
+static void brcmf_usb_rx_fill_all(struct brcmf_usbdev_info *devinfo)
+{
+ struct brcmf_usbreq *req;
+
+ if (devinfo->bus_pub.state != BRCMFMAC_USB_STATE_UP) {
+ brcmf_err("bus is not up=%d\n", devinfo->bus_pub.state);
+ return;
+ }
+ while ((req = brcmf_usb_deq(devinfo, &devinfo->rx_freeq, NULL)) != NULL)
+ brcmf_usb_rx_refill(devinfo, req);
+}
+
+static void
+brcmf_usb_state_change(struct brcmf_usbdev_info *devinfo, int state)
+{
+ struct brcmf_bus *bcmf_bus = devinfo->bus_pub.bus;
+ int old_state;
+
+ brcmf_dbg(USB, "Enter, current state=%d, new state=%d\n",
+ devinfo->bus_pub.state, state);
+
+ if (devinfo->bus_pub.state == state)
+ return;
+
+ old_state = devinfo->bus_pub.state;
+ devinfo->bus_pub.state = state;
+
+ /* update state of upper layer */
+ if (state == BRCMFMAC_USB_STATE_DOWN) {
+ brcmf_dbg(USB, "DBUS is down\n");
+ brcmf_bus_change_state(bcmf_bus, BRCMF_BUS_DOWN);
+ } else if (state == BRCMFMAC_USB_STATE_UP) {
+ brcmf_dbg(USB, "DBUS is up\n");
+ brcmf_bus_change_state(bcmf_bus, BRCMF_BUS_UP);
+ } else {
+ brcmf_dbg(USB, "DBUS current state=%d\n", state);
+ }
+}
+
+static int brcmf_usb_tx(struct device *dev, struct sk_buff *skb)
+{
+ struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev);
+ struct brcmf_usbreq *req;
+ int ret;
+ unsigned long flags;
+
+ brcmf_dbg(USB, "Enter, skb=%p\n", skb);
+ if (devinfo->bus_pub.state != BRCMFMAC_USB_STATE_UP) {
+ ret = -EIO;
+ goto fail;
+ }
+
+ req = brcmf_usb_deq(devinfo, &devinfo->tx_freeq,
+ &devinfo->tx_freecount);
+ if (!req) {
+ brcmf_err("no req to send\n");
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ req->skb = skb;
+ req->devinfo = devinfo;
+ usb_fill_bulk_urb(req->urb, devinfo->usbdev, devinfo->tx_pipe,
+ skb->data, skb->len, brcmf_usb_tx_complete, req);
+ req->urb->transfer_flags |= URB_ZERO_PACKET;
+ brcmf_usb_enq(devinfo, &devinfo->tx_postq, req, NULL);
+ ret = usb_submit_urb(req->urb, GFP_ATOMIC);
+ if (ret) {
+ brcmf_err("brcmf_usb_tx usb_submit_urb FAILED\n");
+ brcmf_usb_del_fromq(devinfo, req);
+ req->skb = NULL;
+ brcmf_usb_enq(devinfo, &devinfo->tx_freeq, req,
+ &devinfo->tx_freecount);
+ goto fail;
+ }
+
+ spin_lock_irqsave(&devinfo->tx_flowblock_lock, flags);
+ if (devinfo->tx_freecount < devinfo->tx_low_watermark &&
+ !devinfo->tx_flowblock) {
+ brcmf_proto_bcdc_txflowblock(dev, true);
+ devinfo->tx_flowblock = true;
+ }
+ spin_unlock_irqrestore(&devinfo->tx_flowblock_lock, flags);
+ return 0;
+
+fail:
+ return ret;
+}
+
+
+static int brcmf_usb_up(struct device *dev)
+{
+ struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev);
+
+ brcmf_dbg(USB, "Enter\n");
+ if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_UP)
+ return 0;
+
+ /* Success, indicate devinfo is fully up */
+ brcmf_usb_state_change(devinfo, BRCMFMAC_USB_STATE_UP);
+
+ if (devinfo->ctl_urb) {
+ devinfo->ctl_in_pipe = usb_rcvctrlpipe(devinfo->usbdev, 0);
+ devinfo->ctl_out_pipe = usb_sndctrlpipe(devinfo->usbdev, 0);
+
+ /* CTL Write */
+ devinfo->ctl_write.bRequestType =
+ USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
+ devinfo->ctl_write.bRequest = 0;
+ devinfo->ctl_write.wValue = cpu_to_le16(0);
+ devinfo->ctl_write.wIndex = cpu_to_le16(devinfo->ifnum);
+
+ /* CTL Read */
+ devinfo->ctl_read.bRequestType =
+ USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
+ devinfo->ctl_read.bRequest = 1;
+ devinfo->ctl_read.wValue = cpu_to_le16(0);
+ devinfo->ctl_read.wIndex = cpu_to_le16(devinfo->ifnum);
+ }
+ brcmf_usb_rx_fill_all(devinfo);
+ return 0;
+}
+
+static void brcmf_cancel_all_urbs(struct brcmf_usbdev_info *devinfo)
+{
+ if (devinfo->ctl_urb)
+ usb_kill_urb(devinfo->ctl_urb);
+ if (devinfo->bulk_urb)
+ usb_kill_urb(devinfo->bulk_urb);
+ brcmf_usb_free_q(&devinfo->tx_postq, true);
+ brcmf_usb_free_q(&devinfo->rx_postq, true);
+}
+
+static void brcmf_usb_down(struct device *dev)
+{
+ struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev);
+
+ brcmf_dbg(USB, "Enter\n");
+ if (devinfo == NULL)
+ return;
+
+ if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_DOWN)
+ return;
+
+ brcmf_usb_state_change(devinfo, BRCMFMAC_USB_STATE_DOWN);
+
+ brcmf_cancel_all_urbs(devinfo);
+}
+
+static void
+brcmf_usb_sync_complete(struct urb *urb)
+{
+ struct brcmf_usbdev_info *devinfo =
+ (struct brcmf_usbdev_info *)urb->context;
+
+ devinfo->ctl_completed = true;
+ brcmf_usb_ioctl_resp_wake(devinfo);
+}
+
+static int brcmf_usb_dl_cmd(struct brcmf_usbdev_info *devinfo, u8 cmd,
+ void *buffer, int buflen)
+{
+ int ret;
+ char *tmpbuf;
+ u16 size;
+
+ if ((!devinfo) || (devinfo->ctl_urb == NULL))
+ return -EINVAL;
+
+ tmpbuf = kmalloc(buflen, GFP_ATOMIC);
+ if (!tmpbuf)
+ return -ENOMEM;
+
+ size = buflen;
+ devinfo->ctl_urb->transfer_buffer_length = size;
+
+ devinfo->ctl_read.wLength = cpu_to_le16p(&size);
+ devinfo->ctl_read.bRequestType = USB_DIR_IN | USB_TYPE_VENDOR |
+ USB_RECIP_INTERFACE;
+ devinfo->ctl_read.bRequest = cmd;
+
+ usb_fill_control_urb(devinfo->ctl_urb,
+ devinfo->usbdev,
+ usb_rcvctrlpipe(devinfo->usbdev, 0),
+ (unsigned char *) &devinfo->ctl_read,
+ (void *) tmpbuf, size,
+ (usb_complete_t)brcmf_usb_sync_complete, devinfo);
+
+ devinfo->ctl_completed = false;
+ ret = usb_submit_urb(devinfo->ctl_urb, GFP_ATOMIC);
+ if (ret < 0) {
+ brcmf_err("usb_submit_urb failed %d\n", ret);
+ goto finalize;
+ }
+
+ if (!brcmf_usb_ioctl_resp_wait(devinfo)) {
+ usb_kill_urb(devinfo->ctl_urb);
+ ret = -ETIMEDOUT;
+ } else {
+ memcpy(buffer, tmpbuf, buflen);
+ }
+
+finalize:
+ kfree(tmpbuf);
+ return ret;
+}
+
+static bool
+brcmf_usb_dlneeded(struct brcmf_usbdev_info *devinfo)
+{
+ struct bootrom_id_le id;
+ u32 chipid, chiprev;
+
+ brcmf_dbg(USB, "Enter\n");
+
+ if (devinfo == NULL)
+ return false;
+
+ /* Check if firmware downloaded already by querying runtime ID */
+ id.chip = cpu_to_le32(0xDEAD);
+ brcmf_usb_dl_cmd(devinfo, DL_GETVER, &id, sizeof(id));
+
+ chipid = le32_to_cpu(id.chip);
+ chiprev = le32_to_cpu(id.chiprev);
+
+ if ((chipid & 0x4300) == 0x4300)
+ brcmf_dbg(USB, "chip %x rev 0x%x\n", chipid, chiprev);
+ else
+ brcmf_dbg(USB, "chip %d rev 0x%x\n", chipid, chiprev);
+ if (chipid == BRCMF_POSTBOOT_ID) {
+ brcmf_dbg(USB, "firmware already downloaded\n");
+ brcmf_usb_dl_cmd(devinfo, DL_RESETCFG, &id, sizeof(id));
+ return false;
+ } else {
+ devinfo->bus_pub.devid = chipid;
+ devinfo->bus_pub.chiprev = chiprev;
+ }
+ return true;
+}
+
+static int
+brcmf_usb_resetcfg(struct brcmf_usbdev_info *devinfo)
+{
+ struct bootrom_id_le id;
+ u32 loop_cnt;
+ int err;
+
+ brcmf_dbg(USB, "Enter\n");
+
+ loop_cnt = 0;
+ do {
+ mdelay(BRCMF_USB_RESET_GETVER_SPINWAIT);
+ loop_cnt++;
+ id.chip = cpu_to_le32(0xDEAD); /* Get the ID */
+ err = brcmf_usb_dl_cmd(devinfo, DL_GETVER, &id, sizeof(id));
+ if ((err) && (err != -ETIMEDOUT))
+ return err;
+ if (id.chip == cpu_to_le32(BRCMF_POSTBOOT_ID))
+ break;
+ } while (loop_cnt < BRCMF_USB_RESET_GETVER_LOOP_CNT);
+
+ if (id.chip == cpu_to_le32(BRCMF_POSTBOOT_ID)) {
+ brcmf_dbg(USB, "postboot chip 0x%x/rev 0x%x\n",
+ le32_to_cpu(id.chip), le32_to_cpu(id.chiprev));
+
+ brcmf_usb_dl_cmd(devinfo, DL_RESETCFG, &id, sizeof(id));
+ return 0;
+ } else {
+ brcmf_err("Cannot talk to Dongle. Firmware is not UP, %d ms\n",
+ BRCMF_USB_RESET_GETVER_SPINWAIT * loop_cnt);
+ return -EINVAL;
+ }
+}
+
+
+static int
+brcmf_usb_dl_send_bulk(struct brcmf_usbdev_info *devinfo, void *buffer, int len)
+{
+ int ret;
+
+ if ((devinfo == NULL) || (devinfo->bulk_urb == NULL))
+ return -EINVAL;
+
+ /* Prepare the URB */
+ usb_fill_bulk_urb(devinfo->bulk_urb, devinfo->usbdev,
+ devinfo->tx_pipe, buffer, len,
+ (usb_complete_t)brcmf_usb_sync_complete, devinfo);
+
+ devinfo->bulk_urb->transfer_flags |= URB_ZERO_PACKET;
+
+ devinfo->ctl_completed = false;
+ ret = usb_submit_urb(devinfo->bulk_urb, GFP_ATOMIC);
+ if (ret) {
+ brcmf_err("usb_submit_urb failed %d\n", ret);
+ return ret;
+ }
+ ret = brcmf_usb_ioctl_resp_wait(devinfo);
+ return (ret == 0);
+}
+
+static int
+brcmf_usb_dl_writeimage(struct brcmf_usbdev_info *devinfo, u8 *fw, int fwlen)
+{
+ unsigned int sendlen, sent, dllen;
+ char *bulkchunk = NULL, *dlpos;
+ struct rdl_state_le state;
+ u32 rdlstate, rdlbytes;
+ int err = 0;
+
+ brcmf_dbg(USB, "Enter, fw %p, len %d\n", fw, fwlen);
+
+ bulkchunk = kmalloc(TRX_RDL_CHUNK, GFP_ATOMIC);
+ if (bulkchunk == NULL) {
+ err = -ENOMEM;
+ goto fail;
+ }
+
+ /* 1) Prepare USB boot loader for runtime image */
+ brcmf_usb_dl_cmd(devinfo, DL_START, &state, sizeof(state));
+
+ rdlstate = le32_to_cpu(state.state);
+ rdlbytes = le32_to_cpu(state.bytes);
+
+ /* 2) Check we are in the Waiting state */
+ if (rdlstate != DL_WAITING) {
+ brcmf_err("Failed to DL_START\n");
+ err = -EINVAL;
+ goto fail;
+ }
+ sent = 0;
+ dlpos = fw;
+ dllen = fwlen;
+
+ /* Get chip id and rev */
+ while (rdlbytes != dllen) {
+ /* Wait until the usb device reports it received all
+ * the bytes we sent */
+ if ((rdlbytes == sent) && (rdlbytes != dllen)) {
+ if ((dllen-sent) < TRX_RDL_CHUNK)
+ sendlen = dllen-sent;
+ else
+ sendlen = TRX_RDL_CHUNK;
+
+ /* simply avoid having to send a ZLP by ensuring we
+ * never have an even
+ * multiple of 64
+ */
+ if (!(sendlen % 64))
+ sendlen -= 4;
+
+ /* send data */
+ memcpy(bulkchunk, dlpos, sendlen);
+ if (brcmf_usb_dl_send_bulk(devinfo, bulkchunk,
+ sendlen)) {
+ brcmf_err("send_bulk failed\n");
+ err = -EINVAL;
+ goto fail;
+ }
+
+ dlpos += sendlen;
+ sent += sendlen;
+ }
+ err = brcmf_usb_dl_cmd(devinfo, DL_GETSTATE, &state,
+ sizeof(state));
+ if (err) {
+ brcmf_err("DL_GETSTATE Failed\n");
+ goto fail;
+ }
+
+ rdlstate = le32_to_cpu(state.state);
+ rdlbytes = le32_to_cpu(state.bytes);
+
+ /* restart if an error is reported */
+ if (rdlstate == DL_BAD_HDR || rdlstate == DL_BAD_CRC) {
+ brcmf_err("Bad Hdr or Bad CRC state %d\n",
+ rdlstate);
+ err = -EINVAL;
+ goto fail;
+ }
+ }
+
+fail:
+ kfree(bulkchunk);
+ brcmf_dbg(USB, "Exit, err=%d\n", err);
+ return err;
+}
+
+static int brcmf_usb_dlstart(struct brcmf_usbdev_info *devinfo, u8 *fw, int len)
+{
+ int err;
+
+ brcmf_dbg(USB, "Enter\n");
+
+ if (devinfo == NULL)
+ return -EINVAL;
+
+ if (devinfo->bus_pub.devid == 0xDEAD)
+ return -EINVAL;
+
+ err = brcmf_usb_dl_writeimage(devinfo, fw, len);
+ if (err == 0)
+ devinfo->bus_pub.state = BRCMFMAC_USB_STATE_DL_DONE;
+ else
+ devinfo->bus_pub.state = BRCMFMAC_USB_STATE_DL_FAIL;
+ brcmf_dbg(USB, "Exit, err=%d\n", err);
+
+ return err;
+}
+
+static int brcmf_usb_dlrun(struct brcmf_usbdev_info *devinfo)
+{
+ struct rdl_state_le state;
+
+ brcmf_dbg(USB, "Enter\n");
+ if (!devinfo)
+ return -EINVAL;
+
+ if (devinfo->bus_pub.devid == 0xDEAD)
+ return -EINVAL;
+
+ /* Check we are runnable */
+ state.state = 0;
+ brcmf_usb_dl_cmd(devinfo, DL_GETSTATE, &state, sizeof(state));
+
+ /* Start the image */
+ if (state.state == cpu_to_le32(DL_RUNNABLE)) {
+ if (brcmf_usb_dl_cmd(devinfo, DL_GO, &state, sizeof(state)))
+ return -ENODEV;
+ if (brcmf_usb_resetcfg(devinfo))
+ return -ENODEV;
+ /* The Dongle may go for re-enumeration. */
+ } else {
+ brcmf_err("Dongle not runnable\n");
+ return -EINVAL;
+ }
+ brcmf_dbg(USB, "Exit\n");
+ return 0;
+}
+
+static int
+brcmf_usb_fw_download(struct brcmf_usbdev_info *devinfo)
+{
+ int err;
+
+ brcmf_dbg(USB, "Enter\n");
+ if (devinfo == NULL)
+ return -ENODEV;
+
+ if (!devinfo->image) {
+ brcmf_err("No firmware!\n");
+ return -ENOENT;
+ }
+
+ err = brcmf_usb_dlstart(devinfo,
+ (u8 *)devinfo->image, devinfo->image_len);
+ if (err == 0)
+ err = brcmf_usb_dlrun(devinfo);
+ return err;
+}
+
+
+static void brcmf_usb_detach(struct brcmf_usbdev_info *devinfo)
+{
+ brcmf_dbg(USB, "Enter, devinfo %p\n", devinfo);
+
+ /* free the URBS */
+ brcmf_usb_free_q(&devinfo->rx_freeq, false);
+ brcmf_usb_free_q(&devinfo->tx_freeq, false);
+
+ usb_free_urb(devinfo->ctl_urb);
+ usb_free_urb(devinfo->bulk_urb);
+
+ kfree(devinfo->tx_reqs);
+ kfree(devinfo->rx_reqs);
+
+ if (devinfo->settings)
+ brcmf_release_module_param(devinfo->settings);
+}
+
+
+static int check_file(const u8 *headers)
+{
+ struct trx_header_le *trx;
+ int actual_len = -1;
+
+ brcmf_dbg(USB, "Enter\n");
+ /* Extract trx header */
+ trx = (struct trx_header_le *) headers;
+ if (trx->magic != cpu_to_le32(TRX_MAGIC))
+ return -1;
+
+ headers += sizeof(struct trx_header_le);
+
+ if (le32_to_cpu(trx->flag_version) & TRX_UNCOMP_IMAGE) {
+ actual_len = le32_to_cpu(trx->offsets[TRX_OFFSETS_DLFWLEN_IDX]);
+ return actual_len + sizeof(struct trx_header_le);
+ }
+ return -1;
+}
+
+
+static
+struct brcmf_usbdev *brcmf_usb_attach(struct brcmf_usbdev_info *devinfo,
+ int nrxq, int ntxq)
+{
+ brcmf_dbg(USB, "Enter\n");
+
+ devinfo->bus_pub.nrxq = nrxq;
+ devinfo->rx_low_watermark = nrxq / 2;
+ devinfo->bus_pub.devinfo = devinfo;
+ devinfo->bus_pub.ntxq = ntxq;
+ devinfo->bus_pub.state = BRCMFMAC_USB_STATE_DOWN;
+
+ /* flow control when too many tx urbs posted */
+ devinfo->tx_low_watermark = ntxq / 4;
+ devinfo->tx_high_watermark = devinfo->tx_low_watermark * 3;
+ devinfo->bus_pub.bus_mtu = BRCMF_USB_MAX_PKT_SIZE;
+
+ /* Initialize other structure content */
+ init_waitqueue_head(&devinfo->ioctl_resp_wait);
+
+ /* Initialize the spinlocks */
+ spin_lock_init(&devinfo->qlock);
+ spin_lock_init(&devinfo->tx_flowblock_lock);
+
+ INIT_LIST_HEAD(&devinfo->rx_freeq);
+ INIT_LIST_HEAD(&devinfo->rx_postq);
+
+ INIT_LIST_HEAD(&devinfo->tx_freeq);
+ INIT_LIST_HEAD(&devinfo->tx_postq);
+
+ devinfo->tx_flowblock = false;
+
+ devinfo->rx_reqs = brcmf_usbdev_qinit(&devinfo->rx_freeq, nrxq);
+ if (!devinfo->rx_reqs)
+ goto error;
+
+ devinfo->tx_reqs = brcmf_usbdev_qinit(&devinfo->tx_freeq, ntxq);
+ if (!devinfo->tx_reqs)
+ goto error;
+ devinfo->tx_freecount = ntxq;
+
+ devinfo->ctl_urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!devinfo->ctl_urb)
+ goto error;
+ devinfo->bulk_urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!devinfo->bulk_urb)
+ goto error;
+
+ return &devinfo->bus_pub;
+
+error:
+ brcmf_err("failed!\n");
+ brcmf_usb_detach(devinfo);
+ return NULL;
+}
+
+static void brcmf_usb_wowl_config(struct device *dev, bool enabled)
+{
+ struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev);
+
+ brcmf_dbg(USB, "Configuring WOWL, enabled=%d\n", enabled);
+ devinfo->wowl_enabled = enabled;
+ if (enabled)
+ device_set_wakeup_enable(devinfo->dev, true);
+ else
+ device_set_wakeup_enable(devinfo->dev, false);
+}
+
+static int brcmf_usb_get_fwname(struct device *dev, u32 chip, u32 chiprev,
+ u8 *fw_name)
+{
+ struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev);
+ int ret = 0;
+
+ if (devinfo->fw_name[0] != '\0')
+ strlcpy(fw_name, devinfo->fw_name, BRCMF_FW_NAME_LEN);
+ else
+ ret = brcmf_fw_map_chip_to_name(chip, chiprev,
+ brcmf_usb_fwnames,
+ ARRAY_SIZE(brcmf_usb_fwnames),
+ fw_name, NULL);
+
+ return ret;
+}
+
+static const struct brcmf_bus_ops brcmf_usb_bus_ops = {
+ .txdata = brcmf_usb_tx,
+ .stop = brcmf_usb_down,
+ .txctl = brcmf_usb_tx_ctlpkt,
+ .rxctl = brcmf_usb_rx_ctlpkt,
+ .wowl_config = brcmf_usb_wowl_config,
+ .get_fwname = brcmf_usb_get_fwname,
+};
+
+static int brcmf_usb_bus_setup(struct brcmf_usbdev_info *devinfo)
+{
+ int ret;
+
+ /* Attach to the common driver interface */
+ ret = brcmf_attach(devinfo->dev, devinfo->settings);
+ if (ret) {
+ brcmf_err("brcmf_attach failed\n");
+ return ret;
+ }
+
+ ret = brcmf_usb_up(devinfo->dev);
+ if (ret)
+ goto fail;
+
+ ret = brcmf_bus_started(devinfo->dev);
+ if (ret)
+ goto fail;
+
+ return 0;
+fail:
+ brcmf_detach(devinfo->dev);
+ return ret;
+}
+
+static void brcmf_usb_probe_phase2(struct device *dev, int ret,
+ const struct firmware *fw,
+ void *nvram, u32 nvlen)
+{
+ struct brcmf_bus *bus = dev_get_drvdata(dev);
+ struct brcmf_usbdev_info *devinfo = bus->bus_priv.usb->devinfo;
+
+ if (ret)
+ goto error;
+
+ brcmf_dbg(USB, "Start fw downloading\n");
+
+ ret = check_file(fw->data);
+ if (ret < 0) {
+ brcmf_err("invalid firmware\n");
+ release_firmware(fw);
+ goto error;
+ }
+
+ devinfo->image = fw->data;
+ devinfo->image_len = fw->size;
+
+ ret = brcmf_usb_fw_download(devinfo);
+ release_firmware(fw);
+ if (ret)
+ goto error;
+
+ ret = brcmf_usb_bus_setup(devinfo);
+ if (ret)
+ goto error;
+
+ mutex_unlock(&devinfo->dev_init_lock);
+ return;
+error:
+ brcmf_dbg(TRACE, "failed: dev=%s, err=%d\n", dev_name(dev), ret);
+ mutex_unlock(&devinfo->dev_init_lock);
+ device_release_driver(dev);
+}
+
+static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo)
+{
+ struct brcmf_bus *bus = NULL;
+ struct brcmf_usbdev *bus_pub = NULL;
+ struct device *dev = devinfo->dev;
+ int ret;
+
+ brcmf_dbg(USB, "Enter\n");
+ bus_pub = brcmf_usb_attach(devinfo, BRCMF_USB_NRXQ, BRCMF_USB_NTXQ);
+ if (!bus_pub)
+ return -ENODEV;
+
+ bus = kzalloc(sizeof(struct brcmf_bus), GFP_ATOMIC);
+ if (!bus) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ bus->dev = dev;
+ bus_pub->bus = bus;
+ bus->bus_priv.usb = bus_pub;
+ dev_set_drvdata(dev, bus);
+ bus->ops = &brcmf_usb_bus_ops;
+ bus->proto_type = BRCMF_PROTO_BCDC;
+ bus->always_use_fws_queue = true;
+#ifdef CONFIG_PM
+ bus->wowl_supported = true;
+#endif
+
+ devinfo->settings = brcmf_get_module_param(bus->dev, BRCMF_BUSTYPE_USB,
+ bus_pub->devid,
+ bus_pub->chiprev);
+ if (!devinfo->settings) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ if (!brcmf_usb_dlneeded(devinfo)) {
+ ret = brcmf_usb_bus_setup(devinfo);
+ if (ret)
+ goto fail;
+ /* we are done */
+ mutex_unlock(&devinfo->dev_init_lock);
+ return 0;
+ }
+ bus->chip = bus_pub->devid;
+ bus->chiprev = bus_pub->chiprev;
+
+ ret = brcmf_fw_map_chip_to_name(bus_pub->devid, bus_pub->chiprev,
+ brcmf_usb_fwnames,
+ ARRAY_SIZE(brcmf_usb_fwnames),
+ devinfo->fw_name, NULL);
+ if (ret)
+ goto fail;
+
+ /* request firmware here */
+ ret = brcmf_fw_get_firmwares(dev, 0, devinfo->fw_name, NULL,
+ brcmf_usb_probe_phase2);
+ if (ret) {
+ brcmf_err("firmware request failed: %d\n", ret);
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ /* Release resources in reverse order */
+ kfree(bus);
+ brcmf_usb_detach(devinfo);
+ return ret;
+}
+
+static void
+brcmf_usb_disconnect_cb(struct brcmf_usbdev_info *devinfo)
+{
+ if (!devinfo)
+ return;
+ brcmf_dbg(USB, "Enter, bus_pub %p\n", devinfo);
+
+ brcmf_detach(devinfo->dev);
+ kfree(devinfo->bus_pub.bus);
+ brcmf_usb_detach(devinfo);
+}
+
+static int
+brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+ struct usb_device *usb = interface_to_usbdev(intf);
+ struct brcmf_usbdev_info *devinfo;
+ struct usb_interface_descriptor *desc;
+ struct usb_endpoint_descriptor *endpoint;
+ int ret = 0;
+ u32 num_of_eps;
+ u8 endpoint_num, ep;
+
+ brcmf_dbg(USB, "Enter 0x%04x:0x%04x\n", id->idVendor, id->idProduct);
+
+ devinfo = kzalloc(sizeof(*devinfo), GFP_ATOMIC);
+ if (devinfo == NULL)
+ return -ENOMEM;
+
+ devinfo->usbdev = usb;
+ devinfo->dev = &usb->dev;
+ /* Take an init lock, to protect for disconnect while still loading.
+ * Necessary because of the asynchronous firmware load construction
+ */
+ mutex_init(&devinfo->dev_init_lock);
+ mutex_lock(&devinfo->dev_init_lock);
+
+ usb_set_intfdata(intf, devinfo);
+
+ /* Check that the device supports only one configuration */
+ if (usb->descriptor.bNumConfigurations != 1) {
+ brcmf_err("Number of configurations: %d not supported\n",
+ usb->descriptor.bNumConfigurations);
+ ret = -ENODEV;
+ goto fail;
+ }
+
+ if ((usb->descriptor.bDeviceClass != USB_CLASS_VENDOR_SPEC) &&
+ (usb->descriptor.bDeviceClass != USB_CLASS_MISC) &&
+ (usb->descriptor.bDeviceClass != USB_CLASS_WIRELESS_CONTROLLER)) {
+ brcmf_err("Device class: 0x%x not supported\n",
+ usb->descriptor.bDeviceClass);
+ ret = -ENODEV;
+ goto fail;
+ }
+
+ desc = &intf->altsetting[0].desc;
+ if ((desc->bInterfaceClass != USB_CLASS_VENDOR_SPEC) ||
+ (desc->bInterfaceSubClass != 2) ||
+ (desc->bInterfaceProtocol != 0xff)) {
+ brcmf_err("non WLAN interface %d: 0x%x:0x%x:0x%x\n",
+ desc->bInterfaceNumber, desc->bInterfaceClass,
+ desc->bInterfaceSubClass, desc->bInterfaceProtocol);
+ ret = -ENODEV;
+ goto fail;
+ }
+
+ num_of_eps = desc->bNumEndpoints;
+ for (ep = 0; ep < num_of_eps; ep++) {
+ endpoint = &intf->altsetting[0].endpoint[ep].desc;
+ endpoint_num = usb_endpoint_num(endpoint);
+ if (!usb_endpoint_xfer_bulk(endpoint))
+ continue;
+ if (usb_endpoint_dir_in(endpoint)) {
+ if (!devinfo->rx_pipe)
+ devinfo->rx_pipe =
+ usb_rcvbulkpipe(usb, endpoint_num);
+ } else {
+ if (!devinfo->tx_pipe)
+ devinfo->tx_pipe =
+ usb_sndbulkpipe(usb, endpoint_num);
+ }
+ }
+ if (devinfo->rx_pipe == 0) {
+ brcmf_err("No RX (in) Bulk EP found\n");
+ ret = -ENODEV;
+ goto fail;
+ }
+ if (devinfo->tx_pipe == 0) {
+ brcmf_err("No TX (out) Bulk EP found\n");
+ ret = -ENODEV;
+ goto fail;
+ }
+
+ devinfo->ifnum = desc->bInterfaceNumber;
+
+ if (usb->speed == USB_SPEED_SUPER_PLUS)
+ brcmf_dbg(USB, "Broadcom super speed plus USB WLAN interface detected\n");
+ else if (usb->speed == USB_SPEED_SUPER)
+ brcmf_dbg(USB, "Broadcom super speed USB WLAN interface detected\n");
+ else if (usb->speed == USB_SPEED_HIGH)
+ brcmf_dbg(USB, "Broadcom high speed USB WLAN interface detected\n");
+ else
+ brcmf_dbg(USB, "Broadcom full speed USB WLAN interface detected\n");
+
+ ret = brcmf_usb_probe_cb(devinfo);
+ if (ret)
+ goto fail;
+
+ /* Success */
+ return 0;
+
+fail:
+ mutex_unlock(&devinfo->dev_init_lock);
+ kfree(devinfo);
+ usb_set_intfdata(intf, NULL);
+ return ret;
+}
+
+static void
+brcmf_usb_disconnect(struct usb_interface *intf)
+{
+ struct brcmf_usbdev_info *devinfo;
+
+ brcmf_dbg(USB, "Enter\n");
+ devinfo = (struct brcmf_usbdev_info *)usb_get_intfdata(intf);
+
+ if (devinfo) {
+ mutex_lock(&devinfo->dev_init_lock);
+ /* Make sure that devinfo still exists. Firmware probe routines
+ * may have released the device and cleared the intfdata.
+ */
+ if (!usb_get_intfdata(intf))
+ goto done;
+
+ brcmf_usb_disconnect_cb(devinfo);
+ kfree(devinfo);
+ }
+done:
+ brcmf_dbg(USB, "Exit\n");
+}
+
+/*
+ * only need to signal the bus being down and update the state.
+ */
+static int brcmf_usb_suspend(struct usb_interface *intf, pm_message_t state)
+{
+ struct usb_device *usb = interface_to_usbdev(intf);
+ struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(&usb->dev);
+
+ brcmf_dbg(USB, "Enter\n");
+ devinfo->bus_pub.state = BRCMFMAC_USB_STATE_SLEEP;
+ if (devinfo->wowl_enabled)
+ brcmf_cancel_all_urbs(devinfo);
+ else
+ brcmf_detach(&usb->dev);
+ return 0;
+}
+
+/*
+ * (re-) start the bus.
+ */
+static int brcmf_usb_resume(struct usb_interface *intf)
+{
+ struct usb_device *usb = interface_to_usbdev(intf);
+ struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(&usb->dev);
+
+ brcmf_dbg(USB, "Enter\n");
+ if (!devinfo->wowl_enabled)
+ return brcmf_usb_bus_setup(devinfo);
+
+ devinfo->bus_pub.state = BRCMFMAC_USB_STATE_UP;
+ brcmf_usb_rx_fill_all(devinfo);
+ return 0;
+}
+
+static int brcmf_usb_reset_resume(struct usb_interface *intf)
+{
+ struct usb_device *usb = interface_to_usbdev(intf);
+ struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(&usb->dev);
+
+ brcmf_dbg(USB, "Enter\n");
+
+ return brcmf_fw_get_firmwares(&usb->dev, 0, devinfo->fw_name, NULL,
+ brcmf_usb_probe_phase2);
+}
+
+#define BRCMF_USB_DEVICE(dev_id) \
+ { USB_DEVICE(BRCM_USB_VENDOR_ID_BROADCOM, dev_id) }
+
+#define LINKSYS_USB_DEVICE(dev_id) \
+ { USB_DEVICE(BRCM_USB_VENDOR_ID_LINKSYS, dev_id) }
+
+#define CYPRESS_USB_DEVICE(dev_id) \
+ { USB_DEVICE(CY_USB_VENDOR_ID_CYPRESS, dev_id) }
+
+static struct usb_device_id brcmf_usb_devid_table[] = {
+ BRCMF_USB_DEVICE(BRCM_USB_43143_DEVICE_ID),
+ BRCMF_USB_DEVICE(BRCM_USB_43236_DEVICE_ID),
+ BRCMF_USB_DEVICE(BRCM_USB_43242_DEVICE_ID),
+ BRCMF_USB_DEVICE(BRCM_USB_43569_DEVICE_ID),
+ LINKSYS_USB_DEVICE(BRCM_USB_43235_LINKSYS_DEVICE_ID),
+ CYPRESS_USB_DEVICE(CY_USB_4373_DEVICE_ID),
+ { USB_DEVICE(BRCM_USB_VENDOR_ID_LG, BRCM_USB_43242_LG_DEVICE_ID) },
+ /* special entry for device with firmware loaded and running */
+ BRCMF_USB_DEVICE(BRCM_USB_BCMFW_DEVICE_ID),
+ CYPRESS_USB_DEVICE(BRCM_USB_BCMFW_DEVICE_ID),
+ { /* end: all zeroes */ }
+};
+
+MODULE_DEVICE_TABLE(usb, brcmf_usb_devid_table);
+
+static struct usb_driver brcmf_usbdrvr = {
+ .name = KBUILD_MODNAME,
+ .probe = brcmf_usb_probe,
+ .disconnect = brcmf_usb_disconnect,
+ .id_table = brcmf_usb_devid_table,
+ .suspend = brcmf_usb_suspend,
+ .resume = brcmf_usb_resume,
+ .reset_resume = brcmf_usb_reset_resume,
+#if LINUX_VERSION_IS_GEQ(3,5,0)
+ .disable_hub_initiated_lpm = 1,
+#endif
+};
+
+static int brcmf_usb_reset_device(struct device *dev, void *notused)
+{
+ /* device past is the usb interface so we
+ * need to use parent here.
+ */
+ brcmf_dev_reset(dev->parent);
+ return 0;
+}
+
+void brcmf_usb_exit(void)
+{
+ struct device_driver *drv = &brcmf_usbdrvr.drvwrap.driver;
+ int ret;
+
+ brcmf_dbg(USB, "Enter\n");
+ ret = driver_for_each_device(drv, NULL, NULL,
+ brcmf_usb_reset_device);
+ usb_deregister(&brcmf_usbdrvr);
+}
+
+void brcmf_usb_register(void)
+{
+ brcmf_dbg(USB, "Enter\n");
+ usb_register(&brcmf_usbdrvr);
+}
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.h
new file mode 100644
index 0000000..f483a8c
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2011 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#ifndef BRCMFMAC_USB_H
+#define BRCMFMAC_USB_H
+
+enum brcmf_usb_state {
+ BRCMFMAC_USB_STATE_DOWN,
+ BRCMFMAC_USB_STATE_DL_FAIL,
+ BRCMFMAC_USB_STATE_DL_DONE,
+ BRCMFMAC_USB_STATE_UP,
+ BRCMFMAC_USB_STATE_SLEEP
+};
+
+struct brcmf_stats {
+ u32 tx_ctlpkts;
+ u32 tx_ctlerrs;
+ u32 rx_ctlpkts;
+ u32 rx_ctlerrs;
+};
+
+struct brcmf_usbdev {
+ struct brcmf_bus *bus;
+ struct brcmf_usbdev_info *devinfo;
+ enum brcmf_usb_state state;
+ struct brcmf_stats stats;
+ int ntxq, nrxq, rxsize;
+ u32 bus_mtu;
+ int devid;
+ int chiprev; /* chip revsion number */
+};
+
+/* IO Request Block (IRB) */
+struct brcmf_usbreq {
+ struct list_head list;
+ struct brcmf_usbdev_info *devinfo;
+ struct urb *urb;
+ struct sk_buff *skb;
+};
+
+#endif /* BRCMFMAC_USB_H */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.c
new file mode 100644
index 0000000..2b88ba1
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.c
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2014 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/vmalloc.h>
+#include <net/cfg80211.h>
+#include <net/netlink.h>
+
+#include <brcmu_wifi.h>
+#include "fwil_types.h"
+#include "core.h"
+#include "p2p.h"
+#include "debug.h"
+#include "cfg80211.h"
+#include "vendor.h"
+#include "fwil.h"
+
+static int brcmf_cfg80211_vndr_cmds_dcmd_handler(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ const void *data, int len)
+{
+ struct brcmf_cfg80211_vif *vif;
+ struct brcmf_if *ifp;
+ const struct brcmf_vndr_dcmd_hdr *cmdhdr = data;
+ struct sk_buff *reply;
+ int ret, payload, ret_len;
+ void *dcmd_buf = NULL, *wr_pointer;
+ u16 msglen, maxmsglen = PAGE_SIZE - 0x100;
+
+ if (len < sizeof(*cmdhdr)) {
+ brcmf_err("vendor command too short: %d\n", len);
+ return -EINVAL;
+ }
+
+ vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
+ ifp = vif->ifp;
+
+ brcmf_dbg(TRACE, "ifidx=%d, cmd=%d\n", ifp->ifidx, cmdhdr->cmd);
+
+ if (cmdhdr->offset > len) {
+ brcmf_err("bad buffer offset %d > %d\n", cmdhdr->offset, len);
+ return -EINVAL;
+ }
+
+ len -= cmdhdr->offset;
+ ret_len = cmdhdr->len;
+ if (ret_len > 0 || len > 0) {
+ if (len > BRCMF_DCMD_MAXLEN) {
+ brcmf_err("oversize input buffer %d\n", len);
+ len = BRCMF_DCMD_MAXLEN;
+ }
+ if (ret_len > BRCMF_DCMD_MAXLEN) {
+ brcmf_err("oversize return buffer %d\n", ret_len);
+ ret_len = BRCMF_DCMD_MAXLEN;
+ }
+ payload = max(ret_len, len) + 1;
+ dcmd_buf = vzalloc(payload);
+ if (NULL == dcmd_buf)
+ return -ENOMEM;
+
+ memcpy(dcmd_buf, (void *)cmdhdr + cmdhdr->offset, len);
+ *(char *)(dcmd_buf + len) = '\0';
+ }
+
+ if (cmdhdr->set)
+ ret = brcmf_fil_cmd_data_set(ifp, cmdhdr->cmd, dcmd_buf,
+ ret_len);
+ else
+ ret = brcmf_fil_cmd_data_get(ifp, cmdhdr->cmd, dcmd_buf,
+ ret_len);
+
+ if (ret != 0) {
+ brcmf_dbg(INFO, "error(%d), return -EPERM\n", ret);
+ ret = -EPERM;
+ goto exit;
+ }
+
+ wr_pointer = dcmd_buf;
+ while (ret_len > 0) {
+ msglen = ret_len > maxmsglen ? maxmsglen : ret_len;
+ ret_len -= msglen;
+ payload = msglen + sizeof(msglen);
+ reply = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, payload);
+ if (NULL == reply) {
+ ret = -ENOMEM;
+ break;
+ }
+
+ if (nla_put(reply, BRCMF_NLATTR_DATA, msglen, wr_pointer) ||
+ nla_put_u16(reply, BRCMF_NLATTR_LEN, msglen)) {
+ kfree_skb(reply);
+ ret = -ENOBUFS;
+ break;
+ }
+
+ ret = cfg80211_vendor_cmd_reply(reply);
+ if (ret)
+ break;
+
+ wr_pointer += msglen;
+ }
+
+exit:
+ vfree(dcmd_buf);
+
+ return ret;
+}
+
+const struct wiphy_vendor_command brcmf_vendor_cmds[] = {
+ {
+ {
+ .vendor_id = BROADCOM_OUI,
+ .subcmd = BRCMF_VNDR_CMDS_DCMD
+ },
+ .flags = WIPHY_VENDOR_CMD_NEED_WDEV |
+ WIPHY_VENDOR_CMD_NEED_NETDEV,
+ .doit = brcmf_cfg80211_vndr_cmds_dcmd_handler
+ },
+};
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.h
new file mode 100644
index 0000000..061b7bf
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2014 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _vendor_h_
+#define _vendor_h_
+
+#define BROADCOM_OUI 0x001018
+
+enum brcmf_vndr_cmds {
+ BRCMF_VNDR_CMDS_UNSPEC,
+ BRCMF_VNDR_CMDS_DCMD,
+ BRCMF_VNDR_CMDS_LAST
+};
+
+/**
+ * enum brcmf_nlattrs - nl80211 message attributes
+ *
+ * @BRCMF_NLATTR_LEN: message body length
+ * @BRCMF_NLATTR_DATA: message body
+ */
+enum brcmf_nlattrs {
+ BRCMF_NLATTR_UNSPEC,
+
+ BRCMF_NLATTR_LEN,
+ BRCMF_NLATTR_DATA,
+
+ __BRCMF_NLATTR_AFTER_LAST,
+ BRCMF_NLATTR_MAX = __BRCMF_NLATTR_AFTER_LAST - 1
+};
+
+/**
+ * struct brcmf_vndr_dcmd_hdr - message header for cfg80211 vendor command dcmd
+ * support
+ *
+ * @cmd: common dongle cmd definition
+ * @len: length of expecting return buffer
+ * @offset: offset of data buffer
+ * @set: get or set request(optional)
+ * @magic: magic number for verification
+ */
+struct brcmf_vndr_dcmd_hdr {
+ uint cmd;
+ int len;
+ uint offset;
+ uint set;
+ uint magic;
+};
+
+extern const struct wiphy_vendor_command brcmf_vendor_cmds[];
+
+#endif /* _vendor_h_ */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmutil/Makefile b/drivers/net/wireless/broadcom/brcm80211/brcmutil/Makefile
new file mode 100644
index 0000000..48cf82a
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmutil/Makefile
@@ -0,0 +1,23 @@
+#
+# Makefile fragment for Broadcom 802.11n Networking Device Driver Utilities
+#
+# Copyright (c) 2011 Broadcom Corporation
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+# SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+# OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+ccflags-y := \
+ -I$(backport_srctree)/drivers/net/wireless/broadcom/brcm80211/brcmutil \
+ -I$(backport_srctree)/drivers/net/wireless/broadcom/brcm80211/include
+
+obj-$(CPTCFG_BRCMUTIL) += brcmutil.o
+brcmutil-objs = utils.o d11.o
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmutil/d11.c b/drivers/net/wireless/broadcom/brcm80211/brcmutil/d11.c
new file mode 100644
index 0000000..d8b79cb
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmutil/d11.c
@@ -0,0 +1,223 @@
+/*
+ * Copyright (c) 2013 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/*********************channel spec common functions*********************/
+
+#include <linux/module.h>
+
+#include <brcmu_utils.h>
+#include <brcmu_wifi.h>
+#include <brcmu_d11.h>
+
+static u16 d11n_sb(enum brcmu_chan_sb sb)
+{
+ switch (sb) {
+ case BRCMU_CHAN_SB_NONE:
+ return BRCMU_CHSPEC_D11N_SB_N;
+ case BRCMU_CHAN_SB_L:
+ return BRCMU_CHSPEC_D11N_SB_L;
+ case BRCMU_CHAN_SB_U:
+ return BRCMU_CHSPEC_D11N_SB_U;
+ default:
+ WARN_ON(1);
+ }
+ return 0;
+}
+
+static u16 d11n_bw(enum brcmu_chan_bw bw)
+{
+ switch (bw) {
+ case BRCMU_CHAN_BW_20:
+ return BRCMU_CHSPEC_D11N_BW_20;
+ case BRCMU_CHAN_BW_40:
+ return BRCMU_CHSPEC_D11N_BW_40;
+ default:
+ WARN_ON(1);
+ }
+ return 0;
+}
+
+static void brcmu_d11n_encchspec(struct brcmu_chan *ch)
+{
+ if (ch->bw == BRCMU_CHAN_BW_20)
+ ch->sb = BRCMU_CHAN_SB_NONE;
+
+ ch->chspec = 0;
+ brcmu_maskset16(&ch->chspec, BRCMU_CHSPEC_CH_MASK,
+ BRCMU_CHSPEC_CH_SHIFT, ch->chnum);
+ brcmu_maskset16(&ch->chspec, BRCMU_CHSPEC_D11N_SB_MASK,
+ 0, d11n_sb(ch->sb));
+ brcmu_maskset16(&ch->chspec, BRCMU_CHSPEC_D11N_BW_MASK,
+ 0, d11n_bw(ch->bw));
+
+ if (ch->chnum <= CH_MAX_2G_CHANNEL)
+ ch->chspec |= BRCMU_CHSPEC_D11N_BND_2G;
+ else
+ ch->chspec |= BRCMU_CHSPEC_D11N_BND_5G;
+}
+
+static u16 d11ac_bw(enum brcmu_chan_bw bw)
+{
+ switch (bw) {
+ case BRCMU_CHAN_BW_20:
+ return BRCMU_CHSPEC_D11AC_BW_20;
+ case BRCMU_CHAN_BW_40:
+ return BRCMU_CHSPEC_D11AC_BW_40;
+ case BRCMU_CHAN_BW_80:
+ return BRCMU_CHSPEC_D11AC_BW_80;
+ default:
+ WARN_ON(1);
+ }
+ return 0;
+}
+
+static void brcmu_d11ac_encchspec(struct brcmu_chan *ch)
+{
+ if (ch->bw == BRCMU_CHAN_BW_20 || ch->sb == BRCMU_CHAN_SB_NONE)
+ ch->sb = BRCMU_CHAN_SB_L;
+
+ brcmu_maskset16(&ch->chspec, BRCMU_CHSPEC_CH_MASK,
+ BRCMU_CHSPEC_CH_SHIFT, ch->chnum);
+ brcmu_maskset16(&ch->chspec, BRCMU_CHSPEC_D11AC_SB_MASK,
+ BRCMU_CHSPEC_D11AC_SB_SHIFT, ch->sb);
+ brcmu_maskset16(&ch->chspec, BRCMU_CHSPEC_D11AC_BW_MASK,
+ 0, d11ac_bw(ch->bw));
+
+ ch->chspec &= ~BRCMU_CHSPEC_D11AC_BND_MASK;
+ if (ch->chnum <= CH_MAX_2G_CHANNEL)
+ ch->chspec |= BRCMU_CHSPEC_D11AC_BND_2G;
+ else
+ ch->chspec |= BRCMU_CHSPEC_D11AC_BND_5G;
+}
+
+static void brcmu_d11n_decchspec(struct brcmu_chan *ch)
+{
+ u16 val;
+
+ ch->chnum = (u8)(ch->chspec & BRCMU_CHSPEC_CH_MASK);
+ ch->control_ch_num = ch->chnum;
+
+ switch (ch->chspec & BRCMU_CHSPEC_D11N_BW_MASK) {
+ case BRCMU_CHSPEC_D11N_BW_20:
+ ch->bw = BRCMU_CHAN_BW_20;
+ ch->sb = BRCMU_CHAN_SB_NONE;
+ break;
+ case BRCMU_CHSPEC_D11N_BW_40:
+ ch->bw = BRCMU_CHAN_BW_40;
+ val = ch->chspec & BRCMU_CHSPEC_D11N_SB_MASK;
+ if (val == BRCMU_CHSPEC_D11N_SB_L) {
+ ch->sb = BRCMU_CHAN_SB_L;
+ ch->control_ch_num -= CH_10MHZ_APART;
+ } else {
+ ch->sb = BRCMU_CHAN_SB_U;
+ ch->control_ch_num += CH_10MHZ_APART;
+ }
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ break;
+ }
+
+ switch (ch->chspec & BRCMU_CHSPEC_D11N_BND_MASK) {
+ case BRCMU_CHSPEC_D11N_BND_5G:
+ ch->band = BRCMU_CHAN_BAND_5G;
+ break;
+ case BRCMU_CHSPEC_D11N_BND_2G:
+ ch->band = BRCMU_CHAN_BAND_2G;
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ break;
+ }
+}
+
+static void brcmu_d11ac_decchspec(struct brcmu_chan *ch)
+{
+ u16 val;
+
+ ch->chnum = (u8)(ch->chspec & BRCMU_CHSPEC_CH_MASK);
+ ch->control_ch_num = ch->chnum;
+
+ switch (ch->chspec & BRCMU_CHSPEC_D11AC_BW_MASK) {
+ case BRCMU_CHSPEC_D11AC_BW_20:
+ ch->bw = BRCMU_CHAN_BW_20;
+ ch->sb = BRCMU_CHAN_SB_NONE;
+ break;
+ case BRCMU_CHSPEC_D11AC_BW_40:
+ ch->bw = BRCMU_CHAN_BW_40;
+ val = ch->chspec & BRCMU_CHSPEC_D11AC_SB_MASK;
+ if (val == BRCMU_CHSPEC_D11AC_SB_L) {
+ ch->sb = BRCMU_CHAN_SB_L;
+ ch->control_ch_num -= CH_10MHZ_APART;
+ } else if (val == BRCMU_CHSPEC_D11AC_SB_U) {
+ ch->sb = BRCMU_CHAN_SB_U;
+ ch->control_ch_num += CH_10MHZ_APART;
+ } else {
+ WARN_ON_ONCE(1);
+ }
+ break;
+ case BRCMU_CHSPEC_D11AC_BW_80:
+ ch->bw = BRCMU_CHAN_BW_80;
+ ch->sb = brcmu_maskget16(ch->chspec, BRCMU_CHSPEC_D11AC_SB_MASK,
+ BRCMU_CHSPEC_D11AC_SB_SHIFT);
+ switch (ch->sb) {
+ case BRCMU_CHAN_SB_LL:
+ ch->control_ch_num -= CH_30MHZ_APART;
+ break;
+ case BRCMU_CHAN_SB_LU:
+ ch->control_ch_num -= CH_10MHZ_APART;
+ break;
+ case BRCMU_CHAN_SB_UL:
+ ch->control_ch_num += CH_10MHZ_APART;
+ break;
+ case BRCMU_CHAN_SB_UU:
+ ch->control_ch_num += CH_30MHZ_APART;
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ break;
+ }
+ break;
+ case BRCMU_CHSPEC_D11AC_BW_8080:
+ case BRCMU_CHSPEC_D11AC_BW_160:
+ default:
+ WARN_ON_ONCE(1);
+ break;
+ }
+
+ switch (ch->chspec & BRCMU_CHSPEC_D11AC_BND_MASK) {
+ case BRCMU_CHSPEC_D11AC_BND_5G:
+ ch->band = BRCMU_CHAN_BAND_5G;
+ break;
+ case BRCMU_CHSPEC_D11AC_BND_2G:
+ ch->band = BRCMU_CHAN_BAND_2G;
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ break;
+ }
+}
+
+void brcmu_d11_attach(struct brcmu_d11inf *d11inf)
+{
+ if (d11inf->io_type == BRCMU_D11N_IOTYPE) {
+ d11inf->encchspec = brcmu_d11n_encchspec;
+ d11inf->decchspec = brcmu_d11n_decchspec;
+ } else {
+ d11inf->encchspec = brcmu_d11ac_encchspec;
+ d11inf->decchspec = brcmu_d11ac_decchspec;
+ }
+}
+EXPORT_SYMBOL(brcmu_d11_attach);
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmutil/utils.c b/drivers/net/wireless/broadcom/brcm80211/brcmutil/utils.c
new file mode 100644
index 0000000..0543607
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmutil/utils.c
@@ -0,0 +1,339 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/netdevice.h>
+#include <linux/module.h>
+
+#include <brcmu_utils.h>
+
+MODULE_AUTHOR("Broadcom Corporation");
+MODULE_DESCRIPTION("Broadcom 802.11n wireless LAN driver utilities.");
+MODULE_SUPPORTED_DEVICE("Broadcom 802.11n WLAN cards");
+MODULE_LICENSE("Dual BSD/GPL");
+
+struct sk_buff *brcmu_pkt_buf_get_skb(uint len)
+{
+ struct sk_buff *skb;
+
+ skb = dev_alloc_skb(len);
+ if (skb) {
+ skb_put(skb, len);
+ skb->priority = 0;
+ }
+
+ return skb;
+}
+EXPORT_SYMBOL(brcmu_pkt_buf_get_skb);
+
+/* Free the driver packet. Free the tag if present */
+void brcmu_pkt_buf_free_skb(struct sk_buff *skb)
+{
+ if (!skb)
+ return;
+
+ WARN_ON(skb->next);
+ dev_kfree_skb_any(skb);
+}
+EXPORT_SYMBOL(brcmu_pkt_buf_free_skb);
+
+/*
+ * osl multiple-precedence packet queue
+ * hi_prec is always >= the number of the highest non-empty precedence
+ */
+struct sk_buff *brcmu_pktq_penq(struct pktq *pq, int prec,
+ struct sk_buff *p)
+{
+ struct sk_buff_head *q;
+
+ if (pktq_full(pq) || pktq_pfull(pq, prec))
+ return NULL;
+
+ q = &pq->q[prec].skblist;
+ skb_queue_tail(q, p);
+ pq->len++;
+
+ if (pq->hi_prec < prec)
+ pq->hi_prec = (u8) prec;
+
+ return p;
+}
+EXPORT_SYMBOL(brcmu_pktq_penq);
+
+struct sk_buff *brcmu_pktq_penq_head(struct pktq *pq, int prec,
+ struct sk_buff *p)
+{
+ struct sk_buff_head *q;
+
+ if (pktq_full(pq) || pktq_pfull(pq, prec))
+ return NULL;
+
+ q = &pq->q[prec].skblist;
+ skb_queue_head(q, p);
+ pq->len++;
+
+ if (pq->hi_prec < prec)
+ pq->hi_prec = (u8) prec;
+
+ return p;
+}
+EXPORT_SYMBOL(brcmu_pktq_penq_head);
+
+struct sk_buff *brcmu_pktq_pdeq(struct pktq *pq, int prec)
+{
+ struct sk_buff_head *q;
+ struct sk_buff *p;
+
+ q = &pq->q[prec].skblist;
+ p = skb_dequeue(q);
+ if (p == NULL)
+ return NULL;
+
+ pq->len--;
+ return p;
+}
+EXPORT_SYMBOL(brcmu_pktq_pdeq);
+
+/*
+ * precedence based dequeue with match function. Passing a NULL pointer
+ * for the match function parameter is considered to be a wildcard so
+ * any packet on the queue is returned. In that case it is no different
+ * from brcmu_pktq_pdeq() above.
+ */
+struct sk_buff *brcmu_pktq_pdeq_match(struct pktq *pq, int prec,
+ bool (*match_fn)(struct sk_buff *skb,
+ void *arg), void *arg)
+{
+ struct sk_buff_head *q;
+ struct sk_buff *p, *next;
+
+ q = &pq->q[prec].skblist;
+ skb_queue_walk_safe(q, p, next) {
+ if (match_fn == NULL || match_fn(p, arg)) {
+ skb_unlink(p, q);
+ pq->len--;
+ return p;
+ }
+ }
+ return NULL;
+}
+EXPORT_SYMBOL(brcmu_pktq_pdeq_match);
+
+struct sk_buff *brcmu_pktq_pdeq_tail(struct pktq *pq, int prec)
+{
+ struct sk_buff_head *q;
+ struct sk_buff *p;
+
+ q = &pq->q[prec].skblist;
+ p = skb_dequeue_tail(q);
+ if (p == NULL)
+ return NULL;
+
+ pq->len--;
+ return p;
+}
+EXPORT_SYMBOL(brcmu_pktq_pdeq_tail);
+
+void
+brcmu_pktq_pflush(struct pktq *pq, int prec, bool dir,
+ bool (*fn)(struct sk_buff *, void *), void *arg)
+{
+ struct sk_buff_head *q;
+ struct sk_buff *p, *next;
+
+ q = &pq->q[prec].skblist;
+ skb_queue_walk_safe(q, p, next) {
+ if (fn == NULL || (*fn) (p, arg)) {
+ skb_unlink(p, q);
+ brcmu_pkt_buf_free_skb(p);
+ pq->len--;
+ }
+ }
+}
+EXPORT_SYMBOL(brcmu_pktq_pflush);
+
+void brcmu_pktq_flush(struct pktq *pq, bool dir,
+ bool (*fn)(struct sk_buff *, void *), void *arg)
+{
+ int prec;
+ for (prec = 0; prec < pq->num_prec; prec++)
+ brcmu_pktq_pflush(pq, prec, dir, fn, arg);
+}
+EXPORT_SYMBOL(brcmu_pktq_flush);
+
+void brcmu_pktq_init(struct pktq *pq, int num_prec, int max_len)
+{
+ int prec;
+
+ /* pq is variable size; only zero out what's requested */
+ memset(pq, 0,
+ offsetof(struct pktq, q) + (sizeof(struct pktq_prec) * num_prec));
+
+ pq->num_prec = (u16) num_prec;
+
+ pq->max = (u16) max_len;
+
+ for (prec = 0; prec < num_prec; prec++) {
+ pq->q[prec].max = pq->max;
+ skb_queue_head_init(&pq->q[prec].skblist);
+ }
+}
+EXPORT_SYMBOL(brcmu_pktq_init);
+
+struct sk_buff *brcmu_pktq_peek_tail(struct pktq *pq, int *prec_out)
+{
+ int prec;
+
+ if (pq->len == 0)
+ return NULL;
+
+ for (prec = 0; prec < pq->hi_prec; prec++)
+ if (!skb_queue_empty(&pq->q[prec].skblist))
+ break;
+
+ if (prec_out)
+ *prec_out = prec;
+
+ return skb_peek_tail(&pq->q[prec].skblist);
+}
+EXPORT_SYMBOL(brcmu_pktq_peek_tail);
+
+/* Return sum of lengths of a specific set of precedences */
+int brcmu_pktq_mlen(struct pktq *pq, uint prec_bmp)
+{
+ int prec, len;
+
+ len = 0;
+
+ for (prec = 0; prec <= pq->hi_prec; prec++)
+ if (prec_bmp & (1 << prec))
+ len += pq->q[prec].skblist.qlen;
+
+ return len;
+}
+EXPORT_SYMBOL(brcmu_pktq_mlen);
+
+/* Priority dequeue from a specific set of precedences */
+struct sk_buff *brcmu_pktq_mdeq(struct pktq *pq, uint prec_bmp,
+ int *prec_out)
+{
+ struct sk_buff_head *q;
+ struct sk_buff *p;
+ int prec;
+
+ if (pq->len == 0)
+ return NULL;
+
+ while ((prec = pq->hi_prec) > 0 &&
+ skb_queue_empty(&pq->q[prec].skblist))
+ pq->hi_prec--;
+
+ while ((prec_bmp & (1 << prec)) == 0 ||
+ skb_queue_empty(&pq->q[prec].skblist))
+ if (prec-- == 0)
+ return NULL;
+
+ q = &pq->q[prec].skblist;
+ p = skb_dequeue(q);
+ if (p == NULL)
+ return NULL;
+
+ pq->len--;
+
+ if (prec_out)
+ *prec_out = prec;
+
+ return p;
+}
+EXPORT_SYMBOL(brcmu_pktq_mdeq);
+
+/* Produce a human-readable string for boardrev */
+char *brcmu_boardrev_str(u32 brev, char *buf)
+{
+ char c;
+
+ if (brev < 0x100) {
+ snprintf(buf, BRCMU_BOARDREV_LEN, "%d.%d",
+ (brev & 0xf0) >> 4, brev & 0xf);
+ } else {
+ c = (brev & 0xf000) == 0x1000 ? 'P' : 'A';
+ snprintf(buf, BRCMU_BOARDREV_LEN, "%c%03x", c, brev & 0xfff);
+ }
+ return buf;
+}
+EXPORT_SYMBOL(brcmu_boardrev_str);
+
+char *brcmu_dotrev_str(u32 dotrev, char *buf)
+{
+ u8 dotval[4];
+
+ if (!dotrev) {
+ snprintf(buf, BRCMU_DOTREV_LEN, "unknown");
+ return buf;
+ }
+ dotval[0] = (dotrev >> 24) & 0xFF;
+ dotval[1] = (dotrev >> 16) & 0xFF;
+ dotval[2] = (dotrev >> 8) & 0xFF;
+ dotval[3] = dotrev & 0xFF;
+
+ if (dotval[3])
+ snprintf(buf, BRCMU_DOTREV_LEN, "%d.%d.%d.%d", dotval[0],
+ dotval[1], dotval[2], dotval[3]);
+ else if (dotval[2])
+ snprintf(buf, BRCMU_DOTREV_LEN, "%d.%d.%d", dotval[0],
+ dotval[1], dotval[2]);
+ else
+ snprintf(buf, BRCMU_DOTREV_LEN, "%d.%d", dotval[0],
+ dotval[1]);
+
+ return buf;
+}
+EXPORT_SYMBOL(brcmu_dotrev_str);
+
+#if defined(DEBUG)
+/* pretty hex print a pkt buffer chain */
+void brcmu_prpkt(const char *msg, struct sk_buff *p0)
+{
+ struct sk_buff *p;
+
+ if (msg && (msg[0] != '\0'))
+ pr_debug("%s:\n", msg);
+
+ for (p = p0; p; p = p->next)
+ print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, p->data, p->len);
+}
+EXPORT_SYMBOL(brcmu_prpkt);
+
+void brcmu_dbg_hex_dump(const void *data, size_t size, const char *fmt, ...)
+{
+ struct va_format vaf;
+ va_list args;
+
+ va_start(args, fmt);
+
+ vaf.fmt = fmt;
+ vaf.va = &args;
+
+ pr_debug("%pV", &vaf);
+
+ va_end(args);
+
+ print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, data, size);
+}
+EXPORT_SYMBOL(brcmu_dbg_hex_dump);
+
+#endif /* defined(DEBUG) */
diff --git a/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h b/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h
new file mode 100644
index 0000000..37b0acd
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _BRCM_HW_IDS_H_
+#define _BRCM_HW_IDS_H_
+
+#include <linux/pci_ids.h>
+#include <linux/mmc/sdio_ids.h>
+
+#define BRCM_USB_VENDOR_ID_BROADCOM 0x0a5c
+#define BRCM_USB_VENDOR_ID_LG 0x043e
+#define BRCM_USB_VENDOR_ID_LINKSYS 0x13b1
+#define CY_USB_VENDOR_ID_CYPRESS 0x04b4
+#define BRCM_PCIE_VENDOR_ID_BROADCOM PCI_VENDOR_ID_BROADCOM
+
+/* Chipcommon Core Chip IDs */
+#define BRCM_CC_43143_CHIP_ID 43143
+#define BRCM_CC_43235_CHIP_ID 43235
+#define BRCM_CC_43236_CHIP_ID 43236
+#define BRCM_CC_43238_CHIP_ID 43238
+#define BRCM_CC_43241_CHIP_ID 0x4324
+#define BRCM_CC_43242_CHIP_ID 43242
+#define BRCM_CC_4329_CHIP_ID 0x4329
+#define BRCM_CC_4330_CHIP_ID 0x4330
+#define BRCM_CC_4334_CHIP_ID 0x4334
+#define BRCM_CC_43340_CHIP_ID 43340
+#define BRCM_CC_43341_CHIP_ID 43341
+#define BRCM_CC_43362_CHIP_ID 43362
+#define BRCM_CC_4335_CHIP_ID 0x4335
+#define BRCM_CC_4339_CHIP_ID 0x4339
+#define BRCM_CC_43430_CHIP_ID 43430
+#define BRCM_CC_4345_CHIP_ID 0x4345
+#define BRCM_CC_43465_CHIP_ID 43465
+#define BRCM_CC_4350_CHIP_ID 0x4350
+#define BRCM_CC_43525_CHIP_ID 43525
+#define BRCM_CC_4354_CHIP_ID 0x4354
+#define BRCM_CC_4356_CHIP_ID 0x4356
+#define BRCM_CC_43566_CHIP_ID 43566
+#define BRCM_CC_43567_CHIP_ID 43567
+#define BRCM_CC_43569_CHIP_ID 43569
+#define BRCM_CC_43570_CHIP_ID 43570
+#define BRCM_CC_4358_CHIP_ID 0x4358
+#define BRCM_CC_4359_CHIP_ID 0x4359
+#define BRCM_CC_43602_CHIP_ID 43602
+#define BRCM_CC_4365_CHIP_ID 0x4365
+#define BRCM_CC_4366_CHIP_ID 0x4366
+#define BRCM_CC_4371_CHIP_ID 0x4371
+#define CY_CC_4373_CHIP_ID 0x4373
+#define CY_CC_43012_CHIP_ID 43012
+
+/* USB Device IDs */
+#define BRCM_USB_43143_DEVICE_ID 0xbd1e
+#define BRCM_USB_43235_LINKSYS_DEVICE_ID 0x0039
+#define BRCM_USB_43236_DEVICE_ID 0xbd17
+#define BRCM_USB_43242_DEVICE_ID 0xbd1f
+#define BRCM_USB_43242_LG_DEVICE_ID 0x3101
+#define BRCM_USB_43569_DEVICE_ID 0xbd27
+#define BRCM_USB_BCMFW_DEVICE_ID 0x0bdc
+#define CY_USB_4373_DEVICE_ID 0xbd29
+
+/* PCIE Device IDs */
+#define BRCM_PCIE_4350_DEVICE_ID 0x43a3
+#define BRCM_PCIE_4354_DEVICE_ID 0x43df
+#define BRCM_PCIE_4356_DEVICE_ID 0x43ec
+#define BRCM_PCIE_43567_DEVICE_ID 0x43d3
+#define BRCM_PCIE_43570_DEVICE_ID 0x43d9
+#define BRCM_PCIE_4358_DEVICE_ID 0x43e9
+#define BRCM_PCIE_4359_DEVICE_ID 0x43ef
+#define BRCM_PCIE_43602_DEVICE_ID 0x43ba
+#define BRCM_PCIE_43602_2G_DEVICE_ID 0x43bb
+#define BRCM_PCIE_43602_5G_DEVICE_ID 0x43bc
+#define BRCM_PCIE_43602_RAW_DEVICE_ID 43602
+#define BRCM_PCIE_4365_DEVICE_ID 0x43ca
+#define BRCM_PCIE_4365_2G_DEVICE_ID 0x43cb
+#define BRCM_PCIE_4365_5G_DEVICE_ID 0x43cc
+#define BRCM_PCIE_4366_DEVICE_ID 0x43c3
+#define BRCM_PCIE_4366_2G_DEVICE_ID 0x43c4
+#define BRCM_PCIE_4366_5G_DEVICE_ID 0x43c5
+#define BRCM_PCIE_4371_DEVICE_ID 0x440d
+
+
+/* brcmsmac IDs */
+#define BCM4313_D11N2G_ID 0x4727 /* 4313 802.11n 2.4G device */
+#define BCM43224_D11N_ID 0x4353 /* 43224 802.11n dualband device */
+#define BCM43224_D11N_ID_VEN1 0x0576 /* Vendor specific 43224 802.11n db */
+#define BCM43225_D11N2G_ID 0x4357 /* 43225 802.11n 2.4GHz device */
+#define BCM43236_D11N_ID 0x4346 /* 43236 802.11n dualband device */
+#define BCM43236_D11N2G_ID 0x4347 /* 43236 802.11n 2.4GHz device */
+
+#define BCM4313_CHIP_ID 0x4313
+#define BCM43224_CHIP_ID 43224
+
+#endif /* _BRCM_HW_IDS_H_ */
diff --git a/drivers/net/wireless/broadcom/brcm80211/include/brcmu_d11.h b/drivers/net/wireless/broadcom/brcm80211/include/brcmu_d11.h
new file mode 100644
index 0000000..8b8b2ec
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/include/brcmu_d11.h
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _BRCMU_D11_H_
+#define _BRCMU_D11_H_
+
+/* d11 io type */
+#define BRCMU_D11N_IOTYPE 1
+#define BRCMU_D11AC_IOTYPE 2
+
+/* A chanspec (channel specification) holds the channel number, band,
+ * bandwidth and control sideband
+ */
+
+/* chanspec binary format */
+
+#define BRCMU_CHSPEC_INVALID 255
+/* bit 0~7 channel number
+ * for 80+80 channels: bit 0~3 low channel id, bit 4~7 high channel id
+ */
+#define BRCMU_CHSPEC_CH_MASK 0x00ff
+#define BRCMU_CHSPEC_CH_SHIFT 0
+#define BRCMU_CHSPEC_CHL_MASK 0x000f
+#define BRCMU_CHSPEC_CHL_SHIFT 0
+#define BRCMU_CHSPEC_CHH_MASK 0x00f0
+#define BRCMU_CHSPEC_CHH_SHIFT 4
+
+/* bit 8~16 for dot 11n IO types
+ * bit 8~9 sideband
+ * bit 10~11 bandwidth
+ * bit 12~13 spectral band
+ * bit 14~15 not used
+ */
+#define BRCMU_CHSPEC_D11N_SB_MASK 0x0300
+#define BRCMU_CHSPEC_D11N_SB_SHIFT 8
+#define BRCMU_CHSPEC_D11N_SB_L 0x0100 /* control lower */
+#define BRCMU_CHSPEC_D11N_SB_U 0x0200 /* control upper */
+#define BRCMU_CHSPEC_D11N_SB_N 0x0300 /* none */
+#define BRCMU_CHSPEC_D11N_BW_MASK 0x0c00
+#define BRCMU_CHSPEC_D11N_BW_SHIFT 10
+#define BRCMU_CHSPEC_D11N_BW_10 0x0400
+#define BRCMU_CHSPEC_D11N_BW_20 0x0800
+#define BRCMU_CHSPEC_D11N_BW_40 0x0c00
+#define BRCMU_CHSPEC_D11N_BND_MASK 0x3000
+#define BRCMU_CHSPEC_D11N_BND_SHIFT 12
+#define BRCMU_CHSPEC_D11N_BND_5G 0x1000
+#define BRCMU_CHSPEC_D11N_BND_2G 0x2000
+
+/* bit 8~16 for dot 11ac IO types
+ * bit 8~10 sideband
+ * bit 11~13 bandwidth
+ * bit 14~15 spectral band
+ */
+#define BRCMU_CHSPEC_D11AC_SB_MASK 0x0700
+#define BRCMU_CHSPEC_D11AC_SB_SHIFT 8
+#define BRCMU_CHSPEC_D11AC_SB_LLL 0x0000
+#define BRCMU_CHSPEC_D11AC_SB_LLU 0x0100
+#define BRCMU_CHSPEC_D11AC_SB_LUL 0x0200
+#define BRCMU_CHSPEC_D11AC_SB_LUU 0x0300
+#define BRCMU_CHSPEC_D11AC_SB_ULL 0x0400
+#define BRCMU_CHSPEC_D11AC_SB_ULU 0x0500
+#define BRCMU_CHSPEC_D11AC_SB_UUL 0x0600
+#define BRCMU_CHSPEC_D11AC_SB_UUU 0x0700
+#define BRCMU_CHSPEC_D11AC_SB_LL BRCMU_CHSPEC_D11AC_SB_LLL
+#define BRCMU_CHSPEC_D11AC_SB_LU BRCMU_CHSPEC_D11AC_SB_LLU
+#define BRCMU_CHSPEC_D11AC_SB_UL BRCMU_CHSPEC_D11AC_SB_LUL
+#define BRCMU_CHSPEC_D11AC_SB_UU BRCMU_CHSPEC_D11AC_SB_LUU
+#define BRCMU_CHSPEC_D11AC_SB_L BRCMU_CHSPEC_D11AC_SB_LLL
+#define BRCMU_CHSPEC_D11AC_SB_U BRCMU_CHSPEC_D11AC_SB_LLU
+#define BRCMU_CHSPEC_D11AC_BW_MASK 0x3800
+#define BRCMU_CHSPEC_D11AC_BW_SHIFT 11
+#define BRCMU_CHSPEC_D11AC_BW_5 0x0000
+#define BRCMU_CHSPEC_D11AC_BW_10 0x0800
+#define BRCMU_CHSPEC_D11AC_BW_20 0x1000
+#define BRCMU_CHSPEC_D11AC_BW_40 0x1800
+#define BRCMU_CHSPEC_D11AC_BW_80 0x2000
+#define BRCMU_CHSPEC_D11AC_BW_160 0x2800
+#define BRCMU_CHSPEC_D11AC_BW_8080 0x3000
+#define BRCMU_CHSPEC_D11AC_BND_MASK 0xc000
+#define BRCMU_CHSPEC_D11AC_BND_SHIFT 14
+#define BRCMU_CHSPEC_D11AC_BND_2G 0x0000
+#define BRCMU_CHSPEC_D11AC_BND_3G 0x4000
+#define BRCMU_CHSPEC_D11AC_BND_4G 0x8000
+#define BRCMU_CHSPEC_D11AC_BND_5G 0xc000
+
+#define BRCMU_CHAN_BAND_2G 0
+#define BRCMU_CHAN_BAND_5G 1
+
+enum brcmu_chan_bw {
+ BRCMU_CHAN_BW_20,
+ BRCMU_CHAN_BW_40,
+ BRCMU_CHAN_BW_80,
+ BRCMU_CHAN_BW_80P80,
+ BRCMU_CHAN_BW_160,
+};
+
+enum brcmu_chan_sb {
+ BRCMU_CHAN_SB_NONE = -1,
+ BRCMU_CHAN_SB_LLL,
+ BRCMU_CHAN_SB_LLU,
+ BRCMU_CHAN_SB_LUL,
+ BRCMU_CHAN_SB_LUU,
+ BRCMU_CHAN_SB_ULL,
+ BRCMU_CHAN_SB_ULU,
+ BRCMU_CHAN_SB_UUL,
+ BRCMU_CHAN_SB_UUU,
+ BRCMU_CHAN_SB_L = BRCMU_CHAN_SB_LLL,
+ BRCMU_CHAN_SB_U = BRCMU_CHAN_SB_LLU,
+ BRCMU_CHAN_SB_LL = BRCMU_CHAN_SB_LLL,
+ BRCMU_CHAN_SB_LU = BRCMU_CHAN_SB_LLU,
+ BRCMU_CHAN_SB_UL = BRCMU_CHAN_SB_LUL,
+ BRCMU_CHAN_SB_UU = BRCMU_CHAN_SB_LUU,
+};
+
+/**
+ * struct brcmu_chan - stores channel formats
+ *
+ * This structure can be used with functions translating chanspec into generic
+ * channel info and the other way.
+ *
+ * @chspec: firmware specific format
+ * @chnum: center channel number
+ * @control_ch_num: control channel number
+ * @band: frequency band
+ * @bw: channel width
+ * @sb: control sideband (location of control channel against the center one)
+ */
+struct brcmu_chan {
+ u16 chspec;
+ u8 chnum;
+ u8 control_ch_num;
+ u8 band;
+ enum brcmu_chan_bw bw;
+ enum brcmu_chan_sb sb;
+};
+
+/**
+ * struct brcmu_d11inf - provides functions translating channel format
+ *
+ * @io_type: determines version of channel format used by firmware
+ * @encchspec: encodes channel info into a chanspec, requires center channel
+ * number, ignores control one
+ * @decchspec: decodes chanspec into generic info
+ */
+struct brcmu_d11inf {
+ u8 io_type;
+
+ void (*encchspec)(struct brcmu_chan *ch);
+ void (*decchspec)(struct brcmu_chan *ch);
+};
+
+void brcmu_d11_attach(struct brcmu_d11inf *d11inf);
+
+#endif /* _BRCMU_CHANNELS_H_ */
diff --git a/drivers/net/wireless/broadcom/brcm80211/include/brcmu_utils.h b/drivers/net/wireless/broadcom/brcm80211/include/brcmu_utils.h
new file mode 100644
index 0000000..4196952
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/include/brcmu_utils.h
@@ -0,0 +1,227 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _BRCMU_UTILS_H_
+#define _BRCMU_UTILS_H_
+
+#include <linux/skbuff.h>
+
+/*
+ * Spin at most 'us' microseconds while 'exp' is true.
+ * Caller should explicitly test 'exp' when this completes
+ * and take appropriate error action if 'exp' is still true.
+ */
+#define SPINWAIT(exp, us) { \
+ uint countdown = (us) + 9; \
+ while ((exp) && (countdown >= 10)) {\
+ udelay(10); \
+ countdown -= 10; \
+ } \
+}
+
+/* osl multi-precedence packet queue */
+#define PKTQ_LEN_DEFAULT 128 /* Max 128 packets */
+#define PKTQ_MAX_PREC 16 /* Maximum precedence levels */
+
+#define BCME_STRLEN 64 /* Max string length for BCM errors */
+
+/* the largest reasonable packet buffer driver uses for ethernet MTU in bytes */
+#define PKTBUFSZ 2048
+
+#ifndef setbit
+#ifndef NBBY /* the BSD family defines NBBY */
+#define NBBY 8 /* 8 bits per byte */
+#endif /* #ifndef NBBY */
+#define setbit(a, i) (((u8 *)a)[(i)/NBBY] |= 1<<((i)%NBBY))
+#define clrbit(a, i) (((u8 *)a)[(i)/NBBY] &= ~(1<<((i)%NBBY)))
+#define isset(a, i) (((const u8 *)a)[(i)/NBBY] & (1<<((i)%NBBY)))
+#define isclr(a, i) ((((const u8 *)a)[(i)/NBBY] & (1<<((i)%NBBY))) == 0)
+#endif /* setbit */
+
+#define NBITS(type) (sizeof(type) * 8)
+#define NBITVAL(nbits) (1 << (nbits))
+#define MAXBITVAL(nbits) ((1 << (nbits)) - 1)
+#define NBITMASK(nbits) MAXBITVAL(nbits)
+#define MAXNBVAL(nbyte) MAXBITVAL((nbyte) * 8)
+
+/* crc defines */
+#define CRC16_INIT_VALUE 0xffff /* Initial CRC16 checksum value */
+#define CRC16_GOOD_VALUE 0xf0b8 /* Good final CRC16 checksum value */
+
+/* 18-bytes of Ethernet address buffer length */
+#define ETHER_ADDR_STR_LEN 18
+
+struct pktq_prec {
+ struct sk_buff_head skblist;
+ u16 max; /* maximum number of queued packets */
+};
+
+/* multi-priority pkt queue */
+struct pktq {
+ u16 num_prec; /* number of precedences in use */
+ u16 hi_prec; /* rapid dequeue hint (>= highest non-empty prec) */
+ u16 max; /* total max packets */
+ u16 len; /* total number of packets */
+ /*
+ * q array must be last since # of elements can be either
+ * PKTQ_MAX_PREC or 1
+ */
+ struct pktq_prec q[PKTQ_MAX_PREC];
+};
+
+/* operations on a specific precedence in packet queue */
+
+static inline int pktq_plen(struct pktq *pq, int prec)
+{
+ return pq->q[prec].skblist.qlen;
+}
+
+static inline int pktq_pavail(struct pktq *pq, int prec)
+{
+ return pq->q[prec].max - pq->q[prec].skblist.qlen;
+}
+
+static inline bool pktq_pfull(struct pktq *pq, int prec)
+{
+ return pq->q[prec].skblist.qlen >= pq->q[prec].max;
+}
+
+static inline bool pktq_pempty(struct pktq *pq, int prec)
+{
+ return skb_queue_empty(&pq->q[prec].skblist);
+}
+
+static inline struct sk_buff *pktq_ppeek(struct pktq *pq, int prec)
+{
+ return skb_peek(&pq->q[prec].skblist);
+}
+
+static inline struct sk_buff *pktq_ppeek_tail(struct pktq *pq, int prec)
+{
+ return skb_peek_tail(&pq->q[prec].skblist);
+}
+
+struct sk_buff *brcmu_pktq_penq(struct pktq *pq, int prec, struct sk_buff *p);
+struct sk_buff *brcmu_pktq_penq_head(struct pktq *pq, int prec,
+ struct sk_buff *p);
+struct sk_buff *brcmu_pktq_pdeq(struct pktq *pq, int prec);
+struct sk_buff *brcmu_pktq_pdeq_tail(struct pktq *pq, int prec);
+struct sk_buff *brcmu_pktq_pdeq_match(struct pktq *pq, int prec,
+ bool (*match_fn)(struct sk_buff *p,
+ void *arg),
+ void *arg);
+
+/* packet primitives */
+struct sk_buff *brcmu_pkt_buf_get_skb(uint len);
+void brcmu_pkt_buf_free_skb(struct sk_buff *skb);
+
+/* Empty the queue at particular precedence level */
+/* callback function fn(pkt, arg) returns true if pkt belongs to if */
+void brcmu_pktq_pflush(struct pktq *pq, int prec, bool dir,
+ bool (*fn)(struct sk_buff *, void *), void *arg);
+
+/* operations on a set of precedences in packet queue */
+
+int brcmu_pktq_mlen(struct pktq *pq, uint prec_bmp);
+struct sk_buff *brcmu_pktq_mdeq(struct pktq *pq, uint prec_bmp, int *prec_out);
+
+/* operations on packet queue as a whole */
+
+static inline int pktq_len(struct pktq *pq)
+{
+ return (int)pq->len;
+}
+
+static inline int pktq_max(struct pktq *pq)
+{
+ return (int)pq->max;
+}
+
+static inline int pktq_avail(struct pktq *pq)
+{
+ return (int)(pq->max - pq->len);
+}
+
+static inline bool pktq_full(struct pktq *pq)
+{
+ return pq->len >= pq->max;
+}
+
+static inline bool pktq_empty(struct pktq *pq)
+{
+ return pq->len == 0;
+}
+
+void brcmu_pktq_init(struct pktq *pq, int num_prec, int max_len);
+/* prec_out may be NULL if caller is not interested in return value */
+struct sk_buff *brcmu_pktq_peek_tail(struct pktq *pq, int *prec_out);
+void brcmu_pktq_flush(struct pktq *pq, bool dir,
+ bool (*fn)(struct sk_buff *, void *), void *arg);
+
+/* externs */
+/* ip address */
+struct ipv4_addr;
+
+/*
+ * bitfield macros using masking and shift
+ *
+ * remark: the mask parameter should be a shifted mask.
+ */
+static inline void brcmu_maskset32(u32 *var, u32 mask, u8 shift, u32 value)
+{
+ value = (value << shift) & mask;
+ *var = (*var & ~mask) | value;
+}
+static inline u32 brcmu_maskget32(u32 var, u32 mask, u8 shift)
+{
+ return (var & mask) >> shift;
+}
+static inline void brcmu_maskset16(u16 *var, u16 mask, u8 shift, u16 value)
+{
+ value = (value << shift) & mask;
+ *var = (*var & ~mask) | value;
+}
+static inline u16 brcmu_maskget16(u16 var, u16 mask, u8 shift)
+{
+ return (var & mask) >> shift;
+}
+
+/* externs */
+/* format/print */
+#ifdef DEBUG
+void brcmu_prpkt(const char *msg, struct sk_buff *p0);
+#else
+#define brcmu_prpkt(a, b)
+#endif /* DEBUG */
+
+#ifdef DEBUG
+__printf(3, 4)
+void brcmu_dbg_hex_dump(const void *data, size_t size, const char *fmt, ...);
+#else
+__printf(3, 4)
+static inline
+void brcmu_dbg_hex_dump(const void *data, size_t size, const char *fmt, ...)
+{
+}
+#endif
+
+#define BRCMU_BOARDREV_LEN 8
+#define BRCMU_DOTREV_LEN 16
+
+char *brcmu_boardrev_str(u32 brev, char *buf);
+char *brcmu_dotrev_str(u32 dotrev, char *buf);
+
+#endif /* _BRCMU_UTILS_H_ */
diff --git a/drivers/net/wireless/broadcom/brcm80211/include/brcmu_wifi.h b/drivers/net/wireless/broadcom/brcm80211/include/brcmu_wifi.h
new file mode 100644
index 0000000..c361163
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/include/brcmu_wifi.h
@@ -0,0 +1,255 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _BRCMU_WIFI_H_
+#define _BRCMU_WIFI_H_
+
+#include <linux/if_ether.h> /* for ETH_ALEN */
+#include <linux/ieee80211.h> /* for WLAN_PMKID_LEN */
+
+/*
+ * A chanspec (u16) holds the channel number, band, bandwidth and control
+ * sideband
+ */
+
+/* channel defines */
+#define CH_UPPER_SB 0x01
+#define CH_LOWER_SB 0x02
+#define CH_EWA_VALID 0x04
+#define CH_30MHZ_APART 6
+#define CH_20MHZ_APART 4
+#define CH_10MHZ_APART 2
+#define CH_5MHZ_APART 1 /* 2G band channels are 5 Mhz apart */
+#define CH_MIN_2G_CHANNEL 1
+#define CH_MAX_2G_CHANNEL 14 /* Max channel in 2G band */
+#define CH_MIN_5G_CHANNEL 34
+
+/* bandstate array indices */
+#define BAND_2G_INDEX 0 /* wlc->bandstate[x] index */
+#define BAND_5G_INDEX 1 /* wlc->bandstate[x] index */
+
+/*
+ * max # supported channels. The max channel no is 216, this is that + 1
+ * rounded up to a multiple of NBBY (8). DO NOT MAKE it > 255: channels are
+ * u8's all over
+*/
+#define MAXCHANNEL 224
+
+#define WL_CHANSPEC_CHAN_MASK 0x00ff
+#define WL_CHANSPEC_CHAN_SHIFT 0
+
+#define WL_CHANSPEC_CTL_SB_MASK 0x0300
+#define WL_CHANSPEC_CTL_SB_SHIFT 8
+#define WL_CHANSPEC_CTL_SB_LOWER 0x0100
+#define WL_CHANSPEC_CTL_SB_UPPER 0x0200
+#define WL_CHANSPEC_CTL_SB_NONE 0x0300
+
+#define WL_CHANSPEC_BW_MASK 0x0C00
+#define WL_CHANSPEC_BW_SHIFT 10
+#define WL_CHANSPEC_BW_10 0x0400
+#define WL_CHANSPEC_BW_20 0x0800
+#define WL_CHANSPEC_BW_40 0x0C00
+#define WL_CHANSPEC_BW_80 0x2000
+
+#define WL_CHANSPEC_BAND_MASK 0xf000
+#define WL_CHANSPEC_BAND_SHIFT 12
+#define WL_CHANSPEC_BAND_5G 0x1000
+#define WL_CHANSPEC_BAND_2G 0x2000
+#define INVCHANSPEC 255
+
+#define WL_CHAN_VALID_HW (1 << 0) /* valid with current HW */
+#define WL_CHAN_VALID_SW (1 << 1) /* valid with country sett. */
+#define WL_CHAN_BAND_5G (1 << 2) /* 5GHz-band channel */
+#define WL_CHAN_RADAR (1 << 3) /* radar sensitive channel */
+#define WL_CHAN_INACTIVE (1 << 4) /* inactive due to radar */
+#define WL_CHAN_PASSIVE (1 << 5) /* channel in passive mode */
+#define WL_CHAN_RESTRICTED (1 << 6) /* restricted use channel */
+
+/* values for band specific 40MHz capabilities */
+#define WLC_N_BW_20ALL 0
+#define WLC_N_BW_40ALL 1
+#define WLC_N_BW_20IN2G_40IN5G 2
+
+#define WLC_BW_20MHZ_BIT BIT(0)
+#define WLC_BW_40MHZ_BIT BIT(1)
+#define WLC_BW_80MHZ_BIT BIT(2)
+#define WLC_BW_160MHZ_BIT BIT(3)
+
+/* Bandwidth capabilities */
+#define WLC_BW_CAP_20MHZ (WLC_BW_20MHZ_BIT)
+#define WLC_BW_CAP_40MHZ (WLC_BW_40MHZ_BIT|WLC_BW_20MHZ_BIT)
+#define WLC_BW_CAP_80MHZ (WLC_BW_80MHZ_BIT|WLC_BW_40MHZ_BIT| \
+ WLC_BW_20MHZ_BIT)
+#define WLC_BW_CAP_160MHZ (WLC_BW_160MHZ_BIT|WLC_BW_80MHZ_BIT| \
+ WLC_BW_40MHZ_BIT|WLC_BW_20MHZ_BIT)
+#define WLC_BW_CAP_UNRESTRICTED 0xFF
+
+/* band types */
+#define WLC_BAND_AUTO 0 /* auto-select */
+#define WLC_BAND_5G 1 /* 5 Ghz */
+#define WLC_BAND_2G 2 /* 2.4 Ghz */
+#define WLC_BAND_ALL 3 /* all bands */
+#define WLC_BAND_INVALID -1 /* Invalid band */
+
+#define CHSPEC_CHANNEL(chspec) ((u8)((chspec) & WL_CHANSPEC_CHAN_MASK))
+#define CHSPEC_BAND(chspec) ((chspec) & WL_CHANSPEC_BAND_MASK)
+
+#define CHSPEC_CTL_SB(chspec) ((chspec) & WL_CHANSPEC_CTL_SB_MASK)
+#define CHSPEC_BW(chspec) ((chspec) & WL_CHANSPEC_BW_MASK)
+
+#define CHSPEC_IS10(chspec) \
+ (((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_10)
+
+#define CHSPEC_IS20(chspec) \
+ (((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_20)
+
+#define CHSPEC_IS40(chspec) \
+ (((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_40)
+
+#define CHSPEC_IS80(chspec) \
+ (((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_80)
+
+#define CHSPEC_IS5G(chspec) \
+ (((chspec) & WL_CHANSPEC_BAND_MASK) == WL_CHANSPEC_BAND_5G)
+
+#define CHSPEC_IS2G(chspec) \
+ (((chspec) & WL_CHANSPEC_BAND_MASK) == WL_CHANSPEC_BAND_2G)
+
+#define CHSPEC_SB_NONE(chspec) \
+ (((chspec) & WL_CHANSPEC_CTL_SB_MASK) == WL_CHANSPEC_CTL_SB_NONE)
+
+#define CHSPEC_SB_UPPER(chspec) \
+ (((chspec) & WL_CHANSPEC_CTL_SB_MASK) == WL_CHANSPEC_CTL_SB_UPPER)
+
+#define CHSPEC_SB_LOWER(chspec) \
+ (((chspec) & WL_CHANSPEC_CTL_SB_MASK) == WL_CHANSPEC_CTL_SB_LOWER)
+
+#define CHSPEC_CTL_CHAN(chspec) \
+ ((CHSPEC_SB_LOWER(chspec)) ? \
+ (lower_20_sb(((chspec) & WL_CHANSPEC_CHAN_MASK))) : \
+ (upper_20_sb(((chspec) & WL_CHANSPEC_CHAN_MASK))))
+
+#define CHSPEC2BAND(chspec) (CHSPEC_IS5G(chspec) ? BRCM_BAND_5G : BRCM_BAND_2G)
+
+#define CHANSPEC_STR_LEN 8
+
+static inline int lower_20_sb(int channel)
+{
+ return channel > CH_10MHZ_APART ? (channel - CH_10MHZ_APART) : 0;
+}
+
+static inline int upper_20_sb(int channel)
+{
+ return (channel < (MAXCHANNEL - CH_10MHZ_APART)) ?
+ channel + CH_10MHZ_APART : 0;
+}
+
+static inline int chspec_bandunit(u16 chspec)
+{
+ return CHSPEC_IS5G(chspec) ? BAND_5G_INDEX : BAND_2G_INDEX;
+}
+
+static inline u16 ch20mhz_chspec(int channel)
+{
+ u16 rc = channel <= CH_MAX_2G_CHANNEL ?
+ WL_CHANSPEC_BAND_2G : WL_CHANSPEC_BAND_5G;
+
+ return (u16)((u16)channel | WL_CHANSPEC_BW_20 |
+ WL_CHANSPEC_CTL_SB_NONE | rc);
+}
+
+static inline int next_20mhz_chan(int channel)
+{
+ return channel < (MAXCHANNEL - CH_20MHZ_APART) ?
+ channel + CH_20MHZ_APART : 0;
+}
+
+/* defined rate in 500kbps */
+#define BRCM_MAXRATE 108 /* in 500kbps units */
+#define BRCM_RATE_1M 2 /* in 500kbps units */
+#define BRCM_RATE_2M 4 /* in 500kbps units */
+#define BRCM_RATE_5M5 11 /* in 500kbps units */
+#define BRCM_RATE_11M 22 /* in 500kbps units */
+#define BRCM_RATE_6M 12 /* in 500kbps units */
+#define BRCM_RATE_9M 18 /* in 500kbps units */
+#define BRCM_RATE_12M 24 /* in 500kbps units */
+#define BRCM_RATE_18M 36 /* in 500kbps units */
+#define BRCM_RATE_24M 48 /* in 500kbps units */
+#define BRCM_RATE_36M 72 /* in 500kbps units */
+#define BRCM_RATE_48M 96 /* in 500kbps units */
+#define BRCM_RATE_54M 108 /* in 500kbps units */
+
+#define BRCM_2G_25MHZ_OFFSET 5 /* 2.4GHz band channel offset */
+
+#define MCSSET_LEN 16
+
+static inline bool ac_bitmap_tst(u8 bitmap, int prec)
+{
+ return (bitmap & (1 << (prec))) != 0;
+}
+
+/* Enumerate crypto algorithms */
+#define CRYPTO_ALGO_OFF 0
+#define CRYPTO_ALGO_WEP1 1
+#define CRYPTO_ALGO_TKIP 2
+#define CRYPTO_ALGO_WEP128 3
+#define CRYPTO_ALGO_AES_CCM 4
+#define CRYPTO_ALGO_AES_RESERVED1 5
+#define CRYPTO_ALGO_AES_RESERVED2 6
+#define CRYPTO_ALGO_NALG 7
+
+/* wireless security bitvec */
+
+#define WEP_ENABLED 0x0001
+#define TKIP_ENABLED 0x0002
+#define AES_ENABLED 0x0004
+#define WSEC_SWFLAG 0x0008
+/* to go into transition mode without setting wep */
+#define SES_OW_ENABLED 0x0040
+/* MFP */
+#define MFP_CAPABLE 0x0200
+#define MFP_REQUIRED 0x0400
+
+/* WPA authentication mode bitvec */
+#define WPA_AUTH_DISABLED 0x0000 /* Legacy (i.e., non-WPA) */
+#define WPA_AUTH_NONE 0x0001 /* none (IBSS) */
+#define WPA_AUTH_UNSPECIFIED 0x0002 /* over 802.1x */
+#define WPA_AUTH_PSK 0x0004 /* Pre-shared key */
+#define WPA_AUTH_RESERVED1 0x0008
+#define WPA_AUTH_RESERVED2 0x0010
+
+#define WPA2_AUTH_RESERVED1 0x0020
+#define WPA2_AUTH_UNSPECIFIED 0x0040 /* over 802.1x */
+#define WPA2_AUTH_PSK 0x0080 /* Pre-shared key */
+#define WPA2_AUTH_RESERVED3 0x0200
+#define WPA2_AUTH_RESERVED4 0x0400
+#define WPA2_AUTH_RESERVED5 0x0800
+#define WPA2_AUTH_1X_SHA256 0x1000 /* 1X with SHA256 key derivation */
+#define WPA2_AUTH_FT 0x4000 /* Fast BSS Transition */
+#define WPA2_AUTH_PSK_SHA256 0x8000 /* PSK with SHA256 key derivation */
+
+#define DOT11_DEFAULT_RTS_LEN 2347
+#define DOT11_DEFAULT_FRAG_LEN 2346
+
+#define DOT11_ICV_AES_LEN 8
+#define DOT11_QOS_LEN 2
+#define DOT11_IV_MAX_LEN 8
+#define DOT11_A4_HDR_LEN 30
+
+#define HT_CAP_RX_STBC_NO 0x0
+#define HT_CAP_RX_STBC_ONE_STREAM 0x1
+
+#endif /* _BRCMU_WIFI_H_ */
diff --git a/drivers/net/wireless/broadcom/brcm80211/include/chipcommon.h b/drivers/net/wireless/broadcom/brcm80211/include/chipcommon.h
new file mode 100644
index 0000000..e1fd499
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/include/chipcommon.h
@@ -0,0 +1,303 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _SBCHIPC_H
+#define _SBCHIPC_H
+
+#include "defs.h" /* for PAD macro */
+
+#define CHIPCREGOFFS(field) offsetof(struct chipcregs, field)
+
+struct chipcregs {
+ u32 chipid; /* 0x0 */
+ u32 capabilities;
+ u32 corecontrol; /* corerev >= 1 */
+ u32 bist;
+
+ /* OTP */
+ u32 otpstatus; /* 0x10, corerev >= 10 */
+ u32 otpcontrol;
+ u32 otpprog;
+ u32 otplayout; /* corerev >= 23 */
+
+ /* Interrupt control */
+ u32 intstatus; /* 0x20 */
+ u32 intmask;
+
+ /* Chip specific regs */
+ u32 chipcontrol; /* 0x28, rev >= 11 */
+ u32 chipstatus; /* 0x2c, rev >= 11 */
+
+ /* Jtag Master */
+ u32 jtagcmd; /* 0x30, rev >= 10 */
+ u32 jtagir;
+ u32 jtagdr;
+ u32 jtagctrl;
+
+ /* serial flash interface registers */
+ u32 flashcontrol; /* 0x40 */
+ u32 flashaddress;
+ u32 flashdata;
+ u32 PAD[1];
+
+ /* Silicon backplane configuration broadcast control */
+ u32 broadcastaddress; /* 0x50 */
+ u32 broadcastdata;
+
+ /* gpio - cleared only by power-on-reset */
+ u32 gpiopullup; /* 0x58, corerev >= 20 */
+ u32 gpiopulldown; /* 0x5c, corerev >= 20 */
+ u32 gpioin; /* 0x60 */
+ u32 gpioout; /* 0x64 */
+ u32 gpioouten; /* 0x68 */
+ u32 gpiocontrol; /* 0x6C */
+ u32 gpiointpolarity; /* 0x70 */
+ u32 gpiointmask; /* 0x74 */
+
+ /* GPIO events corerev >= 11 */
+ u32 gpioevent;
+ u32 gpioeventintmask;
+
+ /* Watchdog timer */
+ u32 watchdog; /* 0x80 */
+
+ /* GPIO events corerev >= 11 */
+ u32 gpioeventintpolarity;
+
+ /* GPIO based LED powersave registers corerev >= 16 */
+ u32 gpiotimerval; /* 0x88 */
+ u32 gpiotimeroutmask;
+
+ /* clock control */
+ u32 clockcontrol_n; /* 0x90 */
+ u32 clockcontrol_sb; /* aka m0 */
+ u32 clockcontrol_pci; /* aka m1 */
+ u32 clockcontrol_m2; /* mii/uart/mipsref */
+ u32 clockcontrol_m3; /* cpu */
+ u32 clkdiv; /* corerev >= 3 */
+ u32 gpiodebugsel; /* corerev >= 28 */
+ u32 capabilities_ext; /* 0xac */
+
+ /* pll delay registers (corerev >= 4) */
+ u32 pll_on_delay; /* 0xb0 */
+ u32 fref_sel_delay;
+ u32 slow_clk_ctl; /* 5 < corerev < 10 */
+ u32 PAD;
+
+ /* Instaclock registers (corerev >= 10) */
+ u32 system_clk_ctl; /* 0xc0 */
+ u32 clkstatestretch;
+ u32 PAD[2];
+
+ /* Indirect backplane access (corerev >= 22) */
+ u32 bp_addrlow; /* 0xd0 */
+ u32 bp_addrhigh;
+ u32 bp_data;
+ u32 PAD;
+ u32 bp_indaccess;
+ u32 PAD[3];
+
+ /* More clock dividers (corerev >= 32) */
+ u32 clkdiv2;
+ u32 PAD[2];
+
+ /* In AI chips, pointer to erom */
+ u32 eromptr; /* 0xfc */
+
+ /* ExtBus control registers (corerev >= 3) */
+ u32 pcmcia_config; /* 0x100 */
+ u32 pcmcia_memwait;
+ u32 pcmcia_attrwait;
+ u32 pcmcia_iowait;
+ u32 ide_config;
+ u32 ide_memwait;
+ u32 ide_attrwait;
+ u32 ide_iowait;
+ u32 prog_config;
+ u32 prog_waitcount;
+ u32 flash_config;
+ u32 flash_waitcount;
+ u32 SECI_config; /* 0x130 SECI configuration */
+ u32 PAD[3];
+
+ /* Enhanced Coexistence Interface (ECI) registers (corerev >= 21) */
+ u32 eci_output; /* 0x140 */
+ u32 eci_control;
+ u32 eci_inputlo;
+ u32 eci_inputmi;
+ u32 eci_inputhi;
+ u32 eci_inputintpolaritylo;
+ u32 eci_inputintpolaritymi;
+ u32 eci_inputintpolarityhi;
+ u32 eci_intmasklo;
+ u32 eci_intmaskmi;
+ u32 eci_intmaskhi;
+ u32 eci_eventlo;
+ u32 eci_eventmi;
+ u32 eci_eventhi;
+ u32 eci_eventmasklo;
+ u32 eci_eventmaskmi;
+ u32 eci_eventmaskhi;
+ u32 PAD[3];
+
+ /* SROM interface (corerev >= 32) */
+ u32 sromcontrol; /* 0x190 */
+ u32 sromaddress;
+ u32 sromdata;
+ u32 PAD[17];
+
+ /* Clock control and hardware workarounds (corerev >= 20) */
+ u32 clk_ctl_st; /* 0x1e0 */
+ u32 hw_war;
+ u32 PAD[70];
+
+ /* UARTs */
+ u8 uart0data; /* 0x300 */
+ u8 uart0imr;
+ u8 uart0fcr;
+ u8 uart0lcr;
+ u8 uart0mcr;
+ u8 uart0lsr;
+ u8 uart0msr;
+ u8 uart0scratch;
+ u8 PAD[248]; /* corerev >= 1 */
+
+ u8 uart1data; /* 0x400 */
+ u8 uart1imr;
+ u8 uart1fcr;
+ u8 uart1lcr;
+ u8 uart1mcr;
+ u8 uart1lsr;
+ u8 uart1msr;
+ u8 uart1scratch;
+ u32 PAD[62];
+
+ /* save/restore, corerev >= 48 */
+ u32 sr_capability; /* 0x500 */
+ u32 sr_control0; /* 0x504 */
+ u32 sr_control1; /* 0x508 */
+ u32 gpio_control; /* 0x50C */
+ u32 PAD[60];
+
+ /* PMU registers (corerev >= 20) */
+ u32 pmucontrol; /* 0x600 */
+ u32 pmucapabilities;
+ u32 pmustatus;
+ u32 res_state;
+ u32 res_pending;
+ u32 pmutimer;
+ u32 min_res_mask;
+ u32 max_res_mask;
+ u32 res_table_sel;
+ u32 res_dep_mask;
+ u32 res_updn_timer;
+ u32 res_timer;
+ u32 clkstretch;
+ u32 pmuwatchdog;
+ u32 gpiosel; /* 0x638, rev >= 1 */
+ u32 gpioenable; /* 0x63c, rev >= 1 */
+ u32 res_req_timer_sel;
+ u32 res_req_timer;
+ u32 res_req_mask;
+ u32 pmucapabilities_ext; /* 0x64c, pmurev >=15 */
+ u32 chipcontrol_addr; /* 0x650 */
+ u32 chipcontrol_data; /* 0x654 */
+ u32 regcontrol_addr;
+ u32 regcontrol_data;
+ u32 pllcontrol_addr;
+ u32 pllcontrol_data;
+ u32 pmustrapopt; /* 0x668, corerev >= 28 */
+ u32 pmu_xtalfreq; /* 0x66C, pmurev >= 10 */
+ u32 retention_ctl; /* 0x670, pmurev >= 15 */
+ u32 PAD[3];
+ u32 retention_grpidx; /* 0x680 */
+ u32 retention_grpctl; /* 0x684 */
+ u32 PAD[94];
+ u16 sromotp[768];
+};
+
+/* chipid */
+#define CID_ID_MASK 0x0000ffff /* Chip Id mask */
+#define CID_REV_MASK 0x000f0000 /* Chip Revision mask */
+#define CID_REV_SHIFT 16 /* Chip Revision shift */
+#define CID_PKG_MASK 0x00f00000 /* Package Option mask */
+#define CID_PKG_SHIFT 20 /* Package Option shift */
+#define CID_CC_MASK 0x0f000000 /* CoreCount (corerev >= 4) */
+#define CID_CC_SHIFT 24
+#define CID_TYPE_MASK 0xf0000000 /* Chip Type */
+#define CID_TYPE_SHIFT 28
+
+/* capabilities */
+#define CC_CAP_UARTS_MASK 0x00000003 /* Number of UARTs */
+#define CC_CAP_MIPSEB 0x00000004 /* MIPS is in big-endian mode */
+#define CC_CAP_UCLKSEL 0x00000018 /* UARTs clock select */
+/* UARTs are driven by internal divided clock */
+#define CC_CAP_UINTCLK 0x00000008
+#define CC_CAP_UARTGPIO 0x00000020 /* UARTs own GPIOs 15:12 */
+#define CC_CAP_EXTBUS_MASK 0x000000c0 /* External bus mask */
+#define CC_CAP_EXTBUS_NONE 0x00000000 /* No ExtBus present */
+#define CC_CAP_EXTBUS_FULL 0x00000040 /* ExtBus: PCMCIA, IDE & Prog */
+#define CC_CAP_EXTBUS_PROG 0x00000080 /* ExtBus: ProgIf only */
+#define CC_CAP_FLASH_MASK 0x00000700 /* Type of flash */
+#define CC_CAP_PLL_MASK 0x00038000 /* Type of PLL */
+#define CC_CAP_PWR_CTL 0x00040000 /* Power control */
+#define CC_CAP_OTPSIZE 0x00380000 /* OTP Size (0 = none) */
+#define CC_CAP_OTPSIZE_SHIFT 19 /* OTP Size shift */
+#define CC_CAP_OTPSIZE_BASE 5 /* OTP Size base */
+#define CC_CAP_JTAGP 0x00400000 /* JTAG Master Present */
+#define CC_CAP_ROM 0x00800000 /* Internal boot rom active */
+#define CC_CAP_BKPLN64 0x08000000 /* 64-bit backplane */
+#define CC_CAP_PMU 0x10000000 /* PMU Present, rev >= 20 */
+#define CC_CAP_SROM 0x40000000 /* Srom Present, rev >= 32 */
+/* Nand flash present, rev >= 35 */
+#define CC_CAP_NFLASH 0x80000000
+
+#define CC_CAP2_SECI 0x00000001 /* SECI Present, rev >= 36 */
+/* GSIO (spi/i2c) present, rev >= 37 */
+#define CC_CAP2_GSIO 0x00000002
+
+/* pmucapabilities */
+#define PCAP_REV_MASK 0x000000ff
+#define PCAP_RC_MASK 0x00001f00
+#define PCAP_RC_SHIFT 8
+#define PCAP_TC_MASK 0x0001e000
+#define PCAP_TC_SHIFT 13
+#define PCAP_PC_MASK 0x001e0000
+#define PCAP_PC_SHIFT 17
+#define PCAP_VC_MASK 0x01e00000
+#define PCAP_VC_SHIFT 21
+#define PCAP_CC_MASK 0x1e000000
+#define PCAP_CC_SHIFT 25
+#define PCAP5_PC_MASK 0x003e0000 /* PMU corerev >= 5 */
+#define PCAP5_PC_SHIFT 17
+#define PCAP5_VC_MASK 0x07c00000
+#define PCAP5_VC_SHIFT 22
+#define PCAP5_CC_MASK 0xf8000000
+#define PCAP5_CC_SHIFT 27
+/* pmucapabilites_ext PMU rev >= 15 */
+#define PCAPEXT_SR_SUPPORTED_MASK (1 << 1)
+/* retention_ctl PMU rev >= 15 */
+#define PMU_RCTL_MACPHY_DISABLE_MASK (1 << 26)
+#define PMU_RCTL_LOGIC_DISABLE_MASK (1 << 27)
+
+
+/*
+* Maximum delay for the PMU state transition in us.
+* This is an upper bound intended for spinwaits etc.
+*/
+#define PMU_MAX_TRANSITION_DLY 15000
+
+#endif /* _SBCHIPC_H */
diff --git a/drivers/net/wireless/broadcom/brcm80211/include/defs.h b/drivers/net/wireless/broadcom/brcm80211/include/defs.h
new file mode 100644
index 0000000..8d1e85e
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/include/defs.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _BRCM_DEFS_H_
+#define _BRCM_DEFS_H_
+
+#include <linux/types.h>
+
+#define SI_BUS 0
+#define PCI_BUS 1
+#define PCMCIA_BUS 2
+#define SDIO_BUS 3
+#define JTAG_BUS 4
+#define USB_BUS 5
+#define SPI_BUS 6
+
+#define OFF 0
+#define ON 1 /* ON = 1 */
+#define AUTO (-1) /* Auto = -1 */
+
+/*
+ * Priority definitions according 802.1D
+ */
+#define PRIO_8021D_NONE 2
+#define PRIO_8021D_BK 1
+#define PRIO_8021D_BE 0
+#define PRIO_8021D_EE 3
+#define PRIO_8021D_CL 4
+#define PRIO_8021D_VI 5
+#define PRIO_8021D_VO 6
+#define PRIO_8021D_NC 7
+
+#define MAXPRIO 7
+#define NUMPRIO (MAXPRIO + 1)
+
+#define WL_NUMRATES 16 /* max # of rates in a rateset */
+
+#define BRCM_CNTRY_BUF_SZ 4 /* Country string is 3 bytes + NUL */
+
+#define BRCM_SET_CHANNEL 30
+#define BRCM_SET_SRL 32
+#define BRCM_SET_LRL 34
+#define BRCM_SET_BCNPRD 76
+
+#define BRCM_GET_CURR_RATESET 114 /* current rateset */
+#define BRCM_GET_PHYLIST 180
+
+/* Bit masks for radio disabled status - returned by WL_GET_RADIO */
+
+#define WL_RADIO_SW_DISABLE (1<<0)
+#define WL_RADIO_HW_DISABLE (1<<1)
+/* some countries don't support any channel */
+#define WL_RADIO_COUNTRY_DISABLE (1<<3)
+
+/* Override bit for SET_TXPWR. if set, ignore other level limits */
+#define WL_TXPWR_OVERRIDE (1U<<31)
+
+/* band types */
+#define BRCM_BAND_AUTO 0 /* auto-select */
+#define BRCM_BAND_5G 1 /* 5 Ghz */
+#define BRCM_BAND_2G 2 /* 2.4 Ghz */
+#define BRCM_BAND_ALL 3 /* all bands */
+
+/* Debug levels */
+#define BRCM_DL_INFO 0x00000001
+#define BRCM_DL_MAC80211 0x00000002
+#define BRCM_DL_RX 0x00000004
+#define BRCM_DL_TX 0x00000008
+#define BRCM_DL_INT 0x00000010
+#define BRCM_DL_DMA 0x00000020
+#define BRCM_DL_HT 0x00000040
+
+/* Values for PM */
+#define PM_OFF 0
+#define PM_MAX 1
+#define PM_FAST 2
+
+/*
+ * Sonics Configuration Space Registers.
+ */
+
+/* core sbconfig regs are top 256bytes of regs */
+#define SBCONFIGOFF 0xf00
+
+/* cpp contortions to concatenate w/arg prescan */
+#ifndef PAD
+#define _PADLINE(line) pad ## line
+#define _XSTR(line) _PADLINE(line)
+#define PAD _XSTR(__LINE__)
+#endif
+
+#endif /* _BRCM_DEFS_H_ */
diff --git a/drivers/net/wireless/broadcom/brcm80211/include/soc.h b/drivers/net/wireless/broadcom/brcm80211/include/soc.h
new file mode 100644
index 0000000..123cfa8
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/include/soc.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _BRCM_SOC_H
+#define _BRCM_SOC_H
+
+#define SI_ENUM_BASE 0x18000000 /* Enumeration space base */
+
+/* Common core control flags */
+#define SICF_BIST_EN 0x8000
+#define SICF_PME_EN 0x4000
+#define SICF_CORE_BITS 0x3ffc
+#define SICF_FGC 0x0002
+#define SICF_CLOCK_EN 0x0001
+
+/* Common core status flags */
+#define SISF_BIST_DONE 0x8000
+#define SISF_BIST_ERROR 0x4000
+#define SISF_GATED_CLK 0x2000
+#define SISF_DMA64 0x1000
+#define SISF_CORE_BITS 0x0fff
+
+#endif /* _BRCM_SOC_H */
diff --git a/fmac.mk b/fmac.mk
new file mode 100644
index 0000000..76e3cdb
--- /dev/null
+++ b/fmac.mk
@@ -0,0 +1,19 @@
+ifeq ($(TARGET_PRODUCT),antares)
+
+DEVICE_PACKAGES += \
+ compat.ko \
+ cfg80211.ko \
+ brcmutil.ko \
+ brcmfmac.ko \
+ wl
+
+else ifeq ($(TARGET_PRODUCT),iot_cygnus)
+
+PRODUCT_PACKAGES += \
+ compat.ko \
+ cfg80211.ko \
+ brcmutil.ko \
+ brcmfmac.ko \
+ wl
+
+endif
diff --git a/include/linux/backport-devcoredump.h b/include/linux/backport-devcoredump.h
new file mode 100644
index 0000000..02ab458
--- /dev/null
+++ b/include/linux/backport-devcoredump.h
@@ -0,0 +1,105 @@
+/*
+ * This file is provided under the GPLv2 license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2015 Intel Deutschland GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ */
+#ifndef __DEVCOREDUMP_H
+#define __DEVCOREDUMP_H
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
+
+/*
+ * _devcd_free_sgtable - free all the memory of the given scatterlist table
+ * (i.e. both pages and scatterlist instances)
+ * NOTE: if two tables allocated and chained using the sg_chain function then
+ * this function should be called only once on the first table
+ * @table: pointer to sg_table to free
+ */
+static inline void _devcd_free_sgtable(struct scatterlist *table)
+{
+ int i;
+ struct page *page;
+ struct scatterlist *iter;
+ struct scatterlist *delete_iter;
+
+ /* free pages */
+ iter = table;
+ for_each_sg(table, iter, sg_nents(table), i) {
+ page = sg_page(iter);
+ if (page)
+ __free_page(page);
+ }
+
+ /* then free all chained tables */
+ iter = table;
+ delete_iter = table; /* always points on a head of a table */
+ while (!sg_is_last(iter)) {
+ iter++;
+ if (sg_is_chain(iter)) {
+ iter = sg_chain_ptr(iter);
+ kfree(delete_iter);
+ delete_iter = iter;
+ }
+ }
+
+ /* free the last table */
+ kfree(delete_iter);
+}
+
+
+#ifdef CPTCFG_BPAUTO_WANT_DEV_COREDUMP
+void dev_coredumpv(struct device *dev, void *data, size_t datalen,
+ gfp_t gfp);
+
+void dev_coredumpm(struct device *dev, struct module *owner,
+ void *data, size_t datalen, gfp_t gfp,
+ ssize_t (*read)(char *buffer, loff_t offset, size_t count,
+ void *data, size_t datalen),
+ void (*free)(void *data));
+
+void dev_coredumpsg(struct device *dev, struct scatterlist *table,
+ size_t datalen, gfp_t gfp);
+#else
+static inline void dev_coredumpv(struct device *dev, void *data,
+ size_t datalen, gfp_t gfp)
+{
+ vfree(data);
+}
+
+static inline void
+dev_coredumpm(struct device *dev, struct module *owner,
+ void *data, size_t datalen, gfp_t gfp,
+ ssize_t (*read)(char *buffer, loff_t offset, size_t count,
+ void *data, size_t datalen),
+ void (*free)(void *data))
+{
+ free(data);
+}
+
+static inline void dev_coredumpsg(struct device *dev, struct scatterlist *table,
+ size_t datalen, gfp_t gfp)
+{
+ _devcd_free_sgtable(table);
+}
+#endif /* CPTCFG_BPAUTO_WANT_DEV_COREDUMP */
+
+#endif /* __DEVCOREDUMP_H */
diff --git a/include/linux/backport-rhashtable.h b/include/linux/backport-rhashtable.h
new file mode 100644
index 0000000..7d56a7e
--- /dev/null
+++ b/include/linux/backport-rhashtable.h
@@ -0,0 +1,1290 @@
+/*
+ * Resizable, Scalable, Concurrent Hash Table
+ *
+ * Copyright (c) 2015-2016 Herbert Xu <herbert@gondor.apana.org.au>
+ * Copyright (c) 2014-2015 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2008-2014 Patrick McHardy <kaber@trash.net>
+ *
+ * Code partially derived from nft_hash
+ * Rewritten with rehash code from br_multicast plus single list
+ * pointer as suggested by Josh Triplett
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _LINUX_RHASHTABLE_H
+#define _LINUX_RHASHTABLE_H
+
+#include <linux/atomic.h>
+#include <linux/compiler.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/jhash.h>
+#include <linux/list_nulls.h>
+#include <linux/workqueue.h>
+#include <linux/mutex.h>
+#include <linux/rculist.h>
+
+/*
+ * The end of the chain is marked with a special nulls marks which has
+ * the following format:
+ *
+ * +-------+-----------------------------------------------------+-+
+ * | Base | Hash |1|
+ * +-------+-----------------------------------------------------+-+
+ *
+ * Base (4 bits) : Reserved to distinguish between multiple tables.
+ * Specified via &struct rhashtable_params.nulls_base.
+ * Hash (27 bits): Full hash (unmasked) of first element added to bucket
+ * 1 (1 bit) : Nulls marker (always set)
+ *
+ * The remaining bits of the next pointer remain unused for now.
+ */
+#define RHT_BASE_BITS 4
+#define RHT_HASH_BITS 27
+#define RHT_BASE_SHIFT RHT_HASH_BITS
+
+/* Base bits plus 1 bit for nulls marker */
+#define RHT_HASH_RESERVED_SPACE (RHT_BASE_BITS + 1)
+
+/* Maximum chain length before rehash
+ *
+ * The maximum (not average) chain length grows with the size of the hash
+ * table, at a rate of (log N)/(log log N).
+ *
+ * The value of 16 is selected so that even if the hash table grew to
+ * 2^32 you would not expect the maximum chain length to exceed it
+ * unless we are under attack (or extremely unlucky).
+ *
+ * As this limit is only to detect attacks, we don't need to set it to a
+ * lower value as you'd need the chain length to vastly exceed 16 to have
+ * any real effect on the system.
+ */
+#define RHT_ELASTICITY 16u
+
+struct rhash_head {
+ struct rhash_head __rcu *next;
+};
+
+struct rhlist_head {
+ struct rhash_head rhead;
+ struct rhlist_head __rcu *next;
+};
+
+/**
+ * struct bucket_table - Table of hash buckets
+ * @size: Number of hash buckets
+ * @nest: Number of bits of first-level nested table.
+ * @rehash: Current bucket being rehashed
+ * @hash_rnd: Random seed to fold into hash
+ * @locks_mask: Mask to apply before accessing locks[]
+ * @locks: Array of spinlocks protecting individual buckets
+ * @walkers: List of active walkers
+ * @rcu: RCU structure for freeing the table
+ * @future_tbl: Table under construction during rehashing
+ * @ntbl: Nested table used when out of memory.
+ * @buckets: size * hash buckets
+ */
+struct bucket_table {
+ unsigned int size;
+ unsigned int nest;
+ unsigned int rehash;
+ u32 hash_rnd;
+ unsigned int locks_mask;
+ spinlock_t *locks;
+ struct list_head walkers;
+ struct rcu_head rcu;
+
+ struct bucket_table __rcu *future_tbl;
+
+ struct rhash_head __rcu *buckets[] ____cacheline_aligned_in_smp;
+};
+
+/**
+ * struct rhashtable_compare_arg - Key for the function rhashtable_compare
+ * @ht: Hash table
+ * @key: Key to compare against
+ */
+struct rhashtable_compare_arg {
+ struct rhashtable *ht;
+ const void *key;
+};
+
+typedef u32 (*rht_hashfn_t)(const void *data, u32 len, u32 seed);
+typedef u32 (*rht_obj_hashfn_t)(const void *data, u32 len, u32 seed);
+typedef int (*rht_obj_cmpfn_t)(struct rhashtable_compare_arg *arg,
+ const void *obj);
+
+struct rhashtable;
+
+/**
+ * struct rhashtable_params - Hash table construction parameters
+ * @nelem_hint: Hint on number of elements, should be 75% of desired size
+ * @key_len: Length of key
+ * @key_offset: Offset of key in struct to be hashed
+ * @head_offset: Offset of rhash_head in struct to be hashed
+ * @max_size: Maximum size while expanding
+ * @min_size: Minimum size while shrinking
+ * @locks_mul: Number of bucket locks to allocate per cpu (default: 128)
+ * @automatic_shrinking: Enable automatic shrinking of tables
+ * @nulls_base: Base value to generate nulls marker
+ * @hashfn: Hash function (default: jhash2 if !(key_len % 4), or jhash)
+ * @obj_hashfn: Function to hash object
+ * @obj_cmpfn: Function to compare key with object
+ */
+struct rhashtable_params {
+ u16 nelem_hint;
+ u16 key_len;
+ u16 key_offset;
+ u16 head_offset;
+ unsigned int max_size;
+ u16 min_size;
+ bool automatic_shrinking;
+ u8 locks_mul;
+ u32 nulls_base;
+ rht_hashfn_t hashfn;
+ rht_obj_hashfn_t obj_hashfn;
+ rht_obj_cmpfn_t obj_cmpfn;
+};
+
+/**
+ * struct rhashtable - Hash table handle
+ * @tbl: Bucket table
+ * @nelems: Number of elements in table
+ * @key_len: Key length for hashfn
+ * @p: Configuration parameters
+ * @max_elems: Maximum number of elements in table
+ * @rhlist: True if this is an rhltable
+ * @run_work: Deferred worker to expand/shrink asynchronously
+ * @mutex: Mutex to protect current/future table swapping
+ * @lock: Spin lock to protect walker list
+ */
+struct rhashtable {
+ struct bucket_table __rcu *tbl;
+ atomic_t nelems;
+ unsigned int key_len;
+ struct rhashtable_params p;
+ unsigned int max_elems;
+ bool rhlist;
+ struct work_struct run_work;
+ struct mutex mutex;
+ spinlock_t lock;
+};
+
+/**
+ * struct rhltable - Hash table with duplicate objects in a list
+ * @ht: Underlying rhtable
+ */
+struct rhltable {
+ struct rhashtable ht;
+};
+
+/**
+ * struct rhashtable_walker - Hash table walker
+ * @list: List entry on list of walkers
+ * @tbl: The table that we were walking over
+ */
+struct rhashtable_walker {
+ struct list_head list;
+ struct bucket_table *tbl;
+};
+
+/**
+ * struct rhashtable_iter - Hash table iterator
+ * @ht: Table to iterate through
+ * @p: Current pointer
+ * @list: Current hash list pointer
+ * @walker: Associated rhashtable walker
+ * @slot: Current slot
+ * @skip: Number of entries to skip in slot
+ */
+struct rhashtable_iter {
+ struct rhashtable *ht;
+ struct rhash_head *p;
+ struct rhlist_head *list;
+ struct rhashtable_walker walker;
+ unsigned int slot;
+ unsigned int skip;
+};
+
+static inline unsigned long rht_marker(const struct rhashtable *ht, u32 hash)
+{
+ return NULLS_MARKER(ht->p.nulls_base + hash);
+}
+
+#define INIT_RHT_NULLS_HEAD(ptr, ht, hash) \
+ ((ptr) = (typeof(ptr)) rht_marker(ht, hash))
+
+static inline bool rht_is_a_nulls(const struct rhash_head *ptr)
+{
+ return ((unsigned long) ptr & 1);
+}
+
+static inline unsigned long rht_get_nulls_value(const struct rhash_head *ptr)
+{
+ return ((unsigned long) ptr) >> 1;
+}
+
+static inline void *rht_obj(const struct rhashtable *ht,
+ const struct rhash_head *he)
+{
+ return (char *)he - ht->p.head_offset;
+}
+
+static inline unsigned int rht_bucket_index(const struct bucket_table *tbl,
+ unsigned int hash)
+{
+ return (hash >> RHT_HASH_RESERVED_SPACE) & (tbl->size - 1);
+}
+
+static inline unsigned int rht_key_hashfn(
+ struct rhashtable *ht, const struct bucket_table *tbl,
+ const void *key, const struct rhashtable_params params)
+{
+ unsigned int hash;
+
+ /* params must be equal to ht->p if it isn't constant. */
+ if (!__builtin_constant_p(params.key_len))
+ hash = ht->p.hashfn(key, ht->key_len, tbl->hash_rnd);
+ else if (params.key_len) {
+ unsigned int key_len = params.key_len;
+
+ if (params.hashfn)
+ hash = params.hashfn(key, key_len, tbl->hash_rnd);
+ else if (key_len & (sizeof(u32) - 1))
+ hash = jhash(key, key_len, tbl->hash_rnd);
+ else
+ hash = jhash2(key, key_len / sizeof(u32),
+ tbl->hash_rnd);
+ } else {
+ unsigned int key_len = ht->p.key_len;
+
+ if (params.hashfn)
+ hash = params.hashfn(key, key_len, tbl->hash_rnd);
+ else
+ hash = jhash(key, key_len, tbl->hash_rnd);
+ }
+
+ return rht_bucket_index(tbl, hash);
+}
+
+static inline unsigned int rht_head_hashfn(
+ struct rhashtable *ht, const struct bucket_table *tbl,
+ const struct rhash_head *he, const struct rhashtable_params params)
+{
+ const char *ptr = rht_obj(ht, he);
+
+ return likely(params.obj_hashfn) ?
+ rht_bucket_index(tbl, params.obj_hashfn(ptr, params.key_len ?:
+ ht->p.key_len,
+ tbl->hash_rnd)) :
+ rht_key_hashfn(ht, tbl, ptr + params.key_offset, params);
+}
+
+/**
+ * rht_grow_above_75 - returns true if nelems > 0.75 * table-size
+ * @ht: hash table
+ * @tbl: current table
+ */
+static inline bool rht_grow_above_75(const struct rhashtable *ht,
+ const struct bucket_table *tbl)
+{
+ /* Expand table when exceeding 75% load */
+ return atomic_read(&ht->nelems) > (tbl->size / 4 * 3) &&
+ (!ht->p.max_size || tbl->size < ht->p.max_size);
+}
+
+/**
+ * rht_shrink_below_30 - returns true if nelems < 0.3 * table-size
+ * @ht: hash table
+ * @tbl: current table
+ */
+static inline bool rht_shrink_below_30(const struct rhashtable *ht,
+ const struct bucket_table *tbl)
+{
+ /* Shrink table beneath 30% load */
+ return atomic_read(&ht->nelems) < (tbl->size * 3 / 10) &&
+ tbl->size > ht->p.min_size;
+}
+
+/**
+ * rht_grow_above_100 - returns true if nelems > table-size
+ * @ht: hash table
+ * @tbl: current table
+ */
+static inline bool rht_grow_above_100(const struct rhashtable *ht,
+ const struct bucket_table *tbl)
+{
+ return atomic_read(&ht->nelems) > tbl->size &&
+ (!ht->p.max_size || tbl->size < ht->p.max_size);
+}
+
+/**
+ * rht_grow_above_max - returns true if table is above maximum
+ * @ht: hash table
+ * @tbl: current table
+ */
+static inline bool rht_grow_above_max(const struct rhashtable *ht,
+ const struct bucket_table *tbl)
+{
+ return atomic_read(&ht->nelems) >= ht->max_elems;
+}
+
+/* The bucket lock is selected based on the hash and protects mutations
+ * on a group of hash buckets.
+ *
+ * A maximum of tbl->size/2 bucket locks is allocated. This ensures that
+ * a single lock always covers both buckets which may both contains
+ * entries which link to the same bucket of the old table during resizing.
+ * This allows to simplify the locking as locking the bucket in both
+ * tables during resize always guarantee protection.
+ *
+ * IMPORTANT: When holding the bucket lock of both the old and new table
+ * during expansions and shrinking, the old bucket lock must always be
+ * acquired first.
+ */
+static inline spinlock_t *rht_bucket_lock(const struct bucket_table *tbl,
+ unsigned int hash)
+{
+ return &tbl->locks[hash & tbl->locks_mask];
+}
+
+#ifdef CONFIG_PROVE_LOCKING
+int lockdep_rht_mutex_is_held(struct rhashtable *ht);
+int lockdep_rht_bucket_is_held(const struct bucket_table *tbl, u32 hash);
+#else
+static inline int lockdep_rht_mutex_is_held(struct rhashtable *ht)
+{
+ return 1;
+}
+
+static inline int lockdep_rht_bucket_is_held(const struct bucket_table *tbl,
+ u32 hash)
+{
+ return 1;
+}
+#endif /* CONFIG_PROVE_LOCKING */
+
+int rhashtable_init(struct rhashtable *ht,
+ const struct rhashtable_params *params);
+int rhltable_init(struct rhltable *hlt,
+ const struct rhashtable_params *params);
+
+void *rhashtable_insert_slow(struct rhashtable *ht, const void *key,
+ struct rhash_head *obj);
+
+void rhashtable_walk_enter(struct rhashtable *ht,
+ struct rhashtable_iter *iter);
+void rhashtable_walk_exit(struct rhashtable_iter *iter);
+int rhashtable_walk_start(struct rhashtable_iter *iter) __acquires(RCU);
+void *rhashtable_walk_next(struct rhashtable_iter *iter);
+void rhashtable_walk_stop(struct rhashtable_iter *iter) __releases(RCU);
+
+void rhashtable_free_and_destroy(struct rhashtable *ht,
+ void (*free_fn)(void *ptr, void *arg),
+ void *arg);
+void rhashtable_destroy(struct rhashtable *ht);
+
+struct rhash_head __rcu **rht_bucket_nested(const struct bucket_table *tbl,
+ unsigned int hash);
+struct rhash_head __rcu **rht_bucket_nested_insert(struct rhashtable *ht,
+ struct bucket_table *tbl,
+ unsigned int hash);
+
+#define rht_dereference(p, ht) \
+ rcu_dereference_protected(p, lockdep_rht_mutex_is_held(ht))
+
+#define rht_dereference_rcu(p, ht) \
+ rcu_dereference_check(p, lockdep_rht_mutex_is_held(ht))
+
+#define rht_dereference_bucket(p, tbl, hash) \
+ rcu_dereference_protected(p, lockdep_rht_bucket_is_held(tbl, hash))
+
+#define rht_dereference_bucket_rcu(p, tbl, hash) \
+ rcu_dereference_check(p, lockdep_rht_bucket_is_held(tbl, hash))
+
+#define rht_entry(tpos, pos, member) \
+ ({ tpos = container_of(pos, typeof(*tpos), member); 1; })
+
+static inline struct rhash_head __rcu *const *rht_bucket(
+ const struct bucket_table *tbl, unsigned int hash)
+{
+ return unlikely(tbl->nest) ? rht_bucket_nested(tbl, hash) :
+ &tbl->buckets[hash];
+}
+
+static inline struct rhash_head __rcu **rht_bucket_var(
+ struct bucket_table *tbl, unsigned int hash)
+{
+ return unlikely(tbl->nest) ? rht_bucket_nested(tbl, hash) :
+ &tbl->buckets[hash];
+}
+
+static inline struct rhash_head __rcu **rht_bucket_insert(
+ struct rhashtable *ht, struct bucket_table *tbl, unsigned int hash)
+{
+ return unlikely(tbl->nest) ? rht_bucket_nested_insert(ht, tbl, hash) :
+ &tbl->buckets[hash];
+}
+
+/**
+ * rht_for_each_continue - continue iterating over hash chain
+ * @pos: the &struct rhash_head to use as a loop cursor.
+ * @head: the previous &struct rhash_head to continue from
+ * @tbl: the &struct bucket_table
+ * @hash: the hash value / bucket index
+ */
+#define rht_for_each_continue(pos, head, tbl, hash) \
+ for (pos = rht_dereference_bucket(head, tbl, hash); \
+ !rht_is_a_nulls(pos); \
+ pos = rht_dereference_bucket((pos)->next, tbl, hash))
+
+/**
+ * rht_for_each - iterate over hash chain
+ * @pos: the &struct rhash_head to use as a loop cursor.
+ * @tbl: the &struct bucket_table
+ * @hash: the hash value / bucket index
+ */
+#define rht_for_each(pos, tbl, hash) \
+ rht_for_each_continue(pos, *rht_bucket(tbl, hash), tbl, hash)
+
+/**
+ * rht_for_each_entry_continue - continue iterating over hash chain
+ * @tpos: the type * to use as a loop cursor.
+ * @pos: the &struct rhash_head to use as a loop cursor.
+ * @head: the previous &struct rhash_head to continue from
+ * @tbl: the &struct bucket_table
+ * @hash: the hash value / bucket index
+ * @member: name of the &struct rhash_head within the hashable struct.
+ */
+#define rht_for_each_entry_continue(tpos, pos, head, tbl, hash, member) \
+ for (pos = rht_dereference_bucket(head, tbl, hash); \
+ (!rht_is_a_nulls(pos)) && rht_entry(tpos, pos, member); \
+ pos = rht_dereference_bucket((pos)->next, tbl, hash))
+
+/**
+ * rht_for_each_entry - iterate over hash chain of given type
+ * @tpos: the type * to use as a loop cursor.
+ * @pos: the &struct rhash_head to use as a loop cursor.
+ * @tbl: the &struct bucket_table
+ * @hash: the hash value / bucket index
+ * @member: name of the &struct rhash_head within the hashable struct.
+ */
+#define rht_for_each_entry(tpos, pos, tbl, hash, member) \
+ rht_for_each_entry_continue(tpos, pos, *rht_bucket(tbl, hash), \
+ tbl, hash, member)
+
+/**
+ * rht_for_each_entry_safe - safely iterate over hash chain of given type
+ * @tpos: the type * to use as a loop cursor.
+ * @pos: the &struct rhash_head to use as a loop cursor.
+ * @next: the &struct rhash_head to use as next in loop cursor.
+ * @tbl: the &struct bucket_table
+ * @hash: the hash value / bucket index
+ * @member: name of the &struct rhash_head within the hashable struct.
+ *
+ * This hash chain list-traversal primitive allows for the looped code to
+ * remove the loop cursor from the list.
+ */
+#define rht_for_each_entry_safe(tpos, pos, next, tbl, hash, member) \
+ for (pos = rht_dereference_bucket(*rht_bucket(tbl, hash), tbl, hash), \
+ next = !rht_is_a_nulls(pos) ? \
+ rht_dereference_bucket(pos->next, tbl, hash) : NULL; \
+ (!rht_is_a_nulls(pos)) && rht_entry(tpos, pos, member); \
+ pos = next, \
+ next = !rht_is_a_nulls(pos) ? \
+ rht_dereference_bucket(pos->next, tbl, hash) : NULL)
+
+/**
+ * rht_for_each_rcu_continue - continue iterating over rcu hash chain
+ * @pos: the &struct rhash_head to use as a loop cursor.
+ * @head: the previous &struct rhash_head to continue from
+ * @tbl: the &struct bucket_table
+ * @hash: the hash value / bucket index
+ *
+ * This hash chain list-traversal primitive may safely run concurrently with
+ * the _rcu mutation primitives such as rhashtable_insert() as long as the
+ * traversal is guarded by rcu_read_lock().
+ */
+#define rht_for_each_rcu_continue(pos, head, tbl, hash) \
+ for (({barrier(); }), \
+ pos = rht_dereference_bucket_rcu(head, tbl, hash); \
+ !rht_is_a_nulls(pos); \
+ pos = rcu_dereference_raw(pos->next))
+
+/**
+ * rht_for_each_rcu - iterate over rcu hash chain
+ * @pos: the &struct rhash_head to use as a loop cursor.
+ * @tbl: the &struct bucket_table
+ * @hash: the hash value / bucket index
+ *
+ * This hash chain list-traversal primitive may safely run concurrently with
+ * the _rcu mutation primitives such as rhashtable_insert() as long as the
+ * traversal is guarded by rcu_read_lock().
+ */
+#define rht_for_each_rcu(pos, tbl, hash) \
+ rht_for_each_rcu_continue(pos, *rht_bucket(tbl, hash), tbl, hash)
+
+/**
+ * rht_for_each_entry_rcu_continue - continue iterating over rcu hash chain
+ * @tpos: the type * to use as a loop cursor.
+ * @pos: the &struct rhash_head to use as a loop cursor.
+ * @head: the previous &struct rhash_head to continue from
+ * @tbl: the &struct bucket_table
+ * @hash: the hash value / bucket index
+ * @member: name of the &struct rhash_head within the hashable struct.
+ *
+ * This hash chain list-traversal primitive may safely run concurrently with
+ * the _rcu mutation primitives such as rhashtable_insert() as long as the
+ * traversal is guarded by rcu_read_lock().
+ */
+#define rht_for_each_entry_rcu_continue(tpos, pos, head, tbl, hash, member) \
+ for (({barrier(); }), \
+ pos = rht_dereference_bucket_rcu(head, tbl, hash); \
+ (!rht_is_a_nulls(pos)) && rht_entry(tpos, pos, member); \
+ pos = rht_dereference_bucket_rcu(pos->next, tbl, hash))
+
+/**
+ * rht_for_each_entry_rcu - iterate over rcu hash chain of given type
+ * @tpos: the type * to use as a loop cursor.
+ * @pos: the &struct rhash_head to use as a loop cursor.
+ * @tbl: the &struct bucket_table
+ * @hash: the hash value / bucket index
+ * @member: name of the &struct rhash_head within the hashable struct.
+ *
+ * This hash chain list-traversal primitive may safely run concurrently with
+ * the _rcu mutation primitives such as rhashtable_insert() as long as the
+ * traversal is guarded by rcu_read_lock().
+ */
+#define rht_for_each_entry_rcu(tpos, pos, tbl, hash, member) \
+ rht_for_each_entry_rcu_continue(tpos, pos, *rht_bucket(tbl, hash), \
+ tbl, hash, member)
+
+/**
+ * rhl_for_each_rcu - iterate over rcu hash table list
+ * @pos: the &struct rlist_head to use as a loop cursor.
+ * @list: the head of the list
+ *
+ * This hash chain list-traversal primitive should be used on the
+ * list returned by rhltable_lookup.
+ */
+#define rhl_for_each_rcu(pos, list) \
+ for (pos = list; pos; pos = rcu_dereference_raw(pos->next))
+
+/**
+ * rhl_for_each_entry_rcu - iterate over rcu hash table list of given type
+ * @tpos: the type * to use as a loop cursor.
+ * @pos: the &struct rlist_head to use as a loop cursor.
+ * @list: the head of the list
+ * @member: name of the &struct rlist_head within the hashable struct.
+ *
+ * This hash chain list-traversal primitive should be used on the
+ * list returned by rhltable_lookup.
+ */
+#define rhl_for_each_entry_rcu(tpos, pos, list, member) \
+ for (pos = list; pos && rht_entry(tpos, pos, member); \
+ pos = rcu_dereference_raw(pos->next))
+
+static inline int rhashtable_compare(struct rhashtable_compare_arg *arg,
+ const void *obj)
+{
+ struct rhashtable *ht = arg->ht;
+ const char *ptr = obj;
+
+ return memcmp(ptr + ht->p.key_offset, arg->key, ht->p.key_len);
+}
+
+/* Internal function, do not use. */
+static inline struct rhash_head *__rhashtable_lookup(
+ struct rhashtable *ht, const void *key,
+ const struct rhashtable_params params)
+{
+ struct rhashtable_compare_arg arg = {
+ .ht = ht,
+ .key = key,
+ };
+ struct bucket_table *tbl;
+ struct rhash_head *he;
+ unsigned int hash;
+
+ tbl = rht_dereference_rcu(ht->tbl, ht);
+restart:
+ hash = rht_key_hashfn(ht, tbl, key, params);
+ rht_for_each_rcu(he, tbl, hash) {
+ if (params.obj_cmpfn ?
+ params.obj_cmpfn(&arg, rht_obj(ht, he)) :
+ rhashtable_compare(&arg, rht_obj(ht, he)))
+ continue;
+ return he;
+ }
+
+ /* Ensure we see any new tables. */
+ smp_rmb();
+
+ tbl = rht_dereference_rcu(tbl->future_tbl, ht);
+ if (unlikely(tbl))
+ goto restart;
+
+ return NULL;
+}
+
+/**
+ * rhashtable_lookup - search hash table
+ * @ht: hash table
+ * @key: the pointer to the key
+ * @params: hash table parameters
+ *
+ * Computes the hash value for the key and traverses the bucket chain looking
+ * for a entry with an identical key. The first matching entry is returned.
+ *
+ * This must only be called under the RCU read lock.
+ *
+ * Returns the first entry on which the compare function returned true.
+ */
+static inline void *rhashtable_lookup(
+ struct rhashtable *ht, const void *key,
+ const struct rhashtable_params params)
+{
+ struct rhash_head *he = __rhashtable_lookup(ht, key, params);
+
+ return he ? rht_obj(ht, he) : NULL;
+}
+
+/**
+ * rhashtable_lookup_fast - search hash table, without RCU read lock
+ * @ht: hash table
+ * @key: the pointer to the key
+ * @params: hash table parameters
+ *
+ * Computes the hash value for the key and traverses the bucket chain looking
+ * for a entry with an identical key. The first matching entry is returned.
+ *
+ * Only use this function when you have other mechanisms guaranteeing
+ * that the object won't go away after the RCU read lock is released.
+ *
+ * Returns the first entry on which the compare function returned true.
+ */
+static inline void *rhashtable_lookup_fast(
+ struct rhashtable *ht, const void *key,
+ const struct rhashtable_params params)
+{
+ void *obj;
+
+ rcu_read_lock();
+ obj = rhashtable_lookup(ht, key, params);
+ rcu_read_unlock();
+
+ return obj;
+}
+
+/**
+ * rhltable_lookup - search hash list table
+ * @hlt: hash table
+ * @key: the pointer to the key
+ * @params: hash table parameters
+ *
+ * Computes the hash value for the key and traverses the bucket chain looking
+ * for a entry with an identical key. All matching entries are returned
+ * in a list.
+ *
+ * This must only be called under the RCU read lock.
+ *
+ * Returns the list of entries that match the given key.
+ */
+static inline struct rhlist_head *rhltable_lookup(
+ struct rhltable *hlt, const void *key,
+ const struct rhashtable_params params)
+{
+ struct rhash_head *he = __rhashtable_lookup(&hlt->ht, key, params);
+
+ return he ? container_of(he, struct rhlist_head, rhead) : NULL;
+}
+
+/* Internal function, please use rhashtable_insert_fast() instead. This
+ * function returns the existing element already in hashes in there is a clash,
+ * otherwise it returns an error via ERR_PTR().
+ */
+static inline void *__rhashtable_insert_fast(
+ struct rhashtable *ht, const void *key, struct rhash_head *obj,
+ const struct rhashtable_params params, bool rhlist)
+{
+ struct rhashtable_compare_arg arg = {
+ .ht = ht,
+ .key = key,
+ };
+ struct rhash_head __rcu **pprev;
+ struct bucket_table *tbl;
+ struct rhash_head *head;
+ spinlock_t *lock;
+ unsigned int hash;
+ int elasticity;
+ void *data;
+
+ rcu_read_lock();
+
+ tbl = rht_dereference_rcu(ht->tbl, ht);
+ hash = rht_head_hashfn(ht, tbl, obj, params);
+ lock = rht_bucket_lock(tbl, hash);
+ spin_lock_bh(lock);
+
+ if (unlikely(rht_dereference_bucket(tbl->future_tbl, tbl, hash))) {
+slow_path:
+ spin_unlock_bh(lock);
+ rcu_read_unlock();
+ return rhashtable_insert_slow(ht, key, obj);
+ }
+
+ elasticity = RHT_ELASTICITY;
+ pprev = rht_bucket_insert(ht, tbl, hash);
+ data = ERR_PTR(-ENOMEM);
+ if (!pprev)
+ goto out;
+
+ rht_for_each_continue(head, *pprev, tbl, hash) {
+ struct rhlist_head *plist;
+ struct rhlist_head *list;
+
+ elasticity--;
+ if (!key ||
+ (params.obj_cmpfn ?
+ params.obj_cmpfn(&arg, rht_obj(ht, head)) :
+ rhashtable_compare(&arg, rht_obj(ht, head))))
+ continue;
+
+ data = rht_obj(ht, head);
+
+ if (!rhlist)
+ goto out;
+
+
+ list = container_of(obj, struct rhlist_head, rhead);
+ plist = container_of(head, struct rhlist_head, rhead);
+
+ RCU_INIT_POINTER(list->next, plist);
+ head = rht_dereference_bucket(head->next, tbl, hash);
+ RCU_INIT_POINTER(list->rhead.next, head);
+ rcu_assign_pointer(*pprev, obj);
+
+ goto good;
+ }
+
+ if (elasticity <= 0)
+ goto slow_path;
+
+ data = ERR_PTR(-E2BIG);
+ if (unlikely(rht_grow_above_max(ht, tbl)))
+ goto out;
+
+ if (unlikely(rht_grow_above_100(ht, tbl)))
+ goto slow_path;
+
+ head = rht_dereference_bucket(*pprev, tbl, hash);
+
+ RCU_INIT_POINTER(obj->next, head);
+ if (rhlist) {
+ struct rhlist_head *list;
+
+ list = container_of(obj, struct rhlist_head, rhead);
+ RCU_INIT_POINTER(list->next, NULL);
+ }
+
+ rcu_assign_pointer(*pprev, obj);
+
+ atomic_inc(&ht->nelems);
+ if (rht_grow_above_75(ht, tbl))
+ schedule_work(&ht->run_work);
+
+good:
+ data = NULL;
+
+out:
+ spin_unlock_bh(lock);
+ rcu_read_unlock();
+
+ return data;
+}
+
+/**
+ * rhashtable_insert_fast - insert object into hash table
+ * @ht: hash table
+ * @obj: pointer to hash head inside object
+ * @params: hash table parameters
+ *
+ * Will take a per bucket spinlock to protect against mutual mutations
+ * on the same bucket. Multiple insertions may occur in parallel unless
+ * they map to the same bucket lock.
+ *
+ * It is safe to call this function from atomic context.
+ *
+ * Will trigger an automatic deferred table resizing if the size grows
+ * beyond the watermark indicated by grow_decision() which can be passed
+ * to rhashtable_init().
+ */
+static inline int rhashtable_insert_fast(
+ struct rhashtable *ht, struct rhash_head *obj,
+ const struct rhashtable_params params)
+{
+ void *ret;
+
+ ret = __rhashtable_insert_fast(ht, NULL, obj, params, false);
+ if (IS_ERR(ret))
+ return PTR_ERR(ret);
+
+ return ret == NULL ? 0 : -EEXIST;
+}
+
+/**
+ * rhltable_insert_key - insert object into hash list table
+ * @hlt: hash list table
+ * @key: the pointer to the key
+ * @list: pointer to hash list head inside object
+ * @params: hash table parameters
+ *
+ * Will take a per bucket spinlock to protect against mutual mutations
+ * on the same bucket. Multiple insertions may occur in parallel unless
+ * they map to the same bucket lock.
+ *
+ * It is safe to call this function from atomic context.
+ *
+ * Will trigger an automatic deferred table resizing if the size grows
+ * beyond the watermark indicated by grow_decision() which can be passed
+ * to rhashtable_init().
+ */
+static inline int rhltable_insert_key(
+ struct rhltable *hlt, const void *key, struct rhlist_head *list,
+ const struct rhashtable_params params)
+{
+ return PTR_ERR(__rhashtable_insert_fast(&hlt->ht, key, &list->rhead,
+ params, true));
+}
+
+/**
+ * rhltable_insert - insert object into hash list table
+ * @hlt: hash list table
+ * @list: pointer to hash list head inside object
+ * @params: hash table parameters
+ *
+ * Will take a per bucket spinlock to protect against mutual mutations
+ * on the same bucket. Multiple insertions may occur in parallel unless
+ * they map to the same bucket lock.
+ *
+ * It is safe to call this function from atomic context.
+ *
+ * Will trigger an automatic deferred table resizing if the size grows
+ * beyond the watermark indicated by grow_decision() which can be passed
+ * to rhashtable_init().
+ */
+static inline int rhltable_insert(
+ struct rhltable *hlt, struct rhlist_head *list,
+ const struct rhashtable_params params)
+{
+ const char *key = rht_obj(&hlt->ht, &list->rhead);
+
+ key += params.key_offset;
+
+ return rhltable_insert_key(hlt, key, list, params);
+}
+
+/**
+ * rhashtable_lookup_insert_fast - lookup and insert object into hash table
+ * @ht: hash table
+ * @obj: pointer to hash head inside object
+ * @params: hash table parameters
+ *
+ * Locks down the bucket chain in both the old and new table if a resize
+ * is in progress to ensure that writers can't remove from the old table
+ * and can't insert to the new table during the atomic operation of search
+ * and insertion. Searches for duplicates in both the old and new table if
+ * a resize is in progress.
+ *
+ * This lookup function may only be used for fixed key hash table (key_len
+ * parameter set). It will BUG() if used inappropriately.
+ *
+ * It is safe to call this function from atomic context.
+ *
+ * Will trigger an automatic deferred table resizing if the size grows
+ * beyond the watermark indicated by grow_decision() which can be passed
+ * to rhashtable_init().
+ */
+static inline int rhashtable_lookup_insert_fast(
+ struct rhashtable *ht, struct rhash_head *obj,
+ const struct rhashtable_params params)
+{
+ const char *key = rht_obj(ht, obj);
+ void *ret;
+
+ BUG_ON(ht->p.obj_hashfn);
+
+ ret = __rhashtable_insert_fast(ht, key + ht->p.key_offset, obj, params,
+ false);
+ if (IS_ERR(ret))
+ return PTR_ERR(ret);
+
+ return ret == NULL ? 0 : -EEXIST;
+}
+
+/**
+ * rhashtable_lookup_get_insert_fast - lookup and insert object into hash table
+ * @ht: hash table
+ * @obj: pointer to hash head inside object
+ * @params: hash table parameters
+ *
+ * Just like rhashtable_lookup_insert_fast(), but this function returns the
+ * object if it exists, NULL if it did not and the insertion was successful,
+ * and an ERR_PTR otherwise.
+ */
+static inline void *rhashtable_lookup_get_insert_fast(
+ struct rhashtable *ht, struct rhash_head *obj,
+ const struct rhashtable_params params)
+{
+ const char *key = rht_obj(ht, obj);
+
+ BUG_ON(ht->p.obj_hashfn);
+
+ return __rhashtable_insert_fast(ht, key + ht->p.key_offset, obj, params,
+ false);
+}
+
+/**
+ * rhashtable_lookup_insert_key - search and insert object to hash table
+ * with explicit key
+ * @ht: hash table
+ * @key: key
+ * @obj: pointer to hash head inside object
+ * @params: hash table parameters
+ *
+ * Locks down the bucket chain in both the old and new table if a resize
+ * is in progress to ensure that writers can't remove from the old table
+ * and can't insert to the new table during the atomic operation of search
+ * and insertion. Searches for duplicates in both the old and new table if
+ * a resize is in progress.
+ *
+ * Lookups may occur in parallel with hashtable mutations and resizing.
+ *
+ * Will trigger an automatic deferred table resizing if the size grows
+ * beyond the watermark indicated by grow_decision() which can be passed
+ * to rhashtable_init().
+ *
+ * Returns zero on success.
+ */
+static inline int rhashtable_lookup_insert_key(
+ struct rhashtable *ht, const void *key, struct rhash_head *obj,
+ const struct rhashtable_params params)
+{
+ void *ret;
+
+ BUG_ON(!ht->p.obj_hashfn || !key);
+
+ ret = __rhashtable_insert_fast(ht, key, obj, params, false);
+ if (IS_ERR(ret))
+ return PTR_ERR(ret);
+
+ return ret == NULL ? 0 : -EEXIST;
+}
+
+/**
+ * rhashtable_lookup_get_insert_key - lookup and insert object into hash table
+ * @ht: hash table
+ * @obj: pointer to hash head inside object
+ * @params: hash table parameters
+ * @data: pointer to element data already in hashes
+ *
+ * Just like rhashtable_lookup_insert_key(), but this function returns the
+ * object if it exists, NULL if it does not and the insertion was successful,
+ * and an ERR_PTR otherwise.
+ */
+static inline void *rhashtable_lookup_get_insert_key(
+ struct rhashtable *ht, const void *key, struct rhash_head *obj,
+ const struct rhashtable_params params)
+{
+ BUG_ON(!ht->p.obj_hashfn || !key);
+
+ return __rhashtable_insert_fast(ht, key, obj, params, false);
+}
+
+/* Internal function, please use rhashtable_remove_fast() instead */
+static inline int __rhashtable_remove_fast_one(
+ struct rhashtable *ht, struct bucket_table *tbl,
+ struct rhash_head *obj, const struct rhashtable_params params,
+ bool rhlist)
+{
+ struct rhash_head __rcu **pprev;
+ struct rhash_head *he;
+ spinlock_t * lock;
+ unsigned int hash;
+ int err = -ENOENT;
+
+ hash = rht_head_hashfn(ht, tbl, obj, params);
+ lock = rht_bucket_lock(tbl, hash);
+
+ spin_lock_bh(lock);
+
+ pprev = rht_bucket_var(tbl, hash);
+ rht_for_each_continue(he, *pprev, tbl, hash) {
+ struct rhlist_head *list;
+
+ list = container_of(he, struct rhlist_head, rhead);
+
+ if (he != obj) {
+ struct rhlist_head __rcu **lpprev;
+
+ pprev = &he->next;
+
+ if (!rhlist)
+ continue;
+
+ do {
+ lpprev = &list->next;
+ list = rht_dereference_bucket(list->next,
+ tbl, hash);
+ } while (list && obj != &list->rhead);
+
+ if (!list)
+ continue;
+
+ list = rht_dereference_bucket(list->next, tbl, hash);
+ RCU_INIT_POINTER(*lpprev, list);
+ err = 0;
+ break;
+ }
+
+ obj = rht_dereference_bucket(obj->next, tbl, hash);
+ err = 1;
+
+ if (rhlist) {
+ list = rht_dereference_bucket(list->next, tbl, hash);
+ if (list) {
+ RCU_INIT_POINTER(list->rhead.next, obj);
+ obj = &list->rhead;
+ err = 0;
+ }
+ }
+
+ rcu_assign_pointer(*pprev, obj);
+ break;
+ }
+
+ spin_unlock_bh(lock);
+
+ if (err > 0) {
+ atomic_dec(&ht->nelems);
+ if (unlikely(ht->p.automatic_shrinking &&
+ rht_shrink_below_30(ht, tbl)))
+ schedule_work(&ht->run_work);
+ err = 0;
+ }
+
+ return err;
+}
+
+/* Internal function, please use rhashtable_remove_fast() instead */
+static inline int __rhashtable_remove_fast(
+ struct rhashtable *ht, struct rhash_head *obj,
+ const struct rhashtable_params params, bool rhlist)
+{
+ struct bucket_table *tbl;
+ int err;
+
+ rcu_read_lock();
+
+ tbl = rht_dereference_rcu(ht->tbl, ht);
+
+ /* Because we have already taken (and released) the bucket
+ * lock in old_tbl, if we find that future_tbl is not yet
+ * visible then that guarantees the entry to still be in
+ * the old tbl if it exists.
+ */
+ while ((err = __rhashtable_remove_fast_one(ht, tbl, obj, params,
+ rhlist)) &&
+ (tbl = rht_dereference_rcu(tbl->future_tbl, ht)))
+ ;
+
+ rcu_read_unlock();
+
+ return err;
+}
+
+/**
+ * rhashtable_remove_fast - remove object from hash table
+ * @ht: hash table
+ * @obj: pointer to hash head inside object
+ * @params: hash table parameters
+ *
+ * Since the hash chain is single linked, the removal operation needs to
+ * walk the bucket chain upon removal. The removal operation is thus
+ * considerable slow if the hash table is not correctly sized.
+ *
+ * Will automatically shrink the table via rhashtable_expand() if the
+ * shrink_decision function specified at rhashtable_init() returns true.
+ *
+ * Returns zero on success, -ENOENT if the entry could not be found.
+ */
+static inline int rhashtable_remove_fast(
+ struct rhashtable *ht, struct rhash_head *obj,
+ const struct rhashtable_params params)
+{
+ return __rhashtable_remove_fast(ht, obj, params, false);
+}
+
+/**
+ * rhltable_remove - remove object from hash list table
+ * @hlt: hash list table
+ * @list: pointer to hash list head inside object
+ * @params: hash table parameters
+ *
+ * Since the hash chain is single linked, the removal operation needs to
+ * walk the bucket chain upon removal. The removal operation is thus
+ * considerable slow if the hash table is not correctly sized.
+ *
+ * Will automatically shrink the table via rhashtable_expand() if the
+ * shrink_decision function specified at rhashtable_init() returns true.
+ *
+ * Returns zero on success, -ENOENT if the entry could not be found.
+ */
+static inline int rhltable_remove(
+ struct rhltable *hlt, struct rhlist_head *list,
+ const struct rhashtable_params params)
+{
+ return __rhashtable_remove_fast(&hlt->ht, &list->rhead, params, true);
+}
+
+/* Internal function, please use rhashtable_replace_fast() instead */
+static inline int __rhashtable_replace_fast(
+ struct rhashtable *ht, struct bucket_table *tbl,
+ struct rhash_head *obj_old, struct rhash_head *obj_new,
+ const struct rhashtable_params params)
+{
+ struct rhash_head __rcu **pprev;
+ struct rhash_head *he;
+ spinlock_t *lock;
+ unsigned int hash;
+ int err = -ENOENT;
+
+ /* Minimally, the old and new objects must have same hash
+ * (which should mean identifiers are the same).
+ */
+ hash = rht_head_hashfn(ht, tbl, obj_old, params);
+ if (hash != rht_head_hashfn(ht, tbl, obj_new, params))
+ return -EINVAL;
+
+ lock = rht_bucket_lock(tbl, hash);
+
+ spin_lock_bh(lock);
+
+ pprev = rht_bucket_var(tbl, hash);
+ rht_for_each_continue(he, *pprev, tbl, hash) {
+ if (he != obj_old) {
+ pprev = &he->next;
+ continue;
+ }
+
+ rcu_assign_pointer(obj_new->next, obj_old->next);
+ rcu_assign_pointer(*pprev, obj_new);
+ err = 0;
+ break;
+ }
+
+ spin_unlock_bh(lock);
+
+ return err;
+}
+
+/**
+ * rhashtable_replace_fast - replace an object in hash table
+ * @ht: hash table
+ * @obj_old: pointer to hash head inside object being replaced
+ * @obj_new: pointer to hash head inside object which is new
+ * @params: hash table parameters
+ *
+ * Replacing an object doesn't affect the number of elements in the hash table
+ * or bucket, so we don't need to worry about shrinking or expanding the
+ * table here.
+ *
+ * Returns zero on success, -ENOENT if the entry could not be found,
+ * -EINVAL if hash is not the same for the old and new objects.
+ */
+static inline int rhashtable_replace_fast(
+ struct rhashtable *ht, struct rhash_head *obj_old,
+ struct rhash_head *obj_new,
+ const struct rhashtable_params params)
+{
+ struct bucket_table *tbl;
+ int err;
+
+ rcu_read_lock();
+
+ tbl = rht_dereference_rcu(ht->tbl, ht);
+
+ /* Because we have already taken (and released) the bucket
+ * lock in old_tbl, if we find that future_tbl is not yet
+ * visible then that guarantees the entry to still be in
+ * the old tbl if it exists.
+ */
+ while ((err = __rhashtable_replace_fast(ht, tbl, obj_old,
+ obj_new, params)) &&
+ (tbl = rht_dereference_rcu(tbl->future_tbl, ht)))
+ ;
+
+ rcu_read_unlock();
+
+ return err;
+}
+
+/* Obsolete function, do not use in new code. */
+static inline int rhashtable_walk_init(struct rhashtable *ht,
+ struct rhashtable_iter *iter, gfp_t gfp)
+{
+ rhashtable_walk_enter(ht, iter);
+ return 0;
+}
+
+/**
+ * rhltable_walk_enter - Initialise an iterator
+ * @hlt: Table to walk over
+ * @iter: Hash table Iterator
+ *
+ * This function prepares a hash table walk.
+ *
+ * Note that if you restart a walk after rhashtable_walk_stop you
+ * may see the same object twice. Also, you may miss objects if
+ * there are removals in between rhashtable_walk_stop and the next
+ * call to rhashtable_walk_start.
+ *
+ * For a completely stable walk you should construct your own data
+ * structure outside the hash table.
+ *
+ * This function may sleep so you must not call it from interrupt
+ * context or with spin locks held.
+ *
+ * You must call rhashtable_walk_exit after this function returns.
+ */
+static inline void rhltable_walk_enter(struct rhltable *hlt,
+ struct rhashtable_iter *iter)
+{
+ return rhashtable_walk_enter(&hlt->ht, iter);
+}
+
+/**
+ * rhltable_free_and_destroy - free elements and destroy hash list table
+ * @hlt: the hash list table to destroy
+ * @free_fn: callback to release resources of element
+ * @arg: pointer passed to free_fn
+ *
+ * See documentation for rhashtable_free_and_destroy.
+ */
+static inline void rhltable_free_and_destroy(struct rhltable *hlt,
+ void (*free_fn)(void *ptr,
+ void *arg),
+ void *arg)
+{
+ return rhashtable_free_and_destroy(&hlt->ht, free_fn, arg);
+}
+
+static inline void rhltable_destroy(struct rhltable *hlt)
+{
+ return rhltable_free_and_destroy(hlt, NULL, NULL);
+}
+
+#endif /* _LINUX_RHASHTABLE_H */
diff --git a/include/linux/bcma/bcma.h b/include/linux/bcma/bcma.h
new file mode 100644
index 0000000..8eeedb2
--- /dev/null
+++ b/include/linux/bcma/bcma.h
@@ -0,0 +1,493 @@
+#ifndef LINUX_BCMA_H_
+#define LINUX_BCMA_H_
+
+#include <linux/pci.h>
+#include <linux/mod_devicetable.h>
+
+#include <linux/bcma/bcma_driver_arm_c9.h>
+#include <linux/bcma/bcma_driver_chipcommon.h>
+#include <linux/bcma/bcma_driver_pci.h>
+#include <linux/bcma/bcma_driver_pcie2.h>
+#include <linux/bcma/bcma_driver_mips.h>
+#include <linux/bcma/bcma_driver_gmac_cmn.h>
+#include <linux/ssb/ssb.h> /* SPROM sharing */
+
+#include <linux/bcma/bcma_regs.h>
+
+struct bcma_device;
+struct bcma_bus;
+
+enum bcma_hosttype {
+ BCMA_HOSTTYPE_PCI,
+ BCMA_HOSTTYPE_SDIO,
+ BCMA_HOSTTYPE_SOC,
+};
+
+struct bcma_chipinfo {
+ u16 id;
+ u8 rev;
+ u8 pkg;
+};
+
+struct bcma_boardinfo {
+ u16 vendor;
+ u16 type;
+};
+
+enum bcma_clkmode {
+ BCMA_CLKMODE_FAST,
+ BCMA_CLKMODE_DYNAMIC,
+};
+
+struct bcma_host_ops {
+ u8 (*read8)(struct bcma_device *core, u16 offset);
+ u16 (*read16)(struct bcma_device *core, u16 offset);
+ u32 (*read32)(struct bcma_device *core, u16 offset);
+ void (*write8)(struct bcma_device *core, u16 offset, u8 value);
+ void (*write16)(struct bcma_device *core, u16 offset, u16 value);
+ void (*write32)(struct bcma_device *core, u16 offset, u32 value);
+#ifdef CONFIG_BCMA_BLOCKIO
+ void (*block_read)(struct bcma_device *core, void *buffer,
+ size_t count, u16 offset, u8 reg_width);
+ void (*block_write)(struct bcma_device *core, const void *buffer,
+ size_t count, u16 offset, u8 reg_width);
+#endif
+ /* Agent ops */
+ u32 (*aread32)(struct bcma_device *core, u16 offset);
+ void (*awrite32)(struct bcma_device *core, u16 offset, u32 value);
+};
+
+/* Core manufacturers */
+#define BCMA_MANUF_ARM 0x43B
+#define BCMA_MANUF_MIPS 0x4A7
+#define BCMA_MANUF_BCM 0x4BF
+
+/* Core class values. */
+#define BCMA_CL_SIM 0x0
+#define BCMA_CL_EROM 0x1
+#define BCMA_CL_CORESIGHT 0x9
+#define BCMA_CL_VERIF 0xB
+#define BCMA_CL_OPTIMO 0xD
+#define BCMA_CL_GEN 0xE
+#define BCMA_CL_PRIMECELL 0xF
+
+/* Core-ID values. */
+#define BCMA_CORE_OOB_ROUTER 0x367 /* Out of band */
+#define BCMA_CORE_4706_CHIPCOMMON 0x500
+#define BCMA_CORE_NS_PCIEG2 0x501
+#define BCMA_CORE_NS_DMA 0x502
+#define BCMA_CORE_NS_SDIO3 0x503
+#define BCMA_CORE_NS_USB20 0x504
+#define BCMA_CORE_NS_USB30 0x505
+#define BCMA_CORE_NS_A9JTAG 0x506
+#define BCMA_CORE_NS_DDR23 0x507
+#define BCMA_CORE_NS_ROM 0x508
+#define BCMA_CORE_NS_NAND 0x509
+#define BCMA_CORE_NS_QSPI 0x50A
+#define BCMA_CORE_NS_CHIPCOMMON_B 0x50B
+#define BCMA_CORE_4706_SOC_RAM 0x50E
+#define BCMA_CORE_ARMCA9 0x510
+#define BCMA_CORE_4706_MAC_GBIT 0x52D
+#define BCMA_CORE_AMEMC 0x52E /* DDR1/2 memory controller core */
+#define BCMA_CORE_ALTA 0x534 /* I2S core */
+#define BCMA_CORE_4706_MAC_GBIT_COMMON 0x5DC
+#define BCMA_CORE_DDR23_PHY 0x5DD
+#define BCMA_CORE_INVALID 0x700
+#define BCMA_CORE_CHIPCOMMON 0x800
+#define BCMA_CORE_ILINE20 0x801
+#define BCMA_CORE_SRAM 0x802
+#define BCMA_CORE_SDRAM 0x803
+#define BCMA_CORE_PCI 0x804
+#define BCMA_CORE_MIPS 0x805
+#define BCMA_CORE_ETHERNET 0x806
+#define BCMA_CORE_V90 0x807
+#define BCMA_CORE_USB11_HOSTDEV 0x808
+#define BCMA_CORE_ADSL 0x809
+#define BCMA_CORE_ILINE100 0x80A
+#define BCMA_CORE_IPSEC 0x80B
+#define BCMA_CORE_UTOPIA 0x80C
+#define BCMA_CORE_PCMCIA 0x80D
+#define BCMA_CORE_INTERNAL_MEM 0x80E
+#define BCMA_CORE_MEMC_SDRAM 0x80F
+#define BCMA_CORE_OFDM 0x810
+#define BCMA_CORE_EXTIF 0x811
+#define BCMA_CORE_80211 0x812
+#define BCMA_CORE_PHY_A 0x813
+#define BCMA_CORE_PHY_B 0x814
+#define BCMA_CORE_PHY_G 0x815
+#define BCMA_CORE_MIPS_3302 0x816
+#define BCMA_CORE_USB11_HOST 0x817
+#define BCMA_CORE_USB11_DEV 0x818
+#define BCMA_CORE_USB20_HOST 0x819
+#define BCMA_CORE_USB20_DEV 0x81A
+#define BCMA_CORE_SDIO_HOST 0x81B
+#define BCMA_CORE_ROBOSWITCH 0x81C
+#define BCMA_CORE_PARA_ATA 0x81D
+#define BCMA_CORE_SATA_XORDMA 0x81E
+#define BCMA_CORE_ETHERNET_GBIT 0x81F
+#define BCMA_CORE_PCIE 0x820
+#define BCMA_CORE_PHY_N 0x821
+#define BCMA_CORE_SRAM_CTL 0x822
+#define BCMA_CORE_MINI_MACPHY 0x823
+#define BCMA_CORE_ARM_1176 0x824
+#define BCMA_CORE_ARM_7TDMI 0x825
+#define BCMA_CORE_PHY_LP 0x826
+#define BCMA_CORE_PMU 0x827
+#define BCMA_CORE_PHY_SSN 0x828
+#define BCMA_CORE_SDIO_DEV 0x829
+#define BCMA_CORE_ARM_CM3 0x82A
+#define BCMA_CORE_PHY_HT 0x82B
+#define BCMA_CORE_MIPS_74K 0x82C
+#define BCMA_CORE_MAC_GBIT 0x82D
+#define BCMA_CORE_DDR12_MEM_CTL 0x82E
+#define BCMA_CORE_PCIE_RC 0x82F /* PCIe Root Complex */
+#define BCMA_CORE_OCP_OCP_BRIDGE 0x830
+#define BCMA_CORE_SHARED_COMMON 0x831
+#define BCMA_CORE_OCP_AHB_BRIDGE 0x832
+#define BCMA_CORE_SPI_HOST 0x833
+#define BCMA_CORE_I2S 0x834
+#define BCMA_CORE_SDR_DDR1_MEM_CTL 0x835 /* SDR/DDR1 memory controller core */
+#define BCMA_CORE_SHIM 0x837 /* SHIM component in ubus/6362 */
+#define BCMA_CORE_PHY_AC 0x83B
+#define BCMA_CORE_PCIE2 0x83C /* PCI Express Gen2 */
+#define BCMA_CORE_USB30_DEV 0x83D
+#define BCMA_CORE_ARM_CR4 0x83E
+#define BCMA_CORE_GCI 0x840
+#define BCMA_CORE_CMEM 0x846 /* CNDS DDR2/3 memory controller */
+#define BCMA_CORE_ARM_CA7 0x847
+#define BCMA_CORE_SYS_MEM 0x849
+#define BCMA_CORE_DEFAULT 0xFFF
+
+#define BCMA_MAX_NR_CORES 16
+#define BCMA_CORE_SIZE 0x1000
+
+/* Chip IDs of PCIe devices */
+#define BCMA_CHIP_ID_BCM4313 0x4313
+#define BCMA_CHIP_ID_BCM43142 43142
+#define BCMA_CHIP_ID_BCM43131 43131
+#define BCMA_CHIP_ID_BCM43217 43217
+#define BCMA_CHIP_ID_BCM43222 43222
+#define BCMA_CHIP_ID_BCM43224 43224
+#define BCMA_PKG_ID_BCM43224_FAB_CSM 0x8
+#define BCMA_PKG_ID_BCM43224_FAB_SMIC 0xa
+#define BCMA_CHIP_ID_BCM43225 43225
+#define BCMA_CHIP_ID_BCM43227 43227
+#define BCMA_CHIP_ID_BCM43228 43228
+#define BCMA_CHIP_ID_BCM43421 43421
+#define BCMA_CHIP_ID_BCM43428 43428
+#define BCMA_CHIP_ID_BCM43431 43431
+#define BCMA_CHIP_ID_BCM43460 43460
+#define BCMA_CHIP_ID_BCM4331 0x4331
+#define BCMA_CHIP_ID_BCM6362 0x6362
+#define BCMA_CHIP_ID_BCM4360 0x4360
+#define BCMA_CHIP_ID_BCM4352 0x4352
+
+/* Chip IDs of SoCs */
+#define BCMA_CHIP_ID_BCM4706 0x5300
+#define BCMA_PKG_ID_BCM4706L 1
+#define BCMA_CHIP_ID_BCM4716 0x4716
+#define BCMA_PKG_ID_BCM4716 8
+#define BCMA_PKG_ID_BCM4717 9
+#define BCMA_PKG_ID_BCM4718 10
+#define BCMA_CHIP_ID_BCM47162 47162
+#define BCMA_CHIP_ID_BCM4748 0x4748
+#define BCMA_CHIP_ID_BCM4749 0x4749
+#define BCMA_CHIP_ID_BCM5356 0x5356
+#define BCMA_CHIP_ID_BCM5357 0x5357
+#define BCMA_PKG_ID_BCM5358 9
+#define BCMA_PKG_ID_BCM47186 10
+#define BCMA_PKG_ID_BCM5357 11
+#define BCMA_CHIP_ID_BCM53572 53572
+#define BCMA_PKG_ID_BCM47188 9
+#define BCMA_CHIP_ID_BCM4707 53010
+#define BCMA_PKG_ID_BCM4707 1
+#define BCMA_PKG_ID_BCM4708 2
+#define BCMA_PKG_ID_BCM4709 0
+#define BCMA_CHIP_ID_BCM47094 53030
+#define BCMA_CHIP_ID_BCM53018 53018
+#define BCMA_CHIP_ID_BCM53573 53573
+#define BCMA_PKG_ID_BCM53573 0
+#define BCMA_PKG_ID_BCM47189 1
+
+/* Board types (on PCI usually equals to the subsystem dev id) */
+/* BCM4313 */
+#define BCMA_BOARD_TYPE_BCM94313BU 0X050F
+#define BCMA_BOARD_TYPE_BCM94313HM 0X0510
+#define BCMA_BOARD_TYPE_BCM94313EPA 0X0511
+#define BCMA_BOARD_TYPE_BCM94313HMG 0X051C
+/* BCM4716 */
+#define BCMA_BOARD_TYPE_BCM94716NR2 0X04CD
+/* BCM43224 */
+#define BCMA_BOARD_TYPE_BCM943224X21 0X056E
+#define BCMA_BOARD_TYPE_BCM943224X21_FCC 0X00D1
+#define BCMA_BOARD_TYPE_BCM943224X21B 0X00E9
+#define BCMA_BOARD_TYPE_BCM943224M93 0X008B
+#define BCMA_BOARD_TYPE_BCM943224M93A 0X0090
+#define BCMA_BOARD_TYPE_BCM943224X16 0X0093
+#define BCMA_BOARD_TYPE_BCM94322X9 0X008D
+#define BCMA_BOARD_TYPE_BCM94322M35E 0X008E
+/* BCM43228 */
+#define BCMA_BOARD_TYPE_BCM943228BU8 0X0540
+#define BCMA_BOARD_TYPE_BCM943228BU9 0X0541
+#define BCMA_BOARD_TYPE_BCM943228BU 0X0542
+#define BCMA_BOARD_TYPE_BCM943227HM4L 0X0543
+#define BCMA_BOARD_TYPE_BCM943227HMB 0X0544
+#define BCMA_BOARD_TYPE_BCM943228HM4L 0X0545
+#define BCMA_BOARD_TYPE_BCM943228SD 0X0573
+/* BCM4331 */
+#define BCMA_BOARD_TYPE_BCM94331X19 0X00D6
+#define BCMA_BOARD_TYPE_BCM94331X28 0X00E4
+#define BCMA_BOARD_TYPE_BCM94331X28B 0X010E
+#define BCMA_BOARD_TYPE_BCM94331PCIEBT3AX 0X00E4
+#define BCMA_BOARD_TYPE_BCM94331X12_2G 0X00EC
+#define BCMA_BOARD_TYPE_BCM94331X12_5G 0X00ED
+#define BCMA_BOARD_TYPE_BCM94331X29B 0X00EF
+#define BCMA_BOARD_TYPE_BCM94331CSAX 0X00EF
+#define BCMA_BOARD_TYPE_BCM94331X19C 0X00F5
+#define BCMA_BOARD_TYPE_BCM94331X33 0X00F4
+#define BCMA_BOARD_TYPE_BCM94331BU 0X0523
+#define BCMA_BOARD_TYPE_BCM94331S9BU 0X0524
+#define BCMA_BOARD_TYPE_BCM94331MC 0X0525
+#define BCMA_BOARD_TYPE_BCM94331MCI 0X0526
+#define BCMA_BOARD_TYPE_BCM94331PCIEBT4 0X0527
+#define BCMA_BOARD_TYPE_BCM94331HM 0X0574
+#define BCMA_BOARD_TYPE_BCM94331PCIEDUAL 0X059B
+#define BCMA_BOARD_TYPE_BCM94331MCH5 0X05A9
+#define BCMA_BOARD_TYPE_BCM94331CS 0X05C6
+#define BCMA_BOARD_TYPE_BCM94331CD 0X05DA
+/* BCM53572 */
+#define BCMA_BOARD_TYPE_BCM953572BU 0X058D
+#define BCMA_BOARD_TYPE_BCM953572NR2 0X058E
+#define BCMA_BOARD_TYPE_BCM947188NR2 0X058F
+#define BCMA_BOARD_TYPE_BCM953572SDRNR2 0X0590
+/* BCM43142 */
+#define BCMA_BOARD_TYPE_BCM943142HM 0X05E0
+
+struct bcma_device {
+ struct bcma_bus *bus;
+ struct bcma_device_id id;
+
+ struct device dev;
+ struct device *dma_dev;
+
+ unsigned int irq;
+ bool dev_registered;
+
+ u8 core_index;
+ u8 core_unit;
+
+ u32 addr;
+ u32 addr_s[8];
+ u32 wrap;
+
+ void __iomem *io_addr;
+ void __iomem *io_wrap;
+
+ void *drvdata;
+ struct list_head list;
+};
+
+static inline void *bcma_get_drvdata(struct bcma_device *core)
+{
+ return core->drvdata;
+}
+static inline void bcma_set_drvdata(struct bcma_device *core, void *drvdata)
+{
+ core->drvdata = drvdata;
+}
+
+struct bcma_driver {
+ const char *name;
+ const struct bcma_device_id *id_table;
+
+ int (*probe)(struct bcma_device *dev);
+ void (*remove)(struct bcma_device *dev);
+ int (*suspend)(struct bcma_device *dev);
+ int (*resume)(struct bcma_device *dev);
+ void (*shutdown)(struct bcma_device *dev);
+
+ struct device_driver drv;
+};
+extern
+int __bcma_driver_register(struct bcma_driver *drv, struct module *owner);
+#define bcma_driver_register(drv) \
+ __bcma_driver_register(drv, THIS_MODULE)
+
+extern void bcma_driver_unregister(struct bcma_driver *drv);
+
+/* module_bcma_driver() - Helper macro for drivers that don't do
+ * anything special in module init/exit. This eliminates a lot of
+ * boilerplate. Each module may only use this macro once, and
+ * calling it replaces module_init() and module_exit()
+ */
+#define module_bcma_driver(__bcma_driver) \
+ module_driver(__bcma_driver, bcma_driver_register, \
+ bcma_driver_unregister)
+
+/* Set a fallback SPROM.
+ * See kdoc at the function definition for complete documentation. */
+extern int bcma_arch_register_fallback_sprom(
+ int (*sprom_callback)(struct bcma_bus *bus,
+ struct ssb_sprom *out));
+
+struct bcma_bus {
+ /* The MMIO area. */
+ void __iomem *mmio;
+
+ const struct bcma_host_ops *ops;
+
+ enum bcma_hosttype hosttype;
+ bool host_is_pcie2; /* Used for BCMA_HOSTTYPE_PCI only */
+ union {
+ /* Pointer to the PCI bus (only for BCMA_HOSTTYPE_PCI) */
+ struct pci_dev *host_pci;
+ /* Pointer to the SDIO device (only for BCMA_HOSTTYPE_SDIO) */
+ struct sdio_func *host_sdio;
+ /* Pointer to platform device (only for BCMA_HOSTTYPE_SOC) */
+ struct platform_device *host_pdev;
+ };
+
+ struct bcma_chipinfo chipinfo;
+
+ struct bcma_boardinfo boardinfo;
+
+ struct bcma_device *mapped_core;
+ struct list_head cores;
+ u8 nr_cores;
+ u8 num;
+
+ struct bcma_drv_cc drv_cc;
+ struct bcma_drv_cc_b drv_cc_b;
+ struct bcma_drv_pci drv_pci[2];
+ struct bcma_drv_pcie2 drv_pcie2;
+ struct bcma_drv_mips drv_mips;
+ struct bcma_drv_gmac_cmn drv_gmac_cmn;
+
+ /* We decided to share SPROM struct with SSB as long as we do not need
+ * any hacks for BCMA. This simplifies drivers code. */
+ struct ssb_sprom sprom;
+};
+
+static inline u32 bcma_read8(struct bcma_device *core, u16 offset)
+{
+ return core->bus->ops->read8(core, offset);
+}
+static inline u32 bcma_read16(struct bcma_device *core, u16 offset)
+{
+ return core->bus->ops->read16(core, offset);
+}
+static inline u32 bcma_read32(struct bcma_device *core, u16 offset)
+{
+ return core->bus->ops->read32(core, offset);
+}
+static inline
+void bcma_write8(struct bcma_device *core, u16 offset, u32 value)
+{
+ core->bus->ops->write8(core, offset, value);
+}
+static inline
+void bcma_write16(struct bcma_device *core, u16 offset, u32 value)
+{
+ core->bus->ops->write16(core, offset, value);
+}
+static inline
+void bcma_write32(struct bcma_device *core, u16 offset, u32 value)
+{
+ core->bus->ops->write32(core, offset, value);
+}
+#ifdef CONFIG_BCMA_BLOCKIO
+static inline void bcma_block_read(struct bcma_device *core, void *buffer,
+ size_t count, u16 offset, u8 reg_width)
+{
+ core->bus->ops->block_read(core, buffer, count, offset, reg_width);
+}
+static inline void bcma_block_write(struct bcma_device *core,
+ const void *buffer, size_t count,
+ u16 offset, u8 reg_width)
+{
+ core->bus->ops->block_write(core, buffer, count, offset, reg_width);
+}
+#endif
+static inline u32 bcma_aread32(struct bcma_device *core, u16 offset)
+{
+ return core->bus->ops->aread32(core, offset);
+}
+static inline
+void bcma_awrite32(struct bcma_device *core, u16 offset, u32 value)
+{
+ core->bus->ops->awrite32(core, offset, value);
+}
+
+static inline void bcma_mask32(struct bcma_device *cc, u16 offset, u32 mask)
+{
+ bcma_write32(cc, offset, bcma_read32(cc, offset) & mask);
+}
+static inline void bcma_set32(struct bcma_device *cc, u16 offset, u32 set)
+{
+ bcma_write32(cc, offset, bcma_read32(cc, offset) | set);
+}
+static inline void bcma_maskset32(struct bcma_device *cc,
+ u16 offset, u32 mask, u32 set)
+{
+ bcma_write32(cc, offset, (bcma_read32(cc, offset) & mask) | set);
+}
+static inline void bcma_mask16(struct bcma_device *cc, u16 offset, u16 mask)
+{
+ bcma_write16(cc, offset, bcma_read16(cc, offset) & mask);
+}
+static inline void bcma_set16(struct bcma_device *cc, u16 offset, u16 set)
+{
+ bcma_write16(cc, offset, bcma_read16(cc, offset) | set);
+}
+static inline void bcma_maskset16(struct bcma_device *cc,
+ u16 offset, u16 mask, u16 set)
+{
+ bcma_write16(cc, offset, (bcma_read16(cc, offset) & mask) | set);
+}
+
+extern struct bcma_device *bcma_find_core_unit(struct bcma_bus *bus, u16 coreid,
+ u8 unit);
+static inline struct bcma_device *bcma_find_core(struct bcma_bus *bus,
+ u16 coreid)
+{
+ return bcma_find_core_unit(bus, coreid, 0);
+}
+
+#ifdef CONFIG_BCMA_HOST_PCI
+extern void bcma_host_pci_up(struct bcma_bus *bus);
+extern void bcma_host_pci_down(struct bcma_bus *bus);
+extern int bcma_host_pci_irq_ctl(struct bcma_bus *bus,
+ struct bcma_device *core, bool enable);
+#else
+static inline void bcma_host_pci_up(struct bcma_bus *bus)
+{
+}
+static inline void bcma_host_pci_down(struct bcma_bus *bus)
+{
+}
+static inline int bcma_host_pci_irq_ctl(struct bcma_bus *bus,
+ struct bcma_device *core, bool enable)
+{
+ if (bus->hosttype == BCMA_HOSTTYPE_PCI)
+ return -ENOTSUPP;
+ return 0;
+}
+#endif
+
+extern bool bcma_core_is_enabled(struct bcma_device *core);
+extern void bcma_core_disable(struct bcma_device *core, u32 flags);
+extern int bcma_core_enable(struct bcma_device *core, u32 flags);
+extern void bcma_core_set_clockmode(struct bcma_device *core,
+ enum bcma_clkmode clkmode);
+extern void bcma_core_pll_ctl(struct bcma_device *core, u32 req, u32 status,
+ bool on);
+extern u32 bcma_chipco_pll_read(struct bcma_drv_cc *cc, u32 offset);
+#define BCMA_DMA_TRANSLATION_MASK 0xC0000000
+#define BCMA_DMA_TRANSLATION_NONE 0x00000000
+#define BCMA_DMA_TRANSLATION_DMA32_CMT 0x40000000 /* Client Mode Translation for 32-bit DMA */
+#define BCMA_DMA_TRANSLATION_DMA64_CMT 0x80000000 /* Client Mode Translation for 64-bit DMA */
+extern u32 bcma_core_dma_translation(struct bcma_device *core);
+
+extern unsigned int bcma_core_irq(struct bcma_device *core, int num);
+
+#endif /* LINUX_BCMA_H_ */
diff --git a/include/linux/bcma/bcma_driver_arm_c9.h b/include/linux/bcma/bcma_driver_arm_c9.h
new file mode 100644
index 0000000..93bd73d
--- /dev/null
+++ b/include/linux/bcma/bcma_driver_arm_c9.h
@@ -0,0 +1,15 @@
+#ifndef LINUX_BCMA_DRIVER_ARM_C9_H_
+#define LINUX_BCMA_DRIVER_ARM_C9_H_
+
+/* DMU (Device Management Unit) */
+#define BCMA_DMU_CRU_USB2_CONTROL 0x0164
+#define BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_NDIV_MASK 0x00000FFC
+#define BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_NDIV_SHIFT 2
+#define BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_PDIV_MASK 0x00007000
+#define BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_PDIV_SHIFT 12
+#define BCMA_DMU_CRU_CLKSET_KEY 0x0180
+#define BCMA_DMU_CRU_STRAPS_CTRL 0x02A0
+#define BCMA_DMU_CRU_STRAPS_CTRL_USB3 0x00000010
+#define BCMA_DMU_CRU_STRAPS_CTRL_4BYTE 0x00008000
+
+#endif /* LINUX_BCMA_DRIVER_ARM_C9_H_ */
diff --git a/include/linux/bcma/bcma_driver_chipcommon.h b/include/linux/bcma/bcma_driver_chipcommon.h
new file mode 100644
index 0000000..2f1c690
--- /dev/null
+++ b/include/linux/bcma/bcma_driver_chipcommon.h
@@ -0,0 +1,715 @@
+#ifndef LINUX_BCMA_DRIVER_CC_H_
+#define LINUX_BCMA_DRIVER_CC_H_
+
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+
+/** ChipCommon core registers. **/
+#define BCMA_CC_ID 0x0000
+#define BCMA_CC_ID_ID 0x0000FFFF
+#define BCMA_CC_ID_ID_SHIFT 0
+#define BCMA_CC_ID_REV 0x000F0000
+#define BCMA_CC_ID_REV_SHIFT 16
+#define BCMA_CC_ID_PKG 0x00F00000
+#define BCMA_CC_ID_PKG_SHIFT 20
+#define BCMA_CC_ID_NRCORES 0x0F000000
+#define BCMA_CC_ID_NRCORES_SHIFT 24
+#define BCMA_CC_ID_TYPE 0xF0000000
+#define BCMA_CC_ID_TYPE_SHIFT 28
+#define BCMA_CC_CAP 0x0004 /* Capabilities */
+#define BCMA_CC_CAP_NRUART 0x00000003 /* # of UARTs */
+#define BCMA_CC_CAP_MIPSEB 0x00000004 /* MIPS in BigEndian Mode */
+#define BCMA_CC_CAP_UARTCLK 0x00000018 /* UART clock select */
+#define BCMA_CC_CAP_UARTCLK_INT 0x00000008 /* UARTs are driven by internal divided clock */
+#define BCMA_CC_CAP_UARTGPIO 0x00000020 /* UARTs on GPIO 15-12 */
+#define BCMA_CC_CAP_EXTBUS 0x000000C0 /* External buses present */
+#define BCMA_CC_CAP_FLASHT 0x00000700 /* Flash Type */
+#define BCMA_CC_FLASHT_NONE 0x00000000 /* No flash */
+#define BCMA_CC_FLASHT_STSER 0x00000100 /* ST serial flash */
+#define BCMA_CC_FLASHT_ATSER 0x00000200 /* Atmel serial flash */
+#define BCMA_CC_FLASHT_NAND 0x00000300 /* NAND flash */
+#define BCMA_CC_FLASHT_PARA 0x00000700 /* Parallel flash */
+#define BCMA_CC_CAP_PLLT 0x00038000 /* PLL Type */
+#define BCMA_PLLTYPE_NONE 0x00000000
+#define BCMA_PLLTYPE_1 0x00010000 /* 48Mhz base, 3 dividers */
+#define BCMA_PLLTYPE_2 0x00020000 /* 48Mhz, 4 dividers */
+#define BCMA_PLLTYPE_3 0x00030000 /* 25Mhz, 2 dividers */
+#define BCMA_PLLTYPE_4 0x00008000 /* 48Mhz, 4 dividers */
+#define BCMA_PLLTYPE_5 0x00018000 /* 25Mhz, 4 dividers */
+#define BCMA_PLLTYPE_6 0x00028000 /* 100/200 or 120/240 only */
+#define BCMA_PLLTYPE_7 0x00038000 /* 25Mhz, 4 dividers */
+#define BCMA_CC_CAP_PCTL 0x00040000 /* Power Control */
+#define BCMA_CC_CAP_OTPS 0x00380000 /* OTP size */
+#define BCMA_CC_CAP_OTPS_SHIFT 19
+#define BCMA_CC_CAP_OTPS_BASE 5
+#define BCMA_CC_CAP_JTAGM 0x00400000 /* JTAG master present */
+#define BCMA_CC_CAP_BROM 0x00800000 /* Internal boot ROM active */
+#define BCMA_CC_CAP_64BIT 0x08000000 /* 64-bit Backplane */
+#define BCMA_CC_CAP_PMU 0x10000000 /* PMU available (rev >= 20) */
+#define BCMA_CC_CAP_ECI 0x20000000 /* ECI available (rev >= 20) */
+#define BCMA_CC_CAP_SPROM 0x40000000 /* SPROM present */
+#define BCMA_CC_CAP_NFLASH 0x80000000 /* NAND flash present (rev >= 35 or BCM4706?) */
+#define BCMA_CC_CORECTL 0x0008
+#define BCMA_CC_CORECTL_UARTCLK0 0x00000001 /* Drive UART with internal clock */
+#define BCMA_CC_CORECTL_SE 0x00000002 /* sync clk out enable (corerev >= 3) */
+#define BCMA_CC_CORECTL_UARTCLKEN 0x00000008 /* UART clock enable (rev >= 21) */
+#define BCMA_CC_BIST 0x000C
+#define BCMA_CC_OTPS 0x0010 /* OTP status */
+#define BCMA_CC_OTPS_PROGFAIL 0x80000000
+#define BCMA_CC_OTPS_PROTECT 0x00000007
+#define BCMA_CC_OTPS_HW_PROTECT 0x00000001
+#define BCMA_CC_OTPS_SW_PROTECT 0x00000002
+#define BCMA_CC_OTPS_CID_PROTECT 0x00000004
+#define BCMA_CC_OTPS_GU_PROG_IND 0x00000F00 /* General Use programmed indication */
+#define BCMA_CC_OTPS_GU_PROG_IND_SHIFT 8
+#define BCMA_CC_OTPS_GU_PROG_HW 0x00000100 /* HW region programmed */
+#define BCMA_CC_OTPC 0x0014 /* OTP control */
+#define BCMA_CC_OTPC_RECWAIT 0xFF000000
+#define BCMA_CC_OTPC_PROGWAIT 0x00FFFF00
+#define BCMA_CC_OTPC_PRW_SHIFT 8
+#define BCMA_CC_OTPC_MAXFAIL 0x00000038
+#define BCMA_CC_OTPC_VSEL 0x00000006
+#define BCMA_CC_OTPC_SELVL 0x00000001
+#define BCMA_CC_OTPP 0x0018 /* OTP prog */
+#define BCMA_CC_OTPP_COL 0x000000FF
+#define BCMA_CC_OTPP_ROW 0x0000FF00
+#define BCMA_CC_OTPP_ROW_SHIFT 8
+#define BCMA_CC_OTPP_READERR 0x10000000
+#define BCMA_CC_OTPP_VALUE 0x20000000
+#define BCMA_CC_OTPP_READ 0x40000000
+#define BCMA_CC_OTPP_START 0x80000000
+#define BCMA_CC_OTPP_BUSY 0x80000000
+#define BCMA_CC_OTPL 0x001C /* OTP layout */
+#define BCMA_CC_OTPL_GURGN_OFFSET 0x00000FFF /* offset of general use region */
+#define BCMA_CC_IRQSTAT 0x0020
+#define BCMA_CC_IRQMASK 0x0024
+#define BCMA_CC_IRQ_GPIO 0x00000001 /* gpio intr */
+#define BCMA_CC_IRQ_EXT 0x00000002 /* ro: ext intr pin (corerev >= 3) */
+#define BCMA_CC_IRQ_WDRESET 0x80000000 /* watchdog reset occurred */
+#define BCMA_CC_CHIPCTL 0x0028 /* Rev >= 11 only */
+#define BCMA_CC_CHIPSTAT 0x002C /* Rev >= 11 only */
+#define BCMA_CC_CHIPST_4313_SPROM_PRESENT 1
+#define BCMA_CC_CHIPST_4313_OTP_PRESENT 2
+#define BCMA_CC_CHIPST_4331_SPROM_PRESENT 2
+#define BCMA_CC_CHIPST_4331_OTP_PRESENT 4
+#define BCMA_CC_CHIPST_43228_ILP_DIV_EN 0x00000001
+#define BCMA_CC_CHIPST_43228_OTP_PRESENT 0x00000002
+#define BCMA_CC_CHIPST_43228_SERDES_REFCLK_PADSEL 0x00000004
+#define BCMA_CC_CHIPST_43228_SDIO_MODE 0x00000008
+#define BCMA_CC_CHIPST_43228_SDIO_OTP_PRESENT 0x00000010
+#define BCMA_CC_CHIPST_43228_SDIO_RESET 0x00000020
+#define BCMA_CC_CHIPST_4706_PKG_OPTION BIT(0) /* 0: full-featured package 1: low-cost package */
+#define BCMA_CC_CHIPST_4706_SFLASH_PRESENT BIT(1) /* 0: parallel, 1: serial flash is present */
+#define BCMA_CC_CHIPST_4706_SFLASH_TYPE BIT(2) /* 0: 8b-p/ST-s flash, 1: 16b-p/Atmal-s flash */
+#define BCMA_CC_CHIPST_4706_MIPS_BENDIAN BIT(3) /* 0: little, 1: big endian */
+#define BCMA_CC_CHIPST_4706_PCIE1_DISABLE BIT(5) /* PCIE1 enable strap pin */
+#define BCMA_CC_CHIPST_5357_NAND_BOOT BIT(4) /* NAND boot, valid for CC rev 38 and/or BCM5357 */
+#define BCMA_CC_CHIPST_4360_XTAL_40MZ 0x00000001
+#define BCMA_CC_JCMD 0x0030 /* Rev >= 10 only */
+#define BCMA_CC_JCMD_START 0x80000000
+#define BCMA_CC_JCMD_BUSY 0x80000000
+#define BCMA_CC_JCMD_PAUSE 0x40000000
+#define BCMA_CC_JCMD0_ACC_MASK 0x0000F000
+#define BCMA_CC_JCMD0_ACC_IRDR 0x00000000
+#define BCMA_CC_JCMD0_ACC_DR 0x00001000
+#define BCMA_CC_JCMD0_ACC_IR 0x00002000
+#define BCMA_CC_JCMD0_ACC_RESET 0x00003000
+#define BCMA_CC_JCMD0_ACC_IRPDR 0x00004000
+#define BCMA_CC_JCMD0_ACC_PDR 0x00005000
+#define BCMA_CC_JCMD0_IRW_MASK 0x00000F00
+#define BCMA_CC_JCMD_ACC_MASK 0x000F0000 /* Changes for corerev 11 */
+#define BCMA_CC_JCMD_ACC_IRDR 0x00000000
+#define BCMA_CC_JCMD_ACC_DR 0x00010000
+#define BCMA_CC_JCMD_ACC_IR 0x00020000
+#define BCMA_CC_JCMD_ACC_RESET 0x00030000
+#define BCMA_CC_JCMD_ACC_IRPDR 0x00040000
+#define BCMA_CC_JCMD_ACC_PDR 0x00050000
+#define BCMA_CC_JCMD_IRW_MASK 0x00001F00
+#define BCMA_CC_JCMD_IRW_SHIFT 8
+#define BCMA_CC_JCMD_DRW_MASK 0x0000003F
+#define BCMA_CC_JIR 0x0034 /* Rev >= 10 only */
+#define BCMA_CC_JDR 0x0038 /* Rev >= 10 only */
+#define BCMA_CC_JCTL 0x003C /* Rev >= 10 only */
+#define BCMA_CC_JCTL_FORCE_CLK 4 /* Force clock */
+#define BCMA_CC_JCTL_EXT_EN 2 /* Enable external targets */
+#define BCMA_CC_JCTL_EN 1 /* Enable Jtag master */
+#define BCMA_CC_FLASHCTL 0x0040
+/* Start/busy bit in flashcontrol */
+#define BCMA_CC_FLASHCTL_OPCODE 0x000000ff
+#define BCMA_CC_FLASHCTL_ACTION 0x00000700
+#define BCMA_CC_FLASHCTL_CS_ACTIVE 0x00001000 /* Chip Select Active, rev >= 20 */
+#define BCMA_CC_FLASHCTL_START 0x80000000
+#define BCMA_CC_FLASHCTL_BUSY BCMA_CC_FLASHCTL_START
+/* Flashcontrol action + opcodes for ST flashes */
+#define BCMA_CC_FLASHCTL_ST_WREN 0x0006 /* Write Enable */
+#define BCMA_CC_FLASHCTL_ST_WRDIS 0x0004 /* Write Disable */
+#define BCMA_CC_FLASHCTL_ST_RDSR 0x0105 /* Read Status Register */
+#define BCMA_CC_FLASHCTL_ST_WRSR 0x0101 /* Write Status Register */
+#define BCMA_CC_FLASHCTL_ST_READ 0x0303 /* Read Data Bytes */
+#define BCMA_CC_FLASHCTL_ST_PP 0x0302 /* Page Program */
+#define BCMA_CC_FLASHCTL_ST_SE 0x02d8 /* Sector Erase */
+#define BCMA_CC_FLASHCTL_ST_BE 0x00c7 /* Bulk Erase */
+#define BCMA_CC_FLASHCTL_ST_DP 0x00b9 /* Deep Power-down */
+#define BCMA_CC_FLASHCTL_ST_RES 0x03ab /* Read Electronic Signature */
+#define BCMA_CC_FLASHCTL_ST_CSA 0x1000 /* Keep chip select asserted */
+#define BCMA_CC_FLASHCTL_ST_SSE 0x0220 /* Sub-sector Erase */
+/* Flashcontrol action + opcodes for Atmel flashes */
+#define BCMA_CC_FLASHCTL_AT_READ 0x07e8
+#define BCMA_CC_FLASHCTL_AT_PAGE_READ 0x07d2
+#define BCMA_CC_FLASHCTL_AT_STATUS 0x01d7
+#define BCMA_CC_FLASHCTL_AT_BUF1_WRITE 0x0384
+#define BCMA_CC_FLASHCTL_AT_BUF2_WRITE 0x0387
+#define BCMA_CC_FLASHCTL_AT_BUF1_ERASE_PROGRAM 0x0283
+#define BCMA_CC_FLASHCTL_AT_BUF2_ERASE_PROGRAM 0x0286
+#define BCMA_CC_FLASHCTL_AT_BUF1_PROGRAM 0x0288
+#define BCMA_CC_FLASHCTL_AT_BUF2_PROGRAM 0x0289
+#define BCMA_CC_FLASHCTL_AT_PAGE_ERASE 0x0281
+#define BCMA_CC_FLASHCTL_AT_BLOCK_ERASE 0x0250
+#define BCMA_CC_FLASHCTL_AT_BUF1_WRITE_ERASE_PROGRAM 0x0382
+#define BCMA_CC_FLASHCTL_AT_BUF2_WRITE_ERASE_PROGRAM 0x0385
+#define BCMA_CC_FLASHCTL_AT_BUF1_LOAD 0x0253
+#define BCMA_CC_FLASHCTL_AT_BUF2_LOAD 0x0255
+#define BCMA_CC_FLASHCTL_AT_BUF1_COMPARE 0x0260
+#define BCMA_CC_FLASHCTL_AT_BUF2_COMPARE 0x0261
+#define BCMA_CC_FLASHCTL_AT_BUF1_REPROGRAM 0x0258
+#define BCMA_CC_FLASHCTL_AT_BUF2_REPROGRAM 0x0259
+#define BCMA_CC_FLASHADDR 0x0044
+#define BCMA_CC_FLASHDATA 0x0048
+/* Status register bits for ST flashes */
+#define BCMA_CC_FLASHDATA_ST_WIP 0x01 /* Write In Progress */
+#define BCMA_CC_FLASHDATA_ST_WEL 0x02 /* Write Enable Latch */
+#define BCMA_CC_FLASHDATA_ST_BP_MASK 0x1c /* Block Protect */
+#define BCMA_CC_FLASHDATA_ST_BP_SHIFT 2
+#define BCMA_CC_FLASHDATA_ST_SRWD 0x80 /* Status Register Write Disable */
+/* Status register bits for Atmel flashes */
+#define BCMA_CC_FLASHDATA_AT_READY 0x80
+#define BCMA_CC_FLASHDATA_AT_MISMATCH 0x40
+#define BCMA_CC_FLASHDATA_AT_ID_MASK 0x38
+#define BCMA_CC_FLASHDATA_AT_ID_SHIFT 3
+#define BCMA_CC_BCAST_ADDR 0x0050
+#define BCMA_CC_BCAST_DATA 0x0054
+#define BCMA_CC_GPIOPULLUP 0x0058 /* Rev >= 20 only */
+#define BCMA_CC_GPIOPULLDOWN 0x005C /* Rev >= 20 only */
+#define BCMA_CC_GPIOIN 0x0060
+#define BCMA_CC_GPIOOUT 0x0064
+#define BCMA_CC_GPIOOUTEN 0x0068
+#define BCMA_CC_GPIOCTL 0x006C
+#define BCMA_CC_GPIOPOL 0x0070
+#define BCMA_CC_GPIOIRQ 0x0074
+#define BCMA_CC_WATCHDOG 0x0080
+#define BCMA_CC_GPIOTIMER 0x0088 /* LED powersave (corerev >= 16) */
+#define BCMA_CC_GPIOTIMER_OFFTIME 0x0000FFFF
+#define BCMA_CC_GPIOTIMER_OFFTIME_SHIFT 0
+#define BCMA_CC_GPIOTIMER_ONTIME 0xFFFF0000
+#define BCMA_CC_GPIOTIMER_ONTIME_SHIFT 16
+#define BCMA_CC_GPIOTOUTM 0x008C /* LED powersave (corerev >= 16) */
+#define BCMA_CC_CLOCK_N 0x0090
+#define BCMA_CC_CLOCK_SB 0x0094
+#define BCMA_CC_CLOCK_PCI 0x0098
+#define BCMA_CC_CLOCK_M2 0x009C
+#define BCMA_CC_CLOCK_MIPS 0x00A0
+#define BCMA_CC_CLKDIV 0x00A4 /* Rev >= 3 only */
+#define BCMA_CC_CLKDIV_SFLASH 0x0F000000
+#define BCMA_CC_CLKDIV_SFLASH_SHIFT 24
+#define BCMA_CC_CLKDIV_OTP 0x000F0000
+#define BCMA_CC_CLKDIV_OTP_SHIFT 16
+#define BCMA_CC_CLKDIV_JTAG 0x00000F00
+#define BCMA_CC_CLKDIV_JTAG_SHIFT 8
+#define BCMA_CC_CLKDIV_UART 0x000000FF
+#define BCMA_CC_CAP_EXT 0x00AC /* Capabilities */
+#define BCMA_CC_CAP_EXT_SECI_PRESENT 0x00000001
+#define BCMA_CC_CAP_EXT_GSIO_PRESENT 0x00000002
+#define BCMA_CC_CAP_EXT_GCI_PRESENT 0x00000004
+#define BCMA_CC_CAP_EXT_SECI_PUART_PRESENT 0x00000008 /* UART present */
+#define BCMA_CC_CAP_EXT_AOB_PRESENT 0x00000040
+#define BCMA_CC_PLLONDELAY 0x00B0 /* Rev >= 4 only */
+#define BCMA_CC_FREFSELDELAY 0x00B4 /* Rev >= 4 only */
+#define BCMA_CC_SLOWCLKCTL 0x00B8 /* 6 <= Rev <= 9 only */
+#define BCMA_CC_SLOWCLKCTL_SRC 0x00000007 /* slow clock source mask */
+#define BCMA_CC_SLOWCLKCTL_SRC_LPO 0x00000000 /* source of slow clock is LPO */
+#define BCMA_CC_SLOWCLKCTL_SRC_XTAL 0x00000001 /* source of slow clock is crystal */
+#define BCMA_CC_SLOECLKCTL_SRC_PCI 0x00000002 /* source of slow clock is PCI */
+#define BCMA_CC_SLOWCLKCTL_LPOFREQ 0x00000200 /* LPOFreqSel, 1: 160Khz, 0: 32KHz */
+#define BCMA_CC_SLOWCLKCTL_LPOPD 0x00000400 /* LPOPowerDown, 1: LPO is disabled, 0: LPO is enabled */
+#define BCMA_CC_SLOWCLKCTL_FSLOW 0x00000800 /* ForceSlowClk, 1: sb/cores running on slow clock, 0: power logic control */
+#define BCMA_CC_SLOWCLKCTL_IPLL 0x00001000 /* IgnorePllOffReq, 1/0: power logic ignores/honors PLL clock disable requests from core */
+#define BCMA_CC_SLOWCLKCTL_ENXTAL 0x00002000 /* XtalControlEn, 1/0: power logic does/doesn't disable crystal when appropriate */
+#define BCMA_CC_SLOWCLKCTL_XTALPU 0x00004000 /* XtalPU (RO), 1/0: crystal running/disabled */
+#define BCMA_CC_SLOWCLKCTL_CLKDIV 0xFFFF0000 /* ClockDivider (SlowClk = 1/(4+divisor)) */
+#define BCMA_CC_SLOWCLKCTL_CLKDIV_SHIFT 16
+#define BCMA_CC_SYSCLKCTL 0x00C0 /* Rev >= 3 only */
+#define BCMA_CC_SYSCLKCTL_IDLPEN 0x00000001 /* ILPen: Enable Idle Low Power */
+#define BCMA_CC_SYSCLKCTL_ALPEN 0x00000002 /* ALPen: Enable Active Low Power */
+#define BCMA_CC_SYSCLKCTL_PLLEN 0x00000004 /* ForcePLLOn */
+#define BCMA_CC_SYSCLKCTL_FORCEALP 0x00000008 /* Force ALP (or HT if ALPen is not set */
+#define BCMA_CC_SYSCLKCTL_FORCEHT 0x00000010 /* Force HT */
+#define BCMA_CC_SYSCLKCTL_CLKDIV 0xFFFF0000 /* ClkDiv (ILP = 1/(4+divisor)) */
+#define BCMA_CC_SYSCLKCTL_CLKDIV_SHIFT 16
+#define BCMA_CC_CLKSTSTR 0x00C4 /* Rev >= 3 only */
+#define BCMA_CC_EROM 0x00FC
+#define BCMA_CC_PCMCIA_CFG 0x0100
+#define BCMA_CC_PCMCIA_MEMWAIT 0x0104
+#define BCMA_CC_PCMCIA_ATTRWAIT 0x0108
+#define BCMA_CC_PCMCIA_IOWAIT 0x010C
+#define BCMA_CC_IDE_CFG 0x0110
+#define BCMA_CC_IDE_MEMWAIT 0x0114
+#define BCMA_CC_IDE_ATTRWAIT 0x0118
+#define BCMA_CC_IDE_IOWAIT 0x011C
+#define BCMA_CC_PROG_CFG 0x0120
+#define BCMA_CC_PROG_WAITCNT 0x0124
+#define BCMA_CC_FLASH_CFG 0x0128
+#define BCMA_CC_FLASH_CFG_DS 0x0010 /* Data size, 0=8bit, 1=16bit */
+#define BCMA_CC_FLASH_WAITCNT 0x012C
+#define BCMA_CC_SROM_CONTROL 0x0190
+#define BCMA_CC_SROM_CONTROL_START 0x80000000
+#define BCMA_CC_SROM_CONTROL_BUSY 0x80000000
+#define BCMA_CC_SROM_CONTROL_OPCODE 0x60000000
+#define BCMA_CC_SROM_CONTROL_OP_READ 0x00000000
+#define BCMA_CC_SROM_CONTROL_OP_WRITE 0x20000000
+#define BCMA_CC_SROM_CONTROL_OP_WRDIS 0x40000000
+#define BCMA_CC_SROM_CONTROL_OP_WREN 0x60000000
+#define BCMA_CC_SROM_CONTROL_OTPSEL 0x00000010
+#define BCMA_CC_SROM_CONTROL_LOCK 0x00000008
+#define BCMA_CC_SROM_CONTROL_SIZE_MASK 0x00000006
+#define BCMA_CC_SROM_CONTROL_SIZE_1K 0x00000000
+#define BCMA_CC_SROM_CONTROL_SIZE_4K 0x00000002
+#define BCMA_CC_SROM_CONTROL_SIZE_16K 0x00000004
+#define BCMA_CC_SROM_CONTROL_SIZE_SHIFT 1
+#define BCMA_CC_SROM_CONTROL_PRESENT 0x00000001
+/* Block 0x140 - 0x190 registers are chipset specific */
+#define BCMA_CC_4706_FLASHSCFG 0x18C /* Flash struct configuration */
+#define BCMA_CC_4706_FLASHSCFG_MASK 0x000000ff
+#define BCMA_CC_4706_FLASHSCFG_SF1 0x00000001 /* 2nd serial flash present */
+#define BCMA_CC_4706_FLASHSCFG_PF1 0x00000002 /* 2nd parallel flash present */
+#define BCMA_CC_4706_FLASHSCFG_SF1_TYPE 0x00000004 /* 2nd serial flash type : 0 : ST, 1 : Atmel */
+#define BCMA_CC_4706_FLASHSCFG_NF1 0x00000008 /* 2nd NAND flash present */
+#define BCMA_CC_4706_FLASHSCFG_1ST_MADDR_SEG_MASK 0x000000f0
+#define BCMA_CC_4706_FLASHSCFG_1ST_MADDR_SEG_4MB 0x00000010 /* 4MB */
+#define BCMA_CC_4706_FLASHSCFG_1ST_MADDR_SEG_8MB 0x00000020 /* 8MB */
+#define BCMA_CC_4706_FLASHSCFG_1ST_MADDR_SEG_16MB 0x00000030 /* 16MB */
+#define BCMA_CC_4706_FLASHSCFG_1ST_MADDR_SEG_32MB 0x00000040 /* 32MB */
+#define BCMA_CC_4706_FLASHSCFG_1ST_MADDR_SEG_64MB 0x00000050 /* 64MB */
+#define BCMA_CC_4706_FLASHSCFG_1ST_MADDR_SEG_128MB 0x00000060 /* 128MB */
+#define BCMA_CC_4706_FLASHSCFG_1ST_MADDR_SEG_256MB 0x00000070 /* 256MB */
+/* NAND flash registers for BCM4706 (corerev = 31) */
+#define BCMA_CC_NFLASH_CTL 0x01A0
+#define BCMA_CC_NFLASH_CTL_ERR 0x08000000
+#define BCMA_CC_NFLASH_CONF 0x01A4
+#define BCMA_CC_NFLASH_COL_ADDR 0x01A8
+#define BCMA_CC_NFLASH_ROW_ADDR 0x01AC
+#define BCMA_CC_NFLASH_DATA 0x01B0
+#define BCMA_CC_NFLASH_WAITCNT0 0x01B4
+/* 0x1E0 is defined as shared BCMA_CLKCTLST */
+#define BCMA_CC_HW_WORKAROUND 0x01E4 /* Hardware workaround (rev >= 20) */
+#define BCMA_CC_UART0_DATA 0x0300
+#define BCMA_CC_UART0_IMR 0x0304
+#define BCMA_CC_UART0_FCR 0x0308
+#define BCMA_CC_UART0_LCR 0x030C
+#define BCMA_CC_UART0_MCR 0x0310
+#define BCMA_CC_UART0_LSR 0x0314
+#define BCMA_CC_UART0_MSR 0x0318
+#define BCMA_CC_UART0_SCRATCH 0x031C
+#define BCMA_CC_UART1_DATA 0x0400
+#define BCMA_CC_UART1_IMR 0x0404
+#define BCMA_CC_UART1_FCR 0x0408
+#define BCMA_CC_UART1_LCR 0x040C
+#define BCMA_CC_UART1_MCR 0x0410
+#define BCMA_CC_UART1_LSR 0x0414
+#define BCMA_CC_UART1_MSR 0x0418
+#define BCMA_CC_UART1_SCRATCH 0x041C
+/* PMU registers (rev >= 20) */
+#define BCMA_CC_PMU_CTL 0x0600 /* PMU control */
+#define BCMA_CC_PMU_CTL_ILP_DIV 0xFFFF0000 /* ILP div mask */
+#define BCMA_CC_PMU_CTL_ILP_DIV_SHIFT 16
+#define BCMA_CC_PMU_CTL_RES 0x00006000 /* reset control mask */
+#define BCMA_CC_PMU_CTL_RES_SHIFT 13
+#define BCMA_CC_PMU_CTL_RES_RELOAD 0x2 /* reload POR values */
+#define BCMA_CC_PMU_CTL_PLL_UPD 0x00000400
+#define BCMA_CC_PMU_CTL_NOILPONW 0x00000200 /* No ILP on wait */
+#define BCMA_CC_PMU_CTL_HTREQEN 0x00000100 /* HT req enable */
+#define BCMA_CC_PMU_CTL_ALPREQEN 0x00000080 /* ALP req enable */
+#define BCMA_CC_PMU_CTL_XTALFREQ 0x0000007C /* Crystal freq */
+#define BCMA_CC_PMU_CTL_XTALFREQ_SHIFT 2
+#define BCMA_CC_PMU_CTL_ILPDIVEN 0x00000002 /* ILP div enable */
+#define BCMA_CC_PMU_CTL_LPOSEL 0x00000001 /* LPO sel */
+#define BCMA_CC_PMU_CAP 0x0604 /* PMU capabilities */
+#define BCMA_CC_PMU_CAP_REVISION 0x000000FF /* Revision mask */
+#define BCMA_CC_PMU_STAT 0x0608 /* PMU status */
+#define BCMA_CC_PMU_STAT_EXT_LPO_AVAIL 0x00000100
+#define BCMA_CC_PMU_STAT_WDRESET 0x00000080
+#define BCMA_CC_PMU_STAT_INTPEND 0x00000040 /* Interrupt pending */
+#define BCMA_CC_PMU_STAT_SBCLKST 0x00000030 /* Backplane clock status? */
+#define BCMA_CC_PMU_STAT_HAVEALP 0x00000008 /* ALP available */
+#define BCMA_CC_PMU_STAT_HAVEHT 0x00000004 /* HT available */
+#define BCMA_CC_PMU_STAT_RESINIT 0x00000003 /* Res init */
+#define BCMA_CC_PMU_RES_STAT 0x060C /* PMU res status */
+#define BCMA_CC_PMU_RES_PEND 0x0610 /* PMU res pending */
+#define BCMA_CC_PMU_TIMER 0x0614 /* PMU timer */
+#define BCMA_CC_PMU_MINRES_MSK 0x0618 /* PMU min res mask */
+#define BCMA_CC_PMU_MAXRES_MSK 0x061C /* PMU max res mask */
+#define BCMA_CC_PMU_RES_TABSEL 0x0620 /* PMU res table sel */
+#define BCMA_CC_PMU_RES_DEPMSK 0x0624 /* PMU res dep mask */
+#define BCMA_CC_PMU_RES_UPDNTM 0x0628 /* PMU res updown timer */
+#define BCMA_CC_PMU_RES_TIMER 0x062C /* PMU res timer */
+#define BCMA_CC_PMU_CLKSTRETCH 0x0630 /* PMU clockstretch */
+#define BCMA_CC_PMU_WATCHDOG 0x0634 /* PMU watchdog */
+#define BCMA_CC_PMU_RES_REQTS 0x0640 /* PMU res req timer sel */
+#define BCMA_CC_PMU_RES_REQT 0x0644 /* PMU res req timer */
+#define BCMA_CC_PMU_RES_REQM 0x0648 /* PMU res req mask */
+#define BCMA_CC_PMU_CHIPCTL_ADDR 0x0650
+#define BCMA_CC_PMU_CHIPCTL_DATA 0x0654
+#define BCMA_CC_PMU_REGCTL_ADDR 0x0658
+#define BCMA_CC_PMU_REGCTL_DATA 0x065C
+#define BCMA_CC_PMU_PLLCTL_ADDR 0x0660
+#define BCMA_CC_PMU_PLLCTL_DATA 0x0664
+#define BCMA_CC_PMU_STRAPOPT 0x0668 /* (corerev >= 28) */
+#define BCMA_CC_PMU_XTAL_FREQ 0x066C /* (pmurev >= 10) */
+#define BCMA_CC_PMU_XTAL_FREQ_ILPCTL_MASK 0x00001FFF
+#define BCMA_CC_PMU_XTAL_FREQ_MEASURE_MASK 0x80000000
+#define BCMA_CC_PMU_XTAL_FREQ_MEASURE_SHIFT 31
+#define BCMA_CC_SPROM 0x0800 /* SPROM beginning */
+/* NAND flash MLC controller registers (corerev >= 38) */
+#define BCMA_CC_NAND_REVISION 0x0C00
+#define BCMA_CC_NAND_CMD_START 0x0C04
+#define BCMA_CC_NAND_CMD_ADDR_X 0x0C08
+#define BCMA_CC_NAND_CMD_ADDR 0x0C0C
+#define BCMA_CC_NAND_CMD_END_ADDR 0x0C10
+#define BCMA_CC_NAND_CS_NAND_SELECT 0x0C14
+#define BCMA_CC_NAND_CS_NAND_XOR 0x0C18
+#define BCMA_CC_NAND_SPARE_RD0 0x0C20
+#define BCMA_CC_NAND_SPARE_RD4 0x0C24
+#define BCMA_CC_NAND_SPARE_RD8 0x0C28
+#define BCMA_CC_NAND_SPARE_RD12 0x0C2C
+#define BCMA_CC_NAND_SPARE_WR0 0x0C30
+#define BCMA_CC_NAND_SPARE_WR4 0x0C34
+#define BCMA_CC_NAND_SPARE_WR8 0x0C38
+#define BCMA_CC_NAND_SPARE_WR12 0x0C3C
+#define BCMA_CC_NAND_ACC_CONTROL 0x0C40
+#define BCMA_CC_NAND_CONFIG 0x0C48
+#define BCMA_CC_NAND_TIMING_1 0x0C50
+#define BCMA_CC_NAND_TIMING_2 0x0C54
+#define BCMA_CC_NAND_SEMAPHORE 0x0C58
+#define BCMA_CC_NAND_DEVID 0x0C60
+#define BCMA_CC_NAND_DEVID_X 0x0C64
+#define BCMA_CC_NAND_BLOCK_LOCK_STATUS 0x0C68
+#define BCMA_CC_NAND_INTFC_STATUS 0x0C6C
+#define BCMA_CC_NAND_ECC_CORR_ADDR_X 0x0C70
+#define BCMA_CC_NAND_ECC_CORR_ADDR 0x0C74
+#define BCMA_CC_NAND_ECC_UNC_ADDR_X 0x0C78
+#define BCMA_CC_NAND_ECC_UNC_ADDR 0x0C7C
+#define BCMA_CC_NAND_READ_ERROR_COUNT 0x0C80
+#define BCMA_CC_NAND_CORR_STAT_THRESHOLD 0x0C84
+#define BCMA_CC_NAND_READ_ADDR_X 0x0C90
+#define BCMA_CC_NAND_READ_ADDR 0x0C94
+#define BCMA_CC_NAND_PAGE_PROGRAM_ADDR_X 0x0C98
+#define BCMA_CC_NAND_PAGE_PROGRAM_ADDR 0x0C9C
+#define BCMA_CC_NAND_COPY_BACK_ADDR_X 0x0CA0
+#define BCMA_CC_NAND_COPY_BACK_ADDR 0x0CA4
+#define BCMA_CC_NAND_BLOCK_ERASE_ADDR_X 0x0CA8
+#define BCMA_CC_NAND_BLOCK_ERASE_ADDR 0x0CAC
+#define BCMA_CC_NAND_INV_READ_ADDR_X 0x0CB0
+#define BCMA_CC_NAND_INV_READ_ADDR 0x0CB4
+#define BCMA_CC_NAND_BLK_WR_PROTECT 0x0CC0
+#define BCMA_CC_NAND_ACC_CONTROL_CS1 0x0CD0
+#define BCMA_CC_NAND_CONFIG_CS1 0x0CD4
+#define BCMA_CC_NAND_TIMING_1_CS1 0x0CD8
+#define BCMA_CC_NAND_TIMING_2_CS1 0x0CDC
+#define BCMA_CC_NAND_SPARE_RD16 0x0D30
+#define BCMA_CC_NAND_SPARE_RD20 0x0D34
+#define BCMA_CC_NAND_SPARE_RD24 0x0D38
+#define BCMA_CC_NAND_SPARE_RD28 0x0D3C
+#define BCMA_CC_NAND_CACHE_ADDR 0x0D40
+#define BCMA_CC_NAND_CACHE_DATA 0x0D44
+#define BCMA_CC_NAND_CTRL_CONFIG 0x0D48
+#define BCMA_CC_NAND_CTRL_STATUS 0x0D4C
+
+/* Divider allocation in 4716/47162/5356 */
+#define BCMA_CC_PMU5_MAINPLL_CPU 1
+#define BCMA_CC_PMU5_MAINPLL_MEM 2
+#define BCMA_CC_PMU5_MAINPLL_SSB 3
+
+/* PLL usage in 4716/47162 */
+#define BCMA_CC_PMU4716_MAINPLL_PLL0 12
+
+/* PLL usage in 5356/5357 */
+#define BCMA_CC_PMU5356_MAINPLL_PLL0 0
+#define BCMA_CC_PMU5357_MAINPLL_PLL0 0
+
+/* 4706 PMU */
+#define BCMA_CC_PMU4706_MAINPLL_PLL0 0
+#define BCMA_CC_PMU6_4706_PROCPLL_OFF 4 /* The CPU PLL */
+#define BCMA_CC_PMU6_4706_PROC_P2DIV_MASK 0x000f0000
+#define BCMA_CC_PMU6_4706_PROC_P2DIV_SHIFT 16
+#define BCMA_CC_PMU6_4706_PROC_P1DIV_MASK 0x0000f000
+#define BCMA_CC_PMU6_4706_PROC_P1DIV_SHIFT 12
+#define BCMA_CC_PMU6_4706_PROC_NDIV_INT_MASK 0x00000ff8
+#define BCMA_CC_PMU6_4706_PROC_NDIV_INT_SHIFT 3
+#define BCMA_CC_PMU6_4706_PROC_NDIV_MODE_MASK 0x00000007
+#define BCMA_CC_PMU6_4706_PROC_NDIV_MODE_SHIFT 0
+
+/* PMU rev 15 */
+#define BCMA_CC_PMU15_PLL_PLLCTL0 0
+#define BCMA_CC_PMU15_PLL_PC0_CLKSEL_MASK 0x00000003
+#define BCMA_CC_PMU15_PLL_PC0_CLKSEL_SHIFT 0
+#define BCMA_CC_PMU15_PLL_PC0_FREQTGT_MASK 0x003FFFFC
+#define BCMA_CC_PMU15_PLL_PC0_FREQTGT_SHIFT 2
+#define BCMA_CC_PMU15_PLL_PC0_PRESCALE_MASK 0x00C00000
+#define BCMA_CC_PMU15_PLL_PC0_PRESCALE_SHIFT 22
+#define BCMA_CC_PMU15_PLL_PC0_KPCTRL_MASK 0x07000000
+#define BCMA_CC_PMU15_PLL_PC0_KPCTRL_SHIFT 24
+#define BCMA_CC_PMU15_PLL_PC0_FCNTCTRL_MASK 0x38000000
+#define BCMA_CC_PMU15_PLL_PC0_FCNTCTRL_SHIFT 27
+#define BCMA_CC_PMU15_PLL_PC0_FDCMODE_MASK 0x40000000
+#define BCMA_CC_PMU15_PLL_PC0_FDCMODE_SHIFT 30
+#define BCMA_CC_PMU15_PLL_PC0_CTRLBIAS_MASK 0x80000000
+#define BCMA_CC_PMU15_PLL_PC0_CTRLBIAS_SHIFT 31
+
+/* ALP clock on pre-PMU chips */
+#define BCMA_CC_PMU_ALP_CLOCK 20000000
+/* HT clock for systems with PMU-enabled chipcommon */
+#define BCMA_CC_PMU_HT_CLOCK 80000000
+
+/* PMU rev 5 (& 6) */
+#define BCMA_CC_PPL_P1P2_OFF 0
+#define BCMA_CC_PPL_P1_MASK 0x0f000000
+#define BCMA_CC_PPL_P1_SHIFT 24
+#define BCMA_CC_PPL_P2_MASK 0x00f00000
+#define BCMA_CC_PPL_P2_SHIFT 20
+#define BCMA_CC_PPL_M14_OFF 1
+#define BCMA_CC_PPL_MDIV_MASK 0x000000ff
+#define BCMA_CC_PPL_MDIV_WIDTH 8
+#define BCMA_CC_PPL_NM5_OFF 2
+#define BCMA_CC_PPL_NDIV_MASK 0xfff00000
+#define BCMA_CC_PPL_NDIV_SHIFT 20
+#define BCMA_CC_PPL_FMAB_OFF 3
+#define BCMA_CC_PPL_MRAT_MASK 0xf0000000
+#define BCMA_CC_PPL_MRAT_SHIFT 28
+#define BCMA_CC_PPL_ABRAT_MASK 0x08000000
+#define BCMA_CC_PPL_ABRAT_SHIFT 27
+#define BCMA_CC_PPL_FDIV_MASK 0x07ffffff
+#define BCMA_CC_PPL_PLLCTL_OFF 4
+#define BCMA_CC_PPL_PCHI_OFF 5
+#define BCMA_CC_PPL_PCHI_MASK 0x0000003f
+
+#define BCMA_CC_PMU_PLL_CTL0 0
+#define BCMA_CC_PMU_PLL_CTL1 1
+#define BCMA_CC_PMU_PLL_CTL2 2
+#define BCMA_CC_PMU_PLL_CTL3 3
+#define BCMA_CC_PMU_PLL_CTL4 4
+#define BCMA_CC_PMU_PLL_CTL5 5
+
+#define BCMA_CC_PMU1_PLL0_PC0_P1DIV_MASK 0x00f00000
+#define BCMA_CC_PMU1_PLL0_PC0_P1DIV_SHIFT 20
+
+#define BCMA_CC_PMU1_PLL0_PC2_NDIV_INT_MASK 0x1ff00000
+#define BCMA_CC_PMU1_PLL0_PC2_NDIV_INT_SHIFT 20
+
+#define BCMA_CCB_MII_MNG_CTL 0x0000
+#define BCMA_CCB_MII_MNG_CMD_DATA 0x0004
+
+/* BCM4331 ChipControl numbers. */
+#define BCMA_CHIPCTL_4331_BT_COEXIST BIT(0) /* 0 disable */
+#define BCMA_CHIPCTL_4331_SECI BIT(1) /* 0 SECI is disabled (JATG functional) */
+#define BCMA_CHIPCTL_4331_EXT_LNA BIT(2) /* 0 disable */
+#define BCMA_CHIPCTL_4331_SPROM_GPIO13_15 BIT(3) /* sprom/gpio13-15 mux */
+#define BCMA_CHIPCTL_4331_EXTPA_EN BIT(4) /* 0 ext pa disable, 1 ext pa enabled */
+#define BCMA_CHIPCTL_4331_GPIOCLK_ON_SPROMCS BIT(5) /* set drive out GPIO_CLK on sprom_cs pin */
+#define BCMA_CHIPCTL_4331_PCIE_MDIO_ON_SPROMCS BIT(6) /* use sprom_cs pin as PCIE mdio interface */
+#define BCMA_CHIPCTL_4331_EXTPA_ON_GPIO2_5 BIT(7) /* aband extpa will be at gpio2/5 and sprom_dout */
+#define BCMA_CHIPCTL_4331_OVR_PIPEAUXCLKEN BIT(8) /* override core control on pipe_AuxClkEnable */
+#define BCMA_CHIPCTL_4331_OVR_PIPEAUXPWRDOWN BIT(9) /* override core control on pipe_AuxPowerDown */
+#define BCMA_CHIPCTL_4331_PCIE_AUXCLKEN BIT(10) /* pcie_auxclkenable */
+#define BCMA_CHIPCTL_4331_PCIE_PIPE_PLLDOWN BIT(11) /* pcie_pipe_pllpowerdown */
+#define BCMA_CHIPCTL_4331_EXTPA_EN2 BIT(12) /* 0 ext pa disable, 1 ext pa enabled */
+#define BCMA_CHIPCTL_4331_BT_SHD0_ON_GPIO4 BIT(16) /* enable bt_shd0 at gpio4 */
+#define BCMA_CHIPCTL_4331_BT_SHD1_ON_GPIO5 BIT(17) /* enable bt_shd1 at gpio5 */
+
+/* 43224 chip-specific ChipControl register bits */
+#define BCMA_CCTRL_43224_GPIO_TOGGLE 0x8000 /* gpio[3:0] pins as btcoex or s/w gpio */
+#define BCMA_CCTRL_43224A0_12MA_LED_DRIVE 0x00F000F0 /* 12 mA drive strength */
+#define BCMA_CCTRL_43224B0_12MA_LED_DRIVE 0xF0 /* 12 mA drive strength for later 43224s */
+
+/* 4313 Chip specific ChipControl register bits */
+#define BCMA_CCTRL_4313_12MA_LED_DRIVE 0x00000007 /* 12 mA drive strengh for later 4313 */
+
+/* BCM5357 ChipControl register bits */
+#define BCMA_CHIPCTL_5357_EXTPA BIT(14)
+#define BCMA_CHIPCTL_5357_ANT_MUX_2O3 BIT(15)
+#define BCMA_CHIPCTL_5357_NFLASH BIT(16)
+#define BCMA_CHIPCTL_5357_I2S_PINS_ENABLE BIT(18)
+#define BCMA_CHIPCTL_5357_I2CSPI_PINS_ENABLE BIT(19)
+
+#define BCMA_RES_4314_LPLDO_PU BIT(0)
+#define BCMA_RES_4314_PMU_SLEEP_DIS BIT(1)
+#define BCMA_RES_4314_PMU_BG_PU BIT(2)
+#define BCMA_RES_4314_CBUCK_LPOM_PU BIT(3)
+#define BCMA_RES_4314_CBUCK_PFM_PU BIT(4)
+#define BCMA_RES_4314_CLDO_PU BIT(5)
+#define BCMA_RES_4314_LPLDO2_LVM BIT(6)
+#define BCMA_RES_4314_WL_PMU_PU BIT(7)
+#define BCMA_RES_4314_LNLDO_PU BIT(8)
+#define BCMA_RES_4314_LDO3P3_PU BIT(9)
+#define BCMA_RES_4314_OTP_PU BIT(10)
+#define BCMA_RES_4314_XTAL_PU BIT(11)
+#define BCMA_RES_4314_WL_PWRSW_PU BIT(12)
+#define BCMA_RES_4314_LQ_AVAIL BIT(13)
+#define BCMA_RES_4314_LOGIC_RET BIT(14)
+#define BCMA_RES_4314_MEM_SLEEP BIT(15)
+#define BCMA_RES_4314_MACPHY_RET BIT(16)
+#define BCMA_RES_4314_WL_CORE_READY BIT(17)
+#define BCMA_RES_4314_ILP_REQ BIT(18)
+#define BCMA_RES_4314_ALP_AVAIL BIT(19)
+#define BCMA_RES_4314_MISC_PWRSW_PU BIT(20)
+#define BCMA_RES_4314_SYNTH_PWRSW_PU BIT(21)
+#define BCMA_RES_4314_RX_PWRSW_PU BIT(22)
+#define BCMA_RES_4314_RADIO_PU BIT(23)
+#define BCMA_RES_4314_VCO_LDO_PU BIT(24)
+#define BCMA_RES_4314_AFE_LDO_PU BIT(25)
+#define BCMA_RES_4314_RX_LDO_PU BIT(26)
+#define BCMA_RES_4314_TX_LDO_PU BIT(27)
+#define BCMA_RES_4314_HT_AVAIL BIT(28)
+#define BCMA_RES_4314_MACPHY_CLK_AVAIL BIT(29)
+
+/* Data for the PMU, if available.
+ * Check availability with ((struct bcma_chipcommon)->capabilities & BCMA_CC_CAP_PMU)
+ */
+struct bcma_chipcommon_pmu {
+ struct bcma_device *core; /* Can be separated core or just ChipCommon one */
+ u8 rev; /* PMU revision */
+ u32 crystalfreq; /* The active crystal frequency (in kHz) */
+};
+
+#ifdef CONFIG_BCMA_PFLASH
+struct bcma_pflash {
+ bool present;
+};
+#endif
+
+#ifdef CONFIG_BCMA_SFLASH
+struct mtd_info;
+
+struct bcma_sflash {
+ bool present;
+ u32 blocksize;
+ u16 numblocks;
+ u32 size;
+};
+#endif
+
+#ifdef CONFIG_BCMA_NFLASH
+struct bcma_nflash {
+ bool present;
+ bool boot; /* This is the flash the SoC boots from */
+};
+#endif
+
+#ifdef CONFIG_BCMA_DRIVER_MIPS
+struct bcma_serial_port {
+ void *regs;
+ unsigned long clockspeed;
+ unsigned int irq;
+ unsigned int baud_base;
+ unsigned int reg_shift;
+};
+#endif /* CONFIG_BCMA_DRIVER_MIPS */
+
+struct bcma_drv_cc {
+ struct bcma_device *core;
+ u32 status;
+ u32 capabilities;
+ u32 capabilities_ext;
+ u8 setup_done:1;
+ u8 early_setup_done:1;
+ /* Fast Powerup Delay constant */
+ u16 fast_pwrup_delay;
+ struct bcma_chipcommon_pmu pmu;
+#ifdef CONFIG_BCMA_PFLASH
+ struct bcma_pflash pflash;
+#endif
+#ifdef CONFIG_BCMA_SFLASH
+ struct bcma_sflash sflash;
+#endif
+#ifdef CONFIG_BCMA_NFLASH
+ struct bcma_nflash nflash;
+#endif
+
+#ifdef CONFIG_BCMA_DRIVER_MIPS
+ int nr_serial_ports;
+ struct bcma_serial_port serial_ports[4];
+#endif /* CONFIG_BCMA_DRIVER_MIPS */
+ u32 ticks_per_ms;
+ struct platform_device *watchdog;
+
+ /* Lock for GPIO register access. */
+ spinlock_t gpio_lock;
+#ifdef CONFIG_BCMA_DRIVER_GPIO
+ struct gpio_chip gpio;
+#endif
+};
+
+struct bcma_drv_cc_b {
+ struct bcma_device *core;
+ u8 setup_done:1;
+ void __iomem *mii;
+};
+
+/* Register access */
+#define bcma_cc_read32(cc, offset) \
+ bcma_read32((cc)->core, offset)
+#define bcma_cc_write32(cc, offset, val) \
+ bcma_write32((cc)->core, offset, val)
+
+#define bcma_cc_mask32(cc, offset, mask) \
+ bcma_cc_write32(cc, offset, bcma_cc_read32(cc, offset) & (mask))
+#define bcma_cc_set32(cc, offset, set) \
+ bcma_cc_write32(cc, offset, bcma_cc_read32(cc, offset) | (set))
+#define bcma_cc_maskset32(cc, offset, mask, set) \
+ bcma_cc_write32(cc, offset, (bcma_cc_read32(cc, offset) & (mask)) | (set))
+
+/* PMU registers access */
+#define bcma_pmu_read32(cc, offset) \
+ bcma_read32((cc)->pmu.core, offset)
+#define bcma_pmu_write32(cc, offset, val) \
+ bcma_write32((cc)->pmu.core, offset, val)
+
+#define bcma_pmu_mask32(cc, offset, mask) \
+ bcma_pmu_write32(cc, offset, bcma_pmu_read32(cc, offset) & (mask))
+#define bcma_pmu_set32(cc, offset, set) \
+ bcma_pmu_write32(cc, offset, bcma_pmu_read32(cc, offset) | (set))
+#define bcma_pmu_maskset32(cc, offset, mask, set) \
+ bcma_pmu_write32(cc, offset, (bcma_pmu_read32(cc, offset) & (mask)) | (set))
+
+extern u32 bcma_chipco_watchdog_timer_set(struct bcma_drv_cc *cc, u32 ticks);
+
+extern u32 bcma_chipco_get_alp_clock(struct bcma_drv_cc *cc);
+
+void bcma_chipco_irq_mask(struct bcma_drv_cc *cc, u32 mask, u32 value);
+
+u32 bcma_chipco_irq_status(struct bcma_drv_cc *cc, u32 mask);
+
+/* Chipcommon GPIO pin access. */
+u32 bcma_chipco_gpio_in(struct bcma_drv_cc *cc, u32 mask);
+u32 bcma_chipco_gpio_out(struct bcma_drv_cc *cc, u32 mask, u32 value);
+u32 bcma_chipco_gpio_outen(struct bcma_drv_cc *cc, u32 mask, u32 value);
+u32 bcma_chipco_gpio_control(struct bcma_drv_cc *cc, u32 mask, u32 value);
+u32 bcma_chipco_gpio_intmask(struct bcma_drv_cc *cc, u32 mask, u32 value);
+u32 bcma_chipco_gpio_polarity(struct bcma_drv_cc *cc, u32 mask, u32 value);
+u32 bcma_chipco_gpio_pullup(struct bcma_drv_cc *cc, u32 mask, u32 value);
+u32 bcma_chipco_gpio_pulldown(struct bcma_drv_cc *cc, u32 mask, u32 value);
+
+/* PMU support */
+extern void bcma_chipco_pll_write(struct bcma_drv_cc *cc, u32 offset,
+ u32 value);
+extern void bcma_chipco_pll_maskset(struct bcma_drv_cc *cc, u32 offset,
+ u32 mask, u32 set);
+extern void bcma_chipco_chipctl_maskset(struct bcma_drv_cc *cc,
+ u32 offset, u32 mask, u32 set);
+extern void bcma_chipco_regctl_maskset(struct bcma_drv_cc *cc,
+ u32 offset, u32 mask, u32 set);
+extern void bcma_pmu_spuravoid_pllupdate(struct bcma_drv_cc *cc, int spuravoid);
+
+extern u32 bcma_pmu_get_bus_clock(struct bcma_drv_cc *cc);
+
+void bcma_chipco_b_mii_write(struct bcma_drv_cc_b *ccb, u32 offset, u32 value);
+
+#endif /* LINUX_BCMA_DRIVER_CC_H_ */
diff --git a/include/linux/bcma/bcma_driver_gmac_cmn.h b/include/linux/bcma/bcma_driver_gmac_cmn.h
new file mode 100644
index 0000000..4354d4e
--- /dev/null
+++ b/include/linux/bcma/bcma_driver_gmac_cmn.h
@@ -0,0 +1,94 @@
+#ifndef LINUX_BCMA_DRIVER_GMAC_CMN_H_
+#define LINUX_BCMA_DRIVER_GMAC_CMN_H_
+
+#include <linux/types.h>
+
+#define BCMA_GMAC_CMN_STAG0 0x000
+#define BCMA_GMAC_CMN_STAG1 0x004
+#define BCMA_GMAC_CMN_STAG2 0x008
+#define BCMA_GMAC_CMN_STAG3 0x00C
+#define BCMA_GMAC_CMN_PARSER_CTL 0x020
+#define BCMA_GMAC_CMN_MIB_MAX_LEN 0x024
+#define BCMA_GMAC_CMN_PHY_ACCESS 0x100
+#define BCMA_GMAC_CMN_PA_DATA_MASK 0x0000ffff
+#define BCMA_GMAC_CMN_PA_ADDR_MASK 0x001f0000
+#define BCMA_GMAC_CMN_PA_ADDR_SHIFT 16
+#define BCMA_GMAC_CMN_PA_REG_MASK 0x1f000000
+#define BCMA_GMAC_CMN_PA_REG_SHIFT 24
+#define BCMA_GMAC_CMN_PA_WRITE 0x20000000
+#define BCMA_GMAC_CMN_PA_START 0x40000000
+#define BCMA_GMAC_CMN_PHY_CTL 0x104
+#define BCMA_GMAC_CMN_PC_EPA_MASK 0x0000001f
+#define BCMA_GMAC_CMN_PC_MCT_MASK 0x007f0000
+#define BCMA_GMAC_CMN_PC_MCT_SHIFT 16
+#define BCMA_GMAC_CMN_PC_MTE 0x00800000
+#define BCMA_GMAC_CMN_GMAC0_RGMII_CTL 0x110
+#define BCMA_GMAC_CMN_CFP_ACCESS 0x200
+#define BCMA_GMAC_CMN_CFP_TCAM_DATA0 0x210
+#define BCMA_GMAC_CMN_CFP_TCAM_DATA1 0x214
+#define BCMA_GMAC_CMN_CFP_TCAM_DATA2 0x218
+#define BCMA_GMAC_CMN_CFP_TCAM_DATA3 0x21C
+#define BCMA_GMAC_CMN_CFP_TCAM_DATA4 0x220
+#define BCMA_GMAC_CMN_CFP_TCAM_DATA5 0x224
+#define BCMA_GMAC_CMN_CFP_TCAM_DATA6 0x228
+#define BCMA_GMAC_CMN_CFP_TCAM_DATA7 0x22C
+#define BCMA_GMAC_CMN_CFP_TCAM_MASK0 0x230
+#define BCMA_GMAC_CMN_CFP_TCAM_MASK1 0x234
+#define BCMA_GMAC_CMN_CFP_TCAM_MASK2 0x238
+#define BCMA_GMAC_CMN_CFP_TCAM_MASK3 0x23C
+#define BCMA_GMAC_CMN_CFP_TCAM_MASK4 0x240
+#define BCMA_GMAC_CMN_CFP_TCAM_MASK5 0x244
+#define BCMA_GMAC_CMN_CFP_TCAM_MASK6 0x248
+#define BCMA_GMAC_CMN_CFP_TCAM_MASK7 0x24C
+#define BCMA_GMAC_CMN_CFP_ACTION_DATA 0x250
+#define BCMA_GMAC_CMN_TCAM_BIST_CTL 0x2A0
+#define BCMA_GMAC_CMN_TCAM_BIST_STATUS 0x2A4
+#define BCMA_GMAC_CMN_TCAM_CMP_STATUS 0x2A8
+#define BCMA_GMAC_CMN_TCAM_DISABLE 0x2AC
+#define BCMA_GMAC_CMN_TCAM_TEST_CTL 0x2F0
+#define BCMA_GMAC_CMN_UDF_0_A3_A0 0x300
+#define BCMA_GMAC_CMN_UDF_0_A7_A4 0x304
+#define BCMA_GMAC_CMN_UDF_0_A8 0x308
+#define BCMA_GMAC_CMN_UDF_1_A3_A0 0x310
+#define BCMA_GMAC_CMN_UDF_1_A7_A4 0x314
+#define BCMA_GMAC_CMN_UDF_1_A8 0x318
+#define BCMA_GMAC_CMN_UDF_2_A3_A0 0x320
+#define BCMA_GMAC_CMN_UDF_2_A7_A4 0x324
+#define BCMA_GMAC_CMN_UDF_2_A8 0x328
+#define BCMA_GMAC_CMN_UDF_0_B3_B0 0x330
+#define BCMA_GMAC_CMN_UDF_0_B7_B4 0x334
+#define BCMA_GMAC_CMN_UDF_0_B8 0x338
+#define BCMA_GMAC_CMN_UDF_1_B3_B0 0x340
+#define BCMA_GMAC_CMN_UDF_1_B7_B4 0x344
+#define BCMA_GMAC_CMN_UDF_1_B8 0x348
+#define BCMA_GMAC_CMN_UDF_2_B3_B0 0x350
+#define BCMA_GMAC_CMN_UDF_2_B7_B4 0x354
+#define BCMA_GMAC_CMN_UDF_2_B8 0x358
+#define BCMA_GMAC_CMN_UDF_0_C3_C0 0x360
+#define BCMA_GMAC_CMN_UDF_0_C7_C4 0x364
+#define BCMA_GMAC_CMN_UDF_0_C8 0x368
+#define BCMA_GMAC_CMN_UDF_1_C3_C0 0x370
+#define BCMA_GMAC_CMN_UDF_1_C7_C4 0x374
+#define BCMA_GMAC_CMN_UDF_1_C8 0x378
+#define BCMA_GMAC_CMN_UDF_2_C3_C0 0x380
+#define BCMA_GMAC_CMN_UDF_2_C7_C4 0x384
+#define BCMA_GMAC_CMN_UDF_2_C8 0x388
+#define BCMA_GMAC_CMN_UDF_0_D3_D0 0x390
+#define BCMA_GMAC_CMN_UDF_0_D7_D4 0x394
+#define BCMA_GMAC_CMN_UDF_0_D11_D8 0x394
+
+struct bcma_drv_gmac_cmn {
+ struct bcma_device *core;
+
+ /* Drivers accessing BCMA_GMAC_CMN_PHY_ACCESS and
+ * BCMA_GMAC_CMN_PHY_CTL need to take that mutex first. */
+ struct mutex phy_mutex;
+};
+
+/* Register access */
+#define gmac_cmn_read16(gc, offset) bcma_read16((gc)->core, offset)
+#define gmac_cmn_read32(gc, offset) bcma_read32((gc)->core, offset)
+#define gmac_cmn_write16(gc, offset, val) bcma_write16((gc)->core, offset, val)
+#define gmac_cmn_write32(gc, offset, val) bcma_write32((gc)->core, offset, val)
+
+#endif /* LINUX_BCMA_DRIVER_GMAC_CMN_H_ */
diff --git a/include/linux/bcma/bcma_driver_mips.h b/include/linux/bcma/bcma_driver_mips.h
new file mode 100644
index 0000000..8eea7f9
--- /dev/null
+++ b/include/linux/bcma/bcma_driver_mips.h
@@ -0,0 +1,44 @@
+#ifndef LINUX_BCMA_DRIVER_MIPS_H_
+#define LINUX_BCMA_DRIVER_MIPS_H_
+
+#define BCMA_MIPS_IPSFLAG 0x0F08
+/* which sbflags get routed to mips interrupt 1 */
+#define BCMA_MIPS_IPSFLAG_IRQ1 0x0000003F
+#define BCMA_MIPS_IPSFLAG_IRQ1_SHIFT 0
+/* which sbflags get routed to mips interrupt 2 */
+#define BCMA_MIPS_IPSFLAG_IRQ2 0x00003F00
+#define BCMA_MIPS_IPSFLAG_IRQ2_SHIFT 8
+/* which sbflags get routed to mips interrupt 3 */
+#define BCMA_MIPS_IPSFLAG_IRQ3 0x003F0000
+#define BCMA_MIPS_IPSFLAG_IRQ3_SHIFT 16
+/* which sbflags get routed to mips interrupt 4 */
+#define BCMA_MIPS_IPSFLAG_IRQ4 0x3F000000
+#define BCMA_MIPS_IPSFLAG_IRQ4_SHIFT 24
+
+/* MIPS 74K core registers */
+#define BCMA_MIPS_MIPS74K_CORECTL 0x0000
+#define BCMA_MIPS_MIPS74K_EXCEPTBASE 0x0004
+#define BCMA_MIPS_MIPS74K_BIST 0x000C
+#define BCMA_MIPS_MIPS74K_INTMASK_INT0 0x0014
+#define BCMA_MIPS_MIPS74K_INTMASK(int) \
+ ((int) * 4 + BCMA_MIPS_MIPS74K_INTMASK_INT0)
+#define BCMA_MIPS_MIPS74K_NMIMASK 0x002C
+#define BCMA_MIPS_MIPS74K_GPIOSEL 0x0040
+#define BCMA_MIPS_MIPS74K_GPIOOUT 0x0044
+#define BCMA_MIPS_MIPS74K_GPIOEN 0x0048
+#define BCMA_MIPS_MIPS74K_CLKCTLST 0x01E0
+
+#define BCMA_MIPS_OOBSELINA74 0x004
+#define BCMA_MIPS_OOBSELOUTA30 0x100
+
+struct bcma_device;
+
+struct bcma_drv_mips {
+ struct bcma_device *core;
+ u8 setup_done:1;
+ u8 early_setup_done:1;
+};
+
+extern u32 bcma_cpu_clock(struct bcma_drv_mips *mcore);
+
+#endif /* LINUX_BCMA_DRIVER_MIPS_H_ */
diff --git a/include/linux/bcma/bcma_driver_pci.h b/include/linux/bcma/bcma_driver_pci.h
new file mode 100644
index 0000000..bca6a5e
--- /dev/null
+++ b/include/linux/bcma/bcma_driver_pci.h
@@ -0,0 +1,263 @@
+#ifndef LINUX_BCMA_DRIVER_PCI_H_
+#define LINUX_BCMA_DRIVER_PCI_H_
+
+#include <linux/types.h>
+
+struct pci_dev;
+
+/** PCI core registers. **/
+#define BCMA_CORE_PCI_CTL 0x0000 /* PCI Control */
+#define BCMA_CORE_PCI_CTL_RST_OE 0x00000001 /* PCI_RESET Output Enable */
+#define BCMA_CORE_PCI_CTL_RST 0x00000002 /* PCI_RESET driven out to pin */
+#define BCMA_CORE_PCI_CTL_CLK_OE 0x00000004 /* Clock gate Output Enable */
+#define BCMA_CORE_PCI_CTL_CLK 0x00000008 /* Gate for clock driven out to pin */
+#define BCMA_CORE_PCI_ARBCTL 0x0010 /* PCI Arbiter Control */
+#define BCMA_CORE_PCI_ARBCTL_INTERN 0x00000001 /* Use internal arbiter */
+#define BCMA_CORE_PCI_ARBCTL_EXTERN 0x00000002 /* Use external arbiter */
+#define BCMA_CORE_PCI_ARBCTL_PARKID 0x00000006 /* Mask, selects which agent is parked on an idle bus */
+#define BCMA_CORE_PCI_ARBCTL_PARKID_LAST 0x00000000 /* Last requestor */
+#define BCMA_CORE_PCI_ARBCTL_PARKID_4710 0x00000002 /* 4710 */
+#define BCMA_CORE_PCI_ARBCTL_PARKID_EXT0 0x00000004 /* External requestor 0 */
+#define BCMA_CORE_PCI_ARBCTL_PARKID_EXT1 0x00000006 /* External requestor 1 */
+#define BCMA_CORE_PCI_ISTAT 0x0020 /* Interrupt status */
+#define BCMA_CORE_PCI_ISTAT_INTA 0x00000001 /* PCI INTA# */
+#define BCMA_CORE_PCI_ISTAT_INTB 0x00000002 /* PCI INTB# */
+#define BCMA_CORE_PCI_ISTAT_SERR 0x00000004 /* PCI SERR# (write to clear) */
+#define BCMA_CORE_PCI_ISTAT_PERR 0x00000008 /* PCI PERR# (write to clear) */
+#define BCMA_CORE_PCI_ISTAT_PME 0x00000010 /* PCI PME# */
+#define BCMA_CORE_PCI_IMASK 0x0024 /* Interrupt mask */
+#define BCMA_CORE_PCI_IMASK_INTA 0x00000001 /* PCI INTA# */
+#define BCMA_CORE_PCI_IMASK_INTB 0x00000002 /* PCI INTB# */
+#define BCMA_CORE_PCI_IMASK_SERR 0x00000004 /* PCI SERR# */
+#define BCMA_CORE_PCI_IMASK_PERR 0x00000008 /* PCI PERR# */
+#define BCMA_CORE_PCI_IMASK_PME 0x00000010 /* PCI PME# */
+#define BCMA_CORE_PCI_MBOX 0x0028 /* Backplane to PCI Mailbox */
+#define BCMA_CORE_PCI_MBOX_F0_0 0x00000100 /* PCI function 0, INT 0 */
+#define BCMA_CORE_PCI_MBOX_F0_1 0x00000200 /* PCI function 0, INT 1 */
+#define BCMA_CORE_PCI_MBOX_F1_0 0x00000400 /* PCI function 1, INT 0 */
+#define BCMA_CORE_PCI_MBOX_F1_1 0x00000800 /* PCI function 1, INT 1 */
+#define BCMA_CORE_PCI_MBOX_F2_0 0x00001000 /* PCI function 2, INT 0 */
+#define BCMA_CORE_PCI_MBOX_F2_1 0x00002000 /* PCI function 2, INT 1 */
+#define BCMA_CORE_PCI_MBOX_F3_0 0x00004000 /* PCI function 3, INT 0 */
+#define BCMA_CORE_PCI_MBOX_F3_1 0x00008000 /* PCI function 3, INT 1 */
+#define BCMA_CORE_PCI_BCAST_ADDR 0x0050 /* Backplane Broadcast Address */
+#define BCMA_CORE_PCI_BCAST_ADDR_MASK 0x000000FF
+#define BCMA_CORE_PCI_BCAST_DATA 0x0054 /* Backplane Broadcast Data */
+#define BCMA_CORE_PCI_GPIO_IN 0x0060 /* rev >= 2 only */
+#define BCMA_CORE_PCI_GPIO_OUT 0x0064 /* rev >= 2 only */
+#define BCMA_CORE_PCI_GPIO_ENABLE 0x0068 /* rev >= 2 only */
+#define BCMA_CORE_PCI_GPIO_CTL 0x006C /* rev >= 2 only */
+#define BCMA_CORE_PCI_SBTOPCI0 0x0100 /* Backplane to PCI translation 0 (sbtopci0) */
+#define BCMA_CORE_PCI_SBTOPCI0_MASK 0xFC000000
+#define BCMA_CORE_PCI_SBTOPCI1 0x0104 /* Backplane to PCI translation 1 (sbtopci1) */
+#define BCMA_CORE_PCI_SBTOPCI1_MASK 0xFC000000
+#define BCMA_CORE_PCI_SBTOPCI2 0x0108 /* Backplane to PCI translation 2 (sbtopci2) */
+#define BCMA_CORE_PCI_SBTOPCI2_MASK 0xC0000000
+#define BCMA_CORE_PCI_CONFIG_ADDR 0x0120 /* pcie config space access */
+#define BCMA_CORE_PCI_CONFIG_DATA 0x0124 /* pcie config space access */
+#define BCMA_CORE_PCI_MDIO_CONTROL 0x0128 /* controls the mdio access */
+#define BCMA_CORE_PCI_MDIOCTL_DIVISOR_MASK 0x7f /* clock to be used on MDIO */
+#define BCMA_CORE_PCI_MDIOCTL_DIVISOR_VAL 0x2
+#define BCMA_CORE_PCI_MDIOCTL_PREAM_EN 0x80 /* Enable preamble sequnce */
+#define BCMA_CORE_PCI_MDIOCTL_ACCESS_DONE 0x100 /* Tranaction complete */
+#define BCMA_CORE_PCI_MDIO_DATA 0x012c /* Data to the mdio access */
+#define BCMA_CORE_PCI_MDIODATA_MASK 0x0000ffff /* data 2 bytes */
+#define BCMA_CORE_PCI_MDIODATA_TA 0x00020000 /* Turnaround */
+#define BCMA_CORE_PCI_MDIODATA_REGADDR_SHF_OLD 18 /* Regaddr shift (rev < 10) */
+#define BCMA_CORE_PCI_MDIODATA_REGADDR_MASK_OLD 0x003c0000 /* Regaddr Mask (rev < 10) */
+#define BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF_OLD 22 /* Physmedia devaddr shift (rev < 10) */
+#define BCMA_CORE_PCI_MDIODATA_DEVADDR_MASK_OLD 0x0fc00000 /* Physmedia devaddr Mask (rev < 10) */
+#define BCMA_CORE_PCI_MDIODATA_REGADDR_SHF 18 /* Regaddr shift */
+#define BCMA_CORE_PCI_MDIODATA_REGADDR_MASK 0x007c0000 /* Regaddr Mask */
+#define BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF 23 /* Physmedia devaddr shift */
+#define BCMA_CORE_PCI_MDIODATA_DEVADDR_MASK 0x0f800000 /* Physmedia devaddr Mask */
+#define BCMA_CORE_PCI_MDIODATA_WRITE 0x10000000 /* write Transaction */
+#define BCMA_CORE_PCI_MDIODATA_READ 0x20000000 /* Read Transaction */
+#define BCMA_CORE_PCI_MDIODATA_START 0x40000000 /* start of Transaction */
+#define BCMA_CORE_PCI_MDIODATA_DEV_ADDR 0x0 /* dev address for serdes */
+#define BCMA_CORE_PCI_MDIODATA_BLK_ADDR 0x1F /* blk address for serdes */
+#define BCMA_CORE_PCI_MDIODATA_DEV_PLL 0x1d /* SERDES PLL Dev */
+#define BCMA_CORE_PCI_MDIODATA_DEV_TX 0x1e /* SERDES TX Dev */
+#define BCMA_CORE_PCI_MDIODATA_DEV_RX 0x1f /* SERDES RX Dev */
+#define BCMA_CORE_PCI_PCIEIND_ADDR 0x0130 /* indirect access to the internal register */
+#define BCMA_CORE_PCI_PCIEIND_DATA 0x0134 /* Data to/from the internal register */
+#define BCMA_CORE_PCI_CLKREQENCTRL 0x0138 /* >= rev 6, Clkreq rdma control */
+#define BCMA_CORE_PCI_PCICFG0 0x0400 /* PCI config space 0 (rev >= 8) */
+#define BCMA_CORE_PCI_PCICFG1 0x0500 /* PCI config space 1 (rev >= 8) */
+#define BCMA_CORE_PCI_PCICFG2 0x0600 /* PCI config space 2 (rev >= 8) */
+#define BCMA_CORE_PCI_PCICFG3 0x0700 /* PCI config space 3 (rev >= 8) */
+#define BCMA_CORE_PCI_SPROM(wordoffset) (0x0800 + ((wordoffset) * 2)) /* SPROM shadow area (72 bytes) */
+#define BCMA_CORE_PCI_SPROM_PI_OFFSET 0 /* first word */
+#define BCMA_CORE_PCI_SPROM_PI_MASK 0xf000 /* bit 15:12 */
+#define BCMA_CORE_PCI_SPROM_PI_SHIFT 12 /* bit 15:12 */
+#define BCMA_CORE_PCI_SPROM_MISC_CONFIG 5 /* word 5 */
+#define BCMA_CORE_PCI_SPROM_L23READY_EXIT_NOPERST 0x8000 /* bit 15 */
+#define BCMA_CORE_PCI_SPROM_CLKREQ_OFFSET_REV5 20 /* word 20 for srom rev <= 5 */
+#define BCMA_CORE_PCI_SPROM_CLKREQ_ENB 0x0800 /* bit 11 */
+
+/* SBtoPCIx */
+#define BCMA_CORE_PCI_SBTOPCI_MEM 0x00000000
+#define BCMA_CORE_PCI_SBTOPCI_IO 0x00000001
+#define BCMA_CORE_PCI_SBTOPCI_CFG0 0x00000002
+#define BCMA_CORE_PCI_SBTOPCI_CFG1 0x00000003
+#define BCMA_CORE_PCI_SBTOPCI_PREF 0x00000004 /* Prefetch enable */
+#define BCMA_CORE_PCI_SBTOPCI_BURST 0x00000008 /* Burst enable */
+#define BCMA_CORE_PCI_SBTOPCI_MRM 0x00000020 /* Memory Read Multiple */
+#define BCMA_CORE_PCI_SBTOPCI_RC 0x00000030 /* Read Command mask (rev >= 11) */
+#define BCMA_CORE_PCI_SBTOPCI_RC_READ 0x00000000 /* Memory read */
+#define BCMA_CORE_PCI_SBTOPCI_RC_READL 0x00000010 /* Memory read line */
+#define BCMA_CORE_PCI_SBTOPCI_RC_READM 0x00000020 /* Memory read multiple */
+
+/* PCIE protocol PHY diagnostic registers */
+#define BCMA_CORE_PCI_PLP_MODEREG 0x200 /* Mode */
+#define BCMA_CORE_PCI_PLP_STATUSREG 0x204 /* Status */
+#define BCMA_CORE_PCI_PLP_POLARITYINV_STAT 0x10 /* Status reg PCIE_PLP_STATUSREG */
+#define BCMA_CORE_PCI_PLP_LTSSMCTRLREG 0x208 /* LTSSM control */
+#define BCMA_CORE_PCI_PLP_LTLINKNUMREG 0x20c /* Link Training Link number */
+#define BCMA_CORE_PCI_PLP_LTLANENUMREG 0x210 /* Link Training Lane number */
+#define BCMA_CORE_PCI_PLP_LTNFTSREG 0x214 /* Link Training N_FTS */
+#define BCMA_CORE_PCI_PLP_ATTNREG 0x218 /* Attention */
+#define BCMA_CORE_PCI_PLP_ATTNMASKREG 0x21C /* Attention Mask */
+#define BCMA_CORE_PCI_PLP_RXERRCTR 0x220 /* Rx Error */
+#define BCMA_CORE_PCI_PLP_RXFRMERRCTR 0x224 /* Rx Framing Error */
+#define BCMA_CORE_PCI_PLP_RXERRTHRESHREG 0x228 /* Rx Error threshold */
+#define BCMA_CORE_PCI_PLP_TESTCTRLREG 0x22C /* Test Control reg */
+#define BCMA_CORE_PCI_PLP_SERDESCTRLOVRDREG 0x230 /* SERDES Control Override */
+#define BCMA_CORE_PCI_PLP_TIMINGOVRDREG 0x234 /* Timing param override */
+#define BCMA_CORE_PCI_PLP_RXTXSMDIAGREG 0x238 /* RXTX State Machine Diag */
+#define BCMA_CORE_PCI_PLP_LTSSMDIAGREG 0x23C /* LTSSM State Machine Diag */
+
+/* PCIE protocol DLLP diagnostic registers */
+#define BCMA_CORE_PCI_DLLP_LCREG 0x100 /* Link Control */
+#define BCMA_CORE_PCI_DLLP_LSREG 0x104 /* Link Status */
+#define BCMA_CORE_PCI_DLLP_LAREG 0x108 /* Link Attention */
+#define BCMA_CORE_PCI_DLLP_LSREG_LINKUP (1 << 16)
+#define BCMA_CORE_PCI_DLLP_LAMASKREG 0x10C /* Link Attention Mask */
+#define BCMA_CORE_PCI_DLLP_NEXTTXSEQNUMREG 0x110 /* Next Tx Seq Num */
+#define BCMA_CORE_PCI_DLLP_ACKEDTXSEQNUMREG 0x114 /* Acked Tx Seq Num */
+#define BCMA_CORE_PCI_DLLP_PURGEDTXSEQNUMREG 0x118 /* Purged Tx Seq Num */
+#define BCMA_CORE_PCI_DLLP_RXSEQNUMREG 0x11C /* Rx Sequence Number */
+#define BCMA_CORE_PCI_DLLP_LRREG 0x120 /* Link Replay */
+#define BCMA_CORE_PCI_DLLP_LACKTOREG 0x124 /* Link Ack Timeout */
+#define BCMA_CORE_PCI_DLLP_PMTHRESHREG 0x128 /* Power Management Threshold */
+#define BCMA_CORE_PCI_ASPMTIMER_EXTEND 0x01000000 /* > rev7: enable extend ASPM timer */
+#define BCMA_CORE_PCI_DLLP_RTRYWPREG 0x12C /* Retry buffer write ptr */
+#define BCMA_CORE_PCI_DLLP_RTRYRPREG 0x130 /* Retry buffer Read ptr */
+#define BCMA_CORE_PCI_DLLP_RTRYPPREG 0x134 /* Retry buffer Purged ptr */
+#define BCMA_CORE_PCI_DLLP_RTRRWREG 0x138 /* Retry buffer Read/Write */
+#define BCMA_CORE_PCI_DLLP_ECTHRESHREG 0x13C /* Error Count Threshold */
+#define BCMA_CORE_PCI_DLLP_TLPERRCTRREG 0x140 /* TLP Error Counter */
+#define BCMA_CORE_PCI_DLLP_ERRCTRREG 0x144 /* Error Counter */
+#define BCMA_CORE_PCI_DLLP_NAKRXCTRREG 0x148 /* NAK Received Counter */
+#define BCMA_CORE_PCI_DLLP_TESTREG 0x14C /* Test */
+#define BCMA_CORE_PCI_DLLP_PKTBIST 0x150 /* Packet BIST */
+#define BCMA_CORE_PCI_DLLP_PCIE11 0x154 /* DLLP PCIE 1.1 reg */
+
+/* SERDES RX registers */
+#define BCMA_CORE_PCI_SERDES_RX_CTRL 1 /* Rx cntrl */
+#define BCMA_CORE_PCI_SERDES_RX_CTRL_FORCE 0x80 /* rxpolarity_force */
+#define BCMA_CORE_PCI_SERDES_RX_CTRL_POLARITY 0x40 /* rxpolarity_value */
+#define BCMA_CORE_PCI_SERDES_RX_TIMER1 2 /* Rx Timer1 */
+#define BCMA_CORE_PCI_SERDES_RX_CDR 6 /* CDR */
+#define BCMA_CORE_PCI_SERDES_RX_CDRBW 7 /* CDR BW */
+
+/* SERDES PLL registers */
+#define BCMA_CORE_PCI_SERDES_PLL_CTRL 1 /* PLL control reg */
+#define BCMA_CORE_PCI_PLL_CTRL_FREQDET_EN 0x4000 /* bit 14 is FREQDET on */
+
+/* PCIcore specific boardflags */
+#define BCMA_CORE_PCI_BFL_NOPCI 0x00000400 /* Board leaves PCI floating */
+
+/* PCIE Config space accessing MACROS */
+#define BCMA_CORE_PCI_CFG_BUS_SHIFT 24 /* Bus shift */
+#define BCMA_CORE_PCI_CFG_SLOT_SHIFT 19 /* Slot/Device shift */
+#define BCMA_CORE_PCI_CFG_FUN_SHIFT 16 /* Function shift */
+#define BCMA_CORE_PCI_CFG_OFF_SHIFT 0 /* Register shift */
+
+#define BCMA_CORE_PCI_CFG_BUS_MASK 0xff /* Bus mask */
+#define BCMA_CORE_PCI_CFG_SLOT_MASK 0x1f /* Slot/Device mask */
+#define BCMA_CORE_PCI_CFG_FUN_MASK 7 /* Function mask */
+#define BCMA_CORE_PCI_CFG_OFF_MASK 0xfff /* Register mask */
+
+#define BCMA_CORE_PCI_CFG_DEVCTRL 0xd8
+
+#define BCMA_CORE_PCI_
+
+/* MDIO devices (SERDES modules) */
+#define BCMA_CORE_PCI_MDIO_IEEE0 0x000
+#define BCMA_CORE_PCI_MDIO_IEEE1 0x001
+#define BCMA_CORE_PCI_MDIO_BLK0 0x800
+#define BCMA_CORE_PCI_MDIO_BLK1 0x801
+#define BCMA_CORE_PCI_MDIO_BLK1_MGMT0 0x16
+#define BCMA_CORE_PCI_MDIO_BLK1_MGMT1 0x17
+#define BCMA_CORE_PCI_MDIO_BLK1_MGMT2 0x18
+#define BCMA_CORE_PCI_MDIO_BLK1_MGMT3 0x19
+#define BCMA_CORE_PCI_MDIO_BLK1_MGMT4 0x1A
+#define BCMA_CORE_PCI_MDIO_BLK2 0x802
+#define BCMA_CORE_PCI_MDIO_BLK3 0x803
+#define BCMA_CORE_PCI_MDIO_BLK4 0x804
+#define BCMA_CORE_PCI_MDIO_TXPLL 0x808 /* TXPLL register block idx */
+#define BCMA_CORE_PCI_MDIO_TXCTRL0 0x820
+#define BCMA_CORE_PCI_MDIO_SERDESID 0x831
+#define BCMA_CORE_PCI_MDIO_RXCTRL0 0x840
+
+/* PCIE Root Capability Register bits (Host mode only) */
+#define BCMA_CORE_PCI_RC_CRS_VISIBILITY 0x0001
+
+struct bcma_drv_pci;
+struct bcma_bus;
+
+#ifdef CONFIG_BCMA_DRIVER_PCI_HOSTMODE
+struct bcma_drv_pci_host {
+ struct bcma_drv_pci *pdev;
+
+ u32 host_cfg_addr;
+ spinlock_t cfgspace_lock;
+
+ struct pci_controller pci_controller;
+ struct pci_ops pci_ops;
+ struct resource mem_resource;
+ struct resource io_resource;
+};
+#endif
+
+struct bcma_drv_pci {
+ struct bcma_device *core;
+ u8 early_setup_done:1;
+ u8 setup_done:1;
+ u8 hostmode:1;
+
+#ifdef CONFIG_BCMA_DRIVER_PCI_HOSTMODE
+ struct bcma_drv_pci_host *host_controller;
+#endif
+};
+
+/* Register access */
+#define pcicore_read16(pc, offset) bcma_read16((pc)->core, offset)
+#define pcicore_read32(pc, offset) bcma_read32((pc)->core, offset)
+#define pcicore_write16(pc, offset, val) bcma_write16((pc)->core, offset, val)
+#define pcicore_write32(pc, offset, val) bcma_write32((pc)->core, offset, val)
+
+#ifdef CONFIG_BCMA_DRIVER_PCI
+extern void bcma_core_pci_power_save(struct bcma_bus *bus, bool up);
+#else
+static inline void bcma_core_pci_power_save(struct bcma_bus *bus, bool up)
+{
+}
+#endif
+
+#ifdef CONFIG_BCMA_DRIVER_PCI_HOSTMODE
+extern int bcma_core_pci_pcibios_map_irq(const struct pci_dev *dev);
+extern int bcma_core_pci_plat_dev_init(struct pci_dev *dev);
+#else
+static inline int bcma_core_pci_pcibios_map_irq(const struct pci_dev *dev)
+{
+ return -ENOTSUPP;
+}
+static inline int bcma_core_pci_plat_dev_init(struct pci_dev *dev)
+{
+ return -ENOTSUPP;
+}
+#endif
+
+#endif /* LINUX_BCMA_DRIVER_PCI_H_ */
diff --git a/include/linux/bcma/bcma_driver_pcie2.h b/include/linux/bcma/bcma_driver_pcie2.h
new file mode 100644
index 0000000..31e6d17
--- /dev/null
+++ b/include/linux/bcma/bcma_driver_pcie2.h
@@ -0,0 +1,158 @@
+#ifndef LINUX_BCMA_DRIVER_PCIE2_H_
+#define LINUX_BCMA_DRIVER_PCIE2_H_
+
+#define BCMA_CORE_PCIE2_CLK_CONTROL 0x0000
+#define PCIE2_CLKC_RST_OE 0x0001 /* When set, drives PCI_RESET out to pin */
+#define PCIE2_CLKC_RST 0x0002 /* Value driven out to pin */
+#define PCIE2_CLKC_SPERST 0x0004 /* SurvivePeRst */
+#define PCIE2_CLKC_DISABLE_L1CLK_GATING 0x0010
+#define PCIE2_CLKC_DLYPERST 0x0100 /* Delay PeRst to CoE Core */
+#define PCIE2_CLKC_DISSPROMLD 0x0200 /* DisableSpromLoadOnPerst */
+#define PCIE2_CLKC_WAKE_MODE_L2 0x1000 /* Wake on L2 */
+#define BCMA_CORE_PCIE2_RC_PM_CONTROL 0x0004
+#define BCMA_CORE_PCIE2_RC_PM_STATUS 0x0008
+#define BCMA_CORE_PCIE2_EP_PM_CONTROL 0x000C
+#define BCMA_CORE_PCIE2_EP_PM_STATUS 0x0010
+#define BCMA_CORE_PCIE2_EP_LTR_CONTROL 0x0014
+#define BCMA_CORE_PCIE2_EP_LTR_STATUS 0x0018
+#define BCMA_CORE_PCIE2_EP_OBFF_STATUS 0x001C
+#define BCMA_CORE_PCIE2_PCIE_ERR_STATUS 0x0020
+#define BCMA_CORE_PCIE2_RC_AXI_CONFIG 0x0100
+#define BCMA_CORE_PCIE2_EP_AXI_CONFIG 0x0104
+#define BCMA_CORE_PCIE2_RXDEBUG_STATUS0 0x0108
+#define BCMA_CORE_PCIE2_RXDEBUG_CONTROL0 0x010C
+#define BCMA_CORE_PCIE2_CONFIGINDADDR 0x0120
+#define BCMA_CORE_PCIE2_CONFIGINDDATA 0x0124
+#define BCMA_CORE_PCIE2_MDIOCONTROL 0x0128
+#define BCMA_CORE_PCIE2_MDIOWRDATA 0x012C
+#define BCMA_CORE_PCIE2_MDIORDDATA 0x0130
+#define BCMA_CORE_PCIE2_DATAINTF 0x0180
+#define BCMA_CORE_PCIE2_D2H_INTRLAZY_0 0x0188
+#define BCMA_CORE_PCIE2_H2D_INTRLAZY_0 0x018c
+#define BCMA_CORE_PCIE2_H2D_INTSTAT_0 0x0190
+#define BCMA_CORE_PCIE2_H2D_INTMASK_0 0x0194
+#define BCMA_CORE_PCIE2_D2H_INTSTAT_0 0x0198
+#define BCMA_CORE_PCIE2_D2H_INTMASK_0 0x019c
+#define BCMA_CORE_PCIE2_LTR_STATE 0x01A0 /* Latency Tolerance Reporting */
+#define PCIE2_LTR_ACTIVE 2
+#define PCIE2_LTR_ACTIVE_IDLE 1
+#define PCIE2_LTR_SLEEP 0
+#define PCIE2_LTR_FINAL_MASK 0x300
+#define PCIE2_LTR_FINAL_SHIFT 8
+#define BCMA_CORE_PCIE2_PWR_INT_STATUS 0x01A4
+#define BCMA_CORE_PCIE2_PWR_INT_MASK 0x01A8
+#define BCMA_CORE_PCIE2_CFG_ADDR 0x01F8
+#define BCMA_CORE_PCIE2_CFG_DATA 0x01FC
+#define BCMA_CORE_PCIE2_SYS_EQ_PAGE 0x0200
+#define BCMA_CORE_PCIE2_SYS_MSI_PAGE 0x0204
+#define BCMA_CORE_PCIE2_SYS_MSI_INTREN 0x0208
+#define BCMA_CORE_PCIE2_SYS_MSI_CTRL0 0x0210
+#define BCMA_CORE_PCIE2_SYS_MSI_CTRL1 0x0214
+#define BCMA_CORE_PCIE2_SYS_MSI_CTRL2 0x0218
+#define BCMA_CORE_PCIE2_SYS_MSI_CTRL3 0x021C
+#define BCMA_CORE_PCIE2_SYS_MSI_CTRL4 0x0220
+#define BCMA_CORE_PCIE2_SYS_MSI_CTRL5 0x0224
+#define BCMA_CORE_PCIE2_SYS_EQ_HEAD0 0x0250
+#define BCMA_CORE_PCIE2_SYS_EQ_TAIL0 0x0254
+#define BCMA_CORE_PCIE2_SYS_EQ_HEAD1 0x0258
+#define BCMA_CORE_PCIE2_SYS_EQ_TAIL1 0x025C
+#define BCMA_CORE_PCIE2_SYS_EQ_HEAD2 0x0260
+#define BCMA_CORE_PCIE2_SYS_EQ_TAIL2 0x0264
+#define BCMA_CORE_PCIE2_SYS_EQ_HEAD3 0x0268
+#define BCMA_CORE_PCIE2_SYS_EQ_TAIL3 0x026C
+#define BCMA_CORE_PCIE2_SYS_EQ_HEAD4 0x0270
+#define BCMA_CORE_PCIE2_SYS_EQ_TAIL4 0x0274
+#define BCMA_CORE_PCIE2_SYS_EQ_HEAD5 0x0278
+#define BCMA_CORE_PCIE2_SYS_EQ_TAIL5 0x027C
+#define BCMA_CORE_PCIE2_SYS_RC_INTX_EN 0x0330
+#define BCMA_CORE_PCIE2_SYS_RC_INTX_CSR 0x0334
+#define BCMA_CORE_PCIE2_SYS_MSI_REQ 0x0340
+#define BCMA_CORE_PCIE2_SYS_HOST_INTR_EN 0x0344
+#define BCMA_CORE_PCIE2_SYS_HOST_INTR_CSR 0x0348
+#define BCMA_CORE_PCIE2_SYS_HOST_INTR0 0x0350
+#define BCMA_CORE_PCIE2_SYS_HOST_INTR1 0x0354
+#define BCMA_CORE_PCIE2_SYS_HOST_INTR2 0x0358
+#define BCMA_CORE_PCIE2_SYS_HOST_INTR3 0x035C
+#define BCMA_CORE_PCIE2_SYS_EP_INT_EN0 0x0360
+#define BCMA_CORE_PCIE2_SYS_EP_INT_EN1 0x0364
+#define BCMA_CORE_PCIE2_SYS_EP_INT_CSR0 0x0370
+#define BCMA_CORE_PCIE2_SYS_EP_INT_CSR1 0x0374
+#define BCMA_CORE_PCIE2_SPROM(wordoffset) (0x0800 + ((wordoffset) * 2))
+#define BCMA_CORE_PCIE2_FUNC0_IMAP0_0 0x0C00
+#define BCMA_CORE_PCIE2_FUNC0_IMAP0_1 0x0C04
+#define BCMA_CORE_PCIE2_FUNC0_IMAP0_2 0x0C08
+#define BCMA_CORE_PCIE2_FUNC0_IMAP0_3 0x0C0C
+#define BCMA_CORE_PCIE2_FUNC0_IMAP0_4 0x0C10
+#define BCMA_CORE_PCIE2_FUNC0_IMAP0_5 0x0C14
+#define BCMA_CORE_PCIE2_FUNC0_IMAP0_6 0x0C18
+#define BCMA_CORE_PCIE2_FUNC0_IMAP0_7 0x0C1C
+#define BCMA_CORE_PCIE2_FUNC1_IMAP0_0 0x0C20
+#define BCMA_CORE_PCIE2_FUNC1_IMAP0_1 0x0C24
+#define BCMA_CORE_PCIE2_FUNC1_IMAP0_2 0x0C28
+#define BCMA_CORE_PCIE2_FUNC1_IMAP0_3 0x0C2C
+#define BCMA_CORE_PCIE2_FUNC1_IMAP0_4 0x0C30
+#define BCMA_CORE_PCIE2_FUNC1_IMAP0_5 0x0C34
+#define BCMA_CORE_PCIE2_FUNC1_IMAP0_6 0x0C38
+#define BCMA_CORE_PCIE2_FUNC1_IMAP0_7 0x0C3C
+#define BCMA_CORE_PCIE2_FUNC0_IMAP1 0x0C80
+#define BCMA_CORE_PCIE2_FUNC1_IMAP1 0x0C88
+#define BCMA_CORE_PCIE2_FUNC0_IMAP2 0x0CC0
+#define BCMA_CORE_PCIE2_FUNC1_IMAP2 0x0CC8
+#define BCMA_CORE_PCIE2_IARR0_LOWER 0x0D00
+#define BCMA_CORE_PCIE2_IARR0_UPPER 0x0D04
+#define BCMA_CORE_PCIE2_IARR1_LOWER 0x0D08
+#define BCMA_CORE_PCIE2_IARR1_UPPER 0x0D0C
+#define BCMA_CORE_PCIE2_IARR2_LOWER 0x0D10
+#define BCMA_CORE_PCIE2_IARR2_UPPER 0x0D14
+#define BCMA_CORE_PCIE2_OARR0 0x0D20
+#define BCMA_CORE_PCIE2_OARR1 0x0D28
+#define BCMA_CORE_PCIE2_OARR2 0x0D30
+#define BCMA_CORE_PCIE2_OMAP0_LOWER 0x0D40
+#define BCMA_CORE_PCIE2_OMAP0_UPPER 0x0D44
+#define BCMA_CORE_PCIE2_OMAP1_LOWER 0x0D48
+#define BCMA_CORE_PCIE2_OMAP1_UPPER 0x0D4C
+#define BCMA_CORE_PCIE2_OMAP2_LOWER 0x0D50
+#define BCMA_CORE_PCIE2_OMAP2_UPPER 0x0D54
+#define BCMA_CORE_PCIE2_FUNC1_IARR1_SIZE 0x0D58
+#define BCMA_CORE_PCIE2_FUNC1_IARR2_SIZE 0x0D5C
+#define BCMA_CORE_PCIE2_MEM_CONTROL 0x0F00
+#define BCMA_CORE_PCIE2_MEM_ECC_ERRLOG0 0x0F04
+#define BCMA_CORE_PCIE2_MEM_ECC_ERRLOG1 0x0F08
+#define BCMA_CORE_PCIE2_LINK_STATUS 0x0F0C
+#define BCMA_CORE_PCIE2_STRAP_STATUS 0x0F10
+#define BCMA_CORE_PCIE2_RESET_STATUS 0x0F14
+#define BCMA_CORE_PCIE2_RESETEN_IN_LINKDOWN 0x0F18
+#define BCMA_CORE_PCIE2_MISC_INTR_EN 0x0F1C
+#define BCMA_CORE_PCIE2_TX_DEBUG_CFG 0x0F20
+#define BCMA_CORE_PCIE2_MISC_CONFIG 0x0F24
+#define BCMA_CORE_PCIE2_MISC_STATUS 0x0F28
+#define BCMA_CORE_PCIE2_INTR_EN 0x0F30
+#define BCMA_CORE_PCIE2_INTR_CLEAR 0x0F34
+#define BCMA_CORE_PCIE2_INTR_STATUS 0x0F38
+
+/* PCIE gen2 config regs */
+#define PCIE2_INTSTATUS 0x090
+#define PCIE2_INTMASK 0x094
+#define PCIE2_SBMBX 0x098
+
+#define PCIE2_PMCR_REFUP 0x1814 /* Trefup time */
+
+#define PCIE2_CAP_DEVSTSCTRL2_OFFSET 0xD4
+#define PCIE2_CAP_DEVSTSCTRL2_LTRENAB 0x400
+#define PCIE2_PVT_REG_PM_CLK_PERIOD 0x184c
+
+struct bcma_drv_pcie2 {
+ struct bcma_device *core;
+
+ u16 reqsize;
+};
+
+#define pcie2_read16(pcie2, offset) bcma_read16((pcie2)->core, offset)
+#define pcie2_read32(pcie2, offset) bcma_read32((pcie2)->core, offset)
+#define pcie2_write16(pcie2, offset, val) bcma_write16((pcie2)->core, offset, val)
+#define pcie2_write32(pcie2, offset, val) bcma_write32((pcie2)->core, offset, val)
+
+#define pcie2_set32(pcie2, offset, set) bcma_set32((pcie2)->core, offset, set)
+#define pcie2_mask32(pcie2, offset, mask) bcma_mask32((pcie2)->core, offset, mask)
+
+#endif /* LINUX_BCMA_DRIVER_PCIE2_H_ */
diff --git a/include/linux/bcma/bcma_regs.h b/include/linux/bcma/bcma_regs.h
new file mode 100644
index 0000000..9986f82
--- /dev/null
+++ b/include/linux/bcma/bcma_regs.h
@@ -0,0 +1,103 @@
+#ifndef LINUX_BCMA_REGS_H_
+#define LINUX_BCMA_REGS_H_
+
+/* Some single registers are shared between many cores */
+/* BCMA_CLKCTLST: ChipCommon (rev >= 20), PCIe, 80211 */
+#define BCMA_CLKCTLST 0x01E0 /* Clock control and status */
+#define BCMA_CLKCTLST_FORCEALP 0x00000001 /* Force ALP request */
+#define BCMA_CLKCTLST_FORCEHT 0x00000002 /* Force HT request */
+#define BCMA_CLKCTLST_FORCEILP 0x00000004 /* Force ILP request */
+#define BCMA_CLKCTLST_HAVEALPREQ 0x00000008 /* ALP available request */
+#define BCMA_CLKCTLST_HAVEHTREQ 0x00000010 /* HT available request */
+#define BCMA_CLKCTLST_HWCROFF 0x00000020 /* Force HW clock request off */
+#define BCMA_CLKCTLST_HQCLKREQ 0x00000040 /* HQ Clock */
+#define BCMA_CLKCTLST_EXTRESREQ 0x00000700 /* Mask of external resource requests */
+#define BCMA_CLKCTLST_EXTRESREQ_SHIFT 8
+#define BCMA_CLKCTLST_HAVEALP 0x00010000 /* ALP available */
+#define BCMA_CLKCTLST_HAVEHT 0x00020000 /* HT available */
+#define BCMA_CLKCTLST_BP_ON_ALP 0x00040000 /* RO: running on ALP clock */
+#define BCMA_CLKCTLST_BP_ON_HT 0x00080000 /* RO: running on HT clock */
+#define BCMA_CLKCTLST_EXTRESST 0x07000000 /* Mask of external resource status */
+#define BCMA_CLKCTLST_EXTRESST_SHIFT 24
+/* Is there any BCM4328 on BCMA bus? */
+#define BCMA_CLKCTLST_4328A0_HAVEHT 0x00010000 /* 4328a0 has reversed bits */
+#define BCMA_CLKCTLST_4328A0_HAVEALP 0x00020000 /* 4328a0 has reversed bits */
+
+/* Agent registers (common for every core) */
+#define BCMA_OOB_SEL_OUT_A30 0x0100
+#define BCMA_IOCTL 0x0408 /* IO control */
+#define BCMA_IOCTL_CLK 0x0001
+#define BCMA_IOCTL_FGC 0x0002
+#define BCMA_IOCTL_CORE_BITS 0x3FFC
+#define BCMA_IOCTL_PME_EN 0x4000
+#define BCMA_IOCTL_BIST_EN 0x8000
+#define BCMA_IOST 0x0500 /* IO status */
+#define BCMA_IOST_CORE_BITS 0x0FFF
+#define BCMA_IOST_DMA64 0x1000
+#define BCMA_IOST_GATED_CLK 0x2000
+#define BCMA_IOST_BIST_ERROR 0x4000
+#define BCMA_IOST_BIST_DONE 0x8000
+#define BCMA_RESET_CTL 0x0800
+#define BCMA_RESET_CTL_RESET 0x0001
+#define BCMA_RESET_ST 0x0804
+
+#define BCMA_NS_ROM_IOST_BOOT_DEV_MASK 0x0003
+#define BCMA_NS_ROM_IOST_BOOT_DEV_NOR 0x0000
+#define BCMA_NS_ROM_IOST_BOOT_DEV_NAND 0x0001
+#define BCMA_NS_ROM_IOST_BOOT_DEV_ROM 0x0002
+
+/* BCMA PCI config space registers. */
+#define BCMA_PCI_PMCSR 0x44
+#define BCMA_PCI_PE 0x100
+#define BCMA_PCI_BAR0_WIN 0x80 /* Backplane address space 0 */
+#define BCMA_PCI_BAR1_WIN 0x84 /* Backplane address space 1 */
+#define BCMA_PCI_SPROMCTL 0x88 /* SPROM control */
+#define BCMA_PCI_SPROMCTL_WE 0x10 /* SPROM write enable */
+#define BCMA_PCI_BAR1_CONTROL 0x8c /* Address space 1 burst control */
+#define BCMA_PCI_IRQS 0x90 /* PCI interrupts */
+#define BCMA_PCI_IRQMASK 0x94 /* PCI IRQ control and mask (pcirev >= 6 only) */
+#define BCMA_PCI_BACKPLANE_IRQS 0x98 /* Backplane Interrupts */
+#define BCMA_PCI_BAR0_WIN2 0xAC
+#define BCMA_PCI_GPIO_IN 0xB0 /* GPIO Input (pcirev >= 3 only) */
+#define BCMA_PCI_GPIO_OUT 0xB4 /* GPIO Output (pcirev >= 3 only) */
+#define BCMA_PCI_GPIO_OUT_ENABLE 0xB8 /* GPIO Output Enable/Disable (pcirev >= 3 only) */
+#define BCMA_PCI_GPIO_SCS 0x10 /* PCI config space bit 4 for 4306c0 slow clock source */
+#define BCMA_PCI_GPIO_HWRAD 0x20 /* PCI config space GPIO 13 for hw radio disable */
+#define BCMA_PCI_GPIO_XTAL 0x40 /* PCI config space GPIO 14 for Xtal powerup */
+#define BCMA_PCI_GPIO_PLL 0x80 /* PCI config space GPIO 15 for PLL powerdown */
+
+#define BCMA_PCIE2_BAR0_WIN2 0x70
+
+/* SiliconBackplane Address Map.
+ * All regions may not exist on all chips.
+ */
+#define BCMA_SOC_SDRAM_BASE 0x00000000U /* Physical SDRAM */
+#define BCMA_SOC_PCI_MEM 0x08000000U /* Host Mode sb2pcitranslation0 (64 MB) */
+#define BCMA_SOC_PCI_MEM_SZ (64 * 1024 * 1024)
+#define BCMA_SOC_PCI_CFG 0x0c000000U /* Host Mode sb2pcitranslation1 (64 MB) */
+#define BCMA_SOC_SDRAM_SWAPPED 0x10000000U /* Byteswapped Physical SDRAM */
+#define BCMA_SOC_SDRAM_R2 0x80000000U /* Region 2 for sdram (512 MB) */
+
+
+#define BCMA_SOC_PCI_DMA 0x40000000U /* Client Mode sb2pcitranslation2 (1 GB) */
+#define BCMA_SOC_PCI_DMA2 0x80000000U /* Client Mode sb2pcitranslation2 (1 GB) */
+#define BCMA_SOC_PCI_DMA_SZ 0x40000000U /* Client Mode sb2pcitranslation2 size in bytes */
+#define BCMA_SOC_PCIE_DMA_L32 0x00000000U /* PCIE Client Mode sb2pcitranslation2
+ * (2 ZettaBytes), low 32 bits
+ */
+#define BCMA_SOC_PCIE_DMA_H32 0x80000000U /* PCIE Client Mode sb2pcitranslation2
+ * (2 ZettaBytes), high 32 bits
+ */
+
+#define BCMA_SOC_PCI1_MEM 0x40000000U /* Host Mode sb2pcitranslation0 (64 MB) */
+#define BCMA_SOC_PCI1_CFG 0x44000000U /* Host Mode sb2pcitranslation1 (64 MB) */
+#define BCMA_SOC_PCIE1_DMA_H32 0xc0000000U /* PCIE Client Mode sb2pcitranslation2
+ * (2 ZettaBytes), high 32 bits
+ */
+
+#define BCMA_SOC_FLASH1 0x1fc00000 /* MIPS Flash Region 1 */
+#define BCMA_SOC_FLASH1_SZ 0x00400000 /* MIPS Size of Flash Region 1 */
+#define BCMA_SOC_FLASH2 0x1c000000 /* Flash Region 2 (region 1 shadowed here) */
+#define BCMA_SOC_FLASH2_SZ 0x02000000 /* Size of Flash Region 2 */
+
+#endif /* LINUX_BCMA_REGS_H_ */
diff --git a/include/linux/bcma/bcma_soc.h b/include/linux/bcma/bcma_soc.h
new file mode 100644
index 0000000..1b5fc0c
--- /dev/null
+++ b/include/linux/bcma/bcma_soc.h
@@ -0,0 +1,15 @@
+#ifndef LINUX_BCMA_SOC_H_
+#define LINUX_BCMA_SOC_H_
+
+#include <linux/bcma/bcma.h>
+
+struct bcma_soc {
+ struct bcma_bus bus;
+};
+
+int __init bcma_host_soc_register(struct bcma_soc *soc);
+int __init bcma_host_soc_init(struct bcma_soc *soc);
+
+int bcma_bus_register(struct bcma_bus *bus);
+
+#endif /* LINUX_BCMA_SOC_H_ */
diff --git a/include/linux/bitfield.h b/include/linux/bitfield.h
new file mode 100644
index 0000000..8b9d6ff
--- /dev/null
+++ b/include/linux/bitfield.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2014 Felix Fietkau <nbd@nbd.name>
+ * Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation
+ *
+ * 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 _LINUX_BITFIELD_H
+#define _LINUX_BITFIELD_H
+
+#include <linux/bug.h>
+
+/*
+ * Bitfield access macros
+ *
+ * FIELD_{GET,PREP} macros take as first parameter shifted mask
+ * from which they extract the base mask and shift amount.
+ * Mask must be a compilation time constant.
+ *
+ * Example:
+ *
+ * #define REG_FIELD_A GENMASK(6, 0)
+ * #define REG_FIELD_B BIT(7)
+ * #define REG_FIELD_C GENMASK(15, 8)
+ * #define REG_FIELD_D GENMASK(31, 16)
+ *
+ * Get:
+ * a = FIELD_GET(REG_FIELD_A, reg);
+ * b = FIELD_GET(REG_FIELD_B, reg);
+ *
+ * Set:
+ * reg = FIELD_PREP(REG_FIELD_A, 1) |
+ * FIELD_PREP(REG_FIELD_B, 0) |
+ * FIELD_PREP(REG_FIELD_C, c) |
+ * FIELD_PREP(REG_FIELD_D, 0x40);
+ *
+ * Modify:
+ * reg &= ~REG_FIELD_C;
+ * reg |= FIELD_PREP(REG_FIELD_C, c);
+ */
+
+#define __bf_shf(x) (__builtin_ffsll(x) - 1)
+
+#define __BF_FIELD_CHECK(_mask, _reg, _val, _pfx) \
+ ({ \
+ BUILD_BUG_ON_MSG(!__builtin_constant_p(_mask), \
+ _pfx "mask is not constant"); \
+ BUILD_BUG_ON_MSG(!(_mask), _pfx "mask is zero"); \
+ BUILD_BUG_ON_MSG(__builtin_constant_p(_val) ? \
+ ~((_mask) >> __bf_shf(_mask)) & (_val) : 0, \
+ _pfx "value too large for the field"); \
+ BUILD_BUG_ON_MSG((_mask) > (typeof(_reg))~0ull, \
+ _pfx "type of reg too small for mask"); \
+ __BUILD_BUG_ON_NOT_POWER_OF_2((_mask) + \
+ (1ULL << __bf_shf(_mask))); \
+ })
+
+/**
+ * FIELD_FIT() - check if value fits in the field
+ * @_mask: shifted mask defining the field's length and position
+ * @_val: value to test against the field
+ *
+ * Return: true if @_val can fit inside @_mask, false if @_val is too big.
+ */
+#define FIELD_FIT(_mask, _val) \
+ ({ \
+ __BF_FIELD_CHECK(_mask, 0ULL, _val, "FIELD_FIT: "); \
+ !((((typeof(_mask))_val) << __bf_shf(_mask)) & ~(_mask)); \
+ })
+
+/**
+ * FIELD_PREP() - prepare a bitfield element
+ * @_mask: shifted mask defining the field's length and position
+ * @_val: value to put in the field
+ *
+ * FIELD_PREP() masks and shifts up the value. The result should
+ * be combined with other fields of the bitfield using logical OR.
+ */
+#define FIELD_PREP(_mask, _val) \
+ ({ \
+ __BF_FIELD_CHECK(_mask, 0ULL, _val, "FIELD_PREP: "); \
+ ((typeof(_mask))(_val) << __bf_shf(_mask)) & (_mask); \
+ })
+
+/**
+ * FIELD_GET() - extract a bitfield element
+ * @_mask: shifted mask defining the field's length and position
+ * @_reg: 32bit value of entire bitfield
+ *
+ * FIELD_GET() extracts the field specified by @_mask from the
+ * bitfield passed in as @_reg by masking and shifting it down.
+ */
+#define FIELD_GET(_mask, _reg) \
+ ({ \
+ __BF_FIELD_CHECK(_mask, _reg, 0U, "FIELD_GET: "); \
+ (typeof(_mask))(((_reg) & (_mask)) >> __bf_shf(_mask)); \
+ })
+
+#endif
diff --git a/include/linux/devcoredump.h b/include/linux/devcoredump.h
new file mode 100644
index 0000000..0a2f9c8
--- /dev/null
+++ b/include/linux/devcoredump.h
@@ -0,0 +1,13 @@
+/* Automatically created during backport process */
+#ifndef CPTCFG_BPAUTO_BUILD_WANT_DEV_COREDUMP
+#include_next <linux/devcoredump.h>
+#include <linux/bp-devcoredump.h>
+#else
+#undef dev_coredumpv
+#define dev_coredumpv LINUX_BACKPORT(dev_coredumpv)
+#undef dev_coredumpm
+#define dev_coredumpm LINUX_BACKPORT(dev_coredumpm)
+#undef dev_coredumpsg
+#define dev_coredumpsg LINUX_BACKPORT(dev_coredumpsg)
+#include <linux/backport-devcoredump.h>
+#endif /* CPTCFG_BPAUTO_BUILD_WANT_DEV_COREDUMP */
diff --git a/include/linux/fixp-arith.h b/include/linux/fixp-arith.h
new file mode 100644
index 0000000..d4686fe
--- /dev/null
+++ b/include/linux/fixp-arith.h
@@ -0,0 +1,156 @@
+#ifndef _FIXP_ARITH_H
+#define _FIXP_ARITH_H
+
+#include <linux/math64.h>
+
+/*
+ * Simplistic fixed-point arithmetics.
+ * Hmm, I'm probably duplicating some code :(
+ *
+ * Copyright (c) 2002 Johann Deneux
+ */
+
+/*
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so by
+ * e-mail - mail your message to <johann.deneux@gmail.com>
+ */
+
+#include <linux/types.h>
+
+static const s32 sin_table[] = {
+ 0x00000000, 0x023be165, 0x04779632, 0x06b2f1d2, 0x08edc7b6, 0x0b27eb5c,
+ 0x0d61304d, 0x0f996a26, 0x11d06c96, 0x14060b67, 0x163a1a7d, 0x186c6ddd,
+ 0x1a9cd9ac, 0x1ccb3236, 0x1ef74bf2, 0x2120fb82, 0x234815ba, 0x256c6f9e,
+ 0x278dde6e, 0x29ac379f, 0x2bc750e8, 0x2ddf003f, 0x2ff31bdd, 0x32037a44,
+ 0x340ff241, 0x36185aee, 0x381c8bb5, 0x3a1c5c56, 0x3c17a4e7, 0x3e0e3ddb,
+ 0x3fffffff, 0x41ecc483, 0x43d464fa, 0x45b6bb5d, 0x4793a20f, 0x496af3e1,
+ 0x4b3c8c11, 0x4d084650, 0x4ecdfec6, 0x508d9210, 0x5246dd48, 0x53f9be04,
+ 0x55a6125a, 0x574bb8e5, 0x58ea90c2, 0x5a827999, 0x5c135399, 0x5d9cff82,
+ 0x5f1f5ea0, 0x609a52d1, 0x620dbe8a, 0x637984d3, 0x64dd894f, 0x6639b039,
+ 0x678dde6d, 0x68d9f963, 0x6a1de735, 0x6b598ea1, 0x6c8cd70a, 0x6db7a879,
+ 0x6ed9eba0, 0x6ff389de, 0x71046d3c, 0x720c8074, 0x730baeec, 0x7401e4bf,
+ 0x74ef0ebb, 0x75d31a5f, 0x76adf5e5, 0x777f903b, 0x7847d908, 0x7906c0af,
+ 0x79bc384c, 0x7a6831b8, 0x7b0a9f8c, 0x7ba3751c, 0x7c32a67c, 0x7cb82884,
+ 0x7d33f0c8, 0x7da5f5a3, 0x7e0e2e31, 0x7e6c924f, 0x7ec11aa3, 0x7f0bc095,
+ 0x7f4c7e52, 0x7f834ecf, 0x7fb02dc4, 0x7fd317b3, 0x7fec09e1, 0x7ffb025e,
+ 0x7fffffff
+};
+
+/**
+ * __fixp_sin32() returns the sin of an angle in degrees
+ *
+ * @degrees: angle, in degrees, from 0 to 360.
+ *
+ * The returned value ranges from -0x7fffffff to +0x7fffffff.
+ */
+static inline s32 __fixp_sin32(int degrees)
+{
+ s32 ret;
+ bool negative = false;
+
+ if (degrees > 180) {
+ negative = true;
+ degrees -= 180;
+ }
+ if (degrees > 90)
+ degrees = 180 - degrees;
+
+ ret = sin_table[degrees];
+
+ return negative ? -ret : ret;
+}
+
+/**
+ * fixp_sin32() returns the sin of an angle in degrees
+ *
+ * @degrees: angle, in degrees. The angle can be positive or negative
+ *
+ * The returned value ranges from -0x7fffffff to +0x7fffffff.
+ */
+static inline s32 fixp_sin32(int degrees)
+{
+ degrees = (degrees % 360 + 360) % 360;
+
+ return __fixp_sin32(degrees);
+}
+
+/* cos(x) = sin(x + 90 degrees) */
+#define fixp_cos32(v) fixp_sin32((v) + 90)
+
+/*
+ * 16 bits variants
+ *
+ * The returned value ranges from -0x7fff to 0x7fff
+ */
+
+#define fixp_sin16(v) (fixp_sin32(v) >> 16)
+#define fixp_cos16(v) (fixp_cos32(v) >> 16)
+
+/**
+ * fixp_sin32_rad() - calculates the sin of an angle in radians
+ *
+ * @radians: angle, in radians
+ * @twopi: value to be used for 2*pi
+ *
+ * Provides a variant for the cases where just 360
+ * values is not enough. This function uses linear
+ * interpolation to a wider range of values given by
+ * twopi var.
+ *
+ * Experimental tests gave a maximum difference of
+ * 0.000038 between the value calculated by sin() and
+ * the one produced by this function, when twopi is
+ * equal to 360000. That seems to be enough precision
+ * for practical purposes.
+ *
+ * Please notice that two high numbers for twopi could cause
+ * overflows, so the routine will not allow values of twopi
+ * bigger than 1^18.
+ */
+static inline s32 fixp_sin32_rad(u32 radians, u32 twopi)
+{
+ int degrees;
+ s32 v1, v2, dx, dy;
+ s64 tmp;
+
+ /*
+ * Avoid too large values for twopi, as we don't want overflows.
+ */
+ BUG_ON(twopi > 1 << 18);
+
+ degrees = (radians * 360) / twopi;
+ tmp = radians - (degrees * twopi) / 360;
+
+ degrees = (degrees % 360 + 360) % 360;
+ v1 = __fixp_sin32(degrees);
+
+ v2 = fixp_sin32(degrees + 1);
+
+ dx = twopi / 360;
+ dy = v2 - v1;
+
+ tmp *= dy;
+
+ return v1 + div_s64(tmp, dx);
+}
+
+/* cos(x) = sin(x + pi/2 radians) */
+
+#define fixp_cos32_rad(rad, twopi) \
+ fixp_sin32_rad(rad + twopi / 4, twopi)
+
+#endif
diff --git a/include/linux/hashtable.h b/include/linux/hashtable.h
new file mode 100644
index 0000000..082dc1b
--- /dev/null
+++ b/include/linux/hashtable.h
@@ -0,0 +1,208 @@
+/*
+ * Statically sized hash table implementation
+ * (C) 2012 Sasha Levin <levinsasha928@gmail.com>
+ */
+
+#ifndef _LINUX_HASHTABLE_H
+#define _LINUX_HASHTABLE_H
+
+#include <linux/list.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/hash.h>
+#include <linux/rculist.h>
+
+#define DEFINE_HASHTABLE(name, bits) \
+ struct hlist_head name[1 << (bits)] = \
+ { [0 ... ((1 << (bits)) - 1)] = HLIST_HEAD_INIT }
+
+#define DEFINE_READ_MOSTLY_HASHTABLE(name, bits) \
+ struct hlist_head name[1 << (bits)] __read_mostly = \
+ { [0 ... ((1 << (bits)) - 1)] = HLIST_HEAD_INIT }
+
+#define DECLARE_HASHTABLE(name, bits) \
+ struct hlist_head name[1 << (bits)]
+
+#define HASH_SIZE(name) (ARRAY_SIZE(name))
+#define HASH_BITS(name) ilog2(HASH_SIZE(name))
+
+/* Use hash_32 when possible to allow for fast 32bit hashing in 64bit kernels. */
+#define hash_min(val, bits) \
+ (sizeof(val) <= 4 ? hash_32(val, bits) : hash_long(val, bits))
+
+static inline void __hash_init(struct hlist_head *ht, unsigned int sz)
+{
+ unsigned int i;
+
+ for (i = 0; i < sz; i++)
+ INIT_HLIST_HEAD(&ht[i]);
+}
+
+/**
+ * hash_init - initialize a hash table
+ * @hashtable: hashtable to be initialized
+ *
+ * Calculates the size of the hashtable from the given parameter, otherwise
+ * same as hash_init_size.
+ *
+ * This has to be a macro since HASH_BITS() will not work on pointers since
+ * it calculates the size during preprocessing.
+ */
+#define hash_init(hashtable) __hash_init(hashtable, HASH_SIZE(hashtable))
+
+/**
+ * hash_add - add an object to a hashtable
+ * @hashtable: hashtable to add to
+ * @node: the &struct hlist_node of the object to be added
+ * @key: the key of the object to be added
+ */
+#define hash_add(hashtable, node, key) \
+ hlist_add_head(node, &hashtable[hash_min(key, HASH_BITS(hashtable))])
+
+/**
+ * hash_add_rcu - add an object to a rcu enabled hashtable
+ * @hashtable: hashtable to add to
+ * @node: the &struct hlist_node of the object to be added
+ * @key: the key of the object to be added
+ */
+#define hash_add_rcu(hashtable, node, key) \
+ hlist_add_head_rcu(node, &hashtable[hash_min(key, HASH_BITS(hashtable))])
+
+/**
+ * hash_hashed - check whether an object is in any hashtable
+ * @node: the &struct hlist_node of the object to be checked
+ */
+static inline bool hash_hashed(struct hlist_node *node)
+{
+ return !hlist_unhashed(node);
+}
+
+static inline bool __hash_empty(struct hlist_head *ht, unsigned int sz)
+{
+ unsigned int i;
+
+ for (i = 0; i < sz; i++)
+ if (!hlist_empty(&ht[i]))
+ return false;
+
+ return true;
+}
+
+/**
+ * hash_empty - check whether a hashtable is empty
+ * @hashtable: hashtable to check
+ *
+ * This has to be a macro since HASH_BITS() will not work on pointers since
+ * it calculates the size during preprocessing.
+ */
+#define hash_empty(hashtable) __hash_empty(hashtable, HASH_SIZE(hashtable))
+
+/**
+ * hash_del - remove an object from a hashtable
+ * @node: &struct hlist_node of the object to remove
+ */
+static inline void hash_del(struct hlist_node *node)
+{
+ hlist_del_init(node);
+}
+
+/**
+ * hash_del_rcu - remove an object from a rcu enabled hashtable
+ * @node: &struct hlist_node of the object to remove
+ */
+static inline void hash_del_rcu(struct hlist_node *node)
+{
+ hlist_del_init_rcu(node);
+}
+
+/**
+ * hash_for_each - iterate over a hashtable
+ * @name: hashtable to iterate
+ * @bkt: integer to use as bucket loop cursor
+ * @obj: the type * to use as a loop cursor for each entry
+ * @member: the name of the hlist_node within the struct
+ */
+#define hash_for_each(name, bkt, obj, member) \
+ for ((bkt) = 0, obj = NULL; obj == NULL && (bkt) < HASH_SIZE(name);\
+ (bkt)++)\
+ hlist_for_each_entry(obj, &name[bkt], member)
+
+/**
+ * hash_for_each_rcu - iterate over a rcu enabled hashtable
+ * @name: hashtable to iterate
+ * @bkt: integer to use as bucket loop cursor
+ * @obj: the type * to use as a loop cursor for each entry
+ * @member: the name of the hlist_node within the struct
+ */
+#define hash_for_each_rcu(name, bkt, obj, member) \
+ for ((bkt) = 0, obj = NULL; obj == NULL && (bkt) < HASH_SIZE(name);\
+ (bkt)++)\
+ hlist_for_each_entry_rcu(obj, &name[bkt], member)
+
+/**
+ * hash_for_each_safe - iterate over a hashtable safe against removal of
+ * hash entry
+ * @name: hashtable to iterate
+ * @bkt: integer to use as bucket loop cursor
+ * @tmp: a &struct used for temporary storage
+ * @obj: the type * to use as a loop cursor for each entry
+ * @member: the name of the hlist_node within the struct
+ */
+#define hash_for_each_safe(name, bkt, tmp, obj, member) \
+ for ((bkt) = 0, obj = NULL; obj == NULL && (bkt) < HASH_SIZE(name);\
+ (bkt)++)\
+ hlist_for_each_entry_safe(obj, tmp, &name[bkt], member)
+
+/**
+ * hash_for_each_possible - iterate over all possible objects hashing to the
+ * same bucket
+ * @name: hashtable to iterate
+ * @obj: the type * to use as a loop cursor for each entry
+ * @member: the name of the hlist_node within the struct
+ * @key: the key of the objects to iterate over
+ */
+#define hash_for_each_possible(name, obj, member, key) \
+ hlist_for_each_entry(obj, &name[hash_min(key, HASH_BITS(name))], member)
+
+/**
+ * hash_for_each_possible_rcu - iterate over all possible objects hashing to the
+ * same bucket in an rcu enabled hashtable
+ * @name: hashtable to iterate
+ * @obj: the type * to use as a loop cursor for each entry
+ * @member: the name of the hlist_node within the struct
+ * @key: the key of the objects to iterate over
+ */
+#define hash_for_each_possible_rcu(name, obj, member, key) \
+ hlist_for_each_entry_rcu(obj, &name[hash_min(key, HASH_BITS(name))],\
+ member)
+
+/**
+ * hash_for_each_possible_rcu_notrace - iterate over all possible objects hashing
+ * to the same bucket in an rcu enabled hashtable in a rcu enabled hashtable
+ * @name: hashtable to iterate
+ * @obj: the type * to use as a loop cursor for each entry
+ * @member: the name of the hlist_node within the struct
+ * @key: the key of the objects to iterate over
+ *
+ * This is the same as hash_for_each_possible_rcu() except that it does
+ * not do any RCU debugging or tracing.
+ */
+#define hash_for_each_possible_rcu_notrace(name, obj, member, key) \
+ hlist_for_each_entry_rcu_notrace(obj, \
+ &name[hash_min(key, HASH_BITS(name))], member)
+
+/**
+ * hash_for_each_possible_safe - iterate over all possible objects hashing to the
+ * same bucket safe against removals
+ * @name: hashtable to iterate
+ * @obj: the type * to use as a loop cursor for each entry
+ * @tmp: a &struct used for temporary storage
+ * @member: the name of the hlist_node within the struct
+ * @key: the key of the objects to iterate over
+ */
+#define hash_for_each_possible_safe(name, obj, tmp, member, key) \
+ hlist_for_each_entry_safe(obj, tmp,\
+ &name[hash_min(key, HASH_BITS(name))], member)
+
+
+#endif
diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
new file mode 100644
index 0000000..34e1bcd
--- /dev/null
+++ b/include/linux/ieee80211.h
@@ -0,0 +1,2713 @@
+/*
+ * IEEE 802.11 defines
+ *
+ * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
+ * <jkmaline@cc.hut.fi>
+ * Copyright (c) 2002-2003, Jouni Malinen <jkmaline@cc.hut.fi>
+ * Copyright (c) 2005, Devicescape Software, Inc.
+ * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
+ * Copyright (c) 2013 - 2014 Intel Mobile Communications GmbH
+ * Copyright (c) 2016 - 2017 Intel Deutschland GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef LINUX_IEEE80211_H
+#define LINUX_IEEE80211_H
+
+#include <linux/types.h>
+#include <linux/if_ether.h>
+#include <linux/etherdevice.h>
+#include <asm/byteorder.h>
+#include <asm/unaligned.h>
+
+/*
+ * DS bit usage
+ *
+ * TA = transmitter address
+ * RA = receiver address
+ * DA = destination address
+ * SA = source address
+ *
+ * ToDS FromDS A1(RA) A2(TA) A3 A4 Use
+ * -----------------------------------------------------------------
+ * 0 0 DA SA BSSID - IBSS/DLS
+ * 0 1 DA BSSID SA - AP -> STA
+ * 1 0 BSSID SA DA - AP <- STA
+ * 1 1 RA TA DA SA unspecified (WDS)
+ */
+
+#define FCS_LEN 4
+
+#define IEEE80211_FCTL_VERS 0x0003
+#define IEEE80211_FCTL_FTYPE 0x000c
+#define IEEE80211_FCTL_STYPE 0x00f0
+#define IEEE80211_FCTL_TODS 0x0100
+#define IEEE80211_FCTL_FROMDS 0x0200
+#define IEEE80211_FCTL_MOREFRAGS 0x0400
+#define IEEE80211_FCTL_RETRY 0x0800
+#define IEEE80211_FCTL_PM 0x1000
+#define IEEE80211_FCTL_MOREDATA 0x2000
+#define IEEE80211_FCTL_PROTECTED 0x4000
+#define IEEE80211_FCTL_ORDER 0x8000
+#define IEEE80211_FCTL_CTL_EXT 0x0f00
+
+#define IEEE80211_SCTL_FRAG 0x000F
+#define IEEE80211_SCTL_SEQ 0xFFF0
+
+#define IEEE80211_FTYPE_MGMT 0x0000
+#define IEEE80211_FTYPE_CTL 0x0004
+#define IEEE80211_FTYPE_DATA 0x0008
+#define IEEE80211_FTYPE_EXT 0x000c
+
+/* management */
+#define IEEE80211_STYPE_ASSOC_REQ 0x0000
+#define IEEE80211_STYPE_ASSOC_RESP 0x0010
+#define IEEE80211_STYPE_REASSOC_REQ 0x0020
+#define IEEE80211_STYPE_REASSOC_RESP 0x0030
+#define IEEE80211_STYPE_PROBE_REQ 0x0040
+#define IEEE80211_STYPE_PROBE_RESP 0x0050
+#define IEEE80211_STYPE_BEACON 0x0080
+#define IEEE80211_STYPE_ATIM 0x0090
+#define IEEE80211_STYPE_DISASSOC 0x00A0
+#define IEEE80211_STYPE_AUTH 0x00B0
+#define IEEE80211_STYPE_DEAUTH 0x00C0
+#define IEEE80211_STYPE_ACTION 0x00D0
+
+/* control */
+#define IEEE80211_STYPE_CTL_EXT 0x0060
+#define IEEE80211_STYPE_BACK_REQ 0x0080
+#define IEEE80211_STYPE_BACK 0x0090
+#define IEEE80211_STYPE_PSPOLL 0x00A0
+#define IEEE80211_STYPE_RTS 0x00B0
+#define IEEE80211_STYPE_CTS 0x00C0
+#define IEEE80211_STYPE_ACK 0x00D0
+#define IEEE80211_STYPE_CFEND 0x00E0
+#define IEEE80211_STYPE_CFENDACK 0x00F0
+
+/* data */
+#define IEEE80211_STYPE_DATA 0x0000
+#define IEEE80211_STYPE_DATA_CFACK 0x0010
+#define IEEE80211_STYPE_DATA_CFPOLL 0x0020
+#define IEEE80211_STYPE_DATA_CFACKPOLL 0x0030
+#define IEEE80211_STYPE_NULLFUNC 0x0040
+#define IEEE80211_STYPE_CFACK 0x0050
+#define IEEE80211_STYPE_CFPOLL 0x0060
+#define IEEE80211_STYPE_CFACKPOLL 0x0070
+#define IEEE80211_STYPE_QOS_DATA 0x0080
+#define IEEE80211_STYPE_QOS_DATA_CFACK 0x0090
+#define IEEE80211_STYPE_QOS_DATA_CFPOLL 0x00A0
+#define IEEE80211_STYPE_QOS_DATA_CFACKPOLL 0x00B0
+#define IEEE80211_STYPE_QOS_NULLFUNC 0x00C0
+#define IEEE80211_STYPE_QOS_CFACK 0x00D0
+#define IEEE80211_STYPE_QOS_CFPOLL 0x00E0
+#define IEEE80211_STYPE_QOS_CFACKPOLL 0x00F0
+
+/* extension, added by 802.11ad */
+#define IEEE80211_STYPE_DMG_BEACON 0x0000
+
+/* control extension - for IEEE80211_FTYPE_CTL | IEEE80211_STYPE_CTL_EXT */
+#define IEEE80211_CTL_EXT_POLL 0x2000
+#define IEEE80211_CTL_EXT_SPR 0x3000
+#define IEEE80211_CTL_EXT_GRANT 0x4000
+#define IEEE80211_CTL_EXT_DMG_CTS 0x5000
+#define IEEE80211_CTL_EXT_DMG_DTS 0x6000
+#define IEEE80211_CTL_EXT_SSW 0x8000
+#define IEEE80211_CTL_EXT_SSW_FBACK 0x9000
+#define IEEE80211_CTL_EXT_SSW_ACK 0xa000
+
+
+#define IEEE80211_SN_MASK ((IEEE80211_SCTL_SEQ) >> 4)
+#define IEEE80211_MAX_SN IEEE80211_SN_MASK
+#define IEEE80211_SN_MODULO (IEEE80211_MAX_SN + 1)
+
+static inline bool ieee80211_sn_less(u16 sn1, u16 sn2)
+{
+ return ((sn1 - sn2) & IEEE80211_SN_MASK) > (IEEE80211_SN_MODULO >> 1);
+}
+
+static inline u16 ieee80211_sn_add(u16 sn1, u16 sn2)
+{
+ return (sn1 + sn2) & IEEE80211_SN_MASK;
+}
+
+static inline u16 ieee80211_sn_inc(u16 sn)
+{
+ return ieee80211_sn_add(sn, 1);
+}
+
+static inline u16 ieee80211_sn_sub(u16 sn1, u16 sn2)
+{
+ return (sn1 - sn2) & IEEE80211_SN_MASK;
+}
+
+#define IEEE80211_SEQ_TO_SN(seq) (((seq) & IEEE80211_SCTL_SEQ) >> 4)
+#define IEEE80211_SN_TO_SEQ(ssn) (((ssn) << 4) & IEEE80211_SCTL_SEQ)
+
+/* miscellaneous IEEE 802.11 constants */
+#define IEEE80211_MAX_FRAG_THRESHOLD 2352
+#define IEEE80211_MAX_RTS_THRESHOLD 2353
+#define IEEE80211_MAX_AID 2007
+#define IEEE80211_MAX_TIM_LEN 251
+#define IEEE80211_MAX_MESH_PEERINGS 63
+/* Maximum size for the MA-UNITDATA primitive, 802.11 standard section
+ 6.2.1.1.2.
+
+ 802.11e clarifies the figure in section 7.1.2. The frame body is
+ up to 2304 octets long (maximum MSDU size) plus any crypt overhead. */
+#define IEEE80211_MAX_DATA_LEN 2304
+/* 802.11ad extends maximum MSDU size for DMG (freq > 40Ghz) networks
+ * to 7920 bytes, see 8.2.3 General frame format
+ */
+#define IEEE80211_MAX_DATA_LEN_DMG 7920
+/* 30 byte 4 addr hdr, 2 byte QoS, 2304 byte MSDU, 12 byte crypt, 4 byte FCS */
+#define IEEE80211_MAX_FRAME_LEN 2352
+
+/* Maximal size of an A-MSDU that can be transported in a HT BA session */
+#define IEEE80211_MAX_MPDU_LEN_HT_BA 4095
+
+/* Maximal size of an A-MSDU */
+#define IEEE80211_MAX_MPDU_LEN_HT_3839 3839
+#define IEEE80211_MAX_MPDU_LEN_HT_7935 7935
+
+#define IEEE80211_MAX_MPDU_LEN_VHT_3895 3895
+#define IEEE80211_MAX_MPDU_LEN_VHT_7991 7991
+#define IEEE80211_MAX_MPDU_LEN_VHT_11454 11454
+
+#define IEEE80211_MAX_SSID_LEN 32
+
+#define IEEE80211_MAX_MESH_ID_LEN 32
+
+#define IEEE80211_FIRST_TSPEC_TSID 8
+#define IEEE80211_NUM_TIDS 16
+
+/* number of user priorities 802.11 uses */
+#define IEEE80211_NUM_UPS 8
+/* number of ACs */
+#define IEEE80211_NUM_ACS 4
+
+#define IEEE80211_QOS_CTL_LEN 2
+/* 1d tag mask */
+#define IEEE80211_QOS_CTL_TAG1D_MASK 0x0007
+/* TID mask */
+#define IEEE80211_QOS_CTL_TID_MASK 0x000f
+/* EOSP */
+#define IEEE80211_QOS_CTL_EOSP 0x0010
+/* ACK policy */
+#define IEEE80211_QOS_CTL_ACK_POLICY_NORMAL 0x0000
+#define IEEE80211_QOS_CTL_ACK_POLICY_NOACK 0x0020
+#define IEEE80211_QOS_CTL_ACK_POLICY_NO_EXPL 0x0040
+#define IEEE80211_QOS_CTL_ACK_POLICY_BLOCKACK 0x0060
+#define IEEE80211_QOS_CTL_ACK_POLICY_MASK 0x0060
+/* A-MSDU 802.11n */
+#define IEEE80211_QOS_CTL_A_MSDU_PRESENT 0x0080
+/* Mesh Control 802.11s */
+#define IEEE80211_QOS_CTL_MESH_CONTROL_PRESENT 0x0100
+
+/* Mesh Power Save Level */
+#define IEEE80211_QOS_CTL_MESH_PS_LEVEL 0x0200
+/* Mesh Receiver Service Period Initiated */
+#define IEEE80211_QOS_CTL_RSPI 0x0400
+
+/* U-APSD queue for WMM IEs sent by AP */
+#define IEEE80211_WMM_IE_AP_QOSINFO_UAPSD (1<<7)
+#define IEEE80211_WMM_IE_AP_QOSINFO_PARAM_SET_CNT_MASK 0x0f
+
+/* U-APSD queues for WMM IEs sent by STA */
+#define IEEE80211_WMM_IE_STA_QOSINFO_AC_VO (1<<0)
+#define IEEE80211_WMM_IE_STA_QOSINFO_AC_VI (1<<1)
+#define IEEE80211_WMM_IE_STA_QOSINFO_AC_BK (1<<2)
+#define IEEE80211_WMM_IE_STA_QOSINFO_AC_BE (1<<3)
+#define IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK 0x0f
+
+/* U-APSD max SP length for WMM IEs sent by STA */
+#define IEEE80211_WMM_IE_STA_QOSINFO_SP_ALL 0x00
+#define IEEE80211_WMM_IE_STA_QOSINFO_SP_2 0x01
+#define IEEE80211_WMM_IE_STA_QOSINFO_SP_4 0x02
+#define IEEE80211_WMM_IE_STA_QOSINFO_SP_6 0x03
+#define IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK 0x03
+#define IEEE80211_WMM_IE_STA_QOSINFO_SP_SHIFT 5
+
+#define IEEE80211_HT_CTL_LEN 4
+
+struct ieee80211_hdr {
+ __le16 frame_control;
+ __le16 duration_id;
+ u8 addr1[ETH_ALEN];
+ u8 addr2[ETH_ALEN];
+ u8 addr3[ETH_ALEN];
+ __le16 seq_ctrl;
+ u8 addr4[ETH_ALEN];
+} __packed __aligned(2);
+
+struct ieee80211_hdr_3addr {
+ __le16 frame_control;
+ __le16 duration_id;
+ u8 addr1[ETH_ALEN];
+ u8 addr2[ETH_ALEN];
+ u8 addr3[ETH_ALEN];
+ __le16 seq_ctrl;
+} __packed __aligned(2);
+
+struct ieee80211_qos_hdr {
+ __le16 frame_control;
+ __le16 duration_id;
+ u8 addr1[ETH_ALEN];
+ u8 addr2[ETH_ALEN];
+ u8 addr3[ETH_ALEN];
+ __le16 seq_ctrl;
+ __le16 qos_ctrl;
+} __packed __aligned(2);
+
+/**
+ * ieee80211_has_tods - check if IEEE80211_FCTL_TODS is set
+ * @fc: frame control bytes in little-endian byteorder
+ */
+static inline bool ieee80211_has_tods(__le16 fc)
+{
+ return (fc & cpu_to_le16(IEEE80211_FCTL_TODS)) != 0;
+}
+
+/**
+ * ieee80211_has_fromds - check if IEEE80211_FCTL_FROMDS is set
+ * @fc: frame control bytes in little-endian byteorder
+ */
+static inline bool ieee80211_has_fromds(__le16 fc)
+{
+ return (fc & cpu_to_le16(IEEE80211_FCTL_FROMDS)) != 0;
+}
+
+/**
+ * ieee80211_has_a4 - check if IEEE80211_FCTL_TODS and IEEE80211_FCTL_FROMDS are set
+ * @fc: frame control bytes in little-endian byteorder
+ */
+static inline bool ieee80211_has_a4(__le16 fc)
+{
+ __le16 tmp = cpu_to_le16(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS);
+ return (fc & tmp) == tmp;
+}
+
+/**
+ * ieee80211_has_morefrags - check if IEEE80211_FCTL_MOREFRAGS is set
+ * @fc: frame control bytes in little-endian byteorder
+ */
+static inline bool ieee80211_has_morefrags(__le16 fc)
+{
+ return (fc & cpu_to_le16(IEEE80211_FCTL_MOREFRAGS)) != 0;
+}
+
+/**
+ * ieee80211_has_retry - check if IEEE80211_FCTL_RETRY is set
+ * @fc: frame control bytes in little-endian byteorder
+ */
+static inline bool ieee80211_has_retry(__le16 fc)
+{
+ return (fc & cpu_to_le16(IEEE80211_FCTL_RETRY)) != 0;
+}
+
+/**
+ * ieee80211_has_pm - check if IEEE80211_FCTL_PM is set
+ * @fc: frame control bytes in little-endian byteorder
+ */
+static inline bool ieee80211_has_pm(__le16 fc)
+{
+ return (fc & cpu_to_le16(IEEE80211_FCTL_PM)) != 0;
+}
+
+/**
+ * ieee80211_has_moredata - check if IEEE80211_FCTL_MOREDATA is set
+ * @fc: frame control bytes in little-endian byteorder
+ */
+static inline bool ieee80211_has_moredata(__le16 fc)
+{
+ return (fc & cpu_to_le16(IEEE80211_FCTL_MOREDATA)) != 0;
+}
+
+/**
+ * ieee80211_has_protected - check if IEEE80211_FCTL_PROTECTED is set
+ * @fc: frame control bytes in little-endian byteorder
+ */
+static inline bool ieee80211_has_protected(__le16 fc)
+{
+ return (fc & cpu_to_le16(IEEE80211_FCTL_PROTECTED)) != 0;
+}
+
+/**
+ * ieee80211_has_order - check if IEEE80211_FCTL_ORDER is set
+ * @fc: frame control bytes in little-endian byteorder
+ */
+static inline bool ieee80211_has_order(__le16 fc)
+{
+ return (fc & cpu_to_le16(IEEE80211_FCTL_ORDER)) != 0;
+}
+
+/**
+ * ieee80211_is_mgmt - check if type is IEEE80211_FTYPE_MGMT
+ * @fc: frame control bytes in little-endian byteorder
+ */
+static inline bool ieee80211_is_mgmt(__le16 fc)
+{
+ return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE)) ==
+ cpu_to_le16(IEEE80211_FTYPE_MGMT);
+}
+
+/**
+ * ieee80211_is_ctl - check if type is IEEE80211_FTYPE_CTL
+ * @fc: frame control bytes in little-endian byteorder
+ */
+static inline bool ieee80211_is_ctl(__le16 fc)
+{
+ return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE)) ==
+ cpu_to_le16(IEEE80211_FTYPE_CTL);
+}
+
+/**
+ * ieee80211_is_data - check if type is IEEE80211_FTYPE_DATA
+ * @fc: frame control bytes in little-endian byteorder
+ */
+static inline bool ieee80211_is_data(__le16 fc)
+{
+ return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE)) ==
+ cpu_to_le16(IEEE80211_FTYPE_DATA);
+}
+
+/**
+ * ieee80211_is_data_qos - check if type is IEEE80211_FTYPE_DATA and IEEE80211_STYPE_QOS_DATA is set
+ * @fc: frame control bytes in little-endian byteorder
+ */
+static inline bool ieee80211_is_data_qos(__le16 fc)
+{
+ /*
+ * mask with QOS_DATA rather than IEEE80211_FCTL_STYPE as we just need
+ * to check the one bit
+ */
+ return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_STYPE_QOS_DATA)) ==
+ cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_QOS_DATA);
+}
+
+/**
+ * ieee80211_is_data_present - check if type is IEEE80211_FTYPE_DATA and has data
+ * @fc: frame control bytes in little-endian byteorder
+ */
+static inline bool ieee80211_is_data_present(__le16 fc)
+{
+ /*
+ * mask with 0x40 and test that that bit is clear to only return true
+ * for the data-containing substypes.
+ */
+ return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | 0x40)) ==
+ cpu_to_le16(IEEE80211_FTYPE_DATA);
+}
+
+/**
+ * ieee80211_is_assoc_req - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_ASSOC_REQ
+ * @fc: frame control bytes in little-endian byteorder
+ */
+static inline bool ieee80211_is_assoc_req(__le16 fc)
+{
+ return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) ==
+ cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ASSOC_REQ);
+}
+
+/**
+ * ieee80211_is_assoc_resp - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_ASSOC_RESP
+ * @fc: frame control bytes in little-endian byteorder
+ */
+static inline bool ieee80211_is_assoc_resp(__le16 fc)
+{
+ return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) ==
+ cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ASSOC_RESP);
+}
+
+/**
+ * ieee80211_is_reassoc_req - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_REASSOC_REQ
+ * @fc: frame control bytes in little-endian byteorder
+ */
+static inline bool ieee80211_is_reassoc_req(__le16 fc)
+{
+ return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) ==
+ cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_REASSOC_REQ);
+}
+
+/**
+ * ieee80211_is_reassoc_resp - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_REASSOC_RESP
+ * @fc: frame control bytes in little-endian byteorder
+ */
+static inline bool ieee80211_is_reassoc_resp(__le16 fc)
+{
+ return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) ==
+ cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_REASSOC_RESP);
+}
+
+/**
+ * ieee80211_is_probe_req - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_PROBE_REQ
+ * @fc: frame control bytes in little-endian byteorder
+ */
+static inline bool ieee80211_is_probe_req(__le16 fc)
+{
+ return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) ==
+ cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_REQ);
+}
+
+/**
+ * ieee80211_is_probe_resp - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_PROBE_RESP
+ * @fc: frame control bytes in little-endian byteorder
+ */
+static inline bool ieee80211_is_probe_resp(__le16 fc)
+{
+ return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) ==
+ cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_RESP);
+}
+
+/**
+ * ieee80211_is_beacon - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_BEACON
+ * @fc: frame control bytes in little-endian byteorder
+ */
+static inline bool ieee80211_is_beacon(__le16 fc)
+{
+ return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) ==
+ cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_BEACON);
+}
+
+/**
+ * ieee80211_is_atim - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_ATIM
+ * @fc: frame control bytes in little-endian byteorder
+ */
+static inline bool ieee80211_is_atim(__le16 fc)
+{
+ return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) ==
+ cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ATIM);
+}
+
+/**
+ * ieee80211_is_disassoc - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_DISASSOC
+ * @fc: frame control bytes in little-endian byteorder
+ */
+static inline bool ieee80211_is_disassoc(__le16 fc)
+{
+ return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) ==
+ cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_DISASSOC);
+}
+
+/**
+ * ieee80211_is_auth - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_AUTH
+ * @fc: frame control bytes in little-endian byteorder
+ */
+static inline bool ieee80211_is_auth(__le16 fc)
+{
+ return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) ==
+ cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH);
+}
+
+/**
+ * ieee80211_is_deauth - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_DEAUTH
+ * @fc: frame control bytes in little-endian byteorder
+ */
+static inline bool ieee80211_is_deauth(__le16 fc)
+{
+ return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) ==
+ cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_DEAUTH);
+}
+
+/**
+ * ieee80211_is_action - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_ACTION
+ * @fc: frame control bytes in little-endian byteorder
+ */
+static inline bool ieee80211_is_action(__le16 fc)
+{
+ return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) ==
+ cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION);
+}
+
+/**
+ * ieee80211_is_back_req - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_BACK_REQ
+ * @fc: frame control bytes in little-endian byteorder
+ */
+static inline bool ieee80211_is_back_req(__le16 fc)
+{
+ return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) ==
+ cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_BACK_REQ);
+}
+
+/**
+ * ieee80211_is_back - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_BACK
+ * @fc: frame control bytes in little-endian byteorder
+ */
+static inline bool ieee80211_is_back(__le16 fc)
+{
+ return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) ==
+ cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_BACK);
+}
+
+/**
+ * ieee80211_is_pspoll - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_PSPOLL
+ * @fc: frame control bytes in little-endian byteorder
+ */
+static inline bool ieee80211_is_pspoll(__le16 fc)
+{
+ return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) ==
+ cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_PSPOLL);
+}
+
+/**
+ * ieee80211_is_rts - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_RTS
+ * @fc: frame control bytes in little-endian byteorder
+ */
+static inline bool ieee80211_is_rts(__le16 fc)
+{
+ return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) ==
+ cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_RTS);
+}
+
+/**
+ * ieee80211_is_cts - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_CTS
+ * @fc: frame control bytes in little-endian byteorder
+ */
+static inline bool ieee80211_is_cts(__le16 fc)
+{
+ return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) ==
+ cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_CTS);
+}
+
+/**
+ * ieee80211_is_ack - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_ACK
+ * @fc: frame control bytes in little-endian byteorder
+ */
+static inline bool ieee80211_is_ack(__le16 fc)
+{
+ return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) ==
+ cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_ACK);
+}
+
+/**
+ * ieee80211_is_cfend - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_CFEND
+ * @fc: frame control bytes in little-endian byteorder
+ */
+static inline bool ieee80211_is_cfend(__le16 fc)
+{
+ return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) ==
+ cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_CFEND);
+}
+
+/**
+ * ieee80211_is_cfendack - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_CFENDACK
+ * @fc: frame control bytes in little-endian byteorder
+ */
+static inline bool ieee80211_is_cfendack(__le16 fc)
+{
+ return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) ==
+ cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_CFENDACK);
+}
+
+/**
+ * ieee80211_is_nullfunc - check if frame is a regular (non-QoS) nullfunc frame
+ * @fc: frame control bytes in little-endian byteorder
+ */
+static inline bool ieee80211_is_nullfunc(__le16 fc)
+{
+ return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) ==
+ cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC);
+}
+
+/**
+ * ieee80211_is_qos_nullfunc - check if frame is a QoS nullfunc frame
+ * @fc: frame control bytes in little-endian byteorder
+ */
+static inline bool ieee80211_is_qos_nullfunc(__le16 fc)
+{
+ return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) ==
+ cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_QOS_NULLFUNC);
+}
+
+/**
+ * ieee80211_is_bufferable_mmpdu - check if frame is bufferable MMPDU
+ * @fc: frame control field in little-endian byteorder
+ */
+static inline bool ieee80211_is_bufferable_mmpdu(__le16 fc)
+{
+ /* IEEE 802.11-2012, definition of "bufferable management frame";
+ * note that this ignores the IBSS special case. */
+ return ieee80211_is_mgmt(fc) &&
+ (ieee80211_is_action(fc) ||
+ ieee80211_is_disassoc(fc) ||
+ ieee80211_is_deauth(fc));
+}
+
+/**
+ * ieee80211_is_first_frag - check if IEEE80211_SCTL_FRAG is not set
+ * @seq_ctrl: frame sequence control bytes in little-endian byteorder
+ */
+static inline bool ieee80211_is_first_frag(__le16 seq_ctrl)
+{
+ return (seq_ctrl & cpu_to_le16(IEEE80211_SCTL_FRAG)) == 0;
+}
+
+/**
+ * ieee80211_is_frag - check if a frame is a fragment
+ * @hdr: 802.11 header of the frame
+ */
+static inline bool ieee80211_is_frag(struct ieee80211_hdr *hdr)
+{
+ return ieee80211_has_morefrags(hdr->frame_control) ||
+ hdr->seq_ctrl & cpu_to_le16(IEEE80211_SCTL_FRAG);
+}
+
+struct ieee80211s_hdr {
+ u8 flags;
+ u8 ttl;
+ __le32 seqnum;
+ u8 eaddr1[ETH_ALEN];
+ u8 eaddr2[ETH_ALEN];
+} __packed __aligned(2);
+
+/* Mesh flags */
+#define MESH_FLAGS_AE_A4 0x1
+#define MESH_FLAGS_AE_A5_A6 0x2
+#define MESH_FLAGS_AE 0x3
+#define MESH_FLAGS_PS_DEEP 0x4
+
+/**
+ * enum ieee80211_preq_flags - mesh PREQ element flags
+ *
+ * @IEEE80211_PREQ_PROACTIVE_PREP_FLAG: proactive PREP subfield
+ */
+enum ieee80211_preq_flags {
+ IEEE80211_PREQ_PROACTIVE_PREP_FLAG = 1<<2,
+};
+
+/**
+ * enum ieee80211_preq_target_flags - mesh PREQ element per target flags
+ *
+ * @IEEE80211_PREQ_TO_FLAG: target only subfield
+ * @IEEE80211_PREQ_USN_FLAG: unknown target HWMP sequence number subfield
+ */
+enum ieee80211_preq_target_flags {
+ IEEE80211_PREQ_TO_FLAG = 1<<0,
+ IEEE80211_PREQ_USN_FLAG = 1<<2,
+};
+
+/**
+ * struct ieee80211_quiet_ie
+ *
+ * This structure refers to "Quiet information element"
+ */
+struct ieee80211_quiet_ie {
+ u8 count;
+ u8 period;
+ __le16 duration;
+ __le16 offset;
+} __packed;
+
+/**
+ * struct ieee80211_msrment_ie
+ *
+ * This structure refers to "Measurement Request/Report information element"
+ */
+struct ieee80211_msrment_ie {
+ u8 token;
+ u8 mode;
+ u8 type;
+ u8 request[0];
+} __packed;
+
+/**
+ * struct ieee80211_channel_sw_ie
+ *
+ * This structure refers to "Channel Switch Announcement information element"
+ */
+struct ieee80211_channel_sw_ie {
+ u8 mode;
+ u8 new_ch_num;
+ u8 count;
+} __packed;
+
+/**
+ * struct ieee80211_ext_chansw_ie
+ *
+ * This structure represents the "Extended Channel Switch Announcement element"
+ */
+struct ieee80211_ext_chansw_ie {
+ u8 mode;
+ u8 new_operating_class;
+ u8 new_ch_num;
+ u8 count;
+} __packed;
+
+/**
+ * struct ieee80211_sec_chan_offs_ie - secondary channel offset IE
+ * @sec_chan_offs: secondary channel offset, uses IEEE80211_HT_PARAM_CHA_SEC_*
+ * values here
+ * This structure represents the "Secondary Channel Offset element"
+ */
+struct ieee80211_sec_chan_offs_ie {
+ u8 sec_chan_offs;
+} __packed;
+
+/**
+ * struct ieee80211_mesh_chansw_params_ie - mesh channel switch parameters IE
+ *
+ * This structure represents the "Mesh Channel Switch Paramters element"
+ */
+struct ieee80211_mesh_chansw_params_ie {
+ u8 mesh_ttl;
+ u8 mesh_flags;
+ __le16 mesh_reason;
+ __le16 mesh_pre_value;
+} __packed;
+
+/**
+ * struct ieee80211_wide_bw_chansw_ie - wide bandwidth channel switch IE
+ */
+struct ieee80211_wide_bw_chansw_ie {
+ u8 new_channel_width;
+ u8 new_center_freq_seg0, new_center_freq_seg1;
+} __packed;
+
+/**
+ * struct ieee80211_tim
+ *
+ * This structure refers to "Traffic Indication Map information element"
+ */
+struct ieee80211_tim_ie {
+ u8 dtim_count;
+ u8 dtim_period;
+ u8 bitmap_ctrl;
+ /* variable size: 1 - 251 bytes */
+ u8 virtual_map[1];
+} __packed;
+
+/**
+ * struct ieee80211_meshconf_ie
+ *
+ * This structure refers to "Mesh Configuration information element"
+ */
+struct ieee80211_meshconf_ie {
+ u8 meshconf_psel;
+ u8 meshconf_pmetric;
+ u8 meshconf_congest;
+ u8 meshconf_synch;
+ u8 meshconf_auth;
+ u8 meshconf_form;
+ u8 meshconf_cap;
+} __packed;
+
+/**
+ * enum mesh_config_capab_flags - Mesh Configuration IE capability field flags
+ *
+ * @IEEE80211_MESHCONF_CAPAB_ACCEPT_PLINKS: STA is willing to establish
+ * additional mesh peerings with other mesh STAs
+ * @IEEE80211_MESHCONF_CAPAB_FORWARDING: the STA forwards MSDUs
+ * @IEEE80211_MESHCONF_CAPAB_TBTT_ADJUSTING: TBTT adjustment procedure
+ * is ongoing
+ * @IEEE80211_MESHCONF_CAPAB_POWER_SAVE_LEVEL: STA is in deep sleep mode or has
+ * neighbors in deep sleep mode
+ */
+enum mesh_config_capab_flags {
+ IEEE80211_MESHCONF_CAPAB_ACCEPT_PLINKS = 0x01,
+ IEEE80211_MESHCONF_CAPAB_FORWARDING = 0x08,
+ IEEE80211_MESHCONF_CAPAB_TBTT_ADJUSTING = 0x20,
+ IEEE80211_MESHCONF_CAPAB_POWER_SAVE_LEVEL = 0x40,
+};
+
+/**
+ * mesh channel switch parameters element's flag indicator
+ *
+ */
+#define WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT BIT(0)
+#define WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR BIT(1)
+#define WLAN_EID_CHAN_SWITCH_PARAM_REASON BIT(2)
+
+/**
+ * struct ieee80211_rann_ie
+ *
+ * This structure refers to "Root Announcement information element"
+ */
+struct ieee80211_rann_ie {
+ u8 rann_flags;
+ u8 rann_hopcount;
+ u8 rann_ttl;
+ u8 rann_addr[ETH_ALEN];
+ __le32 rann_seq;
+ __le32 rann_interval;
+ __le32 rann_metric;
+} __packed;
+
+enum ieee80211_rann_flags {
+ RANN_FLAG_IS_GATE = 1 << 0,
+};
+
+enum ieee80211_ht_chanwidth_values {
+ IEEE80211_HT_CHANWIDTH_20MHZ = 0,
+ IEEE80211_HT_CHANWIDTH_ANY = 1,
+};
+
+/**
+ * enum ieee80211_opmode_bits - VHT operating mode field bits
+ * @IEEE80211_OPMODE_NOTIF_CHANWIDTH_MASK: channel width mask
+ * @IEEE80211_OPMODE_NOTIF_CHANWIDTH_20MHZ: 20 MHz channel width
+ * @IEEE80211_OPMODE_NOTIF_CHANWIDTH_40MHZ: 40 MHz channel width
+ * @IEEE80211_OPMODE_NOTIF_CHANWIDTH_80MHZ: 80 MHz channel width
+ * @IEEE80211_OPMODE_NOTIF_CHANWIDTH_160MHZ: 160 MHz or 80+80 MHz channel width
+ * @IEEE80211_OPMODE_NOTIF_RX_NSS_MASK: number of spatial streams mask
+ * (the NSS value is the value of this field + 1)
+ * @IEEE80211_OPMODE_NOTIF_RX_NSS_SHIFT: number of spatial streams shift
+ * @IEEE80211_OPMODE_NOTIF_RX_NSS_TYPE_BF: indicates streams in SU-MIMO PPDU
+ * using a beamforming steering matrix
+ */
+enum ieee80211_vht_opmode_bits {
+ IEEE80211_OPMODE_NOTIF_CHANWIDTH_MASK = 3,
+ IEEE80211_OPMODE_NOTIF_CHANWIDTH_20MHZ = 0,
+ IEEE80211_OPMODE_NOTIF_CHANWIDTH_40MHZ = 1,
+ IEEE80211_OPMODE_NOTIF_CHANWIDTH_80MHZ = 2,
+ IEEE80211_OPMODE_NOTIF_CHANWIDTH_160MHZ = 3,
+ IEEE80211_OPMODE_NOTIF_RX_NSS_MASK = 0x70,
+ IEEE80211_OPMODE_NOTIF_RX_NSS_SHIFT = 4,
+ IEEE80211_OPMODE_NOTIF_RX_NSS_TYPE_BF = 0x80,
+};
+
+#define WLAN_SA_QUERY_TR_ID_LEN 2
+#define WLAN_MEMBERSHIP_LEN 8
+#define WLAN_USER_POSITION_LEN 16
+
+/**
+ * struct ieee80211_tpc_report_ie
+ *
+ * This structure refers to "TPC Report element"
+ */
+struct ieee80211_tpc_report_ie {
+ u8 tx_power;
+ u8 link_margin;
+} __packed;
+
+struct ieee80211_mgmt {
+ __le16 frame_control;
+ __le16 duration;
+ u8 da[ETH_ALEN];
+ u8 sa[ETH_ALEN];
+ u8 bssid[ETH_ALEN];
+ __le16 seq_ctrl;
+ union {
+ struct {
+ __le16 auth_alg;
+ __le16 auth_transaction;
+ __le16 status_code;
+ /* possibly followed by Challenge text */
+ u8 variable[0];
+ } __packed auth;
+ struct {
+ __le16 reason_code;
+ } __packed deauth;
+ struct {
+ __le16 capab_info;
+ __le16 listen_interval;
+ /* followed by SSID and Supported rates */
+ u8 variable[0];
+ } __packed assoc_req;
+ struct {
+ __le16 capab_info;
+ __le16 status_code;
+ __le16 aid;
+ /* followed by Supported rates */
+ u8 variable[0];
+ } __packed assoc_resp, reassoc_resp;
+ struct {
+ __le16 capab_info;
+ __le16 listen_interval;
+ u8 current_ap[ETH_ALEN];
+ /* followed by SSID and Supported rates */
+ u8 variable[0];
+ } __packed reassoc_req;
+ struct {
+ __le16 reason_code;
+ } __packed disassoc;
+ struct {
+ __le64 timestamp;
+ __le16 beacon_int;
+ __le16 capab_info;
+ /* followed by some of SSID, Supported rates,
+ * FH Params, DS Params, CF Params, IBSS Params, TIM */
+ u8 variable[0];
+ } __packed beacon;
+ struct {
+ /* only variable items: SSID, Supported rates */
+ u8 variable[0];
+ } __packed probe_req;
+ struct {
+ __le64 timestamp;
+ __le16 beacon_int;
+ __le16 capab_info;
+ /* followed by some of SSID, Supported rates,
+ * FH Params, DS Params, CF Params, IBSS Params */
+ u8 variable[0];
+ } __packed probe_resp;
+ struct {
+ u8 category;
+ union {
+ struct {
+ u8 action_code;
+ u8 dialog_token;
+ u8 status_code;
+ u8 variable[0];
+ } __packed wme_action;
+ struct{
+ u8 action_code;
+ u8 variable[0];
+ } __packed chan_switch;
+ struct{
+ u8 action_code;
+ struct ieee80211_ext_chansw_ie data;
+ u8 variable[0];
+ } __packed ext_chan_switch;
+ struct{
+ u8 action_code;
+ u8 dialog_token;
+ u8 element_id;
+ u8 length;
+ struct ieee80211_msrment_ie msr_elem;
+ } __packed measurement;
+ struct{
+ u8 action_code;
+ u8 dialog_token;
+ __le16 capab;
+ __le16 timeout;
+ __le16 start_seq_num;
+ } __packed addba_req;
+ struct{
+ u8 action_code;
+ u8 dialog_token;
+ __le16 status;
+ __le16 capab;
+ __le16 timeout;
+ } __packed addba_resp;
+ struct{
+ u8 action_code;
+ __le16 params;
+ __le16 reason_code;
+ } __packed delba;
+ struct {
+ u8 action_code;
+ u8 variable[0];
+ } __packed self_prot;
+ struct{
+ u8 action_code;
+ u8 variable[0];
+ } __packed mesh_action;
+ struct {
+ u8 action;
+ u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN];
+ } __packed sa_query;
+ struct {
+ u8 action;
+ u8 smps_control;
+ } __packed ht_smps;
+ struct {
+ u8 action_code;
+ u8 chanwidth;
+ } __packed ht_notify_cw;
+ struct {
+ u8 action_code;
+ u8 dialog_token;
+ __le16 capability;
+ u8 variable[0];
+ } __packed tdls_discover_resp;
+ struct {
+ u8 action_code;
+ u8 operating_mode;
+ } __packed vht_opmode_notif;
+ struct {
+ u8 action_code;
+ u8 membership[WLAN_MEMBERSHIP_LEN];
+ u8 position[WLAN_USER_POSITION_LEN];
+ } __packed vht_group_notif;
+ struct {
+ u8 action_code;
+ u8 dialog_token;
+ u8 tpc_elem_id;
+ u8 tpc_elem_length;
+ struct ieee80211_tpc_report_ie tpc;
+ } __packed tpc_report;
+ struct {
+ u8 action_code;
+ u8 dialog_token;
+ u8 follow_up;
+ u8 tod[6];
+ u8 toa[6];
+ __le16 tod_error;
+ __le16 toa_error;
+ u8 variable[0];
+ } __packed ftm;
+ } u;
+ } __packed action;
+ } u;
+} __packed __aligned(2);
+
+/* Supported rates membership selectors */
+#define BSS_MEMBERSHIP_SELECTOR_HT_PHY 127
+#define BSS_MEMBERSHIP_SELECTOR_VHT_PHY 126
+
+/* mgmt header + 1 byte category code */
+#define IEEE80211_MIN_ACTION_SIZE offsetof(struct ieee80211_mgmt, u.action.u)
+
+
+/* Management MIC information element (IEEE 802.11w) */
+struct ieee80211_mmie {
+ u8 element_id;
+ u8 length;
+ __le16 key_id;
+ u8 sequence_number[6];
+ u8 mic[8];
+} __packed;
+
+/* Management MIC information element (IEEE 802.11w) for GMAC and CMAC-256 */
+struct ieee80211_mmie_16 {
+ u8 element_id;
+ u8 length;
+ __le16 key_id;
+ u8 sequence_number[6];
+ u8 mic[16];
+} __packed;
+
+struct ieee80211_vendor_ie {
+ u8 element_id;
+ u8 len;
+ u8 oui[3];
+ u8 oui_type;
+} __packed;
+
+struct ieee80211_wmm_ac_param {
+ u8 aci_aifsn; /* AIFSN, ACM, ACI */
+ u8 cw; /* ECWmin, ECWmax (CW = 2^ECW - 1) */
+ __le16 txop_limit;
+} __packed;
+
+struct ieee80211_wmm_param_ie {
+ u8 element_id; /* Element ID: 221 (0xdd); */
+ u8 len; /* 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 specific QoS info */
+ u8 reserved; /* 0 */
+ /* AC_BE, AC_BK, AC_VI, AC_VO */
+ struct ieee80211_wmm_ac_param ac[4];
+} __packed;
+
+/* Control frames */
+struct ieee80211_rts {
+ __le16 frame_control;
+ __le16 duration;
+ u8 ra[ETH_ALEN];
+ u8 ta[ETH_ALEN];
+} __packed __aligned(2);
+
+struct ieee80211_cts {
+ __le16 frame_control;
+ __le16 duration;
+ u8 ra[ETH_ALEN];
+} __packed __aligned(2);
+
+struct ieee80211_pspoll {
+ __le16 frame_control;
+ __le16 aid;
+ u8 bssid[ETH_ALEN];
+ u8 ta[ETH_ALEN];
+} __packed __aligned(2);
+
+/* TDLS */
+
+/* Channel switch timing */
+struct ieee80211_ch_switch_timing {
+ __le16 switch_time;
+ __le16 switch_timeout;
+} __packed;
+
+/* Link-id information element */
+struct ieee80211_tdls_lnkie {
+ u8 ie_type; /* Link Identifier IE */
+ u8 ie_len;
+ u8 bssid[ETH_ALEN];
+ u8 init_sta[ETH_ALEN];
+ u8 resp_sta[ETH_ALEN];
+} __packed;
+
+struct ieee80211_tdls_data {
+ u8 da[ETH_ALEN];
+ u8 sa[ETH_ALEN];
+ __be16 ether_type;
+ u8 payload_type;
+ u8 category;
+ u8 action_code;
+ union {
+ struct {
+ u8 dialog_token;
+ __le16 capability;
+ u8 variable[0];
+ } __packed setup_req;
+ struct {
+ __le16 status_code;
+ u8 dialog_token;
+ __le16 capability;
+ u8 variable[0];
+ } __packed setup_resp;
+ struct {
+ __le16 status_code;
+ u8 dialog_token;
+ u8 variable[0];
+ } __packed setup_cfm;
+ struct {
+ __le16 reason_code;
+ u8 variable[0];
+ } __packed teardown;
+ struct {
+ u8 dialog_token;
+ u8 variable[0];
+ } __packed discover_req;
+ struct {
+ u8 target_channel;
+ u8 oper_class;
+ u8 variable[0];
+ } __packed chan_switch_req;
+ struct {
+ __le16 status_code;
+ u8 variable[0];
+ } __packed chan_switch_resp;
+ } u;
+} __packed;
+
+/*
+ * Peer-to-Peer IE attribute related definitions.
+ */
+/**
+ * enum ieee80211_p2p_attr_id - identifies type of peer-to-peer attribute.
+ */
+enum ieee80211_p2p_attr_id {
+ IEEE80211_P2P_ATTR_STATUS = 0,
+ IEEE80211_P2P_ATTR_MINOR_REASON,
+ IEEE80211_P2P_ATTR_CAPABILITY,
+ IEEE80211_P2P_ATTR_DEVICE_ID,
+ IEEE80211_P2P_ATTR_GO_INTENT,
+ IEEE80211_P2P_ATTR_GO_CONFIG_TIMEOUT,
+ IEEE80211_P2P_ATTR_LISTEN_CHANNEL,
+ IEEE80211_P2P_ATTR_GROUP_BSSID,
+ IEEE80211_P2P_ATTR_EXT_LISTEN_TIMING,
+ IEEE80211_P2P_ATTR_INTENDED_IFACE_ADDR,
+ IEEE80211_P2P_ATTR_MANAGABILITY,
+ IEEE80211_P2P_ATTR_CHANNEL_LIST,
+ IEEE80211_P2P_ATTR_ABSENCE_NOTICE,
+ IEEE80211_P2P_ATTR_DEVICE_INFO,
+ IEEE80211_P2P_ATTR_GROUP_INFO,
+ IEEE80211_P2P_ATTR_GROUP_ID,
+ IEEE80211_P2P_ATTR_INTERFACE,
+ IEEE80211_P2P_ATTR_OPER_CHANNEL,
+ IEEE80211_P2P_ATTR_INVITE_FLAGS,
+ /* 19 - 220: Reserved */
+ IEEE80211_P2P_ATTR_VENDOR_SPECIFIC = 221,
+
+ IEEE80211_P2P_ATTR_MAX
+};
+
+/* Notice of Absence attribute - described in P2P spec 4.1.14 */
+/* Typical max value used here */
+#define IEEE80211_P2P_NOA_DESC_MAX 4
+
+struct ieee80211_p2p_noa_desc {
+ u8 count;
+ __le32 duration;
+ __le32 interval;
+ __le32 start_time;
+} __packed;
+
+struct ieee80211_p2p_noa_attr {
+ u8 index;
+ u8 oppps_ctwindow;
+ struct ieee80211_p2p_noa_desc desc[IEEE80211_P2P_NOA_DESC_MAX];
+} __packed;
+
+#define IEEE80211_P2P_OPPPS_ENABLE_BIT BIT(7)
+#define IEEE80211_P2P_OPPPS_CTWINDOW_MASK 0x7F
+
+/**
+ * struct ieee80211_bar - HT Block Ack Request
+ *
+ * This structure refers to "HT BlockAckReq" as
+ * described in 802.11n draft section 7.2.1.7.1
+ */
+struct ieee80211_bar {
+ __le16 frame_control;
+ __le16 duration;
+ __u8 ra[ETH_ALEN];
+ __u8 ta[ETH_ALEN];
+ __le16 control;
+ __le16 start_seq_num;
+} __packed;
+
+/* 802.11 BAR control masks */
+#define IEEE80211_BAR_CTRL_ACK_POLICY_NORMAL 0x0000
+#define IEEE80211_BAR_CTRL_MULTI_TID 0x0002
+#define IEEE80211_BAR_CTRL_CBMTID_COMPRESSED_BA 0x0004
+#define IEEE80211_BAR_CTRL_TID_INFO_MASK 0xf000
+#define IEEE80211_BAR_CTRL_TID_INFO_SHIFT 12
+
+#define IEEE80211_HT_MCS_MASK_LEN 10
+
+/**
+ * struct ieee80211_mcs_info - MCS information
+ * @rx_mask: RX mask
+ * @rx_highest: highest supported RX rate. If set represents
+ * the highest supported RX data rate in units of 1 Mbps.
+ * If this field is 0 this value should not be used to
+ * consider the highest RX data rate supported.
+ * @tx_params: TX parameters
+ */
+struct ieee80211_mcs_info {
+ u8 rx_mask[IEEE80211_HT_MCS_MASK_LEN];
+ __le16 rx_highest;
+ u8 tx_params;
+ u8 reserved[3];
+} __packed;
+
+/* 802.11n HT capability MSC set */
+#define IEEE80211_HT_MCS_RX_HIGHEST_MASK 0x3ff
+#define IEEE80211_HT_MCS_TX_DEFINED 0x01
+#define IEEE80211_HT_MCS_TX_RX_DIFF 0x02
+/* value 0 == 1 stream etc */
+#define IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK 0x0C
+#define IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT 2
+#define IEEE80211_HT_MCS_TX_MAX_STREAMS 4
+#define IEEE80211_HT_MCS_TX_UNEQUAL_MODULATION 0x10
+
+/*
+ * 802.11n D5.0 20.3.5 / 20.6 says:
+ * - indices 0 to 7 and 32 are single spatial stream
+ * - 8 to 31 are multiple spatial streams using equal modulation
+ * [8..15 for two streams, 16..23 for three and 24..31 for four]
+ * - remainder are multiple spatial streams using unequal modulation
+ */
+#define IEEE80211_HT_MCS_UNEQUAL_MODULATION_START 33
+#define IEEE80211_HT_MCS_UNEQUAL_MODULATION_START_BYTE \
+ (IEEE80211_HT_MCS_UNEQUAL_MODULATION_START / 8)
+
+/**
+ * struct ieee80211_ht_cap - HT capabilities
+ *
+ * This structure is the "HT capabilities element" as
+ * described in 802.11n D5.0 7.3.2.57
+ */
+struct ieee80211_ht_cap {
+ __le16 cap_info;
+ u8 ampdu_params_info;
+
+ /* 16 bytes MCS information */
+ struct ieee80211_mcs_info mcs;
+
+ __le16 extended_ht_cap_info;
+ __le32 tx_BF_cap_info;
+ u8 antenna_selection_info;
+} __packed;
+
+/* 802.11n HT capabilities masks (for cap_info) */
+#define IEEE80211_HT_CAP_LDPC_CODING 0x0001
+#define IEEE80211_HT_CAP_SUP_WIDTH_20_40 0x0002
+#define IEEE80211_HT_CAP_SM_PS 0x000C
+#define IEEE80211_HT_CAP_SM_PS_SHIFT 2
+#define IEEE80211_HT_CAP_GRN_FLD 0x0010
+#define IEEE80211_HT_CAP_SGI_20 0x0020
+#define IEEE80211_HT_CAP_SGI_40 0x0040
+#define IEEE80211_HT_CAP_TX_STBC 0x0080
+#define IEEE80211_HT_CAP_RX_STBC 0x0300
+#define IEEE80211_HT_CAP_RX_STBC_SHIFT 8
+#define IEEE80211_HT_CAP_DELAY_BA 0x0400
+#define IEEE80211_HT_CAP_MAX_AMSDU 0x0800
+#define IEEE80211_HT_CAP_DSSSCCK40 0x1000
+#define IEEE80211_HT_CAP_RESERVED 0x2000
+#define IEEE80211_HT_CAP_40MHZ_INTOLERANT 0x4000
+#define IEEE80211_HT_CAP_LSIG_TXOP_PROT 0x8000
+
+/* 802.11n HT extended capabilities masks (for extended_ht_cap_info) */
+#define IEEE80211_HT_EXT_CAP_PCO 0x0001
+#define IEEE80211_HT_EXT_CAP_PCO_TIME 0x0006
+#define IEEE80211_HT_EXT_CAP_PCO_TIME_SHIFT 1
+#define IEEE80211_HT_EXT_CAP_MCS_FB 0x0300
+#define IEEE80211_HT_EXT_CAP_MCS_FB_SHIFT 8
+#define IEEE80211_HT_EXT_CAP_HTC_SUP 0x0400
+#define IEEE80211_HT_EXT_CAP_RD_RESPONDER 0x0800
+
+/* 802.11n HT capability AMPDU settings (for ampdu_params_info) */
+#define IEEE80211_HT_AMPDU_PARM_FACTOR 0x03
+#define IEEE80211_HT_AMPDU_PARM_DENSITY 0x1C
+#define IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT 2
+
+/*
+ * Maximum length of AMPDU that the STA can receive in high-throughput (HT).
+ * Length = 2 ^ (13 + max_ampdu_length_exp) - 1 (octets)
+ */
+enum ieee80211_max_ampdu_length_exp {
+ IEEE80211_HT_MAX_AMPDU_8K = 0,
+ IEEE80211_HT_MAX_AMPDU_16K = 1,
+ IEEE80211_HT_MAX_AMPDU_32K = 2,
+ IEEE80211_HT_MAX_AMPDU_64K = 3
+};
+
+/*
+ * Maximum length of AMPDU that the STA can receive in VHT.
+ * Length = 2 ^ (13 + max_ampdu_length_exp) - 1 (octets)
+ */
+enum ieee80211_vht_max_ampdu_length_exp {
+ IEEE80211_VHT_MAX_AMPDU_8K = 0,
+ IEEE80211_VHT_MAX_AMPDU_16K = 1,
+ IEEE80211_VHT_MAX_AMPDU_32K = 2,
+ IEEE80211_VHT_MAX_AMPDU_64K = 3,
+ IEEE80211_VHT_MAX_AMPDU_128K = 4,
+ IEEE80211_VHT_MAX_AMPDU_256K = 5,
+ IEEE80211_VHT_MAX_AMPDU_512K = 6,
+ IEEE80211_VHT_MAX_AMPDU_1024K = 7
+};
+
+#define IEEE80211_HT_MAX_AMPDU_FACTOR 13
+
+/* Minimum MPDU start spacing */
+enum ieee80211_min_mpdu_spacing {
+ IEEE80211_HT_MPDU_DENSITY_NONE = 0, /* No restriction */
+ IEEE80211_HT_MPDU_DENSITY_0_25 = 1, /* 1/4 usec */
+ IEEE80211_HT_MPDU_DENSITY_0_5 = 2, /* 1/2 usec */
+ IEEE80211_HT_MPDU_DENSITY_1 = 3, /* 1 usec */
+ IEEE80211_HT_MPDU_DENSITY_2 = 4, /* 2 usec */
+ IEEE80211_HT_MPDU_DENSITY_4 = 5, /* 4 usec */
+ IEEE80211_HT_MPDU_DENSITY_8 = 6, /* 8 usec */
+ IEEE80211_HT_MPDU_DENSITY_16 = 7 /* 16 usec */
+};
+
+/**
+ * struct ieee80211_ht_operation - HT operation IE
+ *
+ * This structure is the "HT operation element" as
+ * described in 802.11n-2009 7.3.2.57
+ */
+struct ieee80211_ht_operation {
+ u8 primary_chan;
+ u8 ht_param;
+ __le16 operation_mode;
+ __le16 stbc_param;
+ u8 basic_set[16];
+} __packed;
+
+/* for ht_param */
+#define IEEE80211_HT_PARAM_CHA_SEC_OFFSET 0x03
+#define IEEE80211_HT_PARAM_CHA_SEC_NONE 0x00
+#define IEEE80211_HT_PARAM_CHA_SEC_ABOVE 0x01
+#define IEEE80211_HT_PARAM_CHA_SEC_BELOW 0x03
+#define IEEE80211_HT_PARAM_CHAN_WIDTH_ANY 0x04
+#define IEEE80211_HT_PARAM_RIFS_MODE 0x08
+
+/* for operation_mode */
+#define IEEE80211_HT_OP_MODE_PROTECTION 0x0003
+#define IEEE80211_HT_OP_MODE_PROTECTION_NONE 0
+#define IEEE80211_HT_OP_MODE_PROTECTION_NONMEMBER 1
+#define IEEE80211_HT_OP_MODE_PROTECTION_20MHZ 2
+#define IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED 3
+#define IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT 0x0004
+#define IEEE80211_HT_OP_MODE_NON_HT_STA_PRSNT 0x0010
+#define IEEE80211_HT_OP_MODE_CCFS2_SHIFT 5
+#define IEEE80211_HT_OP_MODE_CCFS2_MASK 0x1fe0
+
+/* for stbc_param */
+#define IEEE80211_HT_STBC_PARAM_DUAL_BEACON 0x0040
+#define IEEE80211_HT_STBC_PARAM_DUAL_CTS_PROT 0x0080
+#define IEEE80211_HT_STBC_PARAM_STBC_BEACON 0x0100
+#define IEEE80211_HT_STBC_PARAM_LSIG_TXOP_FULLPROT 0x0200
+#define IEEE80211_HT_STBC_PARAM_PCO_ACTIVE 0x0400
+#define IEEE80211_HT_STBC_PARAM_PCO_PHASE 0x0800
+
+
+/* block-ack parameters */
+#define IEEE80211_ADDBA_PARAM_AMSDU_MASK 0x0001
+#define IEEE80211_ADDBA_PARAM_POLICY_MASK 0x0002
+#define IEEE80211_ADDBA_PARAM_TID_MASK 0x003C
+#define IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK 0xFFC0
+#define IEEE80211_DELBA_PARAM_TID_MASK 0xF000
+#define IEEE80211_DELBA_PARAM_INITIATOR_MASK 0x0800
+
+/*
+ * A-PMDU buffer sizes
+ * According to IEEE802.11n spec size varies from 8K to 64K (in powers of 2)
+ */
+#define IEEE80211_MIN_AMPDU_BUF 0x8
+#define IEEE80211_MAX_AMPDU_BUF 0x40
+
+
+/* Spatial Multiplexing Power Save Modes (for capability) */
+#define WLAN_HT_CAP_SM_PS_STATIC 0
+#define WLAN_HT_CAP_SM_PS_DYNAMIC 1
+#define WLAN_HT_CAP_SM_PS_INVALID 2
+#define WLAN_HT_CAP_SM_PS_DISABLED 3
+
+/* for SM power control field lower two bits */
+#define WLAN_HT_SMPS_CONTROL_DISABLED 0
+#define WLAN_HT_SMPS_CONTROL_STATIC 1
+#define WLAN_HT_SMPS_CONTROL_DYNAMIC 3
+
+/**
+ * struct ieee80211_vht_mcs_info - VHT MCS information
+ * @rx_mcs_map: RX MCS map 2 bits for each stream, total 8 streams
+ * @rx_highest: Indicates highest long GI VHT PPDU data rate
+ * STA can receive. Rate expressed in units of 1 Mbps.
+ * If this field is 0 this value should not be used to
+ * consider the highest RX data rate supported.
+ * The top 3 bits of this field are reserved.
+ * @tx_mcs_map: TX MCS map 2 bits for each stream, total 8 streams
+ * @tx_highest: Indicates highest long GI VHT PPDU data rate
+ * STA can transmit. Rate expressed in units of 1 Mbps.
+ * If this field is 0 this value should not be used to
+ * consider the highest TX data rate supported.
+ * The top 3 bits of this field are reserved.
+ */
+struct ieee80211_vht_mcs_info {
+ __le16 rx_mcs_map;
+ __le16 rx_highest;
+ __le16 tx_mcs_map;
+ __le16 tx_highest;
+} __packed;
+
+/**
+ * enum ieee80211_vht_mcs_support - VHT MCS support definitions
+ * @IEEE80211_VHT_MCS_SUPPORT_0_7: MCSes 0-7 are supported for the
+ * number of streams
+ * @IEEE80211_VHT_MCS_SUPPORT_0_8: MCSes 0-8 are supported
+ * @IEEE80211_VHT_MCS_SUPPORT_0_9: MCSes 0-9 are supported
+ * @IEEE80211_VHT_MCS_NOT_SUPPORTED: This number of streams isn't supported
+ *
+ * These definitions are used in each 2-bit subfield of the @rx_mcs_map
+ * and @tx_mcs_map fields of &struct ieee80211_vht_mcs_info, which are
+ * both split into 8 subfields by number of streams. These values indicate
+ * which MCSes are supported for the number of streams the value appears
+ * for.
+ */
+enum ieee80211_vht_mcs_support {
+ IEEE80211_VHT_MCS_SUPPORT_0_7 = 0,
+ IEEE80211_VHT_MCS_SUPPORT_0_8 = 1,
+ IEEE80211_VHT_MCS_SUPPORT_0_9 = 2,
+ IEEE80211_VHT_MCS_NOT_SUPPORTED = 3,
+};
+
+/**
+ * struct ieee80211_vht_cap - VHT capabilities
+ *
+ * This structure is the "VHT capabilities element" as
+ * described in 802.11ac D3.0 8.4.2.160
+ * @vht_cap_info: VHT capability info
+ * @supp_mcs: VHT MCS supported rates
+ */
+struct ieee80211_vht_cap {
+ __le32 vht_cap_info;
+ struct ieee80211_vht_mcs_info supp_mcs;
+} __packed;
+
+/**
+ * enum ieee80211_vht_chanwidth - VHT channel width
+ * @IEEE80211_VHT_CHANWIDTH_USE_HT: use the HT operation IE to
+ * determine the channel width (20 or 40 MHz)
+ * @IEEE80211_VHT_CHANWIDTH_80MHZ: 80 MHz bandwidth
+ * @IEEE80211_VHT_CHANWIDTH_160MHZ: 160 MHz bandwidth
+ * @IEEE80211_VHT_CHANWIDTH_80P80MHZ: 80+80 MHz bandwidth
+ */
+enum ieee80211_vht_chanwidth {
+ IEEE80211_VHT_CHANWIDTH_USE_HT = 0,
+ IEEE80211_VHT_CHANWIDTH_80MHZ = 1,
+ IEEE80211_VHT_CHANWIDTH_160MHZ = 2,
+ IEEE80211_VHT_CHANWIDTH_80P80MHZ = 3,
+};
+
+/**
+ * struct ieee80211_vht_operation - VHT operation IE
+ *
+ * This structure is the "VHT operation element" as
+ * described in 802.11ac D3.0 8.4.2.161
+ * @chan_width: Operating channel width
+ * @center_freq_seg0_idx: center freq segment 0 index
+ * @center_freq_seg1_idx: center freq segment 1 index
+ * @basic_mcs_set: VHT Basic MCS rate set
+ */
+struct ieee80211_vht_operation {
+ u8 chan_width;
+ u8 center_freq_seg0_idx;
+ u8 center_freq_seg1_idx;
+ __le16 basic_mcs_set;
+} __packed;
+
+
+/* 802.11ac VHT Capabilities */
+#define IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895 0x00000000
+#define IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991 0x00000001
+#define IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 0x00000002
+#define IEEE80211_VHT_CAP_MAX_MPDU_MASK 0x00000003
+#define IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ 0x00000004
+#define IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ 0x00000008
+#define IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK 0x0000000C
+#define IEEE80211_VHT_CAP_RXLDPC 0x00000010
+#define IEEE80211_VHT_CAP_SHORT_GI_80 0x00000020
+#define IEEE80211_VHT_CAP_SHORT_GI_160 0x00000040
+#define IEEE80211_VHT_CAP_TXSTBC 0x00000080
+#define IEEE80211_VHT_CAP_RXSTBC_1 0x00000100
+#define IEEE80211_VHT_CAP_RXSTBC_2 0x00000200
+#define IEEE80211_VHT_CAP_RXSTBC_3 0x00000300
+#define IEEE80211_VHT_CAP_RXSTBC_4 0x00000400
+#define IEEE80211_VHT_CAP_RXSTBC_MASK 0x00000700
+#define IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE 0x00000800
+#define IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE 0x00001000
+#define IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT 13
+#define IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK \
+ (7 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT)
+#define IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT 16
+#define IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK \
+ (7 << IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT)
+#define IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE 0x00080000
+#define IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE 0x00100000
+#define IEEE80211_VHT_CAP_VHT_TXOP_PS 0x00200000
+#define IEEE80211_VHT_CAP_HTC_VHT 0x00400000
+#define IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT 23
+#define IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK \
+ (7 << IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT)
+#define IEEE80211_VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB 0x08000000
+#define IEEE80211_VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB 0x0c000000
+#define IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN 0x10000000
+#define IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN 0x20000000
+
+/* Authentication algorithms */
+#define WLAN_AUTH_OPEN 0
+#define WLAN_AUTH_SHARED_KEY 1
+#define WLAN_AUTH_FT 2
+#define WLAN_AUTH_SAE 3
+#define WLAN_AUTH_FILS_SK 4
+#define WLAN_AUTH_FILS_SK_PFS 5
+#define WLAN_AUTH_FILS_PK 6
+#define WLAN_AUTH_LEAP 128
+
+#define WLAN_AUTH_CHALLENGE_LEN 128
+
+#define WLAN_CAPABILITY_ESS (1<<0)
+#define WLAN_CAPABILITY_IBSS (1<<1)
+
+/*
+ * A mesh STA sets the ESS and IBSS capability bits to zero.
+ * however, this holds true for p2p probe responses (in the p2p_find
+ * phase) as well.
+ */
+#define WLAN_CAPABILITY_IS_STA_BSS(cap) \
+ (!((cap) & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)))
+
+#define WLAN_CAPABILITY_CF_POLLABLE (1<<2)
+#define WLAN_CAPABILITY_CF_POLL_REQUEST (1<<3)
+#define WLAN_CAPABILITY_PRIVACY (1<<4)
+#define WLAN_CAPABILITY_SHORT_PREAMBLE (1<<5)
+#define WLAN_CAPABILITY_PBCC (1<<6)
+#define WLAN_CAPABILITY_CHANNEL_AGILITY (1<<7)
+
+/* 802.11h */
+#define WLAN_CAPABILITY_SPECTRUM_MGMT (1<<8)
+#define WLAN_CAPABILITY_QOS (1<<9)
+#define WLAN_CAPABILITY_SHORT_SLOT_TIME (1<<10)
+#define WLAN_CAPABILITY_APSD (1<<11)
+#define WLAN_CAPABILITY_RADIO_MEASURE (1<<12)
+#define WLAN_CAPABILITY_DSSS_OFDM (1<<13)
+#define WLAN_CAPABILITY_DEL_BACK (1<<14)
+#define WLAN_CAPABILITY_IMM_BACK (1<<15)
+
+/* DMG (60gHz) 802.11ad */
+/* type - bits 0..1 */
+#define WLAN_CAPABILITY_DMG_TYPE_MASK (3<<0)
+#define WLAN_CAPABILITY_DMG_TYPE_IBSS (1<<0) /* Tx by: STA */
+#define WLAN_CAPABILITY_DMG_TYPE_PBSS (2<<0) /* Tx by: PCP */
+#define WLAN_CAPABILITY_DMG_TYPE_AP (3<<0) /* Tx by: AP */
+
+#define WLAN_CAPABILITY_DMG_CBAP_ONLY (1<<2)
+#define WLAN_CAPABILITY_DMG_CBAP_SOURCE (1<<3)
+#define WLAN_CAPABILITY_DMG_PRIVACY (1<<4)
+#define WLAN_CAPABILITY_DMG_ECPAC (1<<5)
+
+#define WLAN_CAPABILITY_DMG_SPECTRUM_MGMT (1<<8)
+#define WLAN_CAPABILITY_DMG_RADIO_MEASURE (1<<12)
+
+/* measurement */
+#define IEEE80211_SPCT_MSR_RPRT_MODE_LATE (1<<0)
+#define IEEE80211_SPCT_MSR_RPRT_MODE_INCAPABLE (1<<1)
+#define IEEE80211_SPCT_MSR_RPRT_MODE_REFUSED (1<<2)
+
+#define IEEE80211_SPCT_MSR_RPRT_TYPE_BASIC 0
+#define IEEE80211_SPCT_MSR_RPRT_TYPE_CCA 1
+#define IEEE80211_SPCT_MSR_RPRT_TYPE_RPI 2
+
+/* 802.11g ERP information element */
+#define WLAN_ERP_NON_ERP_PRESENT (1<<0)
+#define WLAN_ERP_USE_PROTECTION (1<<1)
+#define WLAN_ERP_BARKER_PREAMBLE (1<<2)
+
+/* WLAN_ERP_BARKER_PREAMBLE values */
+enum {
+ WLAN_ERP_PREAMBLE_SHORT = 0,
+ WLAN_ERP_PREAMBLE_LONG = 1,
+};
+
+/* Band ID, 802.11ad #8.4.1.45 */
+enum {
+ IEEE80211_BANDID_TV_WS = 0, /* TV white spaces */
+ IEEE80211_BANDID_SUB1 = 1, /* Sub-1 GHz (excluding TV white spaces) */
+ IEEE80211_BANDID_2G = 2, /* 2.4 GHz */
+ IEEE80211_BANDID_3G = 3, /* 3.6 GHz */
+ IEEE80211_BANDID_5G = 4, /* 4.9 and 5 GHz */
+ IEEE80211_BANDID_60G = 5, /* 60 GHz */
+};
+
+/* Status codes */
+enum ieee80211_statuscode {
+ WLAN_STATUS_SUCCESS = 0,
+ WLAN_STATUS_UNSPECIFIED_FAILURE = 1,
+ WLAN_STATUS_CAPS_UNSUPPORTED = 10,
+ WLAN_STATUS_REASSOC_NO_ASSOC = 11,
+ WLAN_STATUS_ASSOC_DENIED_UNSPEC = 12,
+ WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG = 13,
+ WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION = 14,
+ WLAN_STATUS_CHALLENGE_FAIL = 15,
+ WLAN_STATUS_AUTH_TIMEOUT = 16,
+ WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA = 17,
+ WLAN_STATUS_ASSOC_DENIED_RATES = 18,
+ /* 802.11b */
+ WLAN_STATUS_ASSOC_DENIED_NOSHORTPREAMBLE = 19,
+ WLAN_STATUS_ASSOC_DENIED_NOPBCC = 20,
+ WLAN_STATUS_ASSOC_DENIED_NOAGILITY = 21,
+ /* 802.11h */
+ WLAN_STATUS_ASSOC_DENIED_NOSPECTRUM = 22,
+ WLAN_STATUS_ASSOC_REJECTED_BAD_POWER = 23,
+ WLAN_STATUS_ASSOC_REJECTED_BAD_SUPP_CHAN = 24,
+ /* 802.11g */
+ WLAN_STATUS_ASSOC_DENIED_NOSHORTTIME = 25,
+ WLAN_STATUS_ASSOC_DENIED_NODSSSOFDM = 26,
+ /* 802.11w */
+ WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY = 30,
+ WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION = 31,
+ /* 802.11i */
+ WLAN_STATUS_INVALID_IE = 40,
+ WLAN_STATUS_INVALID_GROUP_CIPHER = 41,
+ WLAN_STATUS_INVALID_PAIRWISE_CIPHER = 42,
+ WLAN_STATUS_INVALID_AKMP = 43,
+ WLAN_STATUS_UNSUPP_RSN_VERSION = 44,
+ WLAN_STATUS_INVALID_RSN_IE_CAP = 45,
+ WLAN_STATUS_CIPHER_SUITE_REJECTED = 46,
+ /* 802.11e */
+ WLAN_STATUS_UNSPECIFIED_QOS = 32,
+ WLAN_STATUS_ASSOC_DENIED_NOBANDWIDTH = 33,
+ WLAN_STATUS_ASSOC_DENIED_LOWACK = 34,
+ WLAN_STATUS_ASSOC_DENIED_UNSUPP_QOS = 35,
+ WLAN_STATUS_REQUEST_DECLINED = 37,
+ WLAN_STATUS_INVALID_QOS_PARAM = 38,
+ WLAN_STATUS_CHANGE_TSPEC = 39,
+ WLAN_STATUS_WAIT_TS_DELAY = 47,
+ WLAN_STATUS_NO_DIRECT_LINK = 48,
+ WLAN_STATUS_STA_NOT_PRESENT = 49,
+ WLAN_STATUS_STA_NOT_QSTA = 50,
+ /* 802.11s */
+ WLAN_STATUS_ANTI_CLOG_REQUIRED = 76,
+ WLAN_STATUS_FCG_NOT_SUPP = 78,
+ WLAN_STATUS_STA_NO_TBTT = 78,
+ /* 802.11ad */
+ WLAN_STATUS_REJECTED_WITH_SUGGESTED_CHANGES = 39,
+ WLAN_STATUS_REJECTED_FOR_DELAY_PERIOD = 47,
+ WLAN_STATUS_REJECT_WITH_SCHEDULE = 83,
+ WLAN_STATUS_PENDING_ADMITTING_FST_SESSION = 86,
+ WLAN_STATUS_PERFORMING_FST_NOW = 87,
+ WLAN_STATUS_PENDING_GAP_IN_BA_WINDOW = 88,
+ WLAN_STATUS_REJECT_U_PID_SETTING = 89,
+ WLAN_STATUS_REJECT_DSE_BAND = 96,
+ WLAN_STATUS_DENIED_WITH_SUGGESTED_BAND_AND_CHANNEL = 99,
+ WLAN_STATUS_DENIED_DUE_TO_SPECTRUM_MANAGEMENT = 103,
+ /* 802.11ai */
+ WLAN_STATUS_FILS_AUTHENTICATION_FAILURE = 108,
+ WLAN_STATUS_UNKNOWN_AUTHENTICATION_SERVER = 109,
+};
+
+
+/* Reason codes */
+enum ieee80211_reasoncode {
+ WLAN_REASON_UNSPECIFIED = 1,
+ WLAN_REASON_PREV_AUTH_NOT_VALID = 2,
+ WLAN_REASON_DEAUTH_LEAVING = 3,
+ WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY = 4,
+ WLAN_REASON_DISASSOC_AP_BUSY = 5,
+ WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA = 6,
+ WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA = 7,
+ WLAN_REASON_DISASSOC_STA_HAS_LEFT = 8,
+ WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH = 9,
+ /* 802.11h */
+ WLAN_REASON_DISASSOC_BAD_POWER = 10,
+ WLAN_REASON_DISASSOC_BAD_SUPP_CHAN = 11,
+ /* 802.11i */
+ WLAN_REASON_INVALID_IE = 13,
+ WLAN_REASON_MIC_FAILURE = 14,
+ WLAN_REASON_4WAY_HANDSHAKE_TIMEOUT = 15,
+ WLAN_REASON_GROUP_KEY_HANDSHAKE_TIMEOUT = 16,
+ WLAN_REASON_IE_DIFFERENT = 17,
+ WLAN_REASON_INVALID_GROUP_CIPHER = 18,
+ WLAN_REASON_INVALID_PAIRWISE_CIPHER = 19,
+ WLAN_REASON_INVALID_AKMP = 20,
+ WLAN_REASON_UNSUPP_RSN_VERSION = 21,
+ WLAN_REASON_INVALID_RSN_IE_CAP = 22,
+ WLAN_REASON_IEEE8021X_FAILED = 23,
+ WLAN_REASON_CIPHER_SUITE_REJECTED = 24,
+ /* TDLS (802.11z) */
+ WLAN_REASON_TDLS_TEARDOWN_UNREACHABLE = 25,
+ WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED = 26,
+ /* 802.11e */
+ WLAN_REASON_DISASSOC_UNSPECIFIED_QOS = 32,
+ WLAN_REASON_DISASSOC_QAP_NO_BANDWIDTH = 33,
+ WLAN_REASON_DISASSOC_LOW_ACK = 34,
+ WLAN_REASON_DISASSOC_QAP_EXCEED_TXOP = 35,
+ WLAN_REASON_QSTA_LEAVE_QBSS = 36,
+ WLAN_REASON_QSTA_NOT_USE = 37,
+ WLAN_REASON_QSTA_REQUIRE_SETUP = 38,
+ WLAN_REASON_QSTA_TIMEOUT = 39,
+ WLAN_REASON_QSTA_CIPHER_NOT_SUPP = 45,
+ /* 802.11s */
+ WLAN_REASON_MESH_PEER_CANCELED = 52,
+ WLAN_REASON_MESH_MAX_PEERS = 53,
+ WLAN_REASON_MESH_CONFIG = 54,
+ WLAN_REASON_MESH_CLOSE = 55,
+ WLAN_REASON_MESH_MAX_RETRIES = 56,
+ WLAN_REASON_MESH_CONFIRM_TIMEOUT = 57,
+ WLAN_REASON_MESH_INVALID_GTK = 58,
+ WLAN_REASON_MESH_INCONSISTENT_PARAM = 59,
+ WLAN_REASON_MESH_INVALID_SECURITY = 60,
+ WLAN_REASON_MESH_PATH_ERROR = 61,
+ WLAN_REASON_MESH_PATH_NOFORWARD = 62,
+ WLAN_REASON_MESH_PATH_DEST_UNREACHABLE = 63,
+ WLAN_REASON_MAC_EXISTS_IN_MBSS = 64,
+ WLAN_REASON_MESH_CHAN_REGULATORY = 65,
+ WLAN_REASON_MESH_CHAN = 66,
+};
+
+
+/* Information Element IDs */
+enum ieee80211_eid {
+ WLAN_EID_SSID = 0,
+ WLAN_EID_SUPP_RATES = 1,
+ WLAN_EID_FH_PARAMS = 2, /* reserved now */
+ WLAN_EID_DS_PARAMS = 3,
+ WLAN_EID_CF_PARAMS = 4,
+ WLAN_EID_TIM = 5,
+ WLAN_EID_IBSS_PARAMS = 6,
+ WLAN_EID_COUNTRY = 7,
+ /* 8, 9 reserved */
+ WLAN_EID_REQUEST = 10,
+ WLAN_EID_QBSS_LOAD = 11,
+ WLAN_EID_EDCA_PARAM_SET = 12,
+ WLAN_EID_TSPEC = 13,
+ WLAN_EID_TCLAS = 14,
+ WLAN_EID_SCHEDULE = 15,
+ WLAN_EID_CHALLENGE = 16,
+ /* 17-31 reserved for challenge text extension */
+ WLAN_EID_PWR_CONSTRAINT = 32,
+ WLAN_EID_PWR_CAPABILITY = 33,
+ WLAN_EID_TPC_REQUEST = 34,
+ WLAN_EID_TPC_REPORT = 35,
+ WLAN_EID_SUPPORTED_CHANNELS = 36,
+ WLAN_EID_CHANNEL_SWITCH = 37,
+ WLAN_EID_MEASURE_REQUEST = 38,
+ WLAN_EID_MEASURE_REPORT = 39,
+ WLAN_EID_QUIET = 40,
+ WLAN_EID_IBSS_DFS = 41,
+ WLAN_EID_ERP_INFO = 42,
+ WLAN_EID_TS_DELAY = 43,
+ WLAN_EID_TCLAS_PROCESSING = 44,
+ WLAN_EID_HT_CAPABILITY = 45,
+ WLAN_EID_QOS_CAPA = 46,
+ /* 47 reserved for Broadcom */
+ WLAN_EID_RSN = 48,
+ WLAN_EID_802_15_COEX = 49,
+ WLAN_EID_EXT_SUPP_RATES = 50,
+ WLAN_EID_AP_CHAN_REPORT = 51,
+ WLAN_EID_NEIGHBOR_REPORT = 52,
+ WLAN_EID_RCPI = 53,
+ WLAN_EID_MOBILITY_DOMAIN = 54,
+ WLAN_EID_FAST_BSS_TRANSITION = 55,
+ WLAN_EID_TIMEOUT_INTERVAL = 56,
+ WLAN_EID_RIC_DATA = 57,
+ WLAN_EID_DSE_REGISTERED_LOCATION = 58,
+ WLAN_EID_SUPPORTED_REGULATORY_CLASSES = 59,
+ WLAN_EID_EXT_CHANSWITCH_ANN = 60,
+ WLAN_EID_HT_OPERATION = 61,
+ WLAN_EID_SECONDARY_CHANNEL_OFFSET = 62,
+ WLAN_EID_BSS_AVG_ACCESS_DELAY = 63,
+ WLAN_EID_ANTENNA_INFO = 64,
+ WLAN_EID_RSNI = 65,
+ WLAN_EID_MEASUREMENT_PILOT_TX_INFO = 66,
+ WLAN_EID_BSS_AVAILABLE_CAPACITY = 67,
+ WLAN_EID_BSS_AC_ACCESS_DELAY = 68,
+ WLAN_EID_TIME_ADVERTISEMENT = 69,
+ WLAN_EID_RRM_ENABLED_CAPABILITIES = 70,
+ WLAN_EID_MULTIPLE_BSSID = 71,
+ WLAN_EID_BSS_COEX_2040 = 72,
+ WLAN_EID_BSS_INTOLERANT_CHL_REPORT = 73,
+ WLAN_EID_OVERLAP_BSS_SCAN_PARAM = 74,
+ WLAN_EID_RIC_DESCRIPTOR = 75,
+ WLAN_EID_MMIE = 76,
+ WLAN_EID_ASSOC_COMEBACK_TIME = 77,
+ WLAN_EID_EVENT_REQUEST = 78,
+ WLAN_EID_EVENT_REPORT = 79,
+ WLAN_EID_DIAGNOSTIC_REQUEST = 80,
+ WLAN_EID_DIAGNOSTIC_REPORT = 81,
+ WLAN_EID_LOCATION_PARAMS = 82,
+ WLAN_EID_NON_TX_BSSID_CAP = 83,
+ WLAN_EID_SSID_LIST = 84,
+ WLAN_EID_MULTI_BSSID_IDX = 85,
+ WLAN_EID_FMS_DESCRIPTOR = 86,
+ WLAN_EID_FMS_REQUEST = 87,
+ WLAN_EID_FMS_RESPONSE = 88,
+ WLAN_EID_QOS_TRAFFIC_CAPA = 89,
+ WLAN_EID_BSS_MAX_IDLE_PERIOD = 90,
+ WLAN_EID_TSF_REQUEST = 91,
+ WLAN_EID_TSF_RESPOSNE = 92,
+ WLAN_EID_WNM_SLEEP_MODE = 93,
+ WLAN_EID_TIM_BCAST_REQ = 94,
+ WLAN_EID_TIM_BCAST_RESP = 95,
+ WLAN_EID_COLL_IF_REPORT = 96,
+ WLAN_EID_CHANNEL_USAGE = 97,
+ WLAN_EID_TIME_ZONE = 98,
+ WLAN_EID_DMS_REQUEST = 99,
+ WLAN_EID_DMS_RESPONSE = 100,
+ WLAN_EID_LINK_ID = 101,
+ WLAN_EID_WAKEUP_SCHEDUL = 102,
+ /* 103 reserved */
+ WLAN_EID_CHAN_SWITCH_TIMING = 104,
+ WLAN_EID_PTI_CONTROL = 105,
+ WLAN_EID_PU_BUFFER_STATUS = 106,
+ WLAN_EID_INTERWORKING = 107,
+ WLAN_EID_ADVERTISEMENT_PROTOCOL = 108,
+ WLAN_EID_EXPEDITED_BW_REQ = 109,
+ WLAN_EID_QOS_MAP_SET = 110,
+ WLAN_EID_ROAMING_CONSORTIUM = 111,
+ WLAN_EID_EMERGENCY_ALERT = 112,
+ WLAN_EID_MESH_CONFIG = 113,
+ WLAN_EID_MESH_ID = 114,
+ WLAN_EID_LINK_METRIC_REPORT = 115,
+ WLAN_EID_CONGESTION_NOTIFICATION = 116,
+ WLAN_EID_PEER_MGMT = 117,
+ WLAN_EID_CHAN_SWITCH_PARAM = 118,
+ WLAN_EID_MESH_AWAKE_WINDOW = 119,
+ WLAN_EID_BEACON_TIMING = 120,
+ WLAN_EID_MCCAOP_SETUP_REQ = 121,
+ WLAN_EID_MCCAOP_SETUP_RESP = 122,
+ WLAN_EID_MCCAOP_ADVERT = 123,
+ WLAN_EID_MCCAOP_TEARDOWN = 124,
+ WLAN_EID_GANN = 125,
+ WLAN_EID_RANN = 126,
+ WLAN_EID_EXT_CAPABILITY = 127,
+ /* 128, 129 reserved for Agere */
+ WLAN_EID_PREQ = 130,
+ WLAN_EID_PREP = 131,
+ WLAN_EID_PERR = 132,
+ /* 133-136 reserved for Cisco */
+ WLAN_EID_PXU = 137,
+ WLAN_EID_PXUC = 138,
+ WLAN_EID_AUTH_MESH_PEER_EXCH = 139,
+ WLAN_EID_MIC = 140,
+ WLAN_EID_DESTINATION_URI = 141,
+ WLAN_EID_UAPSD_COEX = 142,
+ WLAN_EID_WAKEUP_SCHEDULE = 143,
+ WLAN_EID_EXT_SCHEDULE = 144,
+ WLAN_EID_STA_AVAILABILITY = 145,
+ WLAN_EID_DMG_TSPEC = 146,
+ WLAN_EID_DMG_AT = 147,
+ WLAN_EID_DMG_CAP = 148,
+ /* 149 reserved for Cisco */
+ WLAN_EID_CISCO_VENDOR_SPECIFIC = 150,
+ WLAN_EID_DMG_OPERATION = 151,
+ WLAN_EID_DMG_BSS_PARAM_CHANGE = 152,
+ WLAN_EID_DMG_BEAM_REFINEMENT = 153,
+ WLAN_EID_CHANNEL_MEASURE_FEEDBACK = 154,
+ /* 155-156 reserved for Cisco */
+ WLAN_EID_AWAKE_WINDOW = 157,
+ WLAN_EID_MULTI_BAND = 158,
+ WLAN_EID_ADDBA_EXT = 159,
+ WLAN_EID_NEXT_PCP_LIST = 160,
+ WLAN_EID_PCP_HANDOVER = 161,
+ WLAN_EID_DMG_LINK_MARGIN = 162,
+ WLAN_EID_SWITCHING_STREAM = 163,
+ WLAN_EID_SESSION_TRANSITION = 164,
+ WLAN_EID_DYN_TONE_PAIRING_REPORT = 165,
+ WLAN_EID_CLUSTER_REPORT = 166,
+ WLAN_EID_RELAY_CAP = 167,
+ WLAN_EID_RELAY_XFER_PARAM_SET = 168,
+ WLAN_EID_BEAM_LINK_MAINT = 169,
+ WLAN_EID_MULTIPLE_MAC_ADDR = 170,
+ WLAN_EID_U_PID = 171,
+ WLAN_EID_DMG_LINK_ADAPT_ACK = 172,
+ /* 173 reserved for Symbol */
+ WLAN_EID_MCCAOP_ADV_OVERVIEW = 174,
+ WLAN_EID_QUIET_PERIOD_REQ = 175,
+ /* 176 reserved for Symbol */
+ WLAN_EID_QUIET_PERIOD_RESP = 177,
+ /* 178-179 reserved for Symbol */
+ /* 180 reserved for ISO/IEC 20011 */
+ WLAN_EID_EPAC_POLICY = 182,
+ WLAN_EID_CLISTER_TIME_OFF = 183,
+ WLAN_EID_INTER_AC_PRIO = 184,
+ WLAN_EID_SCS_DESCRIPTOR = 185,
+ WLAN_EID_QLOAD_REPORT = 186,
+ WLAN_EID_HCCA_TXOP_UPDATE_COUNT = 187,
+ WLAN_EID_HL_STREAM_ID = 188,
+ WLAN_EID_GCR_GROUP_ADDR = 189,
+ WLAN_EID_ANTENNA_SECTOR_ID_PATTERN = 190,
+ WLAN_EID_VHT_CAPABILITY = 191,
+ WLAN_EID_VHT_OPERATION = 192,
+ WLAN_EID_EXTENDED_BSS_LOAD = 193,
+ WLAN_EID_WIDE_BW_CHANNEL_SWITCH = 194,
+ WLAN_EID_VHT_TX_POWER_ENVELOPE = 195,
+ WLAN_EID_CHANNEL_SWITCH_WRAPPER = 196,
+ WLAN_EID_AID = 197,
+ WLAN_EID_QUIET_CHANNEL = 198,
+ WLAN_EID_OPMODE_NOTIF = 199,
+
+ WLAN_EID_VENDOR_SPECIFIC = 221,
+ WLAN_EID_QOS_PARAMETER = 222,
+ WLAN_EID_CAG_NUMBER = 237,
+ WLAN_EID_AP_CSN = 239,
+ WLAN_EID_FILS_INDICATION = 240,
+ WLAN_EID_DILS = 241,
+ WLAN_EID_FRAGMENT = 242,
+ WLAN_EID_EXTENSION = 255
+};
+
+/* Element ID Extensions for Element ID 255 */
+enum ieee80211_eid_ext {
+ WLAN_EID_EXT_ASSOC_DELAY_INFO = 1,
+ WLAN_EID_EXT_FILS_REQ_PARAMS = 2,
+ WLAN_EID_EXT_FILS_KEY_CONFIRM = 3,
+ WLAN_EID_EXT_FILS_SESSION = 4,
+ WLAN_EID_EXT_FILS_HLP_CONTAINER = 5,
+ WLAN_EID_EXT_FILS_IP_ADDR_ASSIGN = 6,
+ WLAN_EID_EXT_KEY_DELIVERY = 7,
+ WLAN_EID_EXT_FILS_WRAPPED_DATA = 8,
+ WLAN_EID_EXT_FILS_PUBLIC_KEY = 12,
+ WLAN_EID_EXT_FILS_NONCE = 13,
+};
+
+/* Action category code */
+enum ieee80211_category {
+ WLAN_CATEGORY_SPECTRUM_MGMT = 0,
+ WLAN_CATEGORY_QOS = 1,
+ WLAN_CATEGORY_DLS = 2,
+ WLAN_CATEGORY_BACK = 3,
+ WLAN_CATEGORY_PUBLIC = 4,
+ WLAN_CATEGORY_RADIO_MEASUREMENT = 5,
+ WLAN_CATEGORY_HT = 7,
+ WLAN_CATEGORY_SA_QUERY = 8,
+ WLAN_CATEGORY_PROTECTED_DUAL_OF_ACTION = 9,
+ WLAN_CATEGORY_WNM = 10,
+ WLAN_CATEGORY_WNM_UNPROTECTED = 11,
+ WLAN_CATEGORY_TDLS = 12,
+ WLAN_CATEGORY_MESH_ACTION = 13,
+ WLAN_CATEGORY_MULTIHOP_ACTION = 14,
+ WLAN_CATEGORY_SELF_PROTECTED = 15,
+ WLAN_CATEGORY_DMG = 16,
+ WLAN_CATEGORY_WMM = 17,
+ WLAN_CATEGORY_FST = 18,
+ WLAN_CATEGORY_UNPROT_DMG = 20,
+ WLAN_CATEGORY_VHT = 21,
+ WLAN_CATEGORY_VENDOR_SPECIFIC_PROTECTED = 126,
+ WLAN_CATEGORY_VENDOR_SPECIFIC = 127,
+};
+
+/* SPECTRUM_MGMT action code */
+enum ieee80211_spectrum_mgmt_actioncode {
+ WLAN_ACTION_SPCT_MSR_REQ = 0,
+ WLAN_ACTION_SPCT_MSR_RPRT = 1,
+ WLAN_ACTION_SPCT_TPC_REQ = 2,
+ WLAN_ACTION_SPCT_TPC_RPRT = 3,
+ WLAN_ACTION_SPCT_CHL_SWITCH = 4,
+};
+
+/* HT action codes */
+enum ieee80211_ht_actioncode {
+ WLAN_HT_ACTION_NOTIFY_CHANWIDTH = 0,
+ WLAN_HT_ACTION_SMPS = 1,
+ WLAN_HT_ACTION_PSMP = 2,
+ WLAN_HT_ACTION_PCO_PHASE = 3,
+ WLAN_HT_ACTION_CSI = 4,
+ WLAN_HT_ACTION_NONCOMPRESSED_BF = 5,
+ WLAN_HT_ACTION_COMPRESSED_BF = 6,
+ WLAN_HT_ACTION_ASEL_IDX_FEEDBACK = 7,
+};
+
+/* VHT action codes */
+enum ieee80211_vht_actioncode {
+ WLAN_VHT_ACTION_COMPRESSED_BF = 0,
+ WLAN_VHT_ACTION_GROUPID_MGMT = 1,
+ WLAN_VHT_ACTION_OPMODE_NOTIF = 2,
+};
+
+/* Self Protected Action codes */
+enum ieee80211_self_protected_actioncode {
+ WLAN_SP_RESERVED = 0,
+ WLAN_SP_MESH_PEERING_OPEN = 1,
+ WLAN_SP_MESH_PEERING_CONFIRM = 2,
+ WLAN_SP_MESH_PEERING_CLOSE = 3,
+ WLAN_SP_MGK_INFORM = 4,
+ WLAN_SP_MGK_ACK = 5,
+};
+
+/* Mesh action codes */
+enum ieee80211_mesh_actioncode {
+ WLAN_MESH_ACTION_LINK_METRIC_REPORT,
+ WLAN_MESH_ACTION_HWMP_PATH_SELECTION,
+ WLAN_MESH_ACTION_GATE_ANNOUNCEMENT,
+ WLAN_MESH_ACTION_CONGESTION_CONTROL_NOTIFICATION,
+ WLAN_MESH_ACTION_MCCA_SETUP_REQUEST,
+ WLAN_MESH_ACTION_MCCA_SETUP_REPLY,
+ WLAN_MESH_ACTION_MCCA_ADVERTISEMENT_REQUEST,
+ WLAN_MESH_ACTION_MCCA_ADVERTISEMENT,
+ WLAN_MESH_ACTION_MCCA_TEARDOWN,
+ WLAN_MESH_ACTION_TBTT_ADJUSTMENT_REQUEST,
+ WLAN_MESH_ACTION_TBTT_ADJUSTMENT_RESPONSE,
+};
+
+/* Security key length */
+enum ieee80211_key_len {
+ WLAN_KEY_LEN_WEP40 = 5,
+ WLAN_KEY_LEN_WEP104 = 13,
+ WLAN_KEY_LEN_CCMP = 16,
+ WLAN_KEY_LEN_CCMP_256 = 32,
+ WLAN_KEY_LEN_TKIP = 32,
+ WLAN_KEY_LEN_AES_CMAC = 16,
+ WLAN_KEY_LEN_SMS4 = 32,
+ WLAN_KEY_LEN_GCMP = 16,
+ WLAN_KEY_LEN_GCMP_256 = 32,
+ WLAN_KEY_LEN_BIP_CMAC_256 = 32,
+ WLAN_KEY_LEN_BIP_GMAC_128 = 16,
+ WLAN_KEY_LEN_BIP_GMAC_256 = 32,
+};
+
+#define IEEE80211_WEP_IV_LEN 4
+#define IEEE80211_WEP_ICV_LEN 4
+#define IEEE80211_CCMP_HDR_LEN 8
+#define IEEE80211_CCMP_MIC_LEN 8
+#define IEEE80211_CCMP_PN_LEN 6
+#define IEEE80211_CCMP_256_HDR_LEN 8
+#define IEEE80211_CCMP_256_MIC_LEN 16
+#define IEEE80211_CCMP_256_PN_LEN 6
+#define IEEE80211_TKIP_IV_LEN 8
+#define IEEE80211_TKIP_ICV_LEN 4
+#define IEEE80211_CMAC_PN_LEN 6
+#define IEEE80211_GMAC_PN_LEN 6
+#define IEEE80211_GCMP_HDR_LEN 8
+#define IEEE80211_GCMP_MIC_LEN 16
+#define IEEE80211_GCMP_PN_LEN 6
+
+#define FILS_NONCE_LEN 16
+#define FILS_MAX_KEK_LEN 64
+
+#define FILS_ERP_MAX_USERNAME_LEN 16
+#define FILS_ERP_MAX_REALM_LEN 253
+#define FILS_ERP_MAX_RRK_LEN 64
+
+#define PMK_MAX_LEN 48
+
+/* Public action codes */
+enum ieee80211_pub_actioncode {
+ WLAN_PUB_ACTION_EXT_CHANSW_ANN = 4,
+ WLAN_PUB_ACTION_TDLS_DISCOVER_RES = 14,
+};
+
+/* TDLS action codes */
+enum ieee80211_tdls_actioncode {
+ WLAN_TDLS_SETUP_REQUEST = 0,
+ WLAN_TDLS_SETUP_RESPONSE = 1,
+ WLAN_TDLS_SETUP_CONFIRM = 2,
+ WLAN_TDLS_TEARDOWN = 3,
+ WLAN_TDLS_PEER_TRAFFIC_INDICATION = 4,
+ WLAN_TDLS_CHANNEL_SWITCH_REQUEST = 5,
+ WLAN_TDLS_CHANNEL_SWITCH_RESPONSE = 6,
+ WLAN_TDLS_PEER_PSM_REQUEST = 7,
+ WLAN_TDLS_PEER_PSM_RESPONSE = 8,
+ WLAN_TDLS_PEER_TRAFFIC_RESPONSE = 9,
+ WLAN_TDLS_DISCOVERY_REQUEST = 10,
+};
+
+/* Extended Channel Switching capability to be set in the 1st byte of
+ * the @WLAN_EID_EXT_CAPABILITY information element
+ */
+#define WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING BIT(2)
+
+/* TDLS capabilities in the the 4th byte of @WLAN_EID_EXT_CAPABILITY */
+#define WLAN_EXT_CAPA4_TDLS_BUFFER_STA BIT(4)
+#define WLAN_EXT_CAPA4_TDLS_PEER_PSM BIT(5)
+#define WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH BIT(6)
+
+/* Interworking capabilities are set in 7th bit of 4th byte of the
+ * @WLAN_EID_EXT_CAPABILITY information element
+ */
+#define WLAN_EXT_CAPA4_INTERWORKING_ENABLED BIT(7)
+
+/*
+ * TDLS capabililites to be enabled in the 5th byte of the
+ * @WLAN_EID_EXT_CAPABILITY information element
+ */
+#define WLAN_EXT_CAPA5_TDLS_ENABLED BIT(5)
+#define WLAN_EXT_CAPA5_TDLS_PROHIBITED BIT(6)
+#define WLAN_EXT_CAPA5_TDLS_CH_SW_PROHIBITED BIT(7)
+
+#define WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED BIT(5)
+#define WLAN_EXT_CAPA8_OPMODE_NOTIF BIT(6)
+
+/* Defines the maximal number of MSDUs in an A-MSDU. */
+#define WLAN_EXT_CAPA8_MAX_MSDU_IN_AMSDU_LSB BIT(7)
+#define WLAN_EXT_CAPA9_MAX_MSDU_IN_AMSDU_MSB BIT(0)
+
+/*
+ * Fine Timing Measurement Initiator - bit 71 of @WLAN_EID_EXT_CAPABILITY
+ * information element
+ */
+#define WLAN_EXT_CAPA9_FTM_INITIATOR BIT(7)
+
+/* TDLS specific payload type in the LLC/SNAP header */
+#define WLAN_TDLS_SNAP_RFTYPE 0x2
+
+/* BSS Coex IE information field bits */
+#define WLAN_BSS_COEX_INFORMATION_REQUEST BIT(0)
+
+/**
+ * enum ieee80211_mesh_sync_method - mesh synchronization method identifier
+ *
+ * @IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET: the default synchronization method
+ * @IEEE80211_SYNC_METHOD_VENDOR: a vendor specific synchronization method
+ * that will be specified in a vendor specific information element
+ */
+enum ieee80211_mesh_sync_method {
+ IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET = 1,
+ IEEE80211_SYNC_METHOD_VENDOR = 255,
+};
+
+/**
+ * enum ieee80211_mesh_path_protocol - mesh path selection protocol identifier
+ *
+ * @IEEE80211_PATH_PROTOCOL_HWMP: the default path selection protocol
+ * @IEEE80211_PATH_PROTOCOL_VENDOR: a vendor specific protocol that will
+ * be specified in a vendor specific information element
+ */
+enum ieee80211_mesh_path_protocol {
+ IEEE80211_PATH_PROTOCOL_HWMP = 1,
+ IEEE80211_PATH_PROTOCOL_VENDOR = 255,
+};
+
+/**
+ * enum ieee80211_mesh_path_metric - mesh path selection metric identifier
+ *
+ * @IEEE80211_PATH_METRIC_AIRTIME: the default path selection metric
+ * @IEEE80211_PATH_METRIC_VENDOR: a vendor specific metric that will be
+ * specified in a vendor specific information element
+ */
+enum ieee80211_mesh_path_metric {
+ IEEE80211_PATH_METRIC_AIRTIME = 1,
+ IEEE80211_PATH_METRIC_VENDOR = 255,
+};
+
+/**
+ * enum ieee80211_root_mode_identifier - root mesh STA mode identifier
+ *
+ * These attribute are used by dot11MeshHWMPRootMode to set root mesh STA mode
+ *
+ * @IEEE80211_ROOTMODE_NO_ROOT: the mesh STA is not a root mesh STA (default)
+ * @IEEE80211_ROOTMODE_ROOT: the mesh STA is a root mesh STA if greater than
+ * this value
+ * @IEEE80211_PROACTIVE_PREQ_NO_PREP: the mesh STA is a root mesh STA supports
+ * the proactive PREQ with proactive PREP subfield set to 0
+ * @IEEE80211_PROACTIVE_PREQ_WITH_PREP: the mesh STA is a root mesh STA
+ * supports the proactive PREQ with proactive PREP subfield set to 1
+ * @IEEE80211_PROACTIVE_RANN: the mesh STA is a root mesh STA supports
+ * the proactive RANN
+ */
+enum ieee80211_root_mode_identifier {
+ IEEE80211_ROOTMODE_NO_ROOT = 0,
+ IEEE80211_ROOTMODE_ROOT = 1,
+ IEEE80211_PROACTIVE_PREQ_NO_PREP = 2,
+ IEEE80211_PROACTIVE_PREQ_WITH_PREP = 3,
+ IEEE80211_PROACTIVE_RANN = 4,
+};
+
+/*
+ * IEEE 802.11-2007 7.3.2.9 Country information element
+ *
+ * Minimum length is 8 octets, ie len must be evenly
+ * divisible by 2
+ */
+
+/* Although the spec says 8 I'm seeing 6 in practice */
+#define IEEE80211_COUNTRY_IE_MIN_LEN 6
+
+/* The Country String field of the element shall be 3 octets in length */
+#define IEEE80211_COUNTRY_STRING_LEN 3
+
+/*
+ * For regulatory extension stuff see IEEE 802.11-2007
+ * Annex I (page 1141) and Annex J (page 1147). Also
+ * review 7.3.2.9.
+ *
+ * When dot11RegulatoryClassesRequired is true and the
+ * first_channel/reg_extension_id is >= 201 then the IE
+ * compromises of the 'ext' struct represented below:
+ *
+ * - Regulatory extension ID - when generating IE this just needs
+ * to be monotonically increasing for each triplet passed in
+ * the IE
+ * - Regulatory class - index into set of rules
+ * - Coverage class - index into air propagation time (Table 7-27),
+ * in microseconds, you can compute the air propagation time from
+ * the index by multiplying by 3, so index 10 yields a propagation
+ * of 10 us. Valid values are 0-31, values 32-255 are not defined
+ * yet. A value of 0 inicates air propagation of <= 1 us.
+ *
+ * See also Table I.2 for Emission limit sets and table
+ * I.3 for Behavior limit sets. Table J.1 indicates how to map
+ * a reg_class to an emission limit set and behavior limit set.
+ */
+#define IEEE80211_COUNTRY_EXTENSION_ID 201
+
+/*
+ * Channels numbers in the IE must be monotonically increasing
+ * if dot11RegulatoryClassesRequired is not true.
+ *
+ * If dot11RegulatoryClassesRequired is true consecutive
+ * subband triplets following a regulatory triplet shall
+ * have monotonically increasing first_channel number fields.
+ *
+ * Channel numbers shall not overlap.
+ *
+ * Note that max_power is signed.
+ */
+struct ieee80211_country_ie_triplet {
+ union {
+ struct {
+ u8 first_channel;
+ u8 num_channels;
+ s8 max_power;
+ } __packed chans;
+ struct {
+ u8 reg_extension_id;
+ u8 reg_class;
+ u8 coverage_class;
+ } __packed ext;
+ };
+} __packed;
+
+enum ieee80211_timeout_interval_type {
+ WLAN_TIMEOUT_REASSOC_DEADLINE = 1 /* 802.11r */,
+ WLAN_TIMEOUT_KEY_LIFETIME = 2 /* 802.11r */,
+ WLAN_TIMEOUT_ASSOC_COMEBACK = 3 /* 802.11w */,
+};
+
+/**
+ * struct ieee80211_timeout_interval_ie - Timeout Interval element
+ * @type: type, see &enum ieee80211_timeout_interval_type
+ * @value: timeout interval value
+ */
+struct ieee80211_timeout_interval_ie {
+ u8 type;
+ __le32 value;
+} __packed;
+
+/**
+ * enum ieee80211_idle_options - BSS idle options
+ * @WLAN_IDLE_OPTIONS_PROTECTED_KEEP_ALIVE: the station should send an RSN
+ * protected frame to the AP to reset the idle timer at the AP for
+ * the station.
+ */
+enum ieee80211_idle_options {
+ WLAN_IDLE_OPTIONS_PROTECTED_KEEP_ALIVE = BIT(0),
+};
+
+/**
+ * struct ieee80211_bss_max_idle_period_ie
+ *
+ * This structure refers to "BSS Max idle period element"
+ *
+ * @max_idle_period: indicates the time period during which a station can
+ * refrain from transmitting frames to its associated AP without being
+ * disassociated. In units of 1000 TUs.
+ * @idle_options: indicates the options associated with the BSS idle capability
+ * as specified in &enum ieee80211_idle_options.
+ */
+struct ieee80211_bss_max_idle_period_ie {
+ __le16 max_idle_period;
+ u8 idle_options;
+} __packed;
+
+/* BACK action code */
+enum ieee80211_back_actioncode {
+ WLAN_ACTION_ADDBA_REQ = 0,
+ WLAN_ACTION_ADDBA_RESP = 1,
+ WLAN_ACTION_DELBA = 2,
+};
+
+/* BACK (block-ack) parties */
+enum ieee80211_back_parties {
+ WLAN_BACK_RECIPIENT = 0,
+ WLAN_BACK_INITIATOR = 1,
+};
+
+/* SA Query action */
+enum ieee80211_sa_query_action {
+ WLAN_ACTION_SA_QUERY_REQUEST = 0,
+ WLAN_ACTION_SA_QUERY_RESPONSE = 1,
+};
+
+
+#define SUITE(oui, id) (((oui) << 8) | (id))
+
+/* cipher suite selectors */
+#define WLAN_CIPHER_SUITE_USE_GROUP SUITE(0x000FAC, 0)
+#define WLAN_CIPHER_SUITE_WEP40 SUITE(0x000FAC, 1)
+#define WLAN_CIPHER_SUITE_TKIP SUITE(0x000FAC, 2)
+/* reserved: SUITE(0x000FAC, 3) */
+#define WLAN_CIPHER_SUITE_CCMP SUITE(0x000FAC, 4)
+#define WLAN_CIPHER_SUITE_WEP104 SUITE(0x000FAC, 5)
+#define WLAN_CIPHER_SUITE_AES_CMAC SUITE(0x000FAC, 6)
+#define WLAN_CIPHER_SUITE_GCMP SUITE(0x000FAC, 8)
+#define WLAN_CIPHER_SUITE_GCMP_256 SUITE(0x000FAC, 9)
+#define WLAN_CIPHER_SUITE_CCMP_256 SUITE(0x000FAC, 10)
+#define WLAN_CIPHER_SUITE_BIP_GMAC_128 SUITE(0x000FAC, 11)
+#define WLAN_CIPHER_SUITE_BIP_GMAC_256 SUITE(0x000FAC, 12)
+#define WLAN_CIPHER_SUITE_BIP_CMAC_256 SUITE(0x000FAC, 13)
+
+#define WLAN_CIPHER_SUITE_SMS4 SUITE(0x001472, 1)
+
+/* AKM suite selectors */
+#define WLAN_AKM_SUITE_8021X SUITE(0x000FAC, 1)
+#define WLAN_AKM_SUITE_PSK SUITE(0x000FAC, 2)
+#define WLAN_AKM_SUITE_FT_8021X SUITE(0x000FAC, 3)
+#define WLAN_AKM_SUITE_FT_PSK SUITE(0x000FAC, 4)
+#define WLAN_AKM_SUITE_8021X_SHA256 SUITE(0x000FAC, 5)
+#define WLAN_AKM_SUITE_PSK_SHA256 SUITE(0x000FAC, 6)
+#define WLAN_AKM_SUITE_TDLS SUITE(0x000FAC, 7)
+#define WLAN_AKM_SUITE_SAE SUITE(0x000FAC, 8)
+#define WLAN_AKM_SUITE_FT_OVER_SAE SUITE(0x000FAC, 9)
+#define WLAN_AKM_SUITE_8021X_SUITE_B SUITE(0x000FAC, 11)
+#define WLAN_AKM_SUITE_8021X_SUITE_B_192 SUITE(0x000FAC, 12)
+#define WLAN_AKM_SUITE_FILS_SHA256 SUITE(0x000FAC, 14)
+#define WLAN_AKM_SUITE_FILS_SHA384 SUITE(0x000FAC, 15)
+#define WLAN_AKM_SUITE_FT_FILS_SHA256 SUITE(0x000FAC, 16)
+#define WLAN_AKM_SUITE_FT_FILS_SHA384 SUITE(0x000FAC, 17)
+
+#define WLAN_MAX_KEY_LEN 32
+
+#define WLAN_PMK_NAME_LEN 16
+#define WLAN_PMKID_LEN 16
+#define WLAN_PMK_LEN_EAP_LEAP 16
+#define WLAN_PMK_LEN 32
+#define WLAN_PMK_LEN_SUITE_B_192 48
+
+#define WLAN_OUI_WFA 0x506f9a
+#define WLAN_OUI_TYPE_WFA_P2P 9
+#define WLAN_OUI_MICROSOFT 0x0050f2
+#define WLAN_OUI_TYPE_MICROSOFT_WPA 1
+#define WLAN_OUI_TYPE_MICROSOFT_WMM 2
+#define WLAN_OUI_TYPE_MICROSOFT_WPS 4
+
+/*
+ * WMM/802.11e Tspec Element
+ */
+#define IEEE80211_WMM_IE_TSPEC_TID_MASK 0x0F
+#define IEEE80211_WMM_IE_TSPEC_TID_SHIFT 1
+
+enum ieee80211_tspec_status_code {
+ IEEE80211_TSPEC_STATUS_ADMISS_ACCEPTED = 0,
+ IEEE80211_TSPEC_STATUS_ADDTS_INVAL_PARAMS = 0x1,
+};
+
+struct ieee80211_tspec_ie {
+ u8 element_id;
+ u8 len;
+ u8 oui[3];
+ u8 oui_type;
+ u8 oui_subtype;
+ u8 version;
+ __le16 tsinfo;
+ u8 tsinfo_resvd;
+ __le16 nominal_msdu;
+ __le16 max_msdu;
+ __le32 min_service_int;
+ __le32 max_service_int;
+ __le32 inactivity_int;
+ __le32 suspension_int;
+ __le32 service_start_time;
+ __le32 min_data_rate;
+ __le32 mean_data_rate;
+ __le32 peak_data_rate;
+ __le32 max_burst_size;
+ __le32 delay_bound;
+ __le32 min_phy_rate;
+ __le16 sba;
+ __le16 medium_time;
+} __packed;
+
+/**
+ * ieee80211_get_qos_ctl - get pointer to qos control bytes
+ * @hdr: the frame
+ *
+ * The qos ctrl bytes come after the frame_control, duration, seq_num
+ * and 3 or 4 addresses of length ETH_ALEN.
+ * 3 addr: 2 + 2 + 2 + 3*6 = 24
+ * 4 addr: 2 + 2 + 2 + 4*6 = 30
+ */
+static inline u8 *ieee80211_get_qos_ctl(struct ieee80211_hdr *hdr)
+{
+ if (ieee80211_has_a4(hdr->frame_control))
+ return (u8 *)hdr + 30;
+ else
+ return (u8 *)hdr + 24;
+}
+
+/**
+ * ieee80211_get_SA - get pointer to SA
+ * @hdr: the frame
+ *
+ * Given an 802.11 frame, this function returns the offset
+ * to the source address (SA). It does not verify that the
+ * header is long enough to contain the address, and the
+ * header must be long enough to contain the frame control
+ * field.
+ */
+static inline u8 *ieee80211_get_SA(struct ieee80211_hdr *hdr)
+{
+ if (ieee80211_has_a4(hdr->frame_control))
+ return hdr->addr4;
+ if (ieee80211_has_fromds(hdr->frame_control))
+ return hdr->addr3;
+ return hdr->addr2;
+}
+
+/**
+ * ieee80211_get_DA - get pointer to DA
+ * @hdr: the frame
+ *
+ * Given an 802.11 frame, this function returns the offset
+ * to the destination address (DA). It does not verify that
+ * the header is long enough to contain the address, and the
+ * header must be long enough to contain the frame control
+ * field.
+ */
+static inline u8 *ieee80211_get_DA(struct ieee80211_hdr *hdr)
+{
+ if (ieee80211_has_tods(hdr->frame_control))
+ return hdr->addr3;
+ else
+ return hdr->addr1;
+}
+
+/**
+ * _ieee80211_is_robust_mgmt_frame - check if frame is a robust management frame
+ * @hdr: the frame (buffer must include at least the first octet of payload)
+ */
+static inline bool _ieee80211_is_robust_mgmt_frame(struct ieee80211_hdr *hdr)
+{
+ if (ieee80211_is_disassoc(hdr->frame_control) ||
+ ieee80211_is_deauth(hdr->frame_control))
+ return true;
+
+ if (ieee80211_is_action(hdr->frame_control)) {
+ u8 *category;
+
+ /*
+ * Action frames, excluding Public Action frames, are Robust
+ * Management Frames. However, if we are looking at a Protected
+ * frame, skip the check since the data may be encrypted and
+ * the frame has already been found to be a Robust Management
+ * Frame (by the other end).
+ */
+ if (ieee80211_has_protected(hdr->frame_control))
+ return true;
+ category = ((u8 *) hdr) + 24;
+ return *category != WLAN_CATEGORY_PUBLIC &&
+ *category != WLAN_CATEGORY_HT &&
+ *category != WLAN_CATEGORY_WNM_UNPROTECTED &&
+ *category != WLAN_CATEGORY_SELF_PROTECTED &&
+ *category != WLAN_CATEGORY_UNPROT_DMG &&
+ *category != WLAN_CATEGORY_VHT &&
+ *category != WLAN_CATEGORY_VENDOR_SPECIFIC;
+ }
+
+ return false;
+}
+
+/**
+ * ieee80211_is_robust_mgmt_frame - check if skb contains a robust mgmt frame
+ * @skb: the skb containing the frame, length will be checked
+ */
+static inline bool ieee80211_is_robust_mgmt_frame(struct sk_buff *skb)
+{
+ if (skb->len < IEEE80211_MIN_ACTION_SIZE)
+ return false;
+ return _ieee80211_is_robust_mgmt_frame((void *)skb->data);
+}
+
+/**
+ * ieee80211_is_public_action - check if frame is a public action frame
+ * @hdr: the frame
+ * @len: length of the frame
+ */
+static inline bool ieee80211_is_public_action(struct ieee80211_hdr *hdr,
+ size_t len)
+{
+ struct ieee80211_mgmt *mgmt = (void *)hdr;
+
+ if (len < IEEE80211_MIN_ACTION_SIZE)
+ return false;
+ if (!ieee80211_is_action(hdr->frame_control))
+ return false;
+ return mgmt->u.action.category == WLAN_CATEGORY_PUBLIC;
+}
+
+/**
+ * _ieee80211_is_group_privacy_action - check if frame is a group addressed
+ * privacy action frame
+ * @hdr: the frame
+ */
+static inline bool _ieee80211_is_group_privacy_action(struct ieee80211_hdr *hdr)
+{
+ struct ieee80211_mgmt *mgmt = (void *)hdr;
+
+ if (!ieee80211_is_action(hdr->frame_control) ||
+ !is_multicast_ether_addr(hdr->addr1))
+ return false;
+
+ return mgmt->u.action.category == WLAN_CATEGORY_MESH_ACTION ||
+ mgmt->u.action.category == WLAN_CATEGORY_MULTIHOP_ACTION;
+}
+
+/**
+ * ieee80211_is_group_privacy_action - check if frame is a group addressed
+ * privacy action frame
+ * @skb: the skb containing the frame, length will be checked
+ */
+static inline bool ieee80211_is_group_privacy_action(struct sk_buff *skb)
+{
+ if (skb->len < IEEE80211_MIN_ACTION_SIZE)
+ return false;
+ return _ieee80211_is_group_privacy_action((void *)skb->data);
+}
+
+/**
+ * ieee80211_tu_to_usec - convert time units (TU) to microseconds
+ * @tu: the TUs
+ */
+static inline unsigned long ieee80211_tu_to_usec(unsigned long tu)
+{
+ return 1024 * tu;
+}
+
+/**
+ * ieee80211_check_tim - check if AID bit is set in TIM
+ * @tim: the TIM IE
+ * @tim_len: length of the TIM IE
+ * @aid: the AID to look for
+ */
+static inline bool ieee80211_check_tim(const struct ieee80211_tim_ie *tim,
+ u8 tim_len, u16 aid)
+{
+ u8 mask;
+ u8 index, indexn1, indexn2;
+
+ if (unlikely(!tim || tim_len < sizeof(*tim)))
+ return false;
+
+ aid &= 0x3fff;
+ index = aid / 8;
+ mask = 1 << (aid & 7);
+
+ indexn1 = tim->bitmap_ctrl & 0xfe;
+ indexn2 = tim_len + indexn1 - 4;
+
+ if (index < indexn1 || index > indexn2)
+ return false;
+
+ index -= indexn1;
+
+ return !!(tim->virtual_map[index] & mask);
+}
+
+/**
+ * ieee80211_get_tdls_action - get tdls packet action (or -1, if not tdls packet)
+ * @skb: the skb containing the frame, length will not be checked
+ * @hdr_size: the size of the ieee80211_hdr that starts at skb->data
+ *
+ * This function assumes the frame is a data frame, and that the network header
+ * is in the correct place.
+ */
+static inline int ieee80211_get_tdls_action(struct sk_buff *skb, u32 hdr_size)
+{
+ if (!skb_is_nonlinear(skb) &&
+ skb->len > (skb_network_offset(skb) + 2)) {
+ /* Point to where the indication of TDLS should start */
+ const u8 *tdls_data = skb_network_header(skb) - 2;
+
+ if (get_unaligned_be16(tdls_data) == ETH_P_TDLS &&
+ tdls_data[2] == WLAN_TDLS_SNAP_RFTYPE &&
+ tdls_data[3] == WLAN_CATEGORY_TDLS)
+ return tdls_data[4];
+ }
+
+ return -1;
+}
+
+/* convert time units */
+#define TU_TO_JIFFIES(x) (usecs_to_jiffies((x) * 1024))
+#define TU_TO_EXP_TIME(x) (jiffies + TU_TO_JIFFIES(x))
+
+/**
+ * ieee80211_action_contains_tpc - checks if the frame contains TPC element
+ * @skb: the skb containing the frame, length will be checked
+ *
+ * This function checks if it's either TPC report action frame or Link
+ * Measurement report action frame as defined in IEEE Std. 802.11-2012 8.5.2.5
+ * and 8.5.7.5 accordingly.
+ */
+static inline bool ieee80211_action_contains_tpc(struct sk_buff *skb)
+{
+ struct ieee80211_mgmt *mgmt = (void *)skb->data;
+
+ if (!ieee80211_is_action(mgmt->frame_control))
+ return false;
+
+ if (skb->len < IEEE80211_MIN_ACTION_SIZE +
+ sizeof(mgmt->u.action.u.tpc_report))
+ return false;
+
+ /*
+ * TPC report - check that:
+ * category = 0 (Spectrum Management) or 5 (Radio Measurement)
+ * spectrum management action = 3 (TPC/Link Measurement report)
+ * TPC report EID = 35
+ * TPC report element length = 2
+ *
+ * The spectrum management's tpc_report struct is used here both for
+ * parsing tpc_report and radio measurement's link measurement report
+ * frame, since the relevant part is identical in both frames.
+ */
+ if (mgmt->u.action.category != WLAN_CATEGORY_SPECTRUM_MGMT &&
+ mgmt->u.action.category != WLAN_CATEGORY_RADIO_MEASUREMENT)
+ return false;
+
+ /* both spectrum mgmt and link measurement have same action code */
+ if (mgmt->u.action.u.tpc_report.action_code !=
+ WLAN_ACTION_SPCT_TPC_RPRT)
+ return false;
+
+ if (mgmt->u.action.u.tpc_report.tpc_elem_id != WLAN_EID_TPC_REPORT ||
+ mgmt->u.action.u.tpc_report.tpc_elem_length !=
+ sizeof(struct ieee80211_tpc_report_ie))
+ return false;
+
+ return true;
+}
+
+#endif /* LINUX_IEEE80211_H */
diff --git a/include/linux/mmc/sdio_ids.h b/include/linux/mmc/sdio_ids.h
new file mode 100644
index 0000000..e35e637
--- /dev/null
+++ b/include/linux/mmc/sdio_ids.h
@@ -0,0 +1,73 @@
+/*
+ * SDIO Classes, Interface Types, Manufacturer IDs, etc.
+ */
+
+#ifndef LINUX_MMC_SDIO_IDS_H
+#define LINUX_MMC_SDIO_IDS_H
+
+/*
+ * Standard SDIO Function Interfaces
+ */
+
+#define SDIO_CLASS_NONE 0x00 /* Not a SDIO standard interface */
+#define SDIO_CLASS_UART 0x01 /* standard UART interface */
+#define SDIO_CLASS_BT_A 0x02 /* Type-A BlueTooth std interface */
+#define SDIO_CLASS_BT_B 0x03 /* Type-B BlueTooth std interface */
+#define SDIO_CLASS_GPS 0x04 /* GPS standard interface */
+#define SDIO_CLASS_CAMERA 0x05 /* Camera standard interface */
+#define SDIO_CLASS_PHS 0x06 /* PHS standard interface */
+#define SDIO_CLASS_WLAN 0x07 /* WLAN interface */
+#define SDIO_CLASS_ATA 0x08 /* Embedded SDIO-ATA std interface */
+#define SDIO_CLASS_BT_AMP 0x09 /* Type-A Bluetooth AMP interface */
+
+/*
+ * Vendors and devices. Sort key: vendor first, device next.
+ */
+#define SDIO_VENDOR_ID_BROADCOM 0x02d0
+#define SDIO_DEVICE_ID_BROADCOM_43143 0xa887
+#define SDIO_DEVICE_ID_BROADCOM_43241 0x4324
+#define SDIO_DEVICE_ID_BROADCOM_4329 0x4329
+#define SDIO_DEVICE_ID_BROADCOM_4330 0x4330
+#define SDIO_DEVICE_ID_BROADCOM_4334 0x4334
+#define SDIO_DEVICE_ID_BROADCOM_43340 0xa94c
+#define SDIO_DEVICE_ID_BROADCOM_43341 0xa94d
+#define SDIO_DEVICE_ID_BROADCOM_4335_4339 0x4335
+#define SDIO_DEVICE_ID_BROADCOM_4339 0x4339
+#define SDIO_DEVICE_ID_BROADCOM_43362 0xa962
+#define SDIO_DEVICE_ID_BROADCOM_43430 0xa9a6
+#define SDIO_DEVICE_ID_BROADCOM_4345 0x4345
+#define SDIO_DEVICE_ID_BROADCOM_43455 0xa9bf
+#define SDIO_DEVICE_ID_BROADCOM_4354 0x4354
+#define SDIO_DEVICE_ID_BROADCOM_4356 0x4356
+#define SDIO_DEVICE_ID_CYPRESS_4373 0x4373
+#define SDIO_DEVICE_ID_CYPRESS_43012 43012
+
+#define SDIO_VENDOR_ID_INTEL 0x0089
+#define SDIO_DEVICE_ID_INTEL_IWMC3200WIMAX 0x1402
+#define SDIO_DEVICE_ID_INTEL_IWMC3200WIFI 0x1403
+#define SDIO_DEVICE_ID_INTEL_IWMC3200TOP 0x1404
+#define SDIO_DEVICE_ID_INTEL_IWMC3200GPS 0x1405
+#define SDIO_DEVICE_ID_INTEL_IWMC3200BT 0x1406
+#define SDIO_DEVICE_ID_INTEL_IWMC3200WIMAX_2G5 0x1407
+
+#define SDIO_VENDOR_ID_MARVELL 0x02df
+#define SDIO_DEVICE_ID_MARVELL_LIBERTAS 0x9103
+#define SDIO_DEVICE_ID_MARVELL_8688WLAN 0x9104
+#define SDIO_DEVICE_ID_MARVELL_8688BT 0x9105
+#define SDIO_DEVICE_ID_MARVELL_8797_F0 0x9128
+
+#define SDIO_VENDOR_ID_SIANO 0x039a
+#define SDIO_DEVICE_ID_SIANO_NOVA_B0 0x0201
+#define SDIO_DEVICE_ID_SIANO_NICE 0x0202
+#define SDIO_DEVICE_ID_SIANO_VEGA_A0 0x0300
+#define SDIO_DEVICE_ID_SIANO_VENICE 0x0301
+#define SDIO_DEVICE_ID_SIANO_NOVA_A0 0x1100
+#define SDIO_DEVICE_ID_SIANO_STELLAR 0x5347
+
+#define SDIO_VENDOR_ID_TI 0x0097
+#define SDIO_DEVICE_ID_TI_WL1271 0x4076
+
+#define SDIO_VENDOR_ID_STE 0x0020
+#define SDIO_DEVICE_ID_STE_CW1200 0x2280
+
+#endif /* LINUX_MMC_SDIO_IDS_H */
diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
new file mode 100644
index 0000000..5f6b71d
--- /dev/null
+++ b/include/linux/pci_ids.h
@@ -0,0 +1,3061 @@
+/*
+ * PCI Class, Vendor and Device IDs
+ *
+ * Please keep sorted.
+ *
+ * Do not add new entries to this file unless the definitions
+ * are shared between multiple drivers.
+ */
+#ifndef _LINUX_PCI_IDS_H
+#define _LINUX_PCI_IDS_H
+
+/* Device classes and subclasses */
+
+#define PCI_CLASS_NOT_DEFINED 0x0000
+#define PCI_CLASS_NOT_DEFINED_VGA 0x0001
+
+#define PCI_BASE_CLASS_STORAGE 0x01
+#define PCI_CLASS_STORAGE_SCSI 0x0100
+#define PCI_CLASS_STORAGE_IDE 0x0101
+#define PCI_CLASS_STORAGE_FLOPPY 0x0102
+#define PCI_CLASS_STORAGE_IPI 0x0103
+#define PCI_CLASS_STORAGE_RAID 0x0104
+#define PCI_CLASS_STORAGE_SATA 0x0106
+#define PCI_CLASS_STORAGE_SATA_AHCI 0x010601
+#define PCI_CLASS_STORAGE_SAS 0x0107
+#define PCI_CLASS_STORAGE_EXPRESS 0x010802
+#define PCI_CLASS_STORAGE_OTHER 0x0180
+
+
+#define PCI_BASE_CLASS_NETWORK 0x02
+#define PCI_CLASS_NETWORK_ETHERNET 0x0200
+#define PCI_CLASS_NETWORK_TOKEN_RING 0x0201
+#define PCI_CLASS_NETWORK_FDDI 0x0202
+#define PCI_CLASS_NETWORK_ATM 0x0203
+#define PCI_CLASS_NETWORK_OTHER 0x0280
+
+#define PCI_BASE_CLASS_DISPLAY 0x03
+#define PCI_CLASS_DISPLAY_VGA 0x0300
+#define PCI_CLASS_DISPLAY_XGA 0x0301
+#define PCI_CLASS_DISPLAY_3D 0x0302
+#define PCI_CLASS_DISPLAY_OTHER 0x0380
+
+#define PCI_BASE_CLASS_MULTIMEDIA 0x04
+#define PCI_CLASS_MULTIMEDIA_VIDEO 0x0400
+#define PCI_CLASS_MULTIMEDIA_AUDIO 0x0401
+#define PCI_CLASS_MULTIMEDIA_PHONE 0x0402
+#define PCI_CLASS_MULTIMEDIA_OTHER 0x0480
+
+#define PCI_BASE_CLASS_MEMORY 0x05
+#define PCI_CLASS_MEMORY_RAM 0x0500
+#define PCI_CLASS_MEMORY_FLASH 0x0501
+#define PCI_CLASS_MEMORY_OTHER 0x0580
+
+#define PCI_BASE_CLASS_BRIDGE 0x06
+#define PCI_CLASS_BRIDGE_HOST 0x0600
+#define PCI_CLASS_BRIDGE_ISA 0x0601
+#define PCI_CLASS_BRIDGE_EISA 0x0602
+#define PCI_CLASS_BRIDGE_MC 0x0603
+#define PCI_CLASS_BRIDGE_PCI 0x0604
+#define PCI_CLASS_BRIDGE_PCMCIA 0x0605
+#define PCI_CLASS_BRIDGE_NUBUS 0x0606
+#define PCI_CLASS_BRIDGE_CARDBUS 0x0607
+#define PCI_CLASS_BRIDGE_RACEWAY 0x0608
+#define PCI_CLASS_BRIDGE_OTHER 0x0680
+
+#define PCI_BASE_CLASS_COMMUNICATION 0x07
+#define PCI_CLASS_COMMUNICATION_SERIAL 0x0700
+#define PCI_CLASS_COMMUNICATION_PARALLEL 0x0701
+#define PCI_CLASS_COMMUNICATION_MULTISERIAL 0x0702
+#define PCI_CLASS_COMMUNICATION_MODEM 0x0703
+#define PCI_CLASS_COMMUNICATION_OTHER 0x0780
+
+#define PCI_BASE_CLASS_SYSTEM 0x08
+#define PCI_CLASS_SYSTEM_PIC 0x0800
+#define PCI_CLASS_SYSTEM_PIC_IOAPIC 0x080010
+#define PCI_CLASS_SYSTEM_PIC_IOXAPIC 0x080020
+#define PCI_CLASS_SYSTEM_DMA 0x0801
+#define PCI_CLASS_SYSTEM_TIMER 0x0802
+#define PCI_CLASS_SYSTEM_RTC 0x0803
+#define PCI_CLASS_SYSTEM_PCI_HOTPLUG 0x0804
+#define PCI_CLASS_SYSTEM_SDHCI 0x0805
+#define PCI_CLASS_SYSTEM_OTHER 0x0880
+
+#define PCI_BASE_CLASS_INPUT 0x09
+#define PCI_CLASS_INPUT_KEYBOARD 0x0900
+#define PCI_CLASS_INPUT_PEN 0x0901
+#define PCI_CLASS_INPUT_MOUSE 0x0902
+#define PCI_CLASS_INPUT_SCANNER 0x0903
+#define PCI_CLASS_INPUT_GAMEPORT 0x0904
+#define PCI_CLASS_INPUT_OTHER 0x0980
+
+#define PCI_BASE_CLASS_DOCKING 0x0a
+#define PCI_CLASS_DOCKING_GENERIC 0x0a00
+#define PCI_CLASS_DOCKING_OTHER 0x0a80
+
+#define PCI_BASE_CLASS_PROCESSOR 0x0b
+#define PCI_CLASS_PROCESSOR_386 0x0b00
+#define PCI_CLASS_PROCESSOR_486 0x0b01
+#define PCI_CLASS_PROCESSOR_PENTIUM 0x0b02
+#define PCI_CLASS_PROCESSOR_ALPHA 0x0b10
+#define PCI_CLASS_PROCESSOR_POWERPC 0x0b20
+#define PCI_CLASS_PROCESSOR_MIPS 0x0b30
+#define PCI_CLASS_PROCESSOR_CO 0x0b40
+
+#define PCI_BASE_CLASS_SERIAL 0x0c
+#define PCI_CLASS_SERIAL_FIREWIRE 0x0c00
+#define PCI_CLASS_SERIAL_FIREWIRE_OHCI 0x0c0010
+#define PCI_CLASS_SERIAL_ACCESS 0x0c01
+#define PCI_CLASS_SERIAL_SSA 0x0c02
+#define PCI_CLASS_SERIAL_USB 0x0c03
+#define PCI_CLASS_SERIAL_USB_UHCI 0x0c0300
+#define PCI_CLASS_SERIAL_USB_OHCI 0x0c0310
+#define PCI_CLASS_SERIAL_USB_EHCI 0x0c0320
+#define PCI_CLASS_SERIAL_USB_XHCI 0x0c0330
+#define PCI_CLASS_SERIAL_USB_DEVICE 0x0c03fe
+#define PCI_CLASS_SERIAL_FIBER 0x0c04
+#define PCI_CLASS_SERIAL_SMBUS 0x0c05
+
+#define PCI_BASE_CLASS_WIRELESS 0x0d
+#define PCI_CLASS_WIRELESS_RF_CONTROLLER 0x0d10
+#define PCI_CLASS_WIRELESS_WHCI 0x0d1010
+
+#define PCI_BASE_CLASS_INTELLIGENT 0x0e
+#define PCI_CLASS_INTELLIGENT_I2O 0x0e00
+
+#define PCI_BASE_CLASS_SATELLITE 0x0f
+#define PCI_CLASS_SATELLITE_TV 0x0f00
+#define PCI_CLASS_SATELLITE_AUDIO 0x0f01
+#define PCI_CLASS_SATELLITE_VOICE 0x0f03
+#define PCI_CLASS_SATELLITE_DATA 0x0f04
+
+#define PCI_BASE_CLASS_CRYPT 0x10
+#define PCI_CLASS_CRYPT_NETWORK 0x1000
+#define PCI_CLASS_CRYPT_ENTERTAINMENT 0x1001
+#define PCI_CLASS_CRYPT_OTHER 0x1080
+
+#define PCI_BASE_CLASS_SIGNAL_PROCESSING 0x11
+#define PCI_CLASS_SP_DPIO 0x1100
+#define PCI_CLASS_SP_OTHER 0x1180
+
+#define PCI_CLASS_OTHERS 0xff
+
+/* Vendors and devices. Sort key: vendor first, device next. */
+
+#define PCI_VENDOR_ID_TTTECH 0x0357
+#define PCI_DEVICE_ID_TTTECH_MC322 0x000a
+
+#define PCI_VENDOR_ID_DYNALINK 0x0675
+#define PCI_DEVICE_ID_DYNALINK_IS64PH 0x1702
+
+#define PCI_VENDOR_ID_BERKOM 0x0871
+#define PCI_DEVICE_ID_BERKOM_A1T 0xffa1
+#define PCI_DEVICE_ID_BERKOM_T_CONCEPT 0xffa2
+#define PCI_DEVICE_ID_BERKOM_A4T 0xffa4
+#define PCI_DEVICE_ID_BERKOM_SCITEL_QUADRO 0xffa8
+
+#define PCI_VENDOR_ID_COMPAQ 0x0e11
+#define PCI_DEVICE_ID_COMPAQ_TOKENRING 0x0508
+#define PCI_DEVICE_ID_COMPAQ_TACHYON 0xa0fc
+#define PCI_DEVICE_ID_COMPAQ_SMART2P 0xae10
+#define PCI_DEVICE_ID_COMPAQ_NETEL100 0xae32
+#define PCI_DEVICE_ID_COMPAQ_NETEL10 0xae34
+#define PCI_DEVICE_ID_COMPAQ_TRIFLEX_IDE 0xae33
+#define PCI_DEVICE_ID_COMPAQ_NETFLEX3I 0xae35
+#define PCI_DEVICE_ID_COMPAQ_NETEL100D 0xae40
+#define PCI_DEVICE_ID_COMPAQ_NETEL100PI 0xae43
+#define PCI_DEVICE_ID_COMPAQ_NETEL100I 0xb011
+#define PCI_DEVICE_ID_COMPAQ_CISS 0xb060
+#define PCI_DEVICE_ID_COMPAQ_CISSB 0xb178
+#define PCI_DEVICE_ID_COMPAQ_CISSC 0x46
+#define PCI_DEVICE_ID_COMPAQ_THUNDER 0xf130
+#define PCI_DEVICE_ID_COMPAQ_NETFLEX3B 0xf150
+
+#define PCI_VENDOR_ID_NCR 0x1000
+#define PCI_VENDOR_ID_LSI_LOGIC 0x1000
+#define PCI_DEVICE_ID_NCR_53C810 0x0001
+#define PCI_DEVICE_ID_NCR_53C820 0x0002
+#define PCI_DEVICE_ID_NCR_53C825 0x0003
+#define PCI_DEVICE_ID_NCR_53C815 0x0004
+#define PCI_DEVICE_ID_LSI_53C810AP 0x0005
+#define PCI_DEVICE_ID_NCR_53C860 0x0006
+#define PCI_DEVICE_ID_LSI_53C1510 0x000a
+#define PCI_DEVICE_ID_NCR_53C896 0x000b
+#define PCI_DEVICE_ID_NCR_53C895 0x000c
+#define PCI_DEVICE_ID_NCR_53C885 0x000d
+#define PCI_DEVICE_ID_NCR_53C875 0x000f
+#define PCI_DEVICE_ID_NCR_53C1510 0x0010
+#define PCI_DEVICE_ID_LSI_53C895A 0x0012
+#define PCI_DEVICE_ID_LSI_53C875A 0x0013
+#define PCI_DEVICE_ID_LSI_53C1010_33 0x0020
+#define PCI_DEVICE_ID_LSI_53C1010_66 0x0021
+#define PCI_DEVICE_ID_LSI_53C1030 0x0030
+#define PCI_DEVICE_ID_LSI_1030_53C1035 0x0032
+#define PCI_DEVICE_ID_LSI_53C1035 0x0040
+#define PCI_DEVICE_ID_NCR_53C875J 0x008f
+#define PCI_DEVICE_ID_LSI_FC909 0x0621
+#define PCI_DEVICE_ID_LSI_FC929 0x0622
+#define PCI_DEVICE_ID_LSI_FC929_LAN 0x0623
+#define PCI_DEVICE_ID_LSI_FC919 0x0624
+#define PCI_DEVICE_ID_LSI_FC919_LAN 0x0625
+#define PCI_DEVICE_ID_LSI_FC929X 0x0626
+#define PCI_DEVICE_ID_LSI_FC939X 0x0642
+#define PCI_DEVICE_ID_LSI_FC949X 0x0640
+#define PCI_DEVICE_ID_LSI_FC949ES 0x0646
+#define PCI_DEVICE_ID_LSI_FC919X 0x0628
+#define PCI_DEVICE_ID_NCR_YELLOWFIN 0x0701
+#define PCI_DEVICE_ID_LSI_61C102 0x0901
+#define PCI_DEVICE_ID_LSI_63C815 0x1000
+#define PCI_DEVICE_ID_LSI_SAS1064 0x0050
+#define PCI_DEVICE_ID_LSI_SAS1064R 0x0411
+#define PCI_DEVICE_ID_LSI_SAS1066 0x005E
+#define PCI_DEVICE_ID_LSI_SAS1068 0x0054
+#define PCI_DEVICE_ID_LSI_SAS1064A 0x005C
+#define PCI_DEVICE_ID_LSI_SAS1064E 0x0056
+#define PCI_DEVICE_ID_LSI_SAS1066E 0x005A
+#define PCI_DEVICE_ID_LSI_SAS1068E 0x0058
+#define PCI_DEVICE_ID_LSI_SAS1078 0x0060
+
+#define PCI_VENDOR_ID_ATI 0x1002
+/* Mach64 */
+#define PCI_DEVICE_ID_ATI_68800 0x4158
+#define PCI_DEVICE_ID_ATI_215CT222 0x4354
+#define PCI_DEVICE_ID_ATI_210888CX 0x4358
+#define PCI_DEVICE_ID_ATI_215ET222 0x4554
+/* Mach64 / Rage */
+#define PCI_DEVICE_ID_ATI_215GB 0x4742
+#define PCI_DEVICE_ID_ATI_215GD 0x4744
+#define PCI_DEVICE_ID_ATI_215GI 0x4749
+#define PCI_DEVICE_ID_ATI_215GP 0x4750
+#define PCI_DEVICE_ID_ATI_215GQ 0x4751
+#define PCI_DEVICE_ID_ATI_215XL 0x4752
+#define PCI_DEVICE_ID_ATI_215GT 0x4754
+#define PCI_DEVICE_ID_ATI_215GTB 0x4755
+#define PCI_DEVICE_ID_ATI_215_IV 0x4756
+#define PCI_DEVICE_ID_ATI_215_IW 0x4757
+#define PCI_DEVICE_ID_ATI_215_IZ 0x475A
+#define PCI_DEVICE_ID_ATI_210888GX 0x4758
+#define PCI_DEVICE_ID_ATI_215_LB 0x4c42
+#define PCI_DEVICE_ID_ATI_215_LD 0x4c44
+#define PCI_DEVICE_ID_ATI_215_LG 0x4c47
+#define PCI_DEVICE_ID_ATI_215_LI 0x4c49
+#define PCI_DEVICE_ID_ATI_215_LM 0x4c4D
+#define PCI_DEVICE_ID_ATI_215_LN 0x4c4E
+#define PCI_DEVICE_ID_ATI_215_LR 0x4c52
+#define PCI_DEVICE_ID_ATI_215_LS 0x4c53
+#define PCI_DEVICE_ID_ATI_264_LT 0x4c54
+/* Mach64 VT */
+#define PCI_DEVICE_ID_ATI_264VT 0x5654
+#define PCI_DEVICE_ID_ATI_264VU 0x5655
+#define PCI_DEVICE_ID_ATI_264VV 0x5656
+/* Rage128 GL */
+#define PCI_DEVICE_ID_ATI_RAGE128_RE 0x5245
+#define PCI_DEVICE_ID_ATI_RAGE128_RF 0x5246
+#define PCI_DEVICE_ID_ATI_RAGE128_RG 0x5247
+/* Rage128 VR */
+#define PCI_DEVICE_ID_ATI_RAGE128_RK 0x524b
+#define PCI_DEVICE_ID_ATI_RAGE128_RL 0x524c
+#define PCI_DEVICE_ID_ATI_RAGE128_SE 0x5345
+#define PCI_DEVICE_ID_ATI_RAGE128_SF 0x5346
+#define PCI_DEVICE_ID_ATI_RAGE128_SG 0x5347
+#define PCI_DEVICE_ID_ATI_RAGE128_SH 0x5348
+#define PCI_DEVICE_ID_ATI_RAGE128_SK 0x534b
+#define PCI_DEVICE_ID_ATI_RAGE128_SL 0x534c
+#define PCI_DEVICE_ID_ATI_RAGE128_SM 0x534d
+#define PCI_DEVICE_ID_ATI_RAGE128_SN 0x534e
+/* Rage128 Ultra */
+#define PCI_DEVICE_ID_ATI_RAGE128_TF 0x5446
+#define PCI_DEVICE_ID_ATI_RAGE128_TL 0x544c
+#define PCI_DEVICE_ID_ATI_RAGE128_TR 0x5452
+#define PCI_DEVICE_ID_ATI_RAGE128_TS 0x5453
+#define PCI_DEVICE_ID_ATI_RAGE128_TT 0x5454
+#define PCI_DEVICE_ID_ATI_RAGE128_TU 0x5455
+/* Rage128 M3 */
+#define PCI_DEVICE_ID_ATI_RAGE128_LE 0x4c45
+#define PCI_DEVICE_ID_ATI_RAGE128_LF 0x4c46
+/* Rage128 M4 */
+#define PCI_DEVICE_ID_ATI_RAGE128_MF 0x4d46
+#define PCI_DEVICE_ID_ATI_RAGE128_ML 0x4d4c
+/* Rage128 Pro GL */
+#define PCI_DEVICE_ID_ATI_RAGE128_PA 0x5041
+#define PCI_DEVICE_ID_ATI_RAGE128_PB 0x5042
+#define PCI_DEVICE_ID_ATI_RAGE128_PC 0x5043
+#define PCI_DEVICE_ID_ATI_RAGE128_PD 0x5044
+#define PCI_DEVICE_ID_ATI_RAGE128_PE 0x5045
+#define PCI_DEVICE_ID_ATI_RAGE128_PF 0x5046
+/* Rage128 Pro VR */
+#define PCI_DEVICE_ID_ATI_RAGE128_PG 0x5047
+#define PCI_DEVICE_ID_ATI_RAGE128_PH 0x5048
+#define PCI_DEVICE_ID_ATI_RAGE128_PI 0x5049
+#define PCI_DEVICE_ID_ATI_RAGE128_PJ 0x504A
+#define PCI_DEVICE_ID_ATI_RAGE128_PK 0x504B
+#define PCI_DEVICE_ID_ATI_RAGE128_PL 0x504C
+#define PCI_DEVICE_ID_ATI_RAGE128_PM 0x504D
+#define PCI_DEVICE_ID_ATI_RAGE128_PN 0x504E
+#define PCI_DEVICE_ID_ATI_RAGE128_PO 0x504F
+#define PCI_DEVICE_ID_ATI_RAGE128_PP 0x5050
+#define PCI_DEVICE_ID_ATI_RAGE128_PQ 0x5051
+#define PCI_DEVICE_ID_ATI_RAGE128_PR 0x5052
+#define PCI_DEVICE_ID_ATI_RAGE128_PS 0x5053
+#define PCI_DEVICE_ID_ATI_RAGE128_PT 0x5054
+#define PCI_DEVICE_ID_ATI_RAGE128_PU 0x5055
+#define PCI_DEVICE_ID_ATI_RAGE128_PV 0x5056
+#define PCI_DEVICE_ID_ATI_RAGE128_PW 0x5057
+#define PCI_DEVICE_ID_ATI_RAGE128_PX 0x5058
+/* Rage128 M4 */
+/* Radeon R100 */
+#define PCI_DEVICE_ID_ATI_RADEON_QD 0x5144
+#define PCI_DEVICE_ID_ATI_RADEON_QE 0x5145
+#define PCI_DEVICE_ID_ATI_RADEON_QF 0x5146
+#define PCI_DEVICE_ID_ATI_RADEON_QG 0x5147
+/* Radeon RV100 (VE) */
+#define PCI_DEVICE_ID_ATI_RADEON_QY 0x5159
+#define PCI_DEVICE_ID_ATI_RADEON_QZ 0x515a
+/* Radeon R200 (8500) */
+#define PCI_DEVICE_ID_ATI_RADEON_QL 0x514c
+#define PCI_DEVICE_ID_ATI_RADEON_QN 0x514e
+#define PCI_DEVICE_ID_ATI_RADEON_QO 0x514f
+#define PCI_DEVICE_ID_ATI_RADEON_Ql 0x516c
+#define PCI_DEVICE_ID_ATI_RADEON_BB 0x4242
+/* Radeon R200 (9100) */
+#define PCI_DEVICE_ID_ATI_RADEON_QM 0x514d
+/* Radeon RV200 (7500) */
+#define PCI_DEVICE_ID_ATI_RADEON_QW 0x5157
+#define PCI_DEVICE_ID_ATI_RADEON_QX 0x5158
+/* Radeon NV-100 */
+/* Radeon RV250 (9000) */
+#define PCI_DEVICE_ID_ATI_RADEON_Id 0x4964
+#define PCI_DEVICE_ID_ATI_RADEON_Ie 0x4965
+#define PCI_DEVICE_ID_ATI_RADEON_If 0x4966
+#define PCI_DEVICE_ID_ATI_RADEON_Ig 0x4967
+/* Radeon RV280 (9200) */
+#define PCI_DEVICE_ID_ATI_RADEON_Ya 0x5961
+#define PCI_DEVICE_ID_ATI_RADEON_Yd 0x5964
+/* Radeon R300 (9500) */
+/* Radeon R300 (9700) */
+#define PCI_DEVICE_ID_ATI_RADEON_ND 0x4e44
+#define PCI_DEVICE_ID_ATI_RADEON_NE 0x4e45
+#define PCI_DEVICE_ID_ATI_RADEON_NF 0x4e46
+#define PCI_DEVICE_ID_ATI_RADEON_NG 0x4e47
+/* Radeon R350 (9800) */
+/* Radeon RV350 (9600) */
+/* Radeon M6 */
+#define PCI_DEVICE_ID_ATI_RADEON_LY 0x4c59
+#define PCI_DEVICE_ID_ATI_RADEON_LZ 0x4c5a
+/* Radeon M7 */
+#define PCI_DEVICE_ID_ATI_RADEON_LW 0x4c57
+#define PCI_DEVICE_ID_ATI_RADEON_LX 0x4c58
+/* Radeon M9 */
+#define PCI_DEVICE_ID_ATI_RADEON_Ld 0x4c64
+#define PCI_DEVICE_ID_ATI_RADEON_Le 0x4c65
+#define PCI_DEVICE_ID_ATI_RADEON_Lf 0x4c66
+#define PCI_DEVICE_ID_ATI_RADEON_Lg 0x4c67
+/* Radeon */
+/* RadeonIGP */
+#define PCI_DEVICE_ID_ATI_RS100 0xcab0
+#define PCI_DEVICE_ID_ATI_RS200 0xcab2
+#define PCI_DEVICE_ID_ATI_RS200_B 0xcbb2
+#define PCI_DEVICE_ID_ATI_RS250 0xcab3
+#define PCI_DEVICE_ID_ATI_RS300_100 0x5830
+#define PCI_DEVICE_ID_ATI_RS300_133 0x5831
+#define PCI_DEVICE_ID_ATI_RS300_166 0x5832
+#define PCI_DEVICE_ID_ATI_RS300_200 0x5833
+#define PCI_DEVICE_ID_ATI_RS350_100 0x7830
+#define PCI_DEVICE_ID_ATI_RS350_133 0x7831
+#define PCI_DEVICE_ID_ATI_RS350_166 0x7832
+#define PCI_DEVICE_ID_ATI_RS350_200 0x7833
+#define PCI_DEVICE_ID_ATI_RS400_100 0x5a30
+#define PCI_DEVICE_ID_ATI_RS400_133 0x5a31
+#define PCI_DEVICE_ID_ATI_RS400_166 0x5a32
+#define PCI_DEVICE_ID_ATI_RS400_200 0x5a33
+#define PCI_DEVICE_ID_ATI_RS480 0x5950
+/* ATI IXP Chipset */
+#define PCI_DEVICE_ID_ATI_IXP200_IDE 0x4349
+#define PCI_DEVICE_ID_ATI_IXP200_SMBUS 0x4353
+#define PCI_DEVICE_ID_ATI_IXP300_SMBUS 0x4363
+#define PCI_DEVICE_ID_ATI_IXP300_IDE 0x4369
+#define PCI_DEVICE_ID_ATI_IXP300_SATA 0x436e
+#define PCI_DEVICE_ID_ATI_IXP400_SMBUS 0x4372
+#define PCI_DEVICE_ID_ATI_IXP400_IDE 0x4376
+#define PCI_DEVICE_ID_ATI_IXP400_SATA 0x4379
+#define PCI_DEVICE_ID_ATI_IXP400_SATA2 0x437a
+#define PCI_DEVICE_ID_ATI_IXP600_SATA 0x4380
+#define PCI_DEVICE_ID_ATI_SBX00_SMBUS 0x4385
+#define PCI_DEVICE_ID_ATI_IXP600_IDE 0x438c
+#define PCI_DEVICE_ID_ATI_IXP700_SATA 0x4390
+#define PCI_DEVICE_ID_ATI_IXP700_IDE 0x439c
+
+#define PCI_VENDOR_ID_VLSI 0x1004
+#define PCI_DEVICE_ID_VLSI_82C592 0x0005
+#define PCI_DEVICE_ID_VLSI_82C593 0x0006
+#define PCI_DEVICE_ID_VLSI_82C594 0x0007
+#define PCI_DEVICE_ID_VLSI_82C597 0x0009
+#define PCI_DEVICE_ID_VLSI_82C541 0x000c
+#define PCI_DEVICE_ID_VLSI_82C543 0x000d
+#define PCI_DEVICE_ID_VLSI_82C532 0x0101
+#define PCI_DEVICE_ID_VLSI_82C534 0x0102
+#define PCI_DEVICE_ID_VLSI_82C535 0x0104
+#define PCI_DEVICE_ID_VLSI_82C147 0x0105
+#define PCI_DEVICE_ID_VLSI_VAS96011 0x0702
+
+/* AMD RD890 Chipset */
+#define PCI_DEVICE_ID_RD890_IOMMU 0x5a23
+
+#define PCI_VENDOR_ID_ADL 0x1005
+#define PCI_DEVICE_ID_ADL_2301 0x2301
+
+#define PCI_VENDOR_ID_NS 0x100b
+#define PCI_DEVICE_ID_NS_87415 0x0002
+#define PCI_DEVICE_ID_NS_87560_LIO 0x000e
+#define PCI_DEVICE_ID_NS_87560_USB 0x0012
+#define PCI_DEVICE_ID_NS_83815 0x0020
+#define PCI_DEVICE_ID_NS_83820 0x0022
+#define PCI_DEVICE_ID_NS_CS5535_ISA 0x002b
+#define PCI_DEVICE_ID_NS_CS5535_IDE 0x002d
+#define PCI_DEVICE_ID_NS_CS5535_AUDIO 0x002e
+#define PCI_DEVICE_ID_NS_CS5535_USB 0x002f
+#define PCI_DEVICE_ID_NS_GX_VIDEO 0x0030
+#define PCI_DEVICE_ID_NS_SATURN 0x0035
+#define PCI_DEVICE_ID_NS_SCx200_BRIDGE 0x0500
+#define PCI_DEVICE_ID_NS_SCx200_SMI 0x0501
+#define PCI_DEVICE_ID_NS_SCx200_IDE 0x0502
+#define PCI_DEVICE_ID_NS_SCx200_AUDIO 0x0503
+#define PCI_DEVICE_ID_NS_SCx200_VIDEO 0x0504
+#define PCI_DEVICE_ID_NS_SCx200_XBUS 0x0505
+#define PCI_DEVICE_ID_NS_SC1100_BRIDGE 0x0510
+#define PCI_DEVICE_ID_NS_SC1100_SMI 0x0511
+#define PCI_DEVICE_ID_NS_SC1100_XBUS 0x0515
+#define PCI_DEVICE_ID_NS_87410 0xd001
+
+#define PCI_DEVICE_ID_NS_GX_HOST_BRIDGE 0x0028
+
+#define PCI_VENDOR_ID_TSENG 0x100c
+#define PCI_DEVICE_ID_TSENG_W32P_2 0x3202
+#define PCI_DEVICE_ID_TSENG_W32P_b 0x3205
+#define PCI_DEVICE_ID_TSENG_W32P_c 0x3206
+#define PCI_DEVICE_ID_TSENG_W32P_d 0x3207
+#define PCI_DEVICE_ID_TSENG_ET6000 0x3208
+
+#define PCI_VENDOR_ID_WEITEK 0x100e
+#define PCI_DEVICE_ID_WEITEK_P9000 0x9001
+#define PCI_DEVICE_ID_WEITEK_P9100 0x9100
+
+#define PCI_VENDOR_ID_DEC 0x1011
+#define PCI_DEVICE_ID_DEC_BRD 0x0001
+#define PCI_DEVICE_ID_DEC_TULIP 0x0002
+#define PCI_DEVICE_ID_DEC_TGA 0x0004
+#define PCI_DEVICE_ID_DEC_TULIP_FAST 0x0009
+#define PCI_DEVICE_ID_DEC_TGA2 0x000D
+#define PCI_DEVICE_ID_DEC_FDDI 0x000F
+#define PCI_DEVICE_ID_DEC_TULIP_PLUS 0x0014
+#define PCI_DEVICE_ID_DEC_21142 0x0019
+#define PCI_DEVICE_ID_DEC_21052 0x0021
+#define PCI_DEVICE_ID_DEC_21150 0x0022
+#define PCI_DEVICE_ID_DEC_21152 0x0024
+#define PCI_DEVICE_ID_DEC_21153 0x0025
+#define PCI_DEVICE_ID_DEC_21154 0x0026
+#define PCI_DEVICE_ID_DEC_21285 0x1065
+#define PCI_DEVICE_ID_COMPAQ_42XX 0x0046
+
+#define PCI_VENDOR_ID_CIRRUS 0x1013
+#define PCI_DEVICE_ID_CIRRUS_7548 0x0038
+#define PCI_DEVICE_ID_CIRRUS_5430 0x00a0
+#define PCI_DEVICE_ID_CIRRUS_5434_4 0x00a4
+#define PCI_DEVICE_ID_CIRRUS_5434_8 0x00a8
+#define PCI_DEVICE_ID_CIRRUS_5436 0x00ac
+#define PCI_DEVICE_ID_CIRRUS_5446 0x00b8
+#define PCI_DEVICE_ID_CIRRUS_5480 0x00bc
+#define PCI_DEVICE_ID_CIRRUS_5462 0x00d0
+#define PCI_DEVICE_ID_CIRRUS_5464 0x00d4
+#define PCI_DEVICE_ID_CIRRUS_5465 0x00d6
+#define PCI_DEVICE_ID_CIRRUS_6729 0x1100
+#define PCI_DEVICE_ID_CIRRUS_6832 0x1110
+#define PCI_DEVICE_ID_CIRRUS_7543 0x1202
+#define PCI_DEVICE_ID_CIRRUS_4610 0x6001
+#define PCI_DEVICE_ID_CIRRUS_4612 0x6003
+#define PCI_DEVICE_ID_CIRRUS_4615 0x6004
+
+#define PCI_VENDOR_ID_IBM 0x1014
+#define PCI_DEVICE_ID_IBM_TR 0x0018
+#define PCI_DEVICE_ID_IBM_TR_WAKE 0x003e
+#define PCI_DEVICE_ID_IBM_CPC710_PCI64 0x00fc
+#define PCI_DEVICE_ID_IBM_SNIPE 0x0180
+#define PCI_DEVICE_ID_IBM_CITRINE 0x028C
+#define PCI_DEVICE_ID_IBM_GEMSTONE 0xB166
+#define PCI_DEVICE_ID_IBM_OBSIDIAN 0x02BD
+#define PCI_DEVICE_ID_IBM_ICOM_DEV_ID_1 0x0031
+#define PCI_DEVICE_ID_IBM_ICOM_DEV_ID_2 0x0219
+#define PCI_DEVICE_ID_IBM_ICOM_V2_TWO_PORTS_RVX 0x021A
+#define PCI_DEVICE_ID_IBM_ICOM_V2_ONE_PORT_RVX_ONE_PORT_MDM 0x0251
+#define PCI_DEVICE_ID_IBM_ICOM_V2_ONE_PORT_RVX_ONE_PORT_MDM_PCIE 0x0361
+#define PCI_DEVICE_ID_IBM_ICOM_FOUR_PORT_MODEL 0x252
+
+#define PCI_SUBVENDOR_ID_IBM 0x1014
+#define PCI_SUBDEVICE_ID_IBM_SATURN_SERIAL_ONE_PORT 0x03d4
+
+#define PCI_VENDOR_ID_UNISYS 0x1018
+#define PCI_DEVICE_ID_UNISYS_DMA_DIRECTOR 0x001C
+
+#define PCI_VENDOR_ID_COMPEX2 0x101a /* pci.ids says "AT&T GIS (NCR)" */
+#define PCI_DEVICE_ID_COMPEX2_100VG 0x0005
+
+#define PCI_VENDOR_ID_WD 0x101c
+#define PCI_DEVICE_ID_WD_90C 0xc24a
+
+#define PCI_VENDOR_ID_AMI 0x101e
+#define PCI_DEVICE_ID_AMI_MEGARAID3 0x1960
+#define PCI_DEVICE_ID_AMI_MEGARAID 0x9010
+#define PCI_DEVICE_ID_AMI_MEGARAID2 0x9060
+
+#define PCI_VENDOR_ID_AMD 0x1022
+#define PCI_DEVICE_ID_AMD_K8_NB 0x1100
+#define PCI_DEVICE_ID_AMD_K8_NB_ADDRMAP 0x1101
+#define PCI_DEVICE_ID_AMD_K8_NB_MEMCTL 0x1102
+#define PCI_DEVICE_ID_AMD_K8_NB_MISC 0x1103
+#define PCI_DEVICE_ID_AMD_10H_NB_HT 0x1200
+#define PCI_DEVICE_ID_AMD_10H_NB_MAP 0x1201
+#define PCI_DEVICE_ID_AMD_10H_NB_DRAM 0x1202
+#define PCI_DEVICE_ID_AMD_10H_NB_MISC 0x1203
+#define PCI_DEVICE_ID_AMD_10H_NB_LINK 0x1204
+#define PCI_DEVICE_ID_AMD_11H_NB_HT 0x1300
+#define PCI_DEVICE_ID_AMD_11H_NB_MAP 0x1301
+#define PCI_DEVICE_ID_AMD_11H_NB_DRAM 0x1302
+#define PCI_DEVICE_ID_AMD_11H_NB_MISC 0x1303
+#define PCI_DEVICE_ID_AMD_11H_NB_LINK 0x1304
+#define PCI_DEVICE_ID_AMD_15H_M10H_F3 0x1403
+#define PCI_DEVICE_ID_AMD_15H_M30H_NB_F3 0x141d
+#define PCI_DEVICE_ID_AMD_15H_M30H_NB_F4 0x141e
+#define PCI_DEVICE_ID_AMD_15H_M60H_NB_F3 0x1573
+#define PCI_DEVICE_ID_AMD_15H_M60H_NB_F4 0x1574
+#define PCI_DEVICE_ID_AMD_15H_NB_F0 0x1600
+#define PCI_DEVICE_ID_AMD_15H_NB_F1 0x1601
+#define PCI_DEVICE_ID_AMD_15H_NB_F2 0x1602
+#define PCI_DEVICE_ID_AMD_15H_NB_F3 0x1603
+#define PCI_DEVICE_ID_AMD_15H_NB_F4 0x1604
+#define PCI_DEVICE_ID_AMD_15H_NB_F5 0x1605
+#define PCI_DEVICE_ID_AMD_16H_NB_F3 0x1533
+#define PCI_DEVICE_ID_AMD_16H_NB_F4 0x1534
+#define PCI_DEVICE_ID_AMD_16H_M30H_NB_F3 0x1583
+#define PCI_DEVICE_ID_AMD_16H_M30H_NB_F4 0x1584
+#define PCI_DEVICE_ID_AMD_CNB17H_F3 0x1703
+#define PCI_DEVICE_ID_AMD_LANCE 0x2000
+#define PCI_DEVICE_ID_AMD_LANCE_HOME 0x2001
+#define PCI_DEVICE_ID_AMD_SCSI 0x2020
+#define PCI_DEVICE_ID_AMD_SERENADE 0x36c0
+#define PCI_DEVICE_ID_AMD_FE_GATE_7006 0x7006
+#define PCI_DEVICE_ID_AMD_FE_GATE_7007 0x7007
+#define PCI_DEVICE_ID_AMD_FE_GATE_700C 0x700C
+#define PCI_DEVICE_ID_AMD_FE_GATE_700E 0x700E
+#define PCI_DEVICE_ID_AMD_COBRA_7401 0x7401
+#define PCI_DEVICE_ID_AMD_VIPER_7409 0x7409
+#define PCI_DEVICE_ID_AMD_VIPER_740B 0x740B
+#define PCI_DEVICE_ID_AMD_VIPER_7410 0x7410
+#define PCI_DEVICE_ID_AMD_VIPER_7411 0x7411
+#define PCI_DEVICE_ID_AMD_VIPER_7413 0x7413
+#define PCI_DEVICE_ID_AMD_VIPER_7440 0x7440
+#define PCI_DEVICE_ID_AMD_OPUS_7441 0x7441
+#define PCI_DEVICE_ID_AMD_OPUS_7443 0x7443
+#define PCI_DEVICE_ID_AMD_VIPER_7443 0x7443
+#define PCI_DEVICE_ID_AMD_OPUS_7445 0x7445
+#define PCI_DEVICE_ID_AMD_8111_PCI 0x7460
+#define PCI_DEVICE_ID_AMD_8111_LPC 0x7468
+#define PCI_DEVICE_ID_AMD_8111_IDE 0x7469
+#define PCI_DEVICE_ID_AMD_8111_SMBUS2 0x746a
+#define PCI_DEVICE_ID_AMD_8111_SMBUS 0x746b
+#define PCI_DEVICE_ID_AMD_8111_AUDIO 0x746d
+#define PCI_DEVICE_ID_AMD_8151_0 0x7454
+#define PCI_DEVICE_ID_AMD_8131_BRIDGE 0x7450
+#define PCI_DEVICE_ID_AMD_8131_APIC 0x7451
+#define PCI_DEVICE_ID_AMD_8132_BRIDGE 0x7458
+#define PCI_DEVICE_ID_AMD_NL_USB 0x7912
+#define PCI_DEVICE_ID_AMD_CS5535_IDE 0x208F
+#define PCI_DEVICE_ID_AMD_CS5536_ISA 0x2090
+#define PCI_DEVICE_ID_AMD_CS5536_FLASH 0x2091
+#define PCI_DEVICE_ID_AMD_CS5536_AUDIO 0x2093
+#define PCI_DEVICE_ID_AMD_CS5536_OHC 0x2094
+#define PCI_DEVICE_ID_AMD_CS5536_EHC 0x2095
+#define PCI_DEVICE_ID_AMD_CS5536_UDC 0x2096
+#define PCI_DEVICE_ID_AMD_CS5536_UOC 0x2097
+#define PCI_DEVICE_ID_AMD_CS5536_IDE 0x209A
+#define PCI_DEVICE_ID_AMD_LX_VIDEO 0x2081
+#define PCI_DEVICE_ID_AMD_LX_AES 0x2082
+#define PCI_DEVICE_ID_AMD_HUDSON2_SATA_IDE 0x7800
+#define PCI_DEVICE_ID_AMD_HUDSON2_SMBUS 0x780b
+#define PCI_DEVICE_ID_AMD_HUDSON2_IDE 0x780c
+#define PCI_DEVICE_ID_AMD_KERNCZ_SMBUS 0x790b
+
+#define PCI_VENDOR_ID_TRIDENT 0x1023
+#define PCI_DEVICE_ID_TRIDENT_4DWAVE_DX 0x2000
+#define PCI_DEVICE_ID_TRIDENT_4DWAVE_NX 0x2001
+#define PCI_DEVICE_ID_TRIDENT_9320 0x9320
+#define PCI_DEVICE_ID_TRIDENT_9388 0x9388
+#define PCI_DEVICE_ID_TRIDENT_9397 0x9397
+#define PCI_DEVICE_ID_TRIDENT_939A 0x939A
+#define PCI_DEVICE_ID_TRIDENT_9520 0x9520
+#define PCI_DEVICE_ID_TRIDENT_9525 0x9525
+#define PCI_DEVICE_ID_TRIDENT_9420 0x9420
+#define PCI_DEVICE_ID_TRIDENT_9440 0x9440
+#define PCI_DEVICE_ID_TRIDENT_9660 0x9660
+#define PCI_DEVICE_ID_TRIDENT_9750 0x9750
+#define PCI_DEVICE_ID_TRIDENT_9850 0x9850
+#define PCI_DEVICE_ID_TRIDENT_9880 0x9880
+#define PCI_DEVICE_ID_TRIDENT_8400 0x8400
+#define PCI_DEVICE_ID_TRIDENT_8420 0x8420
+#define PCI_DEVICE_ID_TRIDENT_8500 0x8500
+
+#define PCI_VENDOR_ID_AI 0x1025
+#define PCI_DEVICE_ID_AI_M1435 0x1435
+
+#define PCI_VENDOR_ID_DELL 0x1028
+#define PCI_DEVICE_ID_DELL_RACIII 0x0008
+#define PCI_DEVICE_ID_DELL_RAC4 0x0012
+#define PCI_DEVICE_ID_DELL_PERC5 0x0015
+
+#define PCI_VENDOR_ID_MATROX 0x102B
+#define PCI_DEVICE_ID_MATROX_MGA_2 0x0518
+#define PCI_DEVICE_ID_MATROX_MIL 0x0519
+#define PCI_DEVICE_ID_MATROX_MYS 0x051A
+#define PCI_DEVICE_ID_MATROX_MIL_2 0x051b
+#define PCI_DEVICE_ID_MATROX_MYS_AGP 0x051e
+#define PCI_DEVICE_ID_MATROX_MIL_2_AGP 0x051f
+#define PCI_DEVICE_ID_MATROX_MGA_IMP 0x0d10
+#define PCI_DEVICE_ID_MATROX_G100_MM 0x1000
+#define PCI_DEVICE_ID_MATROX_G100_AGP 0x1001
+#define PCI_DEVICE_ID_MATROX_G200_PCI 0x0520
+#define PCI_DEVICE_ID_MATROX_G200_AGP 0x0521
+#define PCI_DEVICE_ID_MATROX_G400 0x0525
+#define PCI_DEVICE_ID_MATROX_G200EV_PCI 0x0530
+#define PCI_DEVICE_ID_MATROX_G550 0x2527
+#define PCI_DEVICE_ID_MATROX_VIA 0x4536
+
+#define PCI_VENDOR_ID_MOBILITY_ELECTRONICS 0x14f2
+
+#define PCI_VENDOR_ID_CT 0x102c
+#define PCI_DEVICE_ID_CT_69000 0x00c0
+#define PCI_DEVICE_ID_CT_65545 0x00d8
+#define PCI_DEVICE_ID_CT_65548 0x00dc
+#define PCI_DEVICE_ID_CT_65550 0x00e0
+#define PCI_DEVICE_ID_CT_65554 0x00e4
+#define PCI_DEVICE_ID_CT_65555 0x00e5
+
+#define PCI_VENDOR_ID_MIRO 0x1031
+#define PCI_DEVICE_ID_MIRO_36050 0x5601
+#define PCI_DEVICE_ID_MIRO_DC10PLUS 0x7efe
+#define PCI_DEVICE_ID_MIRO_DC30PLUS 0xd801
+
+#define PCI_VENDOR_ID_NEC 0x1033
+#define PCI_DEVICE_ID_NEC_CBUS_1 0x0001 /* PCI-Cbus Bridge */
+#define PCI_DEVICE_ID_NEC_LOCAL 0x0002 /* Local Bridge */
+#define PCI_DEVICE_ID_NEC_ATM 0x0003 /* ATM LAN Controller */
+#define PCI_DEVICE_ID_NEC_R4000 0x0004 /* R4000 Bridge */
+#define PCI_DEVICE_ID_NEC_486 0x0005 /* 486 Like Peripheral Bus Bridge */
+#define PCI_DEVICE_ID_NEC_ACCEL_1 0x0006 /* Graphic Accelerator */
+#define PCI_DEVICE_ID_NEC_UXBUS 0x0007 /* UX-Bus Bridge */
+#define PCI_DEVICE_ID_NEC_ACCEL_2 0x0008 /* Graphic Accelerator */
+#define PCI_DEVICE_ID_NEC_GRAPH 0x0009 /* PCI-CoreGraph Bridge */
+#define PCI_DEVICE_ID_NEC_VL 0x0016 /* PCI-VL Bridge */
+#define PCI_DEVICE_ID_NEC_STARALPHA2 0x002c /* STAR ALPHA2 */
+#define PCI_DEVICE_ID_NEC_CBUS_2 0x002d /* PCI-Cbus Bridge */
+#define PCI_DEVICE_ID_NEC_USB 0x0035 /* PCI-USB Host */
+#define PCI_DEVICE_ID_NEC_CBUS_3 0x003b
+#define PCI_DEVICE_ID_NEC_NAPCCARD 0x003e
+#define PCI_DEVICE_ID_NEC_PCX2 0x0046 /* PowerVR */
+#define PCI_DEVICE_ID_NEC_VRC5476 0x009b
+#define PCI_DEVICE_ID_NEC_VRC4173 0x00a5
+#define PCI_DEVICE_ID_NEC_VRC5477_AC97 0x00a6
+#define PCI_DEVICE_ID_NEC_PC9821CS01 0x800c /* PC-9821-CS01 */
+#define PCI_DEVICE_ID_NEC_PC9821NRB06 0x800d /* PC-9821NR-B06 */
+
+#define PCI_VENDOR_ID_FD 0x1036
+#define PCI_DEVICE_ID_FD_36C70 0x0000
+
+#define PCI_VENDOR_ID_SI 0x1039
+#define PCI_DEVICE_ID_SI_5591_AGP 0x0001
+#define PCI_DEVICE_ID_SI_6202 0x0002
+#define PCI_DEVICE_ID_SI_503 0x0008
+#define PCI_DEVICE_ID_SI_ACPI 0x0009
+#define PCI_DEVICE_ID_SI_SMBUS 0x0016
+#define PCI_DEVICE_ID_SI_LPC 0x0018
+#define PCI_DEVICE_ID_SI_5597_VGA 0x0200
+#define PCI_DEVICE_ID_SI_6205 0x0205
+#define PCI_DEVICE_ID_SI_501 0x0406
+#define PCI_DEVICE_ID_SI_496 0x0496
+#define PCI_DEVICE_ID_SI_300 0x0300
+#define PCI_DEVICE_ID_SI_315H 0x0310
+#define PCI_DEVICE_ID_SI_315 0x0315
+#define PCI_DEVICE_ID_SI_315PRO 0x0325
+#define PCI_DEVICE_ID_SI_530 0x0530
+#define PCI_DEVICE_ID_SI_540 0x0540
+#define PCI_DEVICE_ID_SI_550 0x0550
+#define PCI_DEVICE_ID_SI_540_VGA 0x5300
+#define PCI_DEVICE_ID_SI_550_VGA 0x5315
+#define PCI_DEVICE_ID_SI_620 0x0620
+#define PCI_DEVICE_ID_SI_630 0x0630
+#define PCI_DEVICE_ID_SI_633 0x0633
+#define PCI_DEVICE_ID_SI_635 0x0635
+#define PCI_DEVICE_ID_SI_640 0x0640
+#define PCI_DEVICE_ID_SI_645 0x0645
+#define PCI_DEVICE_ID_SI_646 0x0646
+#define PCI_DEVICE_ID_SI_648 0x0648
+#define PCI_DEVICE_ID_SI_650 0x0650
+#define PCI_DEVICE_ID_SI_651 0x0651
+#define PCI_DEVICE_ID_SI_655 0x0655
+#define PCI_DEVICE_ID_SI_661 0x0661
+#define PCI_DEVICE_ID_SI_730 0x0730
+#define PCI_DEVICE_ID_SI_733 0x0733
+#define PCI_DEVICE_ID_SI_630_VGA 0x6300
+#define PCI_DEVICE_ID_SI_735 0x0735
+#define PCI_DEVICE_ID_SI_740 0x0740
+#define PCI_DEVICE_ID_SI_741 0x0741
+#define PCI_DEVICE_ID_SI_745 0x0745
+#define PCI_DEVICE_ID_SI_746 0x0746
+#define PCI_DEVICE_ID_SI_755 0x0755
+#define PCI_DEVICE_ID_SI_760 0x0760
+#define PCI_DEVICE_ID_SI_900 0x0900
+#define PCI_DEVICE_ID_SI_961 0x0961
+#define PCI_DEVICE_ID_SI_962 0x0962
+#define PCI_DEVICE_ID_SI_963 0x0963
+#define PCI_DEVICE_ID_SI_965 0x0965
+#define PCI_DEVICE_ID_SI_966 0x0966
+#define PCI_DEVICE_ID_SI_968 0x0968
+#define PCI_DEVICE_ID_SI_1180 0x1180
+#define PCI_DEVICE_ID_SI_5511 0x5511
+#define PCI_DEVICE_ID_SI_5513 0x5513
+#define PCI_DEVICE_ID_SI_5517 0x5517
+#define PCI_DEVICE_ID_SI_5518 0x5518
+#define PCI_DEVICE_ID_SI_5571 0x5571
+#define PCI_DEVICE_ID_SI_5581 0x5581
+#define PCI_DEVICE_ID_SI_5582 0x5582
+#define PCI_DEVICE_ID_SI_5591 0x5591
+#define PCI_DEVICE_ID_SI_5596 0x5596
+#define PCI_DEVICE_ID_SI_5597 0x5597
+#define PCI_DEVICE_ID_SI_5598 0x5598
+#define PCI_DEVICE_ID_SI_5600 0x5600
+#define PCI_DEVICE_ID_SI_7012 0x7012
+#define PCI_DEVICE_ID_SI_7013 0x7013
+#define PCI_DEVICE_ID_SI_7016 0x7016
+#define PCI_DEVICE_ID_SI_7018 0x7018
+
+#define PCI_VENDOR_ID_HP 0x103c
+#define PCI_VENDOR_ID_HP_3PAR 0x1590
+#define PCI_DEVICE_ID_HP_VISUALIZE_EG 0x1005
+#define PCI_DEVICE_ID_HP_VISUALIZE_FX6 0x1006
+#define PCI_DEVICE_ID_HP_VISUALIZE_FX4 0x1008
+#define PCI_DEVICE_ID_HP_VISUALIZE_FX2 0x100a
+#define PCI_DEVICE_ID_HP_TACHYON 0x1028
+#define PCI_DEVICE_ID_HP_TACHLITE 0x1029
+#define PCI_DEVICE_ID_HP_J2585A 0x1030
+#define PCI_DEVICE_ID_HP_J2585B 0x1031
+#define PCI_DEVICE_ID_HP_J2973A 0x1040
+#define PCI_DEVICE_ID_HP_J2970A 0x1042
+#define PCI_DEVICE_ID_HP_DIVA 0x1048
+#define PCI_DEVICE_ID_HP_DIVA_TOSCA1 0x1049
+#define PCI_DEVICE_ID_HP_DIVA_TOSCA2 0x104A
+#define PCI_DEVICE_ID_HP_DIVA_MAESTRO 0x104B
+#define PCI_DEVICE_ID_HP_REO_IOC 0x10f1
+#define PCI_DEVICE_ID_HP_VISUALIZE_FXE 0x108b
+#define PCI_DEVICE_ID_HP_DIVA_HALFDOME 0x1223
+#define PCI_DEVICE_ID_HP_DIVA_KEYSTONE 0x1226
+#define PCI_DEVICE_ID_HP_DIVA_POWERBAR 0x1227
+#define PCI_DEVICE_ID_HP_ZX1_IOC 0x122a
+#define PCI_DEVICE_ID_HP_PCIX_LBA 0x122e
+#define PCI_DEVICE_ID_HP_SX1000_IOC 0x127c
+#define PCI_DEVICE_ID_HP_DIVA_EVEREST 0x1282
+#define PCI_DEVICE_ID_HP_DIVA_AUX 0x1290
+#define PCI_DEVICE_ID_HP_DIVA_RMP3 0x1301
+#define PCI_DEVICE_ID_HP_DIVA_HURRICANE 0x132a
+#define PCI_DEVICE_ID_HP_CISSA 0x3220
+#define PCI_DEVICE_ID_HP_CISSC 0x3230
+#define PCI_DEVICE_ID_HP_CISSD 0x3238
+#define PCI_DEVICE_ID_HP_CISSE 0x323a
+#define PCI_DEVICE_ID_HP_CISSF 0x323b
+#define PCI_DEVICE_ID_HP_CISSH 0x323c
+#define PCI_DEVICE_ID_HP_CISSI 0x3239
+#define PCI_DEVICE_ID_HP_ZX2_IOC 0x4031
+
+#define PCI_VENDOR_ID_PCTECH 0x1042
+#define PCI_DEVICE_ID_PCTECH_RZ1000 0x1000
+#define PCI_DEVICE_ID_PCTECH_RZ1001 0x1001
+#define PCI_DEVICE_ID_PCTECH_SAMURAI_IDE 0x3020
+
+#define PCI_VENDOR_ID_ASUSTEK 0x1043
+#define PCI_DEVICE_ID_ASUSTEK_0675 0x0675
+
+#define PCI_VENDOR_ID_DPT 0x1044
+#define PCI_DEVICE_ID_DPT 0xa400
+
+#define PCI_VENDOR_ID_OPTI 0x1045
+#define PCI_DEVICE_ID_OPTI_82C558 0xc558
+#define PCI_DEVICE_ID_OPTI_82C621 0xc621
+#define PCI_DEVICE_ID_OPTI_82C700 0xc700
+#define PCI_DEVICE_ID_OPTI_82C825 0xd568
+
+#define PCI_VENDOR_ID_ELSA 0x1048
+#define PCI_DEVICE_ID_ELSA_MICROLINK 0x1000
+#define PCI_DEVICE_ID_ELSA_QS3000 0x3000
+
+#define PCI_VENDOR_ID_STMICRO 0x104A
+#define PCI_DEVICE_ID_STMICRO_USB_HOST 0xCC00
+#define PCI_DEVICE_ID_STMICRO_USB_OHCI 0xCC01
+#define PCI_DEVICE_ID_STMICRO_USB_OTG 0xCC02
+#define PCI_DEVICE_ID_STMICRO_UART_HWFC 0xCC03
+#define PCI_DEVICE_ID_STMICRO_UART_NO_HWFC 0xCC04
+#define PCI_DEVICE_ID_STMICRO_SOC_DMA 0xCC05
+#define PCI_DEVICE_ID_STMICRO_SATA 0xCC06
+#define PCI_DEVICE_ID_STMICRO_I2C 0xCC07
+#define PCI_DEVICE_ID_STMICRO_SPI_HS 0xCC08
+#define PCI_DEVICE_ID_STMICRO_MAC 0xCC09
+#define PCI_DEVICE_ID_STMICRO_SDIO_EMMC 0xCC0A
+#define PCI_DEVICE_ID_STMICRO_SDIO 0xCC0B
+#define PCI_DEVICE_ID_STMICRO_GPIO 0xCC0C
+#define PCI_DEVICE_ID_STMICRO_VIP 0xCC0D
+#define PCI_DEVICE_ID_STMICRO_AUDIO_ROUTER_DMA 0xCC0E
+#define PCI_DEVICE_ID_STMICRO_AUDIO_ROUTER_SRCS 0xCC0F
+#define PCI_DEVICE_ID_STMICRO_AUDIO_ROUTER_MSPS 0xCC10
+#define PCI_DEVICE_ID_STMICRO_CAN 0xCC11
+#define PCI_DEVICE_ID_STMICRO_MLB 0xCC12
+#define PCI_DEVICE_ID_STMICRO_DBP 0xCC13
+#define PCI_DEVICE_ID_STMICRO_SATA_PHY 0xCC14
+#define PCI_DEVICE_ID_STMICRO_ESRAM 0xCC15
+#define PCI_DEVICE_ID_STMICRO_VIC 0xCC16
+
+#define PCI_VENDOR_ID_BUSLOGIC 0x104B
+#define PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER_NC 0x0140
+#define PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER 0x1040
+#define PCI_DEVICE_ID_BUSLOGIC_FLASHPOINT 0x8130
+
+#define PCI_VENDOR_ID_TI 0x104c
+#define PCI_DEVICE_ID_TI_TVP4020 0x3d07
+#define PCI_DEVICE_ID_TI_4450 0x8011
+#define PCI_DEVICE_ID_TI_XX21_XX11 0x8031
+#define PCI_DEVICE_ID_TI_XX21_XX11_FM 0x8033
+#define PCI_DEVICE_ID_TI_XX21_XX11_SD 0x8034
+#define PCI_DEVICE_ID_TI_X515 0x8036
+#define PCI_DEVICE_ID_TI_XX12 0x8039
+#define PCI_DEVICE_ID_TI_XX12_FM 0x803b
+#define PCI_DEVICE_ID_TI_XIO2000A 0x8231
+#define PCI_DEVICE_ID_TI_1130 0xac12
+#define PCI_DEVICE_ID_TI_1031 0xac13
+#define PCI_DEVICE_ID_TI_1131 0xac15
+#define PCI_DEVICE_ID_TI_1250 0xac16
+#define PCI_DEVICE_ID_TI_1220 0xac17
+#define PCI_DEVICE_ID_TI_1221 0xac19
+#define PCI_DEVICE_ID_TI_1210 0xac1a
+#define PCI_DEVICE_ID_TI_1450 0xac1b
+#define PCI_DEVICE_ID_TI_1225 0xac1c
+#define PCI_DEVICE_ID_TI_1251A 0xac1d
+#define PCI_DEVICE_ID_TI_1211 0xac1e
+#define PCI_DEVICE_ID_TI_1251B 0xac1f
+#define PCI_DEVICE_ID_TI_4410 0xac41
+#define PCI_DEVICE_ID_TI_4451 0xac42
+#define PCI_DEVICE_ID_TI_4510 0xac44
+#define PCI_DEVICE_ID_TI_4520 0xac46
+#define PCI_DEVICE_ID_TI_7510 0xac47
+#define PCI_DEVICE_ID_TI_7610 0xac48
+#define PCI_DEVICE_ID_TI_7410 0xac49
+#define PCI_DEVICE_ID_TI_1410 0xac50
+#define PCI_DEVICE_ID_TI_1420 0xac51
+#define PCI_DEVICE_ID_TI_1451A 0xac52
+#define PCI_DEVICE_ID_TI_1620 0xac54
+#define PCI_DEVICE_ID_TI_1520 0xac55
+#define PCI_DEVICE_ID_TI_1510 0xac56
+#define PCI_DEVICE_ID_TI_X620 0xac8d
+#define PCI_DEVICE_ID_TI_X420 0xac8e
+#define PCI_DEVICE_ID_TI_XX20_FM 0xac8f
+#define PCI_DEVICE_ID_TI_DRA74x 0xb500
+#define PCI_DEVICE_ID_TI_DRA72x 0xb501
+
+#define PCI_VENDOR_ID_SONY 0x104d
+
+/* Winbond have two vendor IDs! See 0x10ad as well */
+#define PCI_VENDOR_ID_WINBOND2 0x1050
+#define PCI_DEVICE_ID_WINBOND2_89C940F 0x5a5a
+#define PCI_DEVICE_ID_WINBOND2_6692 0x6692
+
+#define PCI_VENDOR_ID_ANIGMA 0x1051
+#define PCI_DEVICE_ID_ANIGMA_MC145575 0x0100
+
+#define PCI_VENDOR_ID_EFAR 0x1055
+#define PCI_DEVICE_ID_EFAR_SLC90E66_1 0x9130
+#define PCI_DEVICE_ID_EFAR_SLC90E66_3 0x9463
+
+#define PCI_VENDOR_ID_MOTOROLA 0x1057
+#define PCI_DEVICE_ID_MOTOROLA_MPC105 0x0001
+#define PCI_DEVICE_ID_MOTOROLA_MPC106 0x0002
+#define PCI_DEVICE_ID_MOTOROLA_MPC107 0x0004
+#define PCI_DEVICE_ID_MOTOROLA_RAVEN 0x4801
+#define PCI_DEVICE_ID_MOTOROLA_FALCON 0x4802
+#define PCI_DEVICE_ID_MOTOROLA_HAWK 0x4803
+#define PCI_DEVICE_ID_MOTOROLA_HARRIER 0x480b
+#define PCI_DEVICE_ID_MOTOROLA_MPC5200 0x5803
+#define PCI_DEVICE_ID_MOTOROLA_MPC5200B 0x5809
+
+#define PCI_VENDOR_ID_PROMISE 0x105a
+#define PCI_DEVICE_ID_PROMISE_20265 0x0d30
+#define PCI_DEVICE_ID_PROMISE_20267 0x4d30
+#define PCI_DEVICE_ID_PROMISE_20246 0x4d33
+#define PCI_DEVICE_ID_PROMISE_20262 0x4d38
+#define PCI_DEVICE_ID_PROMISE_20263 0x0D38
+#define PCI_DEVICE_ID_PROMISE_20268 0x4d68
+#define PCI_DEVICE_ID_PROMISE_20269 0x4d69
+#define PCI_DEVICE_ID_PROMISE_20270 0x6268
+#define PCI_DEVICE_ID_PROMISE_20271 0x6269
+#define PCI_DEVICE_ID_PROMISE_20275 0x1275
+#define PCI_DEVICE_ID_PROMISE_20276 0x5275
+#define PCI_DEVICE_ID_PROMISE_20277 0x7275
+
+#define PCI_VENDOR_ID_FOXCONN 0x105b
+
+#define PCI_VENDOR_ID_UMC 0x1060
+#define PCI_DEVICE_ID_UMC_UM8673F 0x0101
+#define PCI_DEVICE_ID_UMC_UM8886BF 0x673a
+#define PCI_DEVICE_ID_UMC_UM8886A 0x886a
+
+#define PCI_VENDOR_ID_PICOPOWER 0x1066
+#define PCI_DEVICE_ID_PICOPOWER_PT86C523 0x0002
+#define PCI_DEVICE_ID_PICOPOWER_PT86C523BBP 0x8002
+
+#define PCI_VENDOR_ID_MYLEX 0x1069
+#define PCI_DEVICE_ID_MYLEX_DAC960_P 0x0001
+#define PCI_DEVICE_ID_MYLEX_DAC960_PD 0x0002
+#define PCI_DEVICE_ID_MYLEX_DAC960_PG 0x0010
+#define PCI_DEVICE_ID_MYLEX_DAC960_LA 0x0020
+#define PCI_DEVICE_ID_MYLEX_DAC960_LP 0x0050
+#define PCI_DEVICE_ID_MYLEX_DAC960_BA 0xBA56
+#define PCI_DEVICE_ID_MYLEX_DAC960_GEM 0xB166
+
+#define PCI_VENDOR_ID_APPLE 0x106b
+#define PCI_DEVICE_ID_APPLE_BANDIT 0x0001
+#define PCI_DEVICE_ID_APPLE_HYDRA 0x000e
+#define PCI_DEVICE_ID_APPLE_UNI_N_FW 0x0018
+#define PCI_DEVICE_ID_APPLE_UNI_N_AGP 0x0020
+#define PCI_DEVICE_ID_APPLE_UNI_N_GMAC 0x0021
+#define PCI_DEVICE_ID_APPLE_UNI_N_GMACP 0x0024
+#define PCI_DEVICE_ID_APPLE_UNI_N_AGP_P 0x0027
+#define PCI_DEVICE_ID_APPLE_UNI_N_AGP15 0x002d
+#define PCI_DEVICE_ID_APPLE_UNI_N_PCI15 0x002e
+#define PCI_DEVICE_ID_APPLE_UNI_N_GMAC2 0x0032
+#define PCI_DEVICE_ID_APPLE_UNI_N_ATA 0x0033
+#define PCI_DEVICE_ID_APPLE_UNI_N_AGP2 0x0034
+#define PCI_DEVICE_ID_APPLE_IPID_ATA100 0x003b
+#define PCI_DEVICE_ID_APPLE_K2_ATA100 0x0043
+#define PCI_DEVICE_ID_APPLE_U3_AGP 0x004b
+#define PCI_DEVICE_ID_APPLE_K2_GMAC 0x004c
+#define PCI_DEVICE_ID_APPLE_SH_ATA 0x0050
+#define PCI_DEVICE_ID_APPLE_SH_SUNGEM 0x0051
+#define PCI_DEVICE_ID_APPLE_U3L_AGP 0x0058
+#define PCI_DEVICE_ID_APPLE_U3H_AGP 0x0059
+#define PCI_DEVICE_ID_APPLE_U4_PCIE 0x005b
+#define PCI_DEVICE_ID_APPLE_IPID2_AGP 0x0066
+#define PCI_DEVICE_ID_APPLE_IPID2_ATA 0x0069
+#define PCI_DEVICE_ID_APPLE_IPID2_FW 0x006a
+#define PCI_DEVICE_ID_APPLE_IPID2_GMAC 0x006b
+#define PCI_DEVICE_ID_APPLE_TIGON3 0x1645
+
+#define PCI_VENDOR_ID_YAMAHA 0x1073
+#define PCI_DEVICE_ID_YAMAHA_724 0x0004
+#define PCI_DEVICE_ID_YAMAHA_724F 0x000d
+#define PCI_DEVICE_ID_YAMAHA_740 0x000a
+#define PCI_DEVICE_ID_YAMAHA_740C 0x000c
+#define PCI_DEVICE_ID_YAMAHA_744 0x0010
+#define PCI_DEVICE_ID_YAMAHA_754 0x0012
+
+#define PCI_VENDOR_ID_QLOGIC 0x1077
+#define PCI_DEVICE_ID_QLOGIC_ISP10160 0x1016
+#define PCI_DEVICE_ID_QLOGIC_ISP1020 0x1020
+#define PCI_DEVICE_ID_QLOGIC_ISP1080 0x1080
+#define PCI_DEVICE_ID_QLOGIC_ISP12160 0x1216
+#define PCI_DEVICE_ID_QLOGIC_ISP1240 0x1240
+#define PCI_DEVICE_ID_QLOGIC_ISP1280 0x1280
+#define PCI_DEVICE_ID_QLOGIC_ISP2100 0x2100
+#define PCI_DEVICE_ID_QLOGIC_ISP2200 0x2200
+#define PCI_DEVICE_ID_QLOGIC_ISP2300 0x2300
+#define PCI_DEVICE_ID_QLOGIC_ISP2312 0x2312
+#define PCI_DEVICE_ID_QLOGIC_ISP2322 0x2322
+#define PCI_DEVICE_ID_QLOGIC_ISP6312 0x6312
+#define PCI_DEVICE_ID_QLOGIC_ISP6322 0x6322
+#define PCI_DEVICE_ID_QLOGIC_ISP2422 0x2422
+#define PCI_DEVICE_ID_QLOGIC_ISP2432 0x2432
+#define PCI_DEVICE_ID_QLOGIC_ISP2512 0x2512
+#define PCI_DEVICE_ID_QLOGIC_ISP2522 0x2522
+#define PCI_DEVICE_ID_QLOGIC_ISP5422 0x5422
+#define PCI_DEVICE_ID_QLOGIC_ISP5432 0x5432
+
+#define PCI_VENDOR_ID_CYRIX 0x1078
+#define PCI_DEVICE_ID_CYRIX_5510 0x0000
+#define PCI_DEVICE_ID_CYRIX_PCI_MASTER 0x0001
+#define PCI_DEVICE_ID_CYRIX_5520 0x0002
+#define PCI_DEVICE_ID_CYRIX_5530_LEGACY 0x0100
+#define PCI_DEVICE_ID_CYRIX_5530_IDE 0x0102
+#define PCI_DEVICE_ID_CYRIX_5530_AUDIO 0x0103
+#define PCI_DEVICE_ID_CYRIX_5530_VIDEO 0x0104
+
+#define PCI_VENDOR_ID_CONTAQ 0x1080
+#define PCI_DEVICE_ID_CONTAQ_82C693 0xc693
+
+#define PCI_VENDOR_ID_OLICOM 0x108d
+#define PCI_DEVICE_ID_OLICOM_OC2325 0x0012
+#define PCI_DEVICE_ID_OLICOM_OC2183 0x0013
+#define PCI_DEVICE_ID_OLICOM_OC2326 0x0014
+
+#define PCI_VENDOR_ID_SUN 0x108e
+#define PCI_DEVICE_ID_SUN_EBUS 0x1000
+#define PCI_DEVICE_ID_SUN_HAPPYMEAL 0x1001
+#define PCI_DEVICE_ID_SUN_RIO_EBUS 0x1100
+#define PCI_DEVICE_ID_SUN_RIO_GEM 0x1101
+#define PCI_DEVICE_ID_SUN_RIO_1394 0x1102
+#define PCI_DEVICE_ID_SUN_RIO_USB 0x1103
+#define PCI_DEVICE_ID_SUN_GEM 0x2bad
+#define PCI_DEVICE_ID_SUN_SIMBA 0x5000
+#define PCI_DEVICE_ID_SUN_PBM 0x8000
+#define PCI_DEVICE_ID_SUN_SCHIZO 0x8001
+#define PCI_DEVICE_ID_SUN_SABRE 0xa000
+#define PCI_DEVICE_ID_SUN_HUMMINGBIRD 0xa001
+#define PCI_DEVICE_ID_SUN_TOMATILLO 0xa801
+#define PCI_DEVICE_ID_SUN_CASSINI 0xabba
+
+#define PCI_VENDOR_ID_NI 0x1093
+#define PCI_DEVICE_ID_NI_PCI2322 0xd130
+#define PCI_DEVICE_ID_NI_PCI2324 0xd140
+#define PCI_DEVICE_ID_NI_PCI2328 0xd150
+#define PCI_DEVICE_ID_NI_PXI8422_2322 0xd190
+#define PCI_DEVICE_ID_NI_PXI8422_2324 0xd1a0
+#define PCI_DEVICE_ID_NI_PXI8420_2322 0xd1d0
+#define PCI_DEVICE_ID_NI_PXI8420_2324 0xd1e0
+#define PCI_DEVICE_ID_NI_PXI8420_2328 0xd1f0
+#define PCI_DEVICE_ID_NI_PXI8420_23216 0xd1f1
+#define PCI_DEVICE_ID_NI_PCI2322I 0xd250
+#define PCI_DEVICE_ID_NI_PCI2324I 0xd270
+#define PCI_DEVICE_ID_NI_PCI23216 0xd2b0
+#define PCI_DEVICE_ID_NI_PXI8430_2322 0x7080
+#define PCI_DEVICE_ID_NI_PCI8430_2322 0x70db
+#define PCI_DEVICE_ID_NI_PXI8430_2324 0x70dd
+#define PCI_DEVICE_ID_NI_PCI8430_2324 0x70df
+#define PCI_DEVICE_ID_NI_PXI8430_2328 0x70e2
+#define PCI_DEVICE_ID_NI_PCI8430_2328 0x70e4
+#define PCI_DEVICE_ID_NI_PXI8430_23216 0x70e6
+#define PCI_DEVICE_ID_NI_PCI8430_23216 0x70e7
+#define PCI_DEVICE_ID_NI_PXI8432_2322 0x70e8
+#define PCI_DEVICE_ID_NI_PCI8432_2322 0x70ea
+#define PCI_DEVICE_ID_NI_PXI8432_2324 0x70ec
+#define PCI_DEVICE_ID_NI_PCI8432_2324 0x70ee
+
+#define PCI_VENDOR_ID_CMD 0x1095
+#define PCI_DEVICE_ID_CMD_643 0x0643
+#define PCI_DEVICE_ID_CMD_646 0x0646
+#define PCI_DEVICE_ID_CMD_648 0x0648
+#define PCI_DEVICE_ID_CMD_649 0x0649
+
+#define PCI_DEVICE_ID_SII_680 0x0680
+#define PCI_DEVICE_ID_SII_3112 0x3112
+#define PCI_DEVICE_ID_SII_1210SA 0x0240
+
+#define PCI_VENDOR_ID_BROOKTREE 0x109e
+#define PCI_DEVICE_ID_BROOKTREE_878 0x0878
+#define PCI_DEVICE_ID_BROOKTREE_879 0x0879
+
+#define PCI_VENDOR_ID_SGI 0x10a9
+#define PCI_DEVICE_ID_SGI_IOC3 0x0003
+#define PCI_DEVICE_ID_SGI_LITHIUM 0x1002
+#define PCI_DEVICE_ID_SGI_IOC4 0x100a
+
+#define PCI_VENDOR_ID_WINBOND 0x10ad
+#define PCI_DEVICE_ID_WINBOND_82C105 0x0105
+#define PCI_DEVICE_ID_WINBOND_83C553 0x0565
+
+#define PCI_VENDOR_ID_PLX 0x10b5
+#define PCI_DEVICE_ID_PLX_R685 0x1030
+#define PCI_DEVICE_ID_PLX_ROMULUS 0x106a
+#define PCI_DEVICE_ID_PLX_SPCOM800 0x1076
+#define PCI_DEVICE_ID_PLX_1077 0x1077
+#define PCI_DEVICE_ID_PLX_SPCOM200 0x1103
+#define PCI_DEVICE_ID_PLX_DJINN_ITOO 0x1151
+#define PCI_DEVICE_ID_PLX_R753 0x1152
+#define PCI_DEVICE_ID_PLX_OLITEC 0x1187
+#define PCI_DEVICE_ID_PLX_PCI200SYN 0x3196
+#define PCI_DEVICE_ID_PLX_9030 0x9030
+#define PCI_DEVICE_ID_PLX_9050 0x9050
+#define PCI_DEVICE_ID_PLX_9056 0x9056
+#define PCI_DEVICE_ID_PLX_9080 0x9080
+#define PCI_DEVICE_ID_PLX_GTEK_SERIAL2 0xa001
+
+#define PCI_VENDOR_ID_MADGE 0x10b6
+#define PCI_DEVICE_ID_MADGE_MK2 0x0002
+
+#define PCI_VENDOR_ID_3COM 0x10b7
+#define PCI_DEVICE_ID_3COM_3C985 0x0001
+#define PCI_DEVICE_ID_3COM_3C940 0x1700
+#define PCI_DEVICE_ID_3COM_3C339 0x3390
+#define PCI_DEVICE_ID_3COM_3C359 0x3590
+#define PCI_DEVICE_ID_3COM_3C940B 0x80eb
+#define PCI_DEVICE_ID_3COM_3CR990 0x9900
+#define PCI_DEVICE_ID_3COM_3CR990_TX_95 0x9902
+#define PCI_DEVICE_ID_3COM_3CR990_TX_97 0x9903
+#define PCI_DEVICE_ID_3COM_3CR990B 0x9904
+#define PCI_DEVICE_ID_3COM_3CR990_FX 0x9905
+#define PCI_DEVICE_ID_3COM_3CR990SVR95 0x9908
+#define PCI_DEVICE_ID_3COM_3CR990SVR97 0x9909
+#define PCI_DEVICE_ID_3COM_3CR990SVR 0x990a
+
+#define PCI_VENDOR_ID_AL 0x10b9
+#define PCI_DEVICE_ID_AL_M1533 0x1533
+#define PCI_DEVICE_ID_AL_M1535 0x1535
+#define PCI_DEVICE_ID_AL_M1541 0x1541
+#define PCI_DEVICE_ID_AL_M1563 0x1563
+#define PCI_DEVICE_ID_AL_M1621 0x1621
+#define PCI_DEVICE_ID_AL_M1631 0x1631
+#define PCI_DEVICE_ID_AL_M1632 0x1632
+#define PCI_DEVICE_ID_AL_M1641 0x1641
+#define PCI_DEVICE_ID_AL_M1644 0x1644
+#define PCI_DEVICE_ID_AL_M1647 0x1647
+#define PCI_DEVICE_ID_AL_M1651 0x1651
+#define PCI_DEVICE_ID_AL_M1671 0x1671
+#define PCI_DEVICE_ID_AL_M1681 0x1681
+#define PCI_DEVICE_ID_AL_M1683 0x1683
+#define PCI_DEVICE_ID_AL_M1689 0x1689
+#define PCI_DEVICE_ID_AL_M5219 0x5219
+#define PCI_DEVICE_ID_AL_M5228 0x5228
+#define PCI_DEVICE_ID_AL_M5229 0x5229
+#define PCI_DEVICE_ID_AL_M5451 0x5451
+#define PCI_DEVICE_ID_AL_M7101 0x7101
+
+#define PCI_VENDOR_ID_NEOMAGIC 0x10c8
+#define PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO 0x8005
+#define PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO 0x8006
+#define PCI_DEVICE_ID_NEOMAGIC_NM256XL_PLUS_AUDIO 0x8016
+
+#define PCI_VENDOR_ID_TCONRAD 0x10da
+#define PCI_DEVICE_ID_TCONRAD_TOKENRING 0x0508
+
+#define PCI_VENDOR_ID_NVIDIA 0x10de
+#define PCI_DEVICE_ID_NVIDIA_TNT 0x0020
+#define PCI_DEVICE_ID_NVIDIA_TNT2 0x0028
+#define PCI_DEVICE_ID_NVIDIA_UTNT2 0x0029
+#define PCI_DEVICE_ID_NVIDIA_TNT_UNKNOWN 0x002a
+#define PCI_DEVICE_ID_NVIDIA_VTNT2 0x002C
+#define PCI_DEVICE_ID_NVIDIA_UVTNT2 0x002D
+#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SMBUS 0x0034
+#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_IDE 0x0035
+#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SATA 0x0036
+#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SATA2 0x003e
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6800_ULTRA 0x0040
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6800 0x0041
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6800_LE 0x0042
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6800_GT 0x0045
+#define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_4000 0x004E
+#define PCI_DEVICE_ID_NVIDIA_NFORCE4_SMBUS 0x0052
+#define PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_IDE 0x0053
+#define PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_SATA 0x0054
+#define PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_SATA2 0x0055
+#define PCI_DEVICE_ID_NVIDIA_CK804_AUDIO 0x0059
+#define PCI_DEVICE_ID_NVIDIA_CK804_PCIE 0x005d
+#define PCI_DEVICE_ID_NVIDIA_NFORCE2_SMBUS 0x0064
+#define PCI_DEVICE_ID_NVIDIA_NFORCE2_IDE 0x0065
+#define PCI_DEVICE_ID_NVIDIA_MCP2_MODEM 0x0069
+#define PCI_DEVICE_ID_NVIDIA_MCP2_AUDIO 0x006a
+#define PCI_DEVICE_ID_NVIDIA_NFORCE2S_SMBUS 0x0084
+#define PCI_DEVICE_ID_NVIDIA_NFORCE2S_IDE 0x0085
+#define PCI_DEVICE_ID_NVIDIA_MCP2S_MODEM 0x0089
+#define PCI_DEVICE_ID_NVIDIA_CK8_AUDIO 0x008a
+#define PCI_DEVICE_ID_NVIDIA_NFORCE2S_SATA 0x008e
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_7800_GT 0x0090
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_7800_GTX 0x0091
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_GO_7800 0x0098
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_GO_7800_GTX 0x0099
+#define PCI_DEVICE_ID_NVIDIA_ITNT2 0x00A0
+#define PCI_DEVICE_ID_GEFORCE_6800A 0x00c1
+#define PCI_DEVICE_ID_GEFORCE_6800A_LE 0x00c2
+#define PCI_DEVICE_ID_GEFORCE_GO_6800 0x00c8
+#define PCI_DEVICE_ID_GEFORCE_GO_6800_ULTRA 0x00c9
+#define PCI_DEVICE_ID_QUADRO_FX_GO1400 0x00cc
+#define PCI_DEVICE_ID_QUADRO_FX_1400 0x00ce
+#define PCI_DEVICE_ID_NVIDIA_NFORCE3 0x00d1
+#define PCI_DEVICE_ID_NVIDIA_NFORCE3_SMBUS 0x00d4
+#define PCI_DEVICE_ID_NVIDIA_NFORCE3_IDE 0x00d5
+#define PCI_DEVICE_ID_NVIDIA_MCP3_MODEM 0x00d9
+#define PCI_DEVICE_ID_NVIDIA_MCP3_AUDIO 0x00da
+#define PCI_DEVICE_ID_NVIDIA_NFORCE3S 0x00e1
+#define PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA 0x00e3
+#define PCI_DEVICE_ID_NVIDIA_NFORCE3S_SMBUS 0x00e4
+#define PCI_DEVICE_ID_NVIDIA_NFORCE3S_IDE 0x00e5
+#define PCI_DEVICE_ID_NVIDIA_CK8S_AUDIO 0x00ea
+#define PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA2 0x00ee
+#define PCIE_DEVICE_ID_NVIDIA_GEFORCE_6800_ALT1 0x00f0
+#define PCIE_DEVICE_ID_NVIDIA_GEFORCE_6600_ALT1 0x00f1
+#define PCIE_DEVICE_ID_NVIDIA_GEFORCE_6600_ALT2 0x00f2
+#define PCIE_DEVICE_ID_NVIDIA_GEFORCE_6200_ALT1 0x00f3
+#define PCIE_DEVICE_ID_NVIDIA_GEFORCE_6800_GT 0x00f9
+#define PCIE_DEVICE_ID_NVIDIA_QUADRO_NVS280 0x00fd
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_SDR 0x0100
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_DDR 0x0101
+#define PCI_DEVICE_ID_NVIDIA_QUADRO 0x0103
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE2_MX 0x0110
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE2_MX2 0x0111
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE2_GO 0x0112
+#define PCI_DEVICE_ID_NVIDIA_QUADRO2_MXR 0x0113
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6600_GT 0x0140
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6600 0x0141
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6610_XL 0x0145
+#define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_540 0x014E
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6200 0x014F
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE2_GTS 0x0150
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE2_GTS2 0x0151
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE2_ULTRA 0x0152
+#define PCI_DEVICE_ID_NVIDIA_QUADRO2_PRO 0x0153
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6200_TURBOCACHE 0x0161
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_GO_6200 0x0164
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_GO_6250 0x0166
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_GO_6200_1 0x0167
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_GO_6250_1 0x0168
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_460 0x0170
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_440 0x0171
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_420 0x0172
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_440_SE 0x0173
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_440_GO 0x0174
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_420_GO 0x0175
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_420_GO_M32 0x0176
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_460_GO 0x0177
+#define PCI_DEVICE_ID_NVIDIA_QUADRO4_500XGL 0x0178
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_440_GO_M64 0x0179
+#define PCI_DEVICE_ID_NVIDIA_QUADRO4_200 0x017A
+#define PCI_DEVICE_ID_NVIDIA_QUADRO4_550XGL 0x017B
+#define PCI_DEVICE_ID_NVIDIA_QUADRO4_500_GOGL 0x017C
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_410_GO_M16 0x017D
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_440_8X 0x0181
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_440SE_8X 0x0182
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_420_8X 0x0183
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_4000 0x0185
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_448_GO 0x0186
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_488_GO 0x0187
+#define PCI_DEVICE_ID_NVIDIA_QUADRO4_580_XGL 0x0188
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_MAC 0x0189
+#define PCI_DEVICE_ID_NVIDIA_QUADRO4_280_NVS 0x018A
+#define PCI_DEVICE_ID_NVIDIA_QUADRO4_380_XGL 0x018B
+#define PCI_DEVICE_ID_NVIDIA_IGEFORCE2 0x01a0
+#define PCI_DEVICE_ID_NVIDIA_NFORCE 0x01a4
+#define PCI_DEVICE_ID_NVIDIA_MCP1_AUDIO 0x01b1
+#define PCI_DEVICE_ID_NVIDIA_NFORCE_SMBUS 0x01b4
+#define PCI_DEVICE_ID_NVIDIA_NFORCE_IDE 0x01bc
+#define PCI_DEVICE_ID_NVIDIA_MCP1_MODEM 0x01c1
+#define PCI_DEVICE_ID_NVIDIA_NFORCE2 0x01e0
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE3 0x0200
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE3_1 0x0201
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE3_2 0x0202
+#define PCI_DEVICE_ID_NVIDIA_QUADRO_DDC 0x0203
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6800B 0x0211
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6800B_LE 0x0212
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6800B_GT 0x0215
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4600 0x0250
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4400 0x0251
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4200 0x0253
+#define PCI_DEVICE_ID_NVIDIA_QUADRO4_900XGL 0x0258
+#define PCI_DEVICE_ID_NVIDIA_QUADRO4_750XGL 0x0259
+#define PCI_DEVICE_ID_NVIDIA_QUADRO4_700XGL 0x025B
+#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SMBUS 0x0264
+#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_IDE 0x0265
+#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA 0x0266
+#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA2 0x0267
+#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SMBUS 0x0368
+#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_IDE 0x036E
+#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA 0x037E
+#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA2 0x037F
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4800 0x0280
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4800_8X 0x0281
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4800SE 0x0282
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_4200_GO 0x0286
+#define PCI_DEVICE_ID_NVIDIA_QUADRO4_980_XGL 0x0288
+#define PCI_DEVICE_ID_NVIDIA_QUADRO4_780_XGL 0x0289
+#define PCI_DEVICE_ID_NVIDIA_QUADRO4_700_GOGL 0x028C
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5800_ULTRA 0x0301
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5800 0x0302
+#define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_2000 0x0308
+#define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_1000 0x0309
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5600_ULTRA 0x0311
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5600 0x0312
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5600SE 0x0314
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5600 0x031A
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5650 0x031B
+#define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_GO700 0x031C
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5200 0x0320
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5200_ULTRA 0x0321
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5200_1 0x0322
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5200SE 0x0323
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5200 0x0324
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5250 0x0325
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5500 0x0326
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5100 0x0327
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5250_32 0x0328
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO_5200 0x0329
+#define PCI_DEVICE_ID_NVIDIA_QUADRO_NVS_280_PCI 0x032A
+#define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_500 0x032B
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5300 0x032C
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5100 0x032D
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5900_ULTRA 0x0330
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5900 0x0331
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5900XT 0x0332
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5950_ULTRA 0x0333
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5900ZT 0x0334
+#define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_3000 0x0338
+#define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_700 0x033F
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5700_ULTRA 0x0341
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5700 0x0342
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5700LE 0x0343
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5700VE 0x0344
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5700_1 0x0347
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5700_2 0x0348
+#define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_GO1000 0x034C
+#define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_1100 0x034E
+#define PCI_DEVICE_ID_NVIDIA_MCP55_BRIDGE_V0 0x0360
+#define PCI_DEVICE_ID_NVIDIA_MCP55_BRIDGE_V4 0x0364
+#define PCI_DEVICE_ID_NVIDIA_NVENET_15 0x0373
+#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SATA 0x03E7
+#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SMBUS 0x03EB
+#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_IDE 0x03EC
+#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SATA2 0x03F6
+#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SATA3 0x03F7
+#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP65_SMBUS 0x0446
+#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP65_IDE 0x0448
+#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP67_SMBUS 0x0542
+#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP67_IDE 0x0560
+#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP73_IDE 0x056C
+#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP78S_SMBUS 0x0752
+#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP77_IDE 0x0759
+#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP73_SMBUS 0x07D8
+#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP79_SMBUS 0x0AA2
+#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP89_SATA 0x0D85
+
+#define PCI_VENDOR_ID_IMS 0x10e0
+#define PCI_DEVICE_ID_IMS_TT128 0x9128
+#define PCI_DEVICE_ID_IMS_TT3D 0x9135
+
+#define PCI_VENDOR_ID_AMCC 0x10e8
+
+#define PCI_VENDOR_ID_INTERG 0x10ea
+#define PCI_DEVICE_ID_INTERG_1682 0x1682
+#define PCI_DEVICE_ID_INTERG_2000 0x2000
+#define PCI_DEVICE_ID_INTERG_2010 0x2010
+#define PCI_DEVICE_ID_INTERG_5000 0x5000
+#define PCI_DEVICE_ID_INTERG_5050 0x5050
+
+#define PCI_VENDOR_ID_REALTEK 0x10ec
+#define PCI_DEVICE_ID_REALTEK_8139 0x8139
+
+#define PCI_VENDOR_ID_XILINX 0x10ee
+#define PCI_DEVICE_ID_RME_DIGI96 0x3fc0
+#define PCI_DEVICE_ID_RME_DIGI96_8 0x3fc1
+#define PCI_DEVICE_ID_RME_DIGI96_8_PRO 0x3fc2
+#define PCI_DEVICE_ID_RME_DIGI96_8_PAD_OR_PST 0x3fc3
+#define PCI_DEVICE_ID_XILINX_HAMMERFALL_DSP 0x3fc5
+#define PCI_DEVICE_ID_XILINX_HAMMERFALL_DSP_MADI 0x3fc6
+
+#define PCI_VENDOR_ID_INIT 0x1101
+
+#define PCI_VENDOR_ID_CREATIVE 0x1102 /* duplicate: ECTIVA */
+#define PCI_DEVICE_ID_CREATIVE_EMU10K1 0x0002
+#define PCI_DEVICE_ID_CREATIVE_20K1 0x0005
+#define PCI_DEVICE_ID_CREATIVE_20K2 0x000b
+#define PCI_SUBDEVICE_ID_CREATIVE_SB0760 0x0024
+#define PCI_SUBDEVICE_ID_CREATIVE_SB08801 0x0041
+#define PCI_SUBDEVICE_ID_CREATIVE_SB08802 0x0042
+#define PCI_SUBDEVICE_ID_CREATIVE_SB08803 0x0043
+#define PCI_SUBDEVICE_ID_CREATIVE_SB1270 0x0062
+#define PCI_SUBDEVICE_ID_CREATIVE_HENDRIX 0x6000
+
+#define PCI_VENDOR_ID_ECTIVA 0x1102 /* duplicate: CREATIVE */
+#define PCI_DEVICE_ID_ECTIVA_EV1938 0x8938
+
+#define PCI_VENDOR_ID_TTI 0x1103
+#define PCI_DEVICE_ID_TTI_HPT343 0x0003
+#define PCI_DEVICE_ID_TTI_HPT366 0x0004
+#define PCI_DEVICE_ID_TTI_HPT372 0x0005
+#define PCI_DEVICE_ID_TTI_HPT302 0x0006
+#define PCI_DEVICE_ID_TTI_HPT371 0x0007
+#define PCI_DEVICE_ID_TTI_HPT374 0x0008
+#define PCI_DEVICE_ID_TTI_HPT372N 0x0009 /* apparently a 372N variant? */
+
+#define PCI_VENDOR_ID_VIA 0x1106
+#define PCI_DEVICE_ID_VIA_8763_0 0x0198
+#define PCI_DEVICE_ID_VIA_8380_0 0x0204
+#define PCI_DEVICE_ID_VIA_3238_0 0x0238
+#define PCI_DEVICE_ID_VIA_PT880 0x0258
+#define PCI_DEVICE_ID_VIA_PT880ULTRA 0x0308
+#define PCI_DEVICE_ID_VIA_PX8X0_0 0x0259
+#define PCI_DEVICE_ID_VIA_3269_0 0x0269
+#define PCI_DEVICE_ID_VIA_K8T800PRO_0 0x0282
+#define PCI_DEVICE_ID_VIA_3296_0 0x0296
+#define PCI_DEVICE_ID_VIA_8363_0 0x0305
+#define PCI_DEVICE_ID_VIA_P4M800CE 0x0314
+#define PCI_DEVICE_ID_VIA_P4M890 0x0327
+#define PCI_DEVICE_ID_VIA_VT3324 0x0324
+#define PCI_DEVICE_ID_VIA_VT3336 0x0336
+#define PCI_DEVICE_ID_VIA_VT3351 0x0351
+#define PCI_DEVICE_ID_VIA_VT3364 0x0364
+#define PCI_DEVICE_ID_VIA_8371_0 0x0391
+#define PCI_DEVICE_ID_VIA_6415 0x0415
+#define PCI_DEVICE_ID_VIA_8501_0 0x0501
+#define PCI_DEVICE_ID_VIA_82C561 0x0561
+#define PCI_DEVICE_ID_VIA_82C586_1 0x0571
+#define PCI_DEVICE_ID_VIA_82C576 0x0576
+#define PCI_DEVICE_ID_VIA_82C586_0 0x0586
+#define PCI_DEVICE_ID_VIA_82C596 0x0596
+#define PCI_DEVICE_ID_VIA_82C597_0 0x0597
+#define PCI_DEVICE_ID_VIA_82C598_0 0x0598
+#define PCI_DEVICE_ID_VIA_8601_0 0x0601
+#define PCI_DEVICE_ID_VIA_8605_0 0x0605
+#define PCI_DEVICE_ID_VIA_82C686 0x0686
+#define PCI_DEVICE_ID_VIA_82C691_0 0x0691
+#define PCI_DEVICE_ID_VIA_82C576_1 0x1571
+#define PCI_DEVICE_ID_VIA_82C586_2 0x3038
+#define PCI_DEVICE_ID_VIA_82C586_3 0x3040
+#define PCI_DEVICE_ID_VIA_82C596_3 0x3050
+#define PCI_DEVICE_ID_VIA_82C596B_3 0x3051
+#define PCI_DEVICE_ID_VIA_82C686_4 0x3057
+#define PCI_DEVICE_ID_VIA_82C686_5 0x3058
+#define PCI_DEVICE_ID_VIA_8233_5 0x3059
+#define PCI_DEVICE_ID_VIA_8233_0 0x3074
+#define PCI_DEVICE_ID_VIA_8633_0 0x3091
+#define PCI_DEVICE_ID_VIA_8367_0 0x3099
+#define PCI_DEVICE_ID_VIA_8653_0 0x3101
+#define PCI_DEVICE_ID_VIA_8622 0x3102
+#define PCI_DEVICE_ID_VIA_8235_USB_2 0x3104
+#define PCI_DEVICE_ID_VIA_8233C_0 0x3109
+#define PCI_DEVICE_ID_VIA_8361 0x3112
+#define PCI_DEVICE_ID_VIA_XM266 0x3116
+#define PCI_DEVICE_ID_VIA_612X 0x3119
+#define PCI_DEVICE_ID_VIA_862X_0 0x3123
+#define PCI_DEVICE_ID_VIA_8753_0 0x3128
+#define PCI_DEVICE_ID_VIA_8233A 0x3147
+#define PCI_DEVICE_ID_VIA_8703_51_0 0x3148
+#define PCI_DEVICE_ID_VIA_8237_SATA 0x3149
+#define PCI_DEVICE_ID_VIA_XN266 0x3156
+#define PCI_DEVICE_ID_VIA_6410 0x3164
+#define PCI_DEVICE_ID_VIA_8754C_0 0x3168
+#define PCI_DEVICE_ID_VIA_8235 0x3177
+#define PCI_DEVICE_ID_VIA_8385_0 0x3188
+#define PCI_DEVICE_ID_VIA_8377_0 0x3189
+#define PCI_DEVICE_ID_VIA_8378_0 0x3205
+#define PCI_DEVICE_ID_VIA_8783_0 0x3208
+#define PCI_DEVICE_ID_VIA_8237 0x3227
+#define PCI_DEVICE_ID_VIA_8251 0x3287
+#define PCI_DEVICE_ID_VIA_8261 0x3402
+#define PCI_DEVICE_ID_VIA_8237A 0x3337
+#define PCI_DEVICE_ID_VIA_8237S 0x3372
+#define PCI_DEVICE_ID_VIA_SATA_EIDE 0x5324
+#define PCI_DEVICE_ID_VIA_8231 0x8231
+#define PCI_DEVICE_ID_VIA_8231_4 0x8235
+#define PCI_DEVICE_ID_VIA_8365_1 0x8305
+#define PCI_DEVICE_ID_VIA_CX700 0x8324
+#define PCI_DEVICE_ID_VIA_CX700_IDE 0x0581
+#define PCI_DEVICE_ID_VIA_VX800 0x8353
+#define PCI_DEVICE_ID_VIA_VX855 0x8409
+#define PCI_DEVICE_ID_VIA_VX900 0x8410
+#define PCI_DEVICE_ID_VIA_8371_1 0x8391
+#define PCI_DEVICE_ID_VIA_82C598_1 0x8598
+#define PCI_DEVICE_ID_VIA_838X_1 0xB188
+#define PCI_DEVICE_ID_VIA_83_87XX_1 0xB198
+#define PCI_DEVICE_ID_VIA_VX855_IDE 0xC409
+#define PCI_DEVICE_ID_VIA_ANON 0xFFFF
+
+#define PCI_VENDOR_ID_SIEMENS 0x110A
+#define PCI_DEVICE_ID_SIEMENS_DSCC4 0x2102
+
+#define PCI_VENDOR_ID_VORTEX 0x1119
+#define PCI_DEVICE_ID_VORTEX_GDT60x0 0x0000
+#define PCI_DEVICE_ID_VORTEX_GDT6000B 0x0001
+#define PCI_DEVICE_ID_VORTEX_GDT6x10 0x0002
+#define PCI_DEVICE_ID_VORTEX_GDT6x20 0x0003
+#define PCI_DEVICE_ID_VORTEX_GDT6530 0x0004
+#define PCI_DEVICE_ID_VORTEX_GDT6550 0x0005
+#define PCI_DEVICE_ID_VORTEX_GDT6x17 0x0006
+#define PCI_DEVICE_ID_VORTEX_GDT6x27 0x0007
+#define PCI_DEVICE_ID_VORTEX_GDT6537 0x0008
+#define PCI_DEVICE_ID_VORTEX_GDT6557 0x0009
+#define PCI_DEVICE_ID_VORTEX_GDT6x15 0x000a
+#define PCI_DEVICE_ID_VORTEX_GDT6x25 0x000b
+#define PCI_DEVICE_ID_VORTEX_GDT6535 0x000c
+#define PCI_DEVICE_ID_VORTEX_GDT6555 0x000d
+#define PCI_DEVICE_ID_VORTEX_GDT6x17RP 0x0100
+#define PCI_DEVICE_ID_VORTEX_GDT6x27RP 0x0101
+#define PCI_DEVICE_ID_VORTEX_GDT6537RP 0x0102
+#define PCI_DEVICE_ID_VORTEX_GDT6557RP 0x0103
+#define PCI_DEVICE_ID_VORTEX_GDT6x11RP 0x0104
+#define PCI_DEVICE_ID_VORTEX_GDT6x21RP 0x0105
+
+#define PCI_VENDOR_ID_EF 0x111a
+#define PCI_DEVICE_ID_EF_ATM_FPGA 0x0000
+#define PCI_DEVICE_ID_EF_ATM_ASIC 0x0002
+#define PCI_DEVICE_ID_EF_ATM_LANAI2 0x0003
+#define PCI_DEVICE_ID_EF_ATM_LANAIHB 0x0005
+
+#define PCI_VENDOR_ID_IDT 0x111d
+#define PCI_DEVICE_ID_IDT_IDT77201 0x0001
+
+#define PCI_VENDOR_ID_FORE 0x1127
+#define PCI_DEVICE_ID_FORE_PCA200E 0x0300
+
+#define PCI_VENDOR_ID_PHILIPS 0x1131
+#define PCI_DEVICE_ID_PHILIPS_SAA7146 0x7146
+#define PCI_DEVICE_ID_PHILIPS_SAA9730 0x9730
+
+#define PCI_VENDOR_ID_EICON 0x1133
+#define PCI_DEVICE_ID_EICON_DIVA20 0xe002
+#define PCI_DEVICE_ID_EICON_DIVA20_U 0xe004
+#define PCI_DEVICE_ID_EICON_DIVA201 0xe005
+#define PCI_DEVICE_ID_EICON_DIVA202 0xe00b
+#define PCI_DEVICE_ID_EICON_MAESTRA 0xe010
+#define PCI_DEVICE_ID_EICON_MAESTRAQ 0xe012
+#define PCI_DEVICE_ID_EICON_MAESTRAQ_U 0xe013
+#define PCI_DEVICE_ID_EICON_MAESTRAP 0xe014
+
+#define PCI_VENDOR_ID_CISCO 0x1137
+
+#define PCI_VENDOR_ID_ZIATECH 0x1138
+#define PCI_DEVICE_ID_ZIATECH_5550_HC 0x5550
+
+
+#define PCI_VENDOR_ID_SYSKONNECT 0x1148
+#define PCI_DEVICE_ID_SYSKONNECT_TR 0x4200
+#define PCI_DEVICE_ID_SYSKONNECT_GE 0x4300
+#define PCI_DEVICE_ID_SYSKONNECT_YU 0x4320
+#define PCI_DEVICE_ID_SYSKONNECT_9DXX 0x4400
+#define PCI_DEVICE_ID_SYSKONNECT_9MXX 0x4500
+
+#define PCI_VENDOR_ID_DIGI 0x114f
+#define PCI_DEVICE_ID_DIGI_DF_M_IOM2_E 0x0070
+#define PCI_DEVICE_ID_DIGI_DF_M_E 0x0071
+#define PCI_DEVICE_ID_DIGI_DF_M_IOM2_A 0x0072
+#define PCI_DEVICE_ID_DIGI_DF_M_A 0x0073
+#define PCI_DEVICE_ID_DIGI_NEO_8 0x00B1
+#define PCI_DEVICE_ID_NEO_2DB9 0x00C8
+#define PCI_DEVICE_ID_NEO_2DB9PRI 0x00C9
+#define PCI_DEVICE_ID_NEO_2RJ45 0x00CA
+#define PCI_DEVICE_ID_NEO_2RJ45PRI 0x00CB
+#define PCIE_DEVICE_ID_NEO_4_IBM 0x00F4
+
+#define PCI_VENDOR_ID_XIRCOM 0x115d
+#define PCI_DEVICE_ID_XIRCOM_RBM56G 0x0101
+#define PCI_DEVICE_ID_XIRCOM_X3201_MDM 0x0103
+
+#define PCI_VENDOR_ID_SERVERWORKS 0x1166
+#define PCI_DEVICE_ID_SERVERWORKS_HE 0x0008
+#define PCI_DEVICE_ID_SERVERWORKS_LE 0x0009
+#define PCI_DEVICE_ID_SERVERWORKS_GCNB_LE 0x0017
+#define PCI_DEVICE_ID_SERVERWORKS_HT1000_PXB 0x0036
+#define PCI_DEVICE_ID_SERVERWORKS_EPB 0x0103
+#define PCI_DEVICE_ID_SERVERWORKS_HT2000_PCIE 0x0132
+#define PCI_DEVICE_ID_SERVERWORKS_OSB4 0x0200
+#define PCI_DEVICE_ID_SERVERWORKS_CSB5 0x0201
+#define PCI_DEVICE_ID_SERVERWORKS_CSB6 0x0203
+#define PCI_DEVICE_ID_SERVERWORKS_HT1000SB 0x0205
+#define PCI_DEVICE_ID_SERVERWORKS_OSB4IDE 0x0211
+#define PCI_DEVICE_ID_SERVERWORKS_CSB5IDE 0x0212
+#define PCI_DEVICE_ID_SERVERWORKS_CSB6IDE 0x0213
+#define PCI_DEVICE_ID_SERVERWORKS_HT1000IDE 0x0214
+#define PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2 0x0217
+#define PCI_DEVICE_ID_SERVERWORKS_CSB6LPC 0x0227
+#define PCI_DEVICE_ID_SERVERWORKS_HT1100LD 0x0408
+
+#define PCI_VENDOR_ID_SBE 0x1176
+#define PCI_DEVICE_ID_SBE_WANXL100 0x0301
+#define PCI_DEVICE_ID_SBE_WANXL200 0x0302
+#define PCI_DEVICE_ID_SBE_WANXL400 0x0104
+#define PCI_SUBDEVICE_ID_SBE_T3E3 0x0009
+#define PCI_SUBDEVICE_ID_SBE_2T3E3_P0 0x0901
+#define PCI_SUBDEVICE_ID_SBE_2T3E3_P1 0x0902
+
+#define PCI_VENDOR_ID_TOSHIBA 0x1179
+#define PCI_DEVICE_ID_TOSHIBA_PICCOLO_1 0x0101
+#define PCI_DEVICE_ID_TOSHIBA_PICCOLO_2 0x0102
+#define PCI_DEVICE_ID_TOSHIBA_PICCOLO_3 0x0103
+#define PCI_DEVICE_ID_TOSHIBA_PICCOLO_5 0x0105
+#define PCI_DEVICE_ID_TOSHIBA_TOPIC95 0x060a
+#define PCI_DEVICE_ID_TOSHIBA_TOPIC97 0x060f
+#define PCI_DEVICE_ID_TOSHIBA_TOPIC100 0x0617
+
+#define PCI_VENDOR_ID_TOSHIBA_2 0x102f
+#define PCI_DEVICE_ID_TOSHIBA_TC35815CF 0x0030
+#define PCI_DEVICE_ID_TOSHIBA_TC35815_NWU 0x0031
+#define PCI_DEVICE_ID_TOSHIBA_TC35815_TX4939 0x0032
+#define PCI_DEVICE_ID_TOSHIBA_TC86C001_IDE 0x0105
+#define PCI_DEVICE_ID_TOSHIBA_TC86C001_MISC 0x0108
+#define PCI_DEVICE_ID_TOSHIBA_SPIDER_NET 0x01b3
+
+#define PCI_VENDOR_ID_ATTO 0x117c
+
+#define PCI_VENDOR_ID_RICOH 0x1180
+#define PCI_DEVICE_ID_RICOH_RL5C465 0x0465
+#define PCI_DEVICE_ID_RICOH_RL5C466 0x0466
+#define PCI_DEVICE_ID_RICOH_RL5C475 0x0475
+#define PCI_DEVICE_ID_RICOH_RL5C476 0x0476
+#define PCI_DEVICE_ID_RICOH_RL5C478 0x0478
+#define PCI_DEVICE_ID_RICOH_R5C822 0x0822
+#define PCI_DEVICE_ID_RICOH_R5CE822 0xe822
+#define PCI_DEVICE_ID_RICOH_R5CE823 0xe823
+#define PCI_DEVICE_ID_RICOH_R5C832 0x0832
+#define PCI_DEVICE_ID_RICOH_R5C843 0x0843
+
+#define PCI_VENDOR_ID_DLINK 0x1186
+#define PCI_DEVICE_ID_DLINK_DGE510T 0x4c00
+
+#define PCI_VENDOR_ID_ARTOP 0x1191
+#define PCI_DEVICE_ID_ARTOP_ATP850UF 0x0005
+#define PCI_DEVICE_ID_ARTOP_ATP860 0x0006
+#define PCI_DEVICE_ID_ARTOP_ATP860R 0x0007
+#define PCI_DEVICE_ID_ARTOP_ATP865 0x0008
+#define PCI_DEVICE_ID_ARTOP_ATP865R 0x0009
+#define PCI_DEVICE_ID_ARTOP_ATP867A 0x000A
+#define PCI_DEVICE_ID_ARTOP_ATP867B 0x000B
+#define PCI_DEVICE_ID_ARTOP_AEC7610 0x8002
+#define PCI_DEVICE_ID_ARTOP_AEC7612UW 0x8010
+#define PCI_DEVICE_ID_ARTOP_AEC7612U 0x8020
+#define PCI_DEVICE_ID_ARTOP_AEC7612S 0x8030
+#define PCI_DEVICE_ID_ARTOP_AEC7612D 0x8040
+#define PCI_DEVICE_ID_ARTOP_AEC7612SUW 0x8050
+#define PCI_DEVICE_ID_ARTOP_8060 0x8060
+
+#define PCI_VENDOR_ID_ZEITNET 0x1193
+#define PCI_DEVICE_ID_ZEITNET_1221 0x0001
+#define PCI_DEVICE_ID_ZEITNET_1225 0x0002
+
+#define PCI_VENDOR_ID_FUJITSU_ME 0x119e
+#define PCI_DEVICE_ID_FUJITSU_FS155 0x0001
+#define PCI_DEVICE_ID_FUJITSU_FS50 0x0003
+
+#define PCI_SUBVENDOR_ID_KEYSPAN 0x11a9
+#define PCI_SUBDEVICE_ID_KEYSPAN_SX2 0x5334
+
+#define PCI_VENDOR_ID_MARVELL 0x11ab
+#define PCI_VENDOR_ID_MARVELL_EXT 0x1b4b
+#define PCI_DEVICE_ID_MARVELL_GT64111 0x4146
+#define PCI_DEVICE_ID_MARVELL_GT64260 0x6430
+#define PCI_DEVICE_ID_MARVELL_MV64360 0x6460
+#define PCI_DEVICE_ID_MARVELL_MV64460 0x6480
+#define PCI_DEVICE_ID_MARVELL_88ALP01_NAND 0x4100
+#define PCI_DEVICE_ID_MARVELL_88ALP01_SD 0x4101
+#define PCI_DEVICE_ID_MARVELL_88ALP01_CCIC 0x4102
+
+#define PCI_VENDOR_ID_V3 0x11b0
+#define PCI_DEVICE_ID_V3_V960 0x0001
+#define PCI_DEVICE_ID_V3_V351 0x0002
+
+#define PCI_VENDOR_ID_ATT 0x11c1
+#define PCI_DEVICE_ID_ATT_VENUS_MODEM 0x480
+
+#define PCI_VENDOR_ID_SPECIALIX 0x11cb
+#define PCI_SUBDEVICE_ID_SPECIALIX_SPEED4 0xa004
+
+#define PCI_VENDOR_ID_ANALOG_DEVICES 0x11d4
+#define PCI_DEVICE_ID_AD1889JS 0x1889
+
+#define PCI_DEVICE_ID_SEGA_BBA 0x1234
+
+#define PCI_VENDOR_ID_ZORAN 0x11de
+#define PCI_DEVICE_ID_ZORAN_36057 0x6057
+#define PCI_DEVICE_ID_ZORAN_36120 0x6120
+
+#define PCI_VENDOR_ID_COMPEX 0x11f6
+#define PCI_DEVICE_ID_COMPEX_ENET100VG4 0x0112
+
+#define PCI_VENDOR_ID_PMC_Sierra 0x11f8
+
+#define PCI_VENDOR_ID_RP 0x11fe
+#define PCI_DEVICE_ID_RP32INTF 0x0001
+#define PCI_DEVICE_ID_RP8INTF 0x0002
+#define PCI_DEVICE_ID_RP16INTF 0x0003
+#define PCI_DEVICE_ID_RP4QUAD 0x0004
+#define PCI_DEVICE_ID_RP8OCTA 0x0005
+#define PCI_DEVICE_ID_RP8J 0x0006
+#define PCI_DEVICE_ID_RP4J 0x0007
+#define PCI_DEVICE_ID_RP8SNI 0x0008
+#define PCI_DEVICE_ID_RP16SNI 0x0009
+#define PCI_DEVICE_ID_RPP4 0x000A
+#define PCI_DEVICE_ID_RPP8 0x000B
+#define PCI_DEVICE_ID_RP4M 0x000D
+#define PCI_DEVICE_ID_RP2_232 0x000E
+#define PCI_DEVICE_ID_RP2_422 0x000F
+#define PCI_DEVICE_ID_URP32INTF 0x0801
+#define PCI_DEVICE_ID_URP8INTF 0x0802
+#define PCI_DEVICE_ID_URP16INTF 0x0803
+#define PCI_DEVICE_ID_URP8OCTA 0x0805
+#define PCI_DEVICE_ID_UPCI_RM3_8PORT 0x080C
+#define PCI_DEVICE_ID_UPCI_RM3_4PORT 0x080D
+#define PCI_DEVICE_ID_CRP16INTF 0x0903
+
+#define PCI_VENDOR_ID_CYCLADES 0x120e
+#define PCI_DEVICE_ID_CYCLOM_Y_Lo 0x0100
+#define PCI_DEVICE_ID_CYCLOM_Y_Hi 0x0101
+#define PCI_DEVICE_ID_CYCLOM_4Y_Lo 0x0102
+#define PCI_DEVICE_ID_CYCLOM_4Y_Hi 0x0103
+#define PCI_DEVICE_ID_CYCLOM_8Y_Lo 0x0104
+#define PCI_DEVICE_ID_CYCLOM_8Y_Hi 0x0105
+#define PCI_DEVICE_ID_CYCLOM_Z_Lo 0x0200
+#define PCI_DEVICE_ID_CYCLOM_Z_Hi 0x0201
+#define PCI_DEVICE_ID_PC300_RX_2 0x0300
+#define PCI_DEVICE_ID_PC300_RX_1 0x0301
+#define PCI_DEVICE_ID_PC300_TE_2 0x0310
+#define PCI_DEVICE_ID_PC300_TE_1 0x0311
+#define PCI_DEVICE_ID_PC300_TE_M_2 0x0320
+#define PCI_DEVICE_ID_PC300_TE_M_1 0x0321
+
+#define PCI_VENDOR_ID_ESSENTIAL 0x120f
+#define PCI_DEVICE_ID_ESSENTIAL_ROADRUNNER 0x0001
+
+#define PCI_VENDOR_ID_O2 0x1217
+#define PCI_DEVICE_ID_O2_6729 0x6729
+#define PCI_DEVICE_ID_O2_6730 0x673a
+#define PCI_DEVICE_ID_O2_6832 0x6832
+#define PCI_DEVICE_ID_O2_6836 0x6836
+#define PCI_DEVICE_ID_O2_6812 0x6872
+#define PCI_DEVICE_ID_O2_6933 0x6933
+#define PCI_DEVICE_ID_O2_8120 0x8120
+#define PCI_DEVICE_ID_O2_8220 0x8220
+#define PCI_DEVICE_ID_O2_8221 0x8221
+#define PCI_DEVICE_ID_O2_8320 0x8320
+#define PCI_DEVICE_ID_O2_8321 0x8321
+
+#define PCI_VENDOR_ID_3DFX 0x121a
+#define PCI_DEVICE_ID_3DFX_VOODOO 0x0001
+#define PCI_DEVICE_ID_3DFX_VOODOO2 0x0002
+#define PCI_DEVICE_ID_3DFX_BANSHEE 0x0003
+#define PCI_DEVICE_ID_3DFX_VOODOO3 0x0005
+#define PCI_DEVICE_ID_3DFX_VOODOO5 0x0009
+
+#define PCI_VENDOR_ID_AVM 0x1244
+#define PCI_DEVICE_ID_AVM_B1 0x0700
+#define PCI_DEVICE_ID_AVM_C4 0x0800
+#define PCI_DEVICE_ID_AVM_A1 0x0a00
+#define PCI_DEVICE_ID_AVM_A1_V2 0x0e00
+#define PCI_DEVICE_ID_AVM_C2 0x1100
+#define PCI_DEVICE_ID_AVM_T1 0x1200
+
+#define PCI_VENDOR_ID_STALLION 0x124d
+
+/* Allied Telesyn */
+#define PCI_VENDOR_ID_AT 0x1259
+#define PCI_SUBDEVICE_ID_AT_2700FX 0x2701
+#define PCI_SUBDEVICE_ID_AT_2701FX 0x2703
+
+#define PCI_VENDOR_ID_ESS 0x125d
+#define PCI_DEVICE_ID_ESS_ESS1968 0x1968
+#define PCI_DEVICE_ID_ESS_ESS1978 0x1978
+#define PCI_DEVICE_ID_ESS_ALLEGRO_1 0x1988
+#define PCI_DEVICE_ID_ESS_ALLEGRO 0x1989
+#define PCI_DEVICE_ID_ESS_CANYON3D_2LE 0x1990
+#define PCI_DEVICE_ID_ESS_CANYON3D_2 0x1992
+#define PCI_DEVICE_ID_ESS_MAESTRO3 0x1998
+#define PCI_DEVICE_ID_ESS_MAESTRO3_1 0x1999
+#define PCI_DEVICE_ID_ESS_MAESTRO3_HW 0x199a
+#define PCI_DEVICE_ID_ESS_MAESTRO3_2 0x199b
+
+#define PCI_VENDOR_ID_SATSAGEM 0x1267
+#define PCI_DEVICE_ID_SATSAGEM_NICCY 0x1016
+
+#define PCI_VENDOR_ID_ENSONIQ 0x1274
+#define PCI_DEVICE_ID_ENSONIQ_CT5880 0x5880
+#define PCI_DEVICE_ID_ENSONIQ_ES1370 0x5000
+#define PCI_DEVICE_ID_ENSONIQ_ES1371 0x1371
+
+#define PCI_VENDOR_ID_TRANSMETA 0x1279
+#define PCI_DEVICE_ID_EFFICEON 0x0060
+
+#define PCI_VENDOR_ID_ROCKWELL 0x127A
+
+#define PCI_VENDOR_ID_ITE 0x1283
+#define PCI_DEVICE_ID_ITE_8172 0x8172
+#define PCI_DEVICE_ID_ITE_8211 0x8211
+#define PCI_DEVICE_ID_ITE_8212 0x8212
+#define PCI_DEVICE_ID_ITE_8213 0x8213
+#define PCI_DEVICE_ID_ITE_8152 0x8152
+#define PCI_DEVICE_ID_ITE_8872 0x8872
+#define PCI_DEVICE_ID_ITE_IT8330G_0 0xe886
+
+/* formerly Platform Tech */
+#define PCI_DEVICE_ID_ESS_ESS0100 0x0100
+
+#define PCI_VENDOR_ID_ALTEON 0x12ae
+
+#define PCI_SUBVENDOR_ID_CONNECT_TECH 0x12c4
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_232 0x0001
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_232 0x0002
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_232 0x0003
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485 0x0004
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_4_4 0x0005
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485 0x0006
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485_2_2 0x0007
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_485 0x0008
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_2_6 0x0009
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH081101V1 0x000A
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH041101V1 0x000B
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_20MHZ 0x000C
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_PTM 0x000D
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_NT960PCI 0x0100
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_TITAN_2 0x0201
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_TITAN_4 0x0202
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_232 0x0300
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_232 0x0301
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_232 0x0302
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_1_1 0x0310
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_2 0x0311
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_4 0x0312
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2 0x0320
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4 0x0321
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8 0x0322
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_485 0x0330
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_485 0x0331
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_485 0x0332
+
+#define PCI_VENDOR_ID_NVIDIA_SGS 0x12d2
+#define PCI_DEVICE_ID_NVIDIA_SGS_RIVA128 0x0018
+
+#define PCI_SUBVENDOR_ID_CHASE_PCIFAST 0x12E0
+#define PCI_SUBDEVICE_ID_CHASE_PCIFAST4 0x0031
+#define PCI_SUBDEVICE_ID_CHASE_PCIFAST8 0x0021
+#define PCI_SUBDEVICE_ID_CHASE_PCIFAST16 0x0011
+#define PCI_SUBDEVICE_ID_CHASE_PCIFAST16FMC 0x0041
+#define PCI_SUBVENDOR_ID_CHASE_PCIRAS 0x124D
+#define PCI_SUBDEVICE_ID_CHASE_PCIRAS4 0xF001
+#define PCI_SUBDEVICE_ID_CHASE_PCIRAS8 0xF010
+
+#define PCI_VENDOR_ID_AUREAL 0x12eb
+#define PCI_DEVICE_ID_AUREAL_VORTEX_1 0x0001
+#define PCI_DEVICE_ID_AUREAL_VORTEX_2 0x0002
+#define PCI_DEVICE_ID_AUREAL_ADVANTAGE 0x0003
+
+#define PCI_VENDOR_ID_ELECTRONICDESIGNGMBH 0x12f8
+#define PCI_DEVICE_ID_LML_33R10 0x8a02
+
+#define PCI_VENDOR_ID_ESDGMBH 0x12fe
+#define PCI_DEVICE_ID_ESDGMBH_CPCIASIO4 0x0111
+
+#define PCI_VENDOR_ID_CB 0x1307 /* Measurement Computing */
+
+#define PCI_VENDOR_ID_SIIG 0x131f
+#define PCI_SUBVENDOR_ID_SIIG 0x131f
+#define PCI_DEVICE_ID_SIIG_1S_10x_550 0x1000
+#define PCI_DEVICE_ID_SIIG_1S_10x_650 0x1001
+#define PCI_DEVICE_ID_SIIG_1S_10x_850 0x1002
+#define PCI_DEVICE_ID_SIIG_1S1P_10x_550 0x1010
+#define PCI_DEVICE_ID_SIIG_1S1P_10x_650 0x1011
+#define PCI_DEVICE_ID_SIIG_1S1P_10x_850 0x1012
+#define PCI_DEVICE_ID_SIIG_1P_10x 0x1020
+#define PCI_DEVICE_ID_SIIG_2P_10x 0x1021
+#define PCI_DEVICE_ID_SIIG_2S_10x_550 0x1030
+#define PCI_DEVICE_ID_SIIG_2S_10x_650 0x1031
+#define PCI_DEVICE_ID_SIIG_2S_10x_850 0x1032
+#define PCI_DEVICE_ID_SIIG_2S1P_10x_550 0x1034
+#define PCI_DEVICE_ID_SIIG_2S1P_10x_650 0x1035
+#define PCI_DEVICE_ID_SIIG_2S1P_10x_850 0x1036
+#define PCI_DEVICE_ID_SIIG_4S_10x_550 0x1050
+#define PCI_DEVICE_ID_SIIG_4S_10x_650 0x1051
+#define PCI_DEVICE_ID_SIIG_4S_10x_850 0x1052
+#define PCI_DEVICE_ID_SIIG_1S_20x_550 0x2000
+#define PCI_DEVICE_ID_SIIG_1S_20x_650 0x2001
+#define PCI_DEVICE_ID_SIIG_1S_20x_850 0x2002
+#define PCI_DEVICE_ID_SIIG_1P_20x 0x2020
+#define PCI_DEVICE_ID_SIIG_2P_20x 0x2021
+#define PCI_DEVICE_ID_SIIG_2S_20x_550 0x2030
+#define PCI_DEVICE_ID_SIIG_2S_20x_650 0x2031
+#define PCI_DEVICE_ID_SIIG_2S_20x_850 0x2032
+#define PCI_DEVICE_ID_SIIG_2P1S_20x_550 0x2040
+#define PCI_DEVICE_ID_SIIG_2P1S_20x_650 0x2041
+#define PCI_DEVICE_ID_SIIG_2P1S_20x_850 0x2042
+#define PCI_DEVICE_ID_SIIG_1S1P_20x_550 0x2010
+#define PCI_DEVICE_ID_SIIG_1S1P_20x_650 0x2011
+#define PCI_DEVICE_ID_SIIG_1S1P_20x_850 0x2012
+#define PCI_DEVICE_ID_SIIG_4S_20x_550 0x2050
+#define PCI_DEVICE_ID_SIIG_4S_20x_650 0x2051
+#define PCI_DEVICE_ID_SIIG_4S_20x_850 0x2052
+#define PCI_DEVICE_ID_SIIG_2S1P_20x_550 0x2060
+#define PCI_DEVICE_ID_SIIG_2S1P_20x_650 0x2061
+#define PCI_DEVICE_ID_SIIG_2S1P_20x_850 0x2062
+#define PCI_DEVICE_ID_SIIG_8S_20x_550 0x2080
+#define PCI_DEVICE_ID_SIIG_8S_20x_650 0x2081
+#define PCI_DEVICE_ID_SIIG_8S_20x_850 0x2082
+#define PCI_SUBDEVICE_ID_SIIG_QUARTET_SERIAL 0x2050
+
+#define PCI_VENDOR_ID_RADISYS 0x1331
+
+#define PCI_VENDOR_ID_MICRO_MEMORY 0x1332
+#define PCI_DEVICE_ID_MICRO_MEMORY_5415CN 0x5415
+#define PCI_DEVICE_ID_MICRO_MEMORY_5425CN 0x5425
+#define PCI_DEVICE_ID_MICRO_MEMORY_6155 0x6155
+
+#define PCI_VENDOR_ID_DOMEX 0x134a
+#define PCI_DEVICE_ID_DOMEX_DMX3191D 0x0001
+
+#define PCI_VENDOR_ID_INTASHIELD 0x135a
+#define PCI_DEVICE_ID_INTASHIELD_IS200 0x0d80
+#define PCI_DEVICE_ID_INTASHIELD_IS400 0x0dc0
+
+#define PCI_VENDOR_ID_QUATECH 0x135C
+#define PCI_DEVICE_ID_QUATECH_QSC100 0x0010
+#define PCI_DEVICE_ID_QUATECH_DSC100 0x0020
+#define PCI_DEVICE_ID_QUATECH_DSC200 0x0030
+#define PCI_DEVICE_ID_QUATECH_QSC200 0x0040
+#define PCI_DEVICE_ID_QUATECH_ESC100D 0x0050
+#define PCI_DEVICE_ID_QUATECH_ESC100M 0x0060
+#define PCI_DEVICE_ID_QUATECH_QSCP100 0x0120
+#define PCI_DEVICE_ID_QUATECH_DSCP100 0x0130
+#define PCI_DEVICE_ID_QUATECH_QSCP200 0x0140
+#define PCI_DEVICE_ID_QUATECH_DSCP200 0x0150
+#define PCI_DEVICE_ID_QUATECH_QSCLP100 0x0170
+#define PCI_DEVICE_ID_QUATECH_DSCLP100 0x0180
+#define PCI_DEVICE_ID_QUATECH_DSC100E 0x0181
+#define PCI_DEVICE_ID_QUATECH_SSCLP100 0x0190
+#define PCI_DEVICE_ID_QUATECH_QSCLP200 0x01A0
+#define PCI_DEVICE_ID_QUATECH_DSCLP200 0x01B0
+#define PCI_DEVICE_ID_QUATECH_DSC200E 0x01B1
+#define PCI_DEVICE_ID_QUATECH_SSCLP200 0x01C0
+#define PCI_DEVICE_ID_QUATECH_ESCLP100 0x01E0
+#define PCI_DEVICE_ID_QUATECH_SPPXP_100 0x0278
+
+#define PCI_VENDOR_ID_SEALEVEL 0x135e
+#define PCI_DEVICE_ID_SEALEVEL_U530 0x7101
+#define PCI_DEVICE_ID_SEALEVEL_UCOMM2 0x7201
+#define PCI_DEVICE_ID_SEALEVEL_UCOMM422 0x7402
+#define PCI_DEVICE_ID_SEALEVEL_UCOMM232 0x7202
+#define PCI_DEVICE_ID_SEALEVEL_COMM4 0x7401
+#define PCI_DEVICE_ID_SEALEVEL_COMM8 0x7801
+#define PCI_DEVICE_ID_SEALEVEL_7803 0x7803
+#define PCI_DEVICE_ID_SEALEVEL_UCOMM8 0x7804
+
+#define PCI_VENDOR_ID_HYPERCOPE 0x1365
+#define PCI_DEVICE_ID_HYPERCOPE_PLX 0x9050
+#define PCI_SUBDEVICE_ID_HYPERCOPE_OLD_ERGO 0x0104
+#define PCI_SUBDEVICE_ID_HYPERCOPE_ERGO 0x0106
+#define PCI_SUBDEVICE_ID_HYPERCOPE_METRO 0x0107
+#define PCI_SUBDEVICE_ID_HYPERCOPE_CHAMP2 0x0108
+
+#define PCI_VENDOR_ID_DIGIGRAM 0x1369
+#define PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ES_SERIAL_SUBSYSTEM 0xc001
+#define PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ES_CAE_SERIAL_SUBSYSTEM 0xc002
+
+#define PCI_VENDOR_ID_KAWASAKI 0x136b
+#define PCI_DEVICE_ID_MCHIP_KL5A72002 0xff01
+
+#define PCI_VENDOR_ID_CNET 0x1371
+#define PCI_DEVICE_ID_CNET_GIGACARD 0x434e
+
+#define PCI_VENDOR_ID_LMC 0x1376
+#define PCI_DEVICE_ID_LMC_HSSI 0x0003
+#define PCI_DEVICE_ID_LMC_DS3 0x0004
+#define PCI_DEVICE_ID_LMC_SSI 0x0005
+#define PCI_DEVICE_ID_LMC_T1 0x0006
+
+#define PCI_VENDOR_ID_NETGEAR 0x1385
+#define PCI_DEVICE_ID_NETGEAR_GA620 0x620a
+
+#define PCI_VENDOR_ID_APPLICOM 0x1389
+#define PCI_DEVICE_ID_APPLICOM_PCIGENERIC 0x0001
+#define PCI_DEVICE_ID_APPLICOM_PCI2000IBS_CAN 0x0002
+#define PCI_DEVICE_ID_APPLICOM_PCI2000PFB 0x0003
+
+#define PCI_VENDOR_ID_MOXA 0x1393
+#define PCI_DEVICE_ID_MOXA_RC7000 0x0001
+#define PCI_DEVICE_ID_MOXA_CP102 0x1020
+#define PCI_DEVICE_ID_MOXA_CP102UL 0x1021
+#define PCI_DEVICE_ID_MOXA_CP102U 0x1022
+#define PCI_DEVICE_ID_MOXA_C104 0x1040
+#define PCI_DEVICE_ID_MOXA_CP104U 0x1041
+#define PCI_DEVICE_ID_MOXA_CP104JU 0x1042
+#define PCI_DEVICE_ID_MOXA_CP104EL 0x1043
+#define PCI_DEVICE_ID_MOXA_CT114 0x1140
+#define PCI_DEVICE_ID_MOXA_CP114 0x1141
+#define PCI_DEVICE_ID_MOXA_CP118U 0x1180
+#define PCI_DEVICE_ID_MOXA_CP118EL 0x1181
+#define PCI_DEVICE_ID_MOXA_CP132 0x1320
+#define PCI_DEVICE_ID_MOXA_CP132U 0x1321
+#define PCI_DEVICE_ID_MOXA_CP134U 0x1340
+#define PCI_DEVICE_ID_MOXA_C168 0x1680
+#define PCI_DEVICE_ID_MOXA_CP168U 0x1681
+#define PCI_DEVICE_ID_MOXA_CP168EL 0x1682
+#define PCI_DEVICE_ID_MOXA_CP204J 0x2040
+#define PCI_DEVICE_ID_MOXA_C218 0x2180
+#define PCI_DEVICE_ID_MOXA_C320 0x3200
+
+#define PCI_VENDOR_ID_CCD 0x1397
+#define PCI_DEVICE_ID_CCD_HFC4S 0x08B4
+#define PCI_SUBDEVICE_ID_CCD_PMX2S 0x1234
+#define PCI_DEVICE_ID_CCD_HFC8S 0x16B8
+#define PCI_DEVICE_ID_CCD_2BD0 0x2bd0
+#define PCI_DEVICE_ID_CCD_HFCE1 0x30B1
+#define PCI_SUBDEVICE_ID_CCD_SPD4S 0x3136
+#define PCI_SUBDEVICE_ID_CCD_SPDE1 0x3137
+#define PCI_DEVICE_ID_CCD_B000 0xb000
+#define PCI_DEVICE_ID_CCD_B006 0xb006
+#define PCI_DEVICE_ID_CCD_B007 0xb007
+#define PCI_DEVICE_ID_CCD_B008 0xb008
+#define PCI_DEVICE_ID_CCD_B009 0xb009
+#define PCI_DEVICE_ID_CCD_B00A 0xb00a
+#define PCI_DEVICE_ID_CCD_B00B 0xb00b
+#define PCI_DEVICE_ID_CCD_B00C 0xb00c
+#define PCI_DEVICE_ID_CCD_B100 0xb100
+#define PCI_SUBDEVICE_ID_CCD_IOB4ST 0xB520
+#define PCI_SUBDEVICE_ID_CCD_IOB8STR 0xB521
+#define PCI_SUBDEVICE_ID_CCD_IOB8ST 0xB522
+#define PCI_SUBDEVICE_ID_CCD_IOB1E1 0xB523
+#define PCI_SUBDEVICE_ID_CCD_SWYX4S 0xB540
+#define PCI_SUBDEVICE_ID_CCD_JH4S20 0xB550
+#define PCI_SUBDEVICE_ID_CCD_IOB8ST_1 0xB552
+#define PCI_SUBDEVICE_ID_CCD_JHSE1 0xB553
+#define PCI_SUBDEVICE_ID_CCD_JH8S 0xB55B
+#define PCI_SUBDEVICE_ID_CCD_BN4S 0xB560
+#define PCI_SUBDEVICE_ID_CCD_BN8S 0xB562
+#define PCI_SUBDEVICE_ID_CCD_BNE1 0xB563
+#define PCI_SUBDEVICE_ID_CCD_BNE1D 0xB564
+#define PCI_SUBDEVICE_ID_CCD_BNE1DP 0xB565
+#define PCI_SUBDEVICE_ID_CCD_BN2S 0xB566
+#define PCI_SUBDEVICE_ID_CCD_BN1SM 0xB567
+#define PCI_SUBDEVICE_ID_CCD_BN4SM 0xB568
+#define PCI_SUBDEVICE_ID_CCD_BN2SM 0xB569
+#define PCI_SUBDEVICE_ID_CCD_BNE1M 0xB56A
+#define PCI_SUBDEVICE_ID_CCD_BN8SP 0xB56B
+#define PCI_SUBDEVICE_ID_CCD_HFC4S 0xB620
+#define PCI_SUBDEVICE_ID_CCD_HFC8S 0xB622
+#define PCI_DEVICE_ID_CCD_B700 0xb700
+#define PCI_DEVICE_ID_CCD_B701 0xb701
+#define PCI_SUBDEVICE_ID_CCD_HFCE1 0xC523
+#define PCI_SUBDEVICE_ID_CCD_OV2S 0xE884
+#define PCI_SUBDEVICE_ID_CCD_OV4S 0xE888
+#define PCI_SUBDEVICE_ID_CCD_OV8S 0xE998
+
+#define PCI_VENDOR_ID_EXAR 0x13a8
+#define PCI_DEVICE_ID_EXAR_XR17C152 0x0152
+#define PCI_DEVICE_ID_EXAR_XR17C154 0x0154
+#define PCI_DEVICE_ID_EXAR_XR17C158 0x0158
+#define PCI_DEVICE_ID_EXAR_XR17V352 0x0352
+#define PCI_DEVICE_ID_EXAR_XR17V354 0x0354
+#define PCI_DEVICE_ID_EXAR_XR17V358 0x0358
+
+#define PCI_VENDOR_ID_MICROGATE 0x13c0
+#define PCI_DEVICE_ID_MICROGATE_USC 0x0010
+#define PCI_DEVICE_ID_MICROGATE_SCA 0x0030
+
+#define PCI_VENDOR_ID_3WARE 0x13C1
+#define PCI_DEVICE_ID_3WARE_1000 0x1000
+#define PCI_DEVICE_ID_3WARE_7000 0x1001
+#define PCI_DEVICE_ID_3WARE_9000 0x1002
+
+#define PCI_VENDOR_ID_IOMEGA 0x13ca
+#define PCI_DEVICE_ID_IOMEGA_BUZ 0x4231
+
+#define PCI_VENDOR_ID_ABOCOM 0x13D1
+#define PCI_DEVICE_ID_ABOCOM_2BD1 0x2BD1
+
+#define PCI_VENDOR_ID_SUNDANCE 0x13f0
+
+#define PCI_VENDOR_ID_CMEDIA 0x13f6
+#define PCI_DEVICE_ID_CMEDIA_CM8338A 0x0100
+#define PCI_DEVICE_ID_CMEDIA_CM8338B 0x0101
+#define PCI_DEVICE_ID_CMEDIA_CM8738 0x0111
+#define PCI_DEVICE_ID_CMEDIA_CM8738B 0x0112
+
+#define PCI_VENDOR_ID_ADVANTECH 0x13fe
+
+#define PCI_VENDOR_ID_MEILHAUS 0x1402
+
+#define PCI_VENDOR_ID_LAVA 0x1407
+#define PCI_DEVICE_ID_LAVA_DSERIAL 0x0100 /* 2x 16550 */
+#define PCI_DEVICE_ID_LAVA_QUATRO_A 0x0101 /* 2x 16550, half of 4 port */
+#define PCI_DEVICE_ID_LAVA_QUATRO_B 0x0102 /* 2x 16550, half of 4 port */
+#define PCI_DEVICE_ID_LAVA_QUATTRO_A 0x0120 /* 2x 16550A, half of 4 port */
+#define PCI_DEVICE_ID_LAVA_QUATTRO_B 0x0121 /* 2x 16550A, half of 4 port */
+#define PCI_DEVICE_ID_LAVA_OCTO_A 0x0180 /* 4x 16550A, half of 8 port */
+#define PCI_DEVICE_ID_LAVA_OCTO_B 0x0181 /* 4x 16550A, half of 8 port */
+#define PCI_DEVICE_ID_LAVA_PORT_PLUS 0x0200 /* 2x 16650 */
+#define PCI_DEVICE_ID_LAVA_QUAD_A 0x0201 /* 2x 16650, half of 4 port */
+#define PCI_DEVICE_ID_LAVA_QUAD_B 0x0202 /* 2x 16650, half of 4 port */
+#define PCI_DEVICE_ID_LAVA_SSERIAL 0x0500 /* 1x 16550 */
+#define PCI_DEVICE_ID_LAVA_PORT_650 0x0600 /* 1x 16650 */
+#define PCI_DEVICE_ID_LAVA_PARALLEL 0x8000
+#define PCI_DEVICE_ID_LAVA_DUAL_PAR_A 0x8002 /* The Lava Dual Parallel is */
+#define PCI_DEVICE_ID_LAVA_DUAL_PAR_B 0x8003 /* two PCI devices on a card */
+#define PCI_DEVICE_ID_LAVA_BOCA_IOPPAR 0x8800
+
+#define PCI_VENDOR_ID_TIMEDIA 0x1409
+#define PCI_DEVICE_ID_TIMEDIA_1889 0x7168
+
+#define PCI_VENDOR_ID_ICE 0x1412
+#define PCI_DEVICE_ID_ICE_1712 0x1712
+#define PCI_DEVICE_ID_VT1724 0x1724
+
+#define PCI_VENDOR_ID_OXSEMI 0x1415
+#define PCI_DEVICE_ID_OXSEMI_12PCI840 0x8403
+#define PCI_DEVICE_ID_OXSEMI_PCIe840 0xC000
+#define PCI_DEVICE_ID_OXSEMI_PCIe840_G 0xC004
+#define PCI_DEVICE_ID_OXSEMI_PCIe952_0 0xC100
+#define PCI_DEVICE_ID_OXSEMI_PCIe952_0_G 0xC104
+#define PCI_DEVICE_ID_OXSEMI_PCIe952_1 0xC110
+#define PCI_DEVICE_ID_OXSEMI_PCIe952_1_G 0xC114
+#define PCI_DEVICE_ID_OXSEMI_PCIe952_1_U 0xC118
+#define PCI_DEVICE_ID_OXSEMI_PCIe952_1_GU 0xC11C
+#define PCI_DEVICE_ID_OXSEMI_16PCI954 0x9501
+#define PCI_DEVICE_ID_OXSEMI_C950 0x950B
+#define PCI_DEVICE_ID_OXSEMI_16PCI95N 0x9511
+#define PCI_DEVICE_ID_OXSEMI_16PCI954PP 0x9513
+#define PCI_DEVICE_ID_OXSEMI_16PCI952 0x9521
+#define PCI_DEVICE_ID_OXSEMI_16PCI952PP 0x9523
+#define PCI_SUBDEVICE_ID_OXSEMI_C950 0x0001
+
+#define PCI_VENDOR_ID_CHELSIO 0x1425
+
+#define PCI_VENDOR_ID_ADLINK 0x144a
+
+#define PCI_VENDOR_ID_SAMSUNG 0x144d
+
+#define PCI_VENDOR_ID_GIGABYTE 0x1458
+
+#define PCI_VENDOR_ID_AMBIT 0x1468
+
+#define PCI_VENDOR_ID_MYRICOM 0x14c1
+
+#define PCI_VENDOR_ID_TITAN 0x14D2
+#define PCI_DEVICE_ID_TITAN_010L 0x8001
+#define PCI_DEVICE_ID_TITAN_100L 0x8010
+#define PCI_DEVICE_ID_TITAN_110L 0x8011
+#define PCI_DEVICE_ID_TITAN_200L 0x8020
+#define PCI_DEVICE_ID_TITAN_210L 0x8021
+#define PCI_DEVICE_ID_TITAN_400L 0x8040
+#define PCI_DEVICE_ID_TITAN_800L 0x8080
+#define PCI_DEVICE_ID_TITAN_100 0xA001
+#define PCI_DEVICE_ID_TITAN_200 0xA005
+#define PCI_DEVICE_ID_TITAN_400 0xA003
+#define PCI_DEVICE_ID_TITAN_800B 0xA004
+
+#define PCI_VENDOR_ID_PANACOM 0x14d4
+#define PCI_DEVICE_ID_PANACOM_QUADMODEM 0x0400
+#define PCI_DEVICE_ID_PANACOM_DUALMODEM 0x0402
+
+#define PCI_VENDOR_ID_SIPACKETS 0x14d9
+#define PCI_DEVICE_ID_SP1011 0x0010
+
+#define PCI_VENDOR_ID_AFAVLAB 0x14db
+#define PCI_DEVICE_ID_AFAVLAB_P028 0x2180
+#define PCI_DEVICE_ID_AFAVLAB_P030 0x2182
+#define PCI_SUBDEVICE_ID_AFAVLAB_P061 0x2150
+
+#define PCI_VENDOR_ID_AMPLICON 0x14dc
+
+#define PCI_VENDOR_ID_BCM_GVC 0x14a4
+#define PCI_VENDOR_ID_BROADCOM 0x14e4
+#define PCI_DEVICE_ID_TIGON3_5752 0x1600
+#define PCI_DEVICE_ID_TIGON3_5752M 0x1601
+#define PCI_DEVICE_ID_NX2_5709 0x1639
+#define PCI_DEVICE_ID_NX2_5709S 0x163a
+#define PCI_DEVICE_ID_TIGON3_5700 0x1644
+#define PCI_DEVICE_ID_TIGON3_5701 0x1645
+#define PCI_DEVICE_ID_TIGON3_5702 0x1646
+#define PCI_DEVICE_ID_TIGON3_5703 0x1647
+#define PCI_DEVICE_ID_TIGON3_5704 0x1648
+#define PCI_DEVICE_ID_TIGON3_5704S_2 0x1649
+#define PCI_DEVICE_ID_NX2_5706 0x164a
+#define PCI_DEVICE_ID_NX2_5708 0x164c
+#define PCI_DEVICE_ID_TIGON3_5702FE 0x164d
+#define PCI_DEVICE_ID_NX2_57710 0x164e
+#define PCI_DEVICE_ID_NX2_57711 0x164f
+#define PCI_DEVICE_ID_NX2_57711E 0x1650
+#define PCI_DEVICE_ID_TIGON3_5705 0x1653
+#define PCI_DEVICE_ID_TIGON3_5705_2 0x1654
+#define PCI_DEVICE_ID_TIGON3_5719 0x1657
+#define PCI_DEVICE_ID_TIGON3_5721 0x1659
+#define PCI_DEVICE_ID_TIGON3_5722 0x165a
+#define PCI_DEVICE_ID_TIGON3_5723 0x165b
+#define PCI_DEVICE_ID_TIGON3_5705M 0x165d
+#define PCI_DEVICE_ID_TIGON3_5705M_2 0x165e
+#define PCI_DEVICE_ID_NX2_57712 0x1662
+#define PCI_DEVICE_ID_NX2_57712E 0x1663
+#define PCI_DEVICE_ID_NX2_57712_MF 0x1663
+#define PCI_DEVICE_ID_TIGON3_5714 0x1668
+#define PCI_DEVICE_ID_TIGON3_5714S 0x1669
+#define PCI_DEVICE_ID_TIGON3_5780 0x166a
+#define PCI_DEVICE_ID_TIGON3_5780S 0x166b
+#define PCI_DEVICE_ID_TIGON3_5705F 0x166e
+#define PCI_DEVICE_ID_NX2_57712_VF 0x166f
+#define PCI_DEVICE_ID_TIGON3_5754M 0x1672
+#define PCI_DEVICE_ID_TIGON3_5755M 0x1673
+#define PCI_DEVICE_ID_TIGON3_5756 0x1674
+#define PCI_DEVICE_ID_TIGON3_5750 0x1676
+#define PCI_DEVICE_ID_TIGON3_5751 0x1677
+#define PCI_DEVICE_ID_TIGON3_5715 0x1678
+#define PCI_DEVICE_ID_TIGON3_5715S 0x1679
+#define PCI_DEVICE_ID_TIGON3_5754 0x167a
+#define PCI_DEVICE_ID_TIGON3_5755 0x167b
+#define PCI_DEVICE_ID_TIGON3_5751M 0x167d
+#define PCI_DEVICE_ID_TIGON3_5751F 0x167e
+#define PCI_DEVICE_ID_TIGON3_5787F 0x167f
+#define PCI_DEVICE_ID_TIGON3_5761E 0x1680
+#define PCI_DEVICE_ID_TIGON3_5761 0x1681
+#define PCI_DEVICE_ID_TIGON3_5764 0x1684
+#define PCI_DEVICE_ID_NX2_57800 0x168a
+#define PCI_DEVICE_ID_NX2_57840 0x168d
+#define PCI_DEVICE_ID_NX2_57810 0x168e
+#define PCI_DEVICE_ID_TIGON3_5787M 0x1693
+#define PCI_DEVICE_ID_TIGON3_5782 0x1696
+#define PCI_DEVICE_ID_TIGON3_5784 0x1698
+#define PCI_DEVICE_ID_TIGON3_5786 0x169a
+#define PCI_DEVICE_ID_TIGON3_5787 0x169b
+#define PCI_DEVICE_ID_TIGON3_5788 0x169c
+#define PCI_DEVICE_ID_TIGON3_5789 0x169d
+#define PCI_DEVICE_ID_NX2_57840_4_10 0x16a1
+#define PCI_DEVICE_ID_NX2_57840_2_20 0x16a2
+#define PCI_DEVICE_ID_NX2_57840_MF 0x16a4
+#define PCI_DEVICE_ID_NX2_57800_MF 0x16a5
+#define PCI_DEVICE_ID_TIGON3_5702X 0x16a6
+#define PCI_DEVICE_ID_TIGON3_5703X 0x16a7
+#define PCI_DEVICE_ID_TIGON3_5704S 0x16a8
+#define PCI_DEVICE_ID_NX2_57800_VF 0x16a9
+#define PCI_DEVICE_ID_NX2_5706S 0x16aa
+#define PCI_DEVICE_ID_NX2_5708S 0x16ac
+#define PCI_DEVICE_ID_NX2_57840_VF 0x16ad
+#define PCI_DEVICE_ID_NX2_57810_MF 0x16ae
+#define PCI_DEVICE_ID_NX2_57810_VF 0x16af
+#define PCI_DEVICE_ID_TIGON3_5702A3 0x16c6
+#define PCI_DEVICE_ID_TIGON3_5703A3 0x16c7
+#define PCI_DEVICE_ID_TIGON3_5781 0x16dd
+#define PCI_DEVICE_ID_TIGON3_5753 0x16f7
+#define PCI_DEVICE_ID_TIGON3_5753M 0x16fd
+#define PCI_DEVICE_ID_TIGON3_5753F 0x16fe
+#define PCI_DEVICE_ID_TIGON3_5901 0x170d
+#define PCI_DEVICE_ID_BCM4401B1 0x170c
+#define PCI_DEVICE_ID_TIGON3_5901_2 0x170e
+#define PCI_DEVICE_ID_TIGON3_5906 0x1712
+#define PCI_DEVICE_ID_TIGON3_5906M 0x1713
+#define PCI_DEVICE_ID_BCM4401 0x4401
+#define PCI_DEVICE_ID_BCM4401B0 0x4402
+
+#define PCI_VENDOR_ID_TOPIC 0x151f
+#define PCI_DEVICE_ID_TOPIC_TP560 0x0000
+
+#define PCI_VENDOR_ID_MAINPINE 0x1522
+#define PCI_DEVICE_ID_MAINPINE_PBRIDGE 0x0100
+#define PCI_VENDOR_ID_ENE 0x1524
+#define PCI_DEVICE_ID_ENE_CB710_FLASH 0x0510
+#define PCI_DEVICE_ID_ENE_CB712_SD 0x0550
+#define PCI_DEVICE_ID_ENE_CB712_SD_2 0x0551
+#define PCI_DEVICE_ID_ENE_CB714_SD 0x0750
+#define PCI_DEVICE_ID_ENE_CB714_SD_2 0x0751
+#define PCI_DEVICE_ID_ENE_1211 0x1211
+#define PCI_DEVICE_ID_ENE_1225 0x1225
+#define PCI_DEVICE_ID_ENE_1410 0x1410
+#define PCI_DEVICE_ID_ENE_710 0x1411
+#define PCI_DEVICE_ID_ENE_712 0x1412
+#define PCI_DEVICE_ID_ENE_1420 0x1420
+#define PCI_DEVICE_ID_ENE_720 0x1421
+#define PCI_DEVICE_ID_ENE_722 0x1422
+
+#define PCI_SUBVENDOR_ID_PERLE 0x155f
+#define PCI_SUBDEVICE_ID_PCI_RAS4 0xf001
+#define PCI_SUBDEVICE_ID_PCI_RAS8 0xf010
+
+#define PCI_VENDOR_ID_SYBA 0x1592
+#define PCI_DEVICE_ID_SYBA_2P_EPP 0x0782
+#define PCI_DEVICE_ID_SYBA_1P_ECP 0x0783
+
+#define PCI_VENDOR_ID_MORETON 0x15aa
+#define PCI_DEVICE_ID_RASTEL_2PORT 0x2000
+
+#define PCI_VENDOR_ID_VMWARE 0x15ad
+#define PCI_DEVICE_ID_VMWARE_VMXNET3 0x07b0
+
+#define PCI_VENDOR_ID_ZOLTRIX 0x15b0
+#define PCI_DEVICE_ID_ZOLTRIX_2BD0 0x2bd0
+
+#define PCI_VENDOR_ID_MELLANOX 0x15b3
+#define PCI_DEVICE_ID_MELLANOX_CONNECTX3 0x1003
+#define PCI_DEVICE_ID_MELLANOX_CONNECTX3_PRO 0x1007
+#define PCI_DEVICE_ID_MELLANOX_CONNECTIB 0x1011
+#define PCI_DEVICE_ID_MELLANOX_CONNECTX4 0x1013
+#define PCI_DEVICE_ID_MELLANOX_CONNECTX4_LX 0x1015
+#define PCI_DEVICE_ID_MELLANOX_TAVOR 0x5a44
+#define PCI_DEVICE_ID_MELLANOX_TAVOR_BRIDGE 0x5a46
+#define PCI_DEVICE_ID_MELLANOX_SINAI_OLD 0x5e8c
+#define PCI_DEVICE_ID_MELLANOX_SINAI 0x6274
+#define PCI_DEVICE_ID_MELLANOX_ARBEL_COMPAT 0x6278
+#define PCI_DEVICE_ID_MELLANOX_ARBEL 0x6282
+#define PCI_DEVICE_ID_MELLANOX_HERMON_SDR 0x6340
+#define PCI_DEVICE_ID_MELLANOX_HERMON_DDR 0x634a
+#define PCI_DEVICE_ID_MELLANOX_HERMON_QDR 0x6354
+#define PCI_DEVICE_ID_MELLANOX_HERMON_EN 0x6368
+#define PCI_DEVICE_ID_MELLANOX_CONNECTX_EN 0x6372
+#define PCI_DEVICE_ID_MELLANOX_HERMON_DDR_GEN2 0x6732
+#define PCI_DEVICE_ID_MELLANOX_HERMON_QDR_GEN2 0x673c
+#define PCI_DEVICE_ID_MELLANOX_CONNECTX_EN_5_GEN2 0x6746
+#define PCI_DEVICE_ID_MELLANOX_HERMON_EN_GEN2 0x6750
+#define PCI_DEVICE_ID_MELLANOX_CONNECTX_EN_T_GEN2 0x675a
+#define PCI_DEVICE_ID_MELLANOX_CONNECTX_EN_GEN2 0x6764
+#define PCI_DEVICE_ID_MELLANOX_CONNECTX2 0x676e
+
+#define PCI_VENDOR_ID_DFI 0x15bd
+
+#define PCI_VENDOR_ID_QUICKNET 0x15e2
+#define PCI_DEVICE_ID_QUICKNET_XJ 0x0500
+
+/*
+ * ADDI-DATA GmbH communication cards <info@addi-data.com>
+ */
+#define PCI_VENDOR_ID_ADDIDATA 0x15B8
+#define PCI_DEVICE_ID_ADDIDATA_APCI7500 0x7000
+#define PCI_DEVICE_ID_ADDIDATA_APCI7420 0x7001
+#define PCI_DEVICE_ID_ADDIDATA_APCI7300 0x7002
+#define PCI_DEVICE_ID_ADDIDATA_APCI7500_2 0x7009
+#define PCI_DEVICE_ID_ADDIDATA_APCI7420_2 0x700A
+#define PCI_DEVICE_ID_ADDIDATA_APCI7300_2 0x700B
+#define PCI_DEVICE_ID_ADDIDATA_APCI7500_3 0x700C
+#define PCI_DEVICE_ID_ADDIDATA_APCI7420_3 0x700D
+#define PCI_DEVICE_ID_ADDIDATA_APCI7300_3 0x700E
+#define PCI_DEVICE_ID_ADDIDATA_APCI7800_3 0x700F
+#define PCI_DEVICE_ID_ADDIDATA_APCIe7300 0x7010
+#define PCI_DEVICE_ID_ADDIDATA_APCIe7420 0x7011
+#define PCI_DEVICE_ID_ADDIDATA_APCIe7500 0x7012
+#define PCI_DEVICE_ID_ADDIDATA_APCIe7800 0x7013
+
+#define PCI_VENDOR_ID_PDC 0x15e9
+
+#define PCI_VENDOR_ID_FARSITE 0x1619
+#define PCI_DEVICE_ID_FARSITE_T2P 0x0400
+#define PCI_DEVICE_ID_FARSITE_T4P 0x0440
+#define PCI_DEVICE_ID_FARSITE_T1U 0x0610
+#define PCI_DEVICE_ID_FARSITE_T2U 0x0620
+#define PCI_DEVICE_ID_FARSITE_T4U 0x0640
+#define PCI_DEVICE_ID_FARSITE_TE1 0x1610
+#define PCI_DEVICE_ID_FARSITE_TE1C 0x1612
+
+#define PCI_VENDOR_ID_ARIMA 0x161f
+
+#define PCI_VENDOR_ID_BROCADE 0x1657
+#define PCI_DEVICE_ID_BROCADE_CT 0x0014
+#define PCI_DEVICE_ID_BROCADE_FC_8G1P 0x0017
+#define PCI_DEVICE_ID_BROCADE_CT_FC 0x0021
+
+#define PCI_VENDOR_ID_SIBYTE 0x166d
+#define PCI_DEVICE_ID_BCM1250_PCI 0x0001
+#define PCI_DEVICE_ID_BCM1250_HT 0x0002
+
+#define PCI_VENDOR_ID_ATHEROS 0x168c
+
+#define PCI_VENDOR_ID_NETCELL 0x169c
+#define PCI_DEVICE_ID_REVOLUTION 0x0044
+
+#define PCI_VENDOR_ID_CENATEK 0x16CA
+#define PCI_DEVICE_ID_CENATEK_IDE 0x0001
+
+#define PCI_VENDOR_ID_SYNOPSYS 0x16c3
+
+#define PCI_VENDOR_ID_VITESSE 0x1725
+#define PCI_DEVICE_ID_VITESSE_VSC7174 0x7174
+
+#define PCI_VENDOR_ID_LINKSYS 0x1737
+#define PCI_DEVICE_ID_LINKSYS_EG1064 0x1064
+
+#define PCI_VENDOR_ID_ALTIMA 0x173b
+#define PCI_DEVICE_ID_ALTIMA_AC1000 0x03e8
+#define PCI_DEVICE_ID_ALTIMA_AC1001 0x03e9
+#define PCI_DEVICE_ID_ALTIMA_AC9100 0x03ea
+#define PCI_DEVICE_ID_ALTIMA_AC1003 0x03eb
+
+#define PCI_VENDOR_ID_CAVIUM 0x177d
+
+#define PCI_VENDOR_ID_TECHWELL 0x1797
+#define PCI_DEVICE_ID_TECHWELL_6800 0x6800
+#define PCI_DEVICE_ID_TECHWELL_6801 0x6801
+#define PCI_DEVICE_ID_TECHWELL_6804 0x6804
+#define PCI_DEVICE_ID_TECHWELL_6816_1 0x6810
+#define PCI_DEVICE_ID_TECHWELL_6816_2 0x6811
+#define PCI_DEVICE_ID_TECHWELL_6816_3 0x6812
+#define PCI_DEVICE_ID_TECHWELL_6816_4 0x6813
+
+#define PCI_VENDOR_ID_BELKIN 0x1799
+#define PCI_DEVICE_ID_BELKIN_F5D7010V7 0x701f
+
+#define PCI_VENDOR_ID_RDC 0x17f3
+#define PCI_DEVICE_ID_RDC_R6020 0x6020
+#define PCI_DEVICE_ID_RDC_R6030 0x6030
+#define PCI_DEVICE_ID_RDC_R6040 0x6040
+#define PCI_DEVICE_ID_RDC_R6060 0x6060
+#define PCI_DEVICE_ID_RDC_R6061 0x6061
+#define PCI_DEVICE_ID_RDC_D1010 0x1010
+
+#define PCI_VENDOR_ID_LENOVO 0x17aa
+
+#define PCI_VENDOR_ID_ARECA 0x17d3
+#define PCI_DEVICE_ID_ARECA_1110 0x1110
+#define PCI_DEVICE_ID_ARECA_1120 0x1120
+#define PCI_DEVICE_ID_ARECA_1130 0x1130
+#define PCI_DEVICE_ID_ARECA_1160 0x1160
+#define PCI_DEVICE_ID_ARECA_1170 0x1170
+#define PCI_DEVICE_ID_ARECA_1200 0x1200
+#define PCI_DEVICE_ID_ARECA_1201 0x1201
+#define PCI_DEVICE_ID_ARECA_1202 0x1202
+#define PCI_DEVICE_ID_ARECA_1210 0x1210
+#define PCI_DEVICE_ID_ARECA_1220 0x1220
+#define PCI_DEVICE_ID_ARECA_1230 0x1230
+#define PCI_DEVICE_ID_ARECA_1260 0x1260
+#define PCI_DEVICE_ID_ARECA_1270 0x1270
+#define PCI_DEVICE_ID_ARECA_1280 0x1280
+#define PCI_DEVICE_ID_ARECA_1380 0x1380
+#define PCI_DEVICE_ID_ARECA_1381 0x1381
+#define PCI_DEVICE_ID_ARECA_1680 0x1680
+#define PCI_DEVICE_ID_ARECA_1681 0x1681
+
+#define PCI_VENDOR_ID_S2IO 0x17d5
+#define PCI_DEVICE_ID_S2IO_WIN 0x5731
+#define PCI_DEVICE_ID_S2IO_UNI 0x5831
+#define PCI_DEVICE_ID_HERC_WIN 0x5732
+#define PCI_DEVICE_ID_HERC_UNI 0x5832
+
+#define PCI_VENDOR_ID_SITECOM 0x182d
+#define PCI_DEVICE_ID_SITECOM_DC105V2 0x3069
+
+#define PCI_VENDOR_ID_TOPSPIN 0x1867
+
+#define PCI_VENDOR_ID_COMMTECH 0x18f7
+
+#define PCI_VENDOR_ID_SILAN 0x1904
+
+#define PCI_VENDOR_ID_RENESAS 0x1912
+#define PCI_DEVICE_ID_RENESAS_SH7781 0x0001
+#define PCI_DEVICE_ID_RENESAS_SH7780 0x0002
+#define PCI_DEVICE_ID_RENESAS_SH7763 0x0004
+#define PCI_DEVICE_ID_RENESAS_SH7785 0x0007
+#define PCI_DEVICE_ID_RENESAS_SH7786 0x0010
+
+#define PCI_VENDOR_ID_SOLARFLARE 0x1924
+#define PCI_DEVICE_ID_SOLARFLARE_SFC4000A_0 0x0703
+#define PCI_DEVICE_ID_SOLARFLARE_SFC4000A_1 0x6703
+#define PCI_DEVICE_ID_SOLARFLARE_SFC4000B 0x0710
+
+#define PCI_VENDOR_ID_TDI 0x192E
+#define PCI_DEVICE_ID_TDI_EHCI 0x0101
+
+#define PCI_VENDOR_ID_FREESCALE 0x1957
+#define PCI_DEVICE_ID_MPC8308 0xc006
+#define PCI_DEVICE_ID_MPC8315E 0x00b4
+#define PCI_DEVICE_ID_MPC8315 0x00b5
+#define PCI_DEVICE_ID_MPC8314E 0x00b6
+#define PCI_DEVICE_ID_MPC8314 0x00b7
+#define PCI_DEVICE_ID_MPC8378E 0x00c4
+#define PCI_DEVICE_ID_MPC8378 0x00c5
+#define PCI_DEVICE_ID_MPC8377E 0x00c6
+#define PCI_DEVICE_ID_MPC8377 0x00c7
+#define PCI_DEVICE_ID_MPC8548E 0x0012
+#define PCI_DEVICE_ID_MPC8548 0x0013
+#define PCI_DEVICE_ID_MPC8543E 0x0014
+#define PCI_DEVICE_ID_MPC8543 0x0015
+#define PCI_DEVICE_ID_MPC8547E 0x0018
+#define PCI_DEVICE_ID_MPC8545E 0x0019
+#define PCI_DEVICE_ID_MPC8545 0x001a
+#define PCI_DEVICE_ID_MPC8569E 0x0061
+#define PCI_DEVICE_ID_MPC8569 0x0060
+#define PCI_DEVICE_ID_MPC8568E 0x0020
+#define PCI_DEVICE_ID_MPC8568 0x0021
+#define PCI_DEVICE_ID_MPC8567E 0x0022
+#define PCI_DEVICE_ID_MPC8567 0x0023
+#define PCI_DEVICE_ID_MPC8533E 0x0030
+#define PCI_DEVICE_ID_MPC8533 0x0031
+#define PCI_DEVICE_ID_MPC8544E 0x0032
+#define PCI_DEVICE_ID_MPC8544 0x0033
+#define PCI_DEVICE_ID_MPC8572E 0x0040
+#define PCI_DEVICE_ID_MPC8572 0x0041
+#define PCI_DEVICE_ID_MPC8536E 0x0050
+#define PCI_DEVICE_ID_MPC8536 0x0051
+#define PCI_DEVICE_ID_P2020E 0x0070
+#define PCI_DEVICE_ID_P2020 0x0071
+#define PCI_DEVICE_ID_P2010E 0x0078
+#define PCI_DEVICE_ID_P2010 0x0079
+#define PCI_DEVICE_ID_P1020E 0x0100
+#define PCI_DEVICE_ID_P1020 0x0101
+#define PCI_DEVICE_ID_P1021E 0x0102
+#define PCI_DEVICE_ID_P1021 0x0103
+#define PCI_DEVICE_ID_P1011E 0x0108
+#define PCI_DEVICE_ID_P1011 0x0109
+#define PCI_DEVICE_ID_P1022E 0x0110
+#define PCI_DEVICE_ID_P1022 0x0111
+#define PCI_DEVICE_ID_P1013E 0x0118
+#define PCI_DEVICE_ID_P1013 0x0119
+#define PCI_DEVICE_ID_P4080E 0x0400
+#define PCI_DEVICE_ID_P4080 0x0401
+#define PCI_DEVICE_ID_P4040E 0x0408
+#define PCI_DEVICE_ID_P4040 0x0409
+#define PCI_DEVICE_ID_P2040E 0x0410
+#define PCI_DEVICE_ID_P2040 0x0411
+#define PCI_DEVICE_ID_P3041E 0x041E
+#define PCI_DEVICE_ID_P3041 0x041F
+#define PCI_DEVICE_ID_P5020E 0x0420
+#define PCI_DEVICE_ID_P5020 0x0421
+#define PCI_DEVICE_ID_P5010E 0x0428
+#define PCI_DEVICE_ID_P5010 0x0429
+#define PCI_DEVICE_ID_MPC8641 0x7010
+#define PCI_DEVICE_ID_MPC8641D 0x7011
+#define PCI_DEVICE_ID_MPC8610 0x7018
+
+#define PCI_VENDOR_ID_PASEMI 0x1959
+
+#define PCI_VENDOR_ID_ATTANSIC 0x1969
+#define PCI_DEVICE_ID_ATTANSIC_L1 0x1048
+#define PCI_DEVICE_ID_ATTANSIC_L2 0x2048
+
+#define PCI_VENDOR_ID_JMICRON 0x197B
+#define PCI_DEVICE_ID_JMICRON_JMB360 0x2360
+#define PCI_DEVICE_ID_JMICRON_JMB361 0x2361
+#define PCI_DEVICE_ID_JMICRON_JMB362 0x2362
+#define PCI_DEVICE_ID_JMICRON_JMB363 0x2363
+#define PCI_DEVICE_ID_JMICRON_JMB364 0x2364
+#define PCI_DEVICE_ID_JMICRON_JMB365 0x2365
+#define PCI_DEVICE_ID_JMICRON_JMB366 0x2366
+#define PCI_DEVICE_ID_JMICRON_JMB368 0x2368
+#define PCI_DEVICE_ID_JMICRON_JMB369 0x2369
+#define PCI_DEVICE_ID_JMICRON_JMB38X_SD 0x2381
+#define PCI_DEVICE_ID_JMICRON_JMB38X_MMC 0x2382
+#define PCI_DEVICE_ID_JMICRON_JMB38X_MS 0x2383
+#define PCI_DEVICE_ID_JMICRON_JMB385_MS 0x2388
+#define PCI_DEVICE_ID_JMICRON_JMB388_SD 0x2391
+#define PCI_DEVICE_ID_JMICRON_JMB388_ESD 0x2392
+#define PCI_DEVICE_ID_JMICRON_JMB390_MS 0x2393
+
+#define PCI_VENDOR_ID_KORENIX 0x1982
+#define PCI_DEVICE_ID_KORENIX_JETCARDF0 0x1600
+#define PCI_DEVICE_ID_KORENIX_JETCARDF1 0x16ff
+#define PCI_DEVICE_ID_KORENIX_JETCARDF2 0x1700
+#define PCI_DEVICE_ID_KORENIX_JETCARDF3 0x17ff
+
+#define PCI_VENDOR_ID_HUAWEI 0x19e5
+
+#define PCI_VENDOR_ID_NETRONOME 0x19ee
+#define PCI_DEVICE_ID_NETRONOME_NFP3200 0x3200
+#define PCI_DEVICE_ID_NETRONOME_NFP3240 0x3240
+#define PCI_DEVICE_ID_NETRONOME_NFP4000 0x4000
+#define PCI_DEVICE_ID_NETRONOME_NFP6000 0x6000
+#define PCI_DEVICE_ID_NETRONOME_NFP6000_VF 0x6003
+
+#define PCI_VENDOR_ID_QMI 0x1a32
+
+#define PCI_VENDOR_ID_AZWAVE 0x1a3b
+
+#define PCI_VENDOR_ID_REDHAT_QUMRANET 0x1af4
+#define PCI_SUBVENDOR_ID_REDHAT_QUMRANET 0x1af4
+#define PCI_SUBDEVICE_ID_QEMU 0x1100
+
+#define PCI_VENDOR_ID_ASMEDIA 0x1b21
+
+#define PCI_VENDOR_ID_CIRCUITCO 0x1cc8
+#define PCI_SUBSYSTEM_ID_CIRCUITCO_MINNOWBOARD 0x0001
+
+#define PCI_VENDOR_ID_TEKRAM 0x1de1
+#define PCI_DEVICE_ID_TEKRAM_DC290 0xdc29
+
+#define PCI_VENDOR_ID_TEHUTI 0x1fc9
+#define PCI_DEVICE_ID_TEHUTI_3009 0x3009
+#define PCI_DEVICE_ID_TEHUTI_3010 0x3010
+#define PCI_DEVICE_ID_TEHUTI_3014 0x3014
+
+#define PCI_VENDOR_ID_HINT 0x3388
+#define PCI_DEVICE_ID_HINT_VXPROII_IDE 0x8013
+
+#define PCI_VENDOR_ID_3DLABS 0x3d3d
+#define PCI_DEVICE_ID_3DLABS_PERMEDIA2 0x0007
+#define PCI_DEVICE_ID_3DLABS_PERMEDIA2V 0x0009
+
+#define PCI_VENDOR_ID_NETXEN 0x4040
+#define PCI_DEVICE_ID_NX2031_10GXSR 0x0001
+#define PCI_DEVICE_ID_NX2031_10GCX4 0x0002
+#define PCI_DEVICE_ID_NX2031_4GCU 0x0003
+#define PCI_DEVICE_ID_NX2031_IMEZ 0x0004
+#define PCI_DEVICE_ID_NX2031_HMEZ 0x0005
+#define PCI_DEVICE_ID_NX2031_XG_MGMT 0x0024
+#define PCI_DEVICE_ID_NX2031_XG_MGMT2 0x0025
+#define PCI_DEVICE_ID_NX3031 0x0100
+
+#define PCI_VENDOR_ID_AKS 0x416c
+#define PCI_DEVICE_ID_AKS_ALADDINCARD 0x0100
+
+#define PCI_VENDOR_ID_ACCESSIO 0x494f
+#define PCI_DEVICE_ID_ACCESSIO_WDG_CSM 0x22c0
+
+#define PCI_VENDOR_ID_S3 0x5333
+#define PCI_DEVICE_ID_S3_TRIO 0x8811
+#define PCI_DEVICE_ID_S3_868 0x8880
+#define PCI_DEVICE_ID_S3_968 0x88f0
+#define PCI_DEVICE_ID_S3_SAVAGE4 0x8a25
+#define PCI_DEVICE_ID_S3_PROSAVAGE8 0x8d04
+#define PCI_DEVICE_ID_S3_SONICVIBES 0xca00
+
+#define PCI_VENDOR_ID_DUNORD 0x5544
+#define PCI_DEVICE_ID_DUNORD_I3000 0x0001
+
+#define PCI_VENDOR_ID_DCI 0x6666
+#define PCI_DEVICE_ID_DCI_PCCOM4 0x0001
+#define PCI_DEVICE_ID_DCI_PCCOM8 0x0002
+#define PCI_DEVICE_ID_DCI_PCCOM2 0x0004
+
+#define PCI_VENDOR_ID_INTEL 0x8086
+#define PCI_DEVICE_ID_INTEL_EESSC 0x0008
+#define PCI_DEVICE_ID_INTEL_PXHD_0 0x0320
+#define PCI_DEVICE_ID_INTEL_PXHD_1 0x0321
+#define PCI_DEVICE_ID_INTEL_PXH_0 0x0329
+#define PCI_DEVICE_ID_INTEL_PXH_1 0x032A
+#define PCI_DEVICE_ID_INTEL_PXHV 0x032C
+#define PCI_DEVICE_ID_INTEL_80332_0 0x0330
+#define PCI_DEVICE_ID_INTEL_80332_1 0x0332
+#define PCI_DEVICE_ID_INTEL_80333_0 0x0370
+#define PCI_DEVICE_ID_INTEL_80333_1 0x0372
+#define PCI_DEVICE_ID_INTEL_82375 0x0482
+#define PCI_DEVICE_ID_INTEL_82424 0x0483
+#define PCI_DEVICE_ID_INTEL_82378 0x0484
+#define PCI_DEVICE_ID_INTEL_MRST_SD0 0x0807
+#define PCI_DEVICE_ID_INTEL_MRST_SD1 0x0808
+#define PCI_DEVICE_ID_INTEL_MFD_SD 0x0820
+#define PCI_DEVICE_ID_INTEL_MFD_SDIO1 0x0821
+#define PCI_DEVICE_ID_INTEL_MFD_SDIO2 0x0822
+#define PCI_DEVICE_ID_INTEL_MFD_EMMC0 0x0823
+#define PCI_DEVICE_ID_INTEL_MFD_EMMC1 0x0824
+#define PCI_DEVICE_ID_INTEL_MRST_SD2 0x084F
+#define PCI_DEVICE_ID_INTEL_QUARK_X1000_ILB 0x095E
+#define PCI_DEVICE_ID_INTEL_I960 0x0960
+#define PCI_DEVICE_ID_INTEL_I960RM 0x0962
+#define PCI_DEVICE_ID_INTEL_CENTERTON_ILB 0x0c60
+#define PCI_DEVICE_ID_INTEL_8257X_SOL 0x1062
+#define PCI_DEVICE_ID_INTEL_82573E_SOL 0x1085
+#define PCI_DEVICE_ID_INTEL_82573L_SOL 0x108F
+#define PCI_DEVICE_ID_INTEL_82815_MC 0x1130
+#define PCI_DEVICE_ID_INTEL_82815_CGC 0x1132
+#define PCI_DEVICE_ID_INTEL_82092AA_0 0x1221
+#define PCI_DEVICE_ID_INTEL_7505_0 0x2550
+#define PCI_DEVICE_ID_INTEL_7205_0 0x255d
+#define PCI_DEVICE_ID_INTEL_82437 0x122d
+#define PCI_DEVICE_ID_INTEL_82371FB_0 0x122e
+#define PCI_DEVICE_ID_INTEL_82371FB_1 0x1230
+#define PCI_DEVICE_ID_INTEL_82371MX 0x1234
+#define PCI_DEVICE_ID_INTEL_82441 0x1237
+#define PCI_DEVICE_ID_INTEL_82380FB 0x124b
+#define PCI_DEVICE_ID_INTEL_82439 0x1250
+#define PCI_DEVICE_ID_INTEL_LIGHT_RIDGE 0x1513 /* Tbt 1 Gen 1 */
+#define PCI_DEVICE_ID_INTEL_EAGLE_RIDGE 0x151a
+#define PCI_DEVICE_ID_INTEL_LIGHT_PEAK 0x151b
+#define PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_4C 0x1547 /* Tbt 1 Gen 2 */
+#define PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_2C 0x1548
+#define PCI_DEVICE_ID_INTEL_PORT_RIDGE 0x1549
+#define PCI_DEVICE_ID_INTEL_REDWOOD_RIDGE_2C_NHI 0x1566 /* Tbt 1 Gen 3 */
+#define PCI_DEVICE_ID_INTEL_REDWOOD_RIDGE_2C_BRIDGE 0x1567
+#define PCI_DEVICE_ID_INTEL_REDWOOD_RIDGE_4C_NHI 0x1568
+#define PCI_DEVICE_ID_INTEL_REDWOOD_RIDGE_4C_BRIDGE 0x1569
+#define PCI_DEVICE_ID_INTEL_FALCON_RIDGE_2C_NHI 0x156a /* Thunderbolt 2 */
+#define PCI_DEVICE_ID_INTEL_FALCON_RIDGE_2C_BRIDGE 0x156b
+#define PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_NHI 0x156c
+#define PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_BRIDGE 0x156d
+#define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_2C_NHI 0x1575 /* Thunderbolt 3 */
+#define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_2C_BRIDGE 0x1576
+#define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_4C_NHI 0x1577
+#define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_4C_BRIDGE 0x1578
+#define PCI_DEVICE_ID_INTEL_80960_RP 0x1960
+#define PCI_DEVICE_ID_INTEL_82840_HB 0x1a21
+#define PCI_DEVICE_ID_INTEL_82845_HB 0x1a30
+#define PCI_DEVICE_ID_INTEL_IOAT 0x1a38
+#define PCI_DEVICE_ID_INTEL_COUGARPOINT_LPC_MIN 0x1c41
+#define PCI_DEVICE_ID_INTEL_COUGARPOINT_LPC_MAX 0x1c5f
+#define PCI_DEVICE_ID_INTEL_PATSBURG_LPC_0 0x1d40
+#define PCI_DEVICE_ID_INTEL_PATSBURG_LPC_1 0x1d41
+#define PCI_DEVICE_ID_INTEL_PANTHERPOINT_XHCI 0x1e31
+#define PCI_DEVICE_ID_INTEL_PANTHERPOINT_LPC_MIN 0x1e40
+#define PCI_DEVICE_ID_INTEL_PANTHERPOINT_LPC_MAX 0x1e5f
+#define PCI_DEVICE_ID_INTEL_DH89XXCC_LPC_MIN 0x2310
+#define PCI_DEVICE_ID_INTEL_DH89XXCC_LPC_MAX 0x231f
+#define PCI_DEVICE_ID_INTEL_82801AA_0 0x2410
+#define PCI_DEVICE_ID_INTEL_82801AA_1 0x2411
+#define PCI_DEVICE_ID_INTEL_82801AA_3 0x2413
+#define PCI_DEVICE_ID_INTEL_82801AA_5 0x2415
+#define PCI_DEVICE_ID_INTEL_82801AA_6 0x2416
+#define PCI_DEVICE_ID_INTEL_82801AA_8 0x2418
+#define PCI_DEVICE_ID_INTEL_82801AB_0 0x2420
+#define PCI_DEVICE_ID_INTEL_82801AB_1 0x2421
+#define PCI_DEVICE_ID_INTEL_82801AB_3 0x2423
+#define PCI_DEVICE_ID_INTEL_82801AB_5 0x2425
+#define PCI_DEVICE_ID_INTEL_82801AB_6 0x2426
+#define PCI_DEVICE_ID_INTEL_82801AB_8 0x2428
+#define PCI_DEVICE_ID_INTEL_82801BA_0 0x2440
+#define PCI_DEVICE_ID_INTEL_82801BA_2 0x2443
+#define PCI_DEVICE_ID_INTEL_82801BA_4 0x2445
+#define PCI_DEVICE_ID_INTEL_82801BA_6 0x2448
+#define PCI_DEVICE_ID_INTEL_82801BA_8 0x244a
+#define PCI_DEVICE_ID_INTEL_82801BA_9 0x244b
+#define PCI_DEVICE_ID_INTEL_82801BA_10 0x244c
+#define PCI_DEVICE_ID_INTEL_82801BA_11 0x244e
+#define PCI_DEVICE_ID_INTEL_82801E_0 0x2450
+#define PCI_DEVICE_ID_INTEL_82801E_11 0x245b
+#define PCI_DEVICE_ID_INTEL_82801CA_0 0x2480
+#define PCI_DEVICE_ID_INTEL_82801CA_3 0x2483
+#define PCI_DEVICE_ID_INTEL_82801CA_5 0x2485
+#define PCI_DEVICE_ID_INTEL_82801CA_6 0x2486
+#define PCI_DEVICE_ID_INTEL_82801CA_10 0x248a
+#define PCI_DEVICE_ID_INTEL_82801CA_11 0x248b
+#define PCI_DEVICE_ID_INTEL_82801CA_12 0x248c
+#define PCI_DEVICE_ID_INTEL_82801DB_0 0x24c0
+#define PCI_DEVICE_ID_INTEL_82801DB_1 0x24c1
+#define PCI_DEVICE_ID_INTEL_82801DB_2 0x24c2
+#define PCI_DEVICE_ID_INTEL_82801DB_3 0x24c3
+#define PCI_DEVICE_ID_INTEL_82801DB_5 0x24c5
+#define PCI_DEVICE_ID_INTEL_82801DB_6 0x24c6
+#define PCI_DEVICE_ID_INTEL_82801DB_9 0x24c9
+#define PCI_DEVICE_ID_INTEL_82801DB_10 0x24ca
+#define PCI_DEVICE_ID_INTEL_82801DB_11 0x24cb
+#define PCI_DEVICE_ID_INTEL_82801DB_12 0x24cc
+#define PCI_DEVICE_ID_INTEL_82801EB_0 0x24d0
+#define PCI_DEVICE_ID_INTEL_82801EB_1 0x24d1
+#define PCI_DEVICE_ID_INTEL_82801EB_3 0x24d3
+#define PCI_DEVICE_ID_INTEL_82801EB_5 0x24d5
+#define PCI_DEVICE_ID_INTEL_82801EB_6 0x24d6
+#define PCI_DEVICE_ID_INTEL_82801EB_11 0x24db
+#define PCI_DEVICE_ID_INTEL_82801EB_12 0x24dc
+#define PCI_DEVICE_ID_INTEL_82801EB_13 0x24dd
+#define PCI_DEVICE_ID_INTEL_ESB_1 0x25a1
+#define PCI_DEVICE_ID_INTEL_ESB_2 0x25a2
+#define PCI_DEVICE_ID_INTEL_ESB_4 0x25a4
+#define PCI_DEVICE_ID_INTEL_ESB_5 0x25a6
+#define PCI_DEVICE_ID_INTEL_ESB_9 0x25ab
+#define PCI_DEVICE_ID_INTEL_ESB_10 0x25ac
+#define PCI_DEVICE_ID_INTEL_82820_HB 0x2500
+#define PCI_DEVICE_ID_INTEL_82820_UP_HB 0x2501
+#define PCI_DEVICE_ID_INTEL_82850_HB 0x2530
+#define PCI_DEVICE_ID_INTEL_82860_HB 0x2531
+#define PCI_DEVICE_ID_INTEL_E7501_MCH 0x254c
+#define PCI_DEVICE_ID_INTEL_82845G_HB 0x2560
+#define PCI_DEVICE_ID_INTEL_82845G_IG 0x2562
+#define PCI_DEVICE_ID_INTEL_82865_HB 0x2570
+#define PCI_DEVICE_ID_INTEL_82865_IG 0x2572
+#define PCI_DEVICE_ID_INTEL_82875_HB 0x2578
+#define PCI_DEVICE_ID_INTEL_82915G_HB 0x2580
+#define PCI_DEVICE_ID_INTEL_82915G_IG 0x2582
+#define PCI_DEVICE_ID_INTEL_82915GM_HB 0x2590
+#define PCI_DEVICE_ID_INTEL_82915GM_IG 0x2592
+#define PCI_DEVICE_ID_INTEL_5000_ERR 0x25F0
+#define PCI_DEVICE_ID_INTEL_5000_FBD0 0x25F5
+#define PCI_DEVICE_ID_INTEL_5000_FBD1 0x25F6
+#define PCI_DEVICE_ID_INTEL_82945G_HB 0x2770
+#define PCI_DEVICE_ID_INTEL_82945G_IG 0x2772
+#define PCI_DEVICE_ID_INTEL_3000_HB 0x2778
+#define PCI_DEVICE_ID_INTEL_82945GM_HB 0x27A0
+#define PCI_DEVICE_ID_INTEL_82945GM_IG 0x27A2
+#define PCI_DEVICE_ID_INTEL_ICH6_0 0x2640
+#define PCI_DEVICE_ID_INTEL_ICH6_1 0x2641
+#define PCI_DEVICE_ID_INTEL_ICH6_2 0x2642
+#define PCI_DEVICE_ID_INTEL_ICH6_16 0x266a
+#define PCI_DEVICE_ID_INTEL_ICH6_17 0x266d
+#define PCI_DEVICE_ID_INTEL_ICH6_18 0x266e
+#define PCI_DEVICE_ID_INTEL_ICH6_19 0x266f
+#define PCI_DEVICE_ID_INTEL_ESB2_0 0x2670
+#define PCI_DEVICE_ID_INTEL_ESB2_14 0x2698
+#define PCI_DEVICE_ID_INTEL_ESB2_17 0x269b
+#define PCI_DEVICE_ID_INTEL_ESB2_18 0x269e
+#define PCI_DEVICE_ID_INTEL_ICH7_0 0x27b8
+#define PCI_DEVICE_ID_INTEL_ICH7_1 0x27b9
+#define PCI_DEVICE_ID_INTEL_ICH7_30 0x27b0
+#define PCI_DEVICE_ID_INTEL_TGP_LPC 0x27bc
+#define PCI_DEVICE_ID_INTEL_ICH7_31 0x27bd
+#define PCI_DEVICE_ID_INTEL_ICH7_17 0x27da
+#define PCI_DEVICE_ID_INTEL_ICH7_19 0x27dd
+#define PCI_DEVICE_ID_INTEL_ICH7_20 0x27de
+#define PCI_DEVICE_ID_INTEL_ICH7_21 0x27df
+#define PCI_DEVICE_ID_INTEL_ICH8_0 0x2810
+#define PCI_DEVICE_ID_INTEL_ICH8_1 0x2811
+#define PCI_DEVICE_ID_INTEL_ICH8_2 0x2812
+#define PCI_DEVICE_ID_INTEL_ICH8_3 0x2814
+#define PCI_DEVICE_ID_INTEL_ICH8_4 0x2815
+#define PCI_DEVICE_ID_INTEL_ICH8_5 0x283e
+#define PCI_DEVICE_ID_INTEL_ICH8_6 0x2850
+#define PCI_DEVICE_ID_INTEL_ICH9_0 0x2910
+#define PCI_DEVICE_ID_INTEL_ICH9_1 0x2917
+#define PCI_DEVICE_ID_INTEL_ICH9_2 0x2912
+#define PCI_DEVICE_ID_INTEL_ICH9_3 0x2913
+#define PCI_DEVICE_ID_INTEL_ICH9_4 0x2914
+#define PCI_DEVICE_ID_INTEL_ICH9_5 0x2919
+#define PCI_DEVICE_ID_INTEL_ICH9_6 0x2930
+#define PCI_DEVICE_ID_INTEL_ICH9_7 0x2916
+#define PCI_DEVICE_ID_INTEL_ICH9_8 0x2918
+#define PCI_DEVICE_ID_INTEL_I7_MCR 0x2c18
+#define PCI_DEVICE_ID_INTEL_I7_MC_TAD 0x2c19
+#define PCI_DEVICE_ID_INTEL_I7_MC_RAS 0x2c1a
+#define PCI_DEVICE_ID_INTEL_I7_MC_TEST 0x2c1c
+#define PCI_DEVICE_ID_INTEL_I7_MC_CH0_CTRL 0x2c20
+#define PCI_DEVICE_ID_INTEL_I7_MC_CH0_ADDR 0x2c21
+#define PCI_DEVICE_ID_INTEL_I7_MC_CH0_RANK 0x2c22
+#define PCI_DEVICE_ID_INTEL_I7_MC_CH0_TC 0x2c23
+#define PCI_DEVICE_ID_INTEL_I7_MC_CH1_CTRL 0x2c28
+#define PCI_DEVICE_ID_INTEL_I7_MC_CH1_ADDR 0x2c29
+#define PCI_DEVICE_ID_INTEL_I7_MC_CH1_RANK 0x2c2a
+#define PCI_DEVICE_ID_INTEL_I7_MC_CH1_TC 0x2c2b
+#define PCI_DEVICE_ID_INTEL_I7_MC_CH2_CTRL 0x2c30
+#define PCI_DEVICE_ID_INTEL_I7_MC_CH2_ADDR 0x2c31
+#define PCI_DEVICE_ID_INTEL_I7_MC_CH2_RANK 0x2c32
+#define PCI_DEVICE_ID_INTEL_I7_MC_CH2_TC 0x2c33
+#define PCI_DEVICE_ID_INTEL_I7_NONCORE 0x2c41
+#define PCI_DEVICE_ID_INTEL_I7_NONCORE_ALT 0x2c40
+#define PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE 0x2c50
+#define PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE_ALT 0x2c51
+#define PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE_REV2 0x2c70
+#define PCI_DEVICE_ID_INTEL_LYNNFIELD_SAD 0x2c81
+#define PCI_DEVICE_ID_INTEL_LYNNFIELD_QPI_LINK0 0x2c90
+#define PCI_DEVICE_ID_INTEL_LYNNFIELD_QPI_PHY0 0x2c91
+#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MCR 0x2c98
+#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TAD 0x2c99
+#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TEST 0x2c9C
+#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_CTRL 0x2ca0
+#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_ADDR 0x2ca1
+#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_RANK 0x2ca2
+#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_TC 0x2ca3
+#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_CTRL 0x2ca8
+#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_ADDR 0x2ca9
+#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_RANK 0x2caa
+#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_TC 0x2cab
+#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MCR_REV2 0x2d98
+#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TAD_REV2 0x2d99
+#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_RAS_REV2 0x2d9a
+#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TEST_REV2 0x2d9c
+#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_CTRL_REV2 0x2da0
+#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_ADDR_REV2 0x2da1
+#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_RANK_REV2 0x2da2
+#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_TC_REV2 0x2da3
+#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_CTRL_REV2 0x2da8
+#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_ADDR_REV2 0x2da9
+#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_RANK_REV2 0x2daa
+#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_TC_REV2 0x2dab
+#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_CTRL_REV2 0x2db0
+#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_ADDR_REV2 0x2db1
+#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_RANK_REV2 0x2db2
+#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_TC_REV2 0x2db3
+#define PCI_DEVICE_ID_INTEL_82855PM_HB 0x3340
+#define PCI_DEVICE_ID_INTEL_IOAT_TBG4 0x3429
+#define PCI_DEVICE_ID_INTEL_IOAT_TBG5 0x342a
+#define PCI_DEVICE_ID_INTEL_IOAT_TBG6 0x342b
+#define PCI_DEVICE_ID_INTEL_IOAT_TBG7 0x342c
+#define PCI_DEVICE_ID_INTEL_X58_HUB_MGMT 0x342e
+#define PCI_DEVICE_ID_INTEL_IOAT_TBG0 0x3430
+#define PCI_DEVICE_ID_INTEL_IOAT_TBG1 0x3431
+#define PCI_DEVICE_ID_INTEL_IOAT_TBG2 0x3432
+#define PCI_DEVICE_ID_INTEL_IOAT_TBG3 0x3433
+#define PCI_DEVICE_ID_INTEL_82830_HB 0x3575
+#define PCI_DEVICE_ID_INTEL_82830_CGC 0x3577
+#define PCI_DEVICE_ID_INTEL_82854_HB 0x358c
+#define PCI_DEVICE_ID_INTEL_82854_IG 0x358e
+#define PCI_DEVICE_ID_INTEL_82855GM_HB 0x3580
+#define PCI_DEVICE_ID_INTEL_82855GM_IG 0x3582
+#define PCI_DEVICE_ID_INTEL_E7520_MCH 0x3590
+#define PCI_DEVICE_ID_INTEL_E7320_MCH 0x3592
+#define PCI_DEVICE_ID_INTEL_MCH_PA 0x3595
+#define PCI_DEVICE_ID_INTEL_MCH_PA1 0x3596
+#define PCI_DEVICE_ID_INTEL_MCH_PB 0x3597
+#define PCI_DEVICE_ID_INTEL_MCH_PB1 0x3598
+#define PCI_DEVICE_ID_INTEL_MCH_PC 0x3599
+#define PCI_DEVICE_ID_INTEL_MCH_PC1 0x359a
+#define PCI_DEVICE_ID_INTEL_E7525_MCH 0x359e
+#define PCI_DEVICE_ID_INTEL_I7300_MCH_ERR 0x360c
+#define PCI_DEVICE_ID_INTEL_I7300_MCH_FB0 0x360f
+#define PCI_DEVICE_ID_INTEL_I7300_MCH_FB1 0x3610
+#define PCI_DEVICE_ID_INTEL_IOAT_CNB 0x360b
+#define PCI_DEVICE_ID_INTEL_FBD_CNB 0x360c
+#define PCI_DEVICE_ID_INTEL_IOAT_JSF0 0x3710
+#define PCI_DEVICE_ID_INTEL_IOAT_JSF1 0x3711
+#define PCI_DEVICE_ID_INTEL_IOAT_JSF2 0x3712
+#define PCI_DEVICE_ID_INTEL_IOAT_JSF3 0x3713
+#define PCI_DEVICE_ID_INTEL_IOAT_JSF4 0x3714
+#define PCI_DEVICE_ID_INTEL_IOAT_JSF5 0x3715
+#define PCI_DEVICE_ID_INTEL_IOAT_JSF6 0x3716
+#define PCI_DEVICE_ID_INTEL_IOAT_JSF7 0x3717
+#define PCI_DEVICE_ID_INTEL_IOAT_JSF8 0x3718
+#define PCI_DEVICE_ID_INTEL_IOAT_JSF9 0x3719
+#define PCI_DEVICE_ID_INTEL_ICH10_0 0x3a14
+#define PCI_DEVICE_ID_INTEL_ICH10_1 0x3a16
+#define PCI_DEVICE_ID_INTEL_ICH10_2 0x3a18
+#define PCI_DEVICE_ID_INTEL_ICH10_3 0x3a1a
+#define PCI_DEVICE_ID_INTEL_ICH10_4 0x3a30
+#define PCI_DEVICE_ID_INTEL_ICH10_5 0x3a60
+#define PCI_DEVICE_ID_INTEL_5_3400_SERIES_LPC_MIN 0x3b00
+#define PCI_DEVICE_ID_INTEL_5_3400_SERIES_LPC_MAX 0x3b1f
+#define PCI_DEVICE_ID_INTEL_IOAT_SNB0 0x3c20
+#define PCI_DEVICE_ID_INTEL_IOAT_SNB1 0x3c21
+#define PCI_DEVICE_ID_INTEL_IOAT_SNB2 0x3c22
+#define PCI_DEVICE_ID_INTEL_IOAT_SNB3 0x3c23
+#define PCI_DEVICE_ID_INTEL_IOAT_SNB4 0x3c24
+#define PCI_DEVICE_ID_INTEL_IOAT_SNB5 0x3c25
+#define PCI_DEVICE_ID_INTEL_IOAT_SNB6 0x3c26
+#define PCI_DEVICE_ID_INTEL_IOAT_SNB7 0x3c27
+#define PCI_DEVICE_ID_INTEL_IOAT_SNB8 0x3c2e
+#define PCI_DEVICE_ID_INTEL_IOAT_SNB9 0x3c2f
+#define PCI_DEVICE_ID_INTEL_UNC_HA 0x3c46
+#define PCI_DEVICE_ID_INTEL_UNC_IMC0 0x3cb0
+#define PCI_DEVICE_ID_INTEL_UNC_IMC1 0x3cb1
+#define PCI_DEVICE_ID_INTEL_UNC_IMC2 0x3cb4
+#define PCI_DEVICE_ID_INTEL_UNC_IMC3 0x3cb5
+#define PCI_DEVICE_ID_INTEL_UNC_QPI0 0x3c41
+#define PCI_DEVICE_ID_INTEL_UNC_QPI1 0x3c42
+#define PCI_DEVICE_ID_INTEL_UNC_R2PCIE 0x3c43
+#define PCI_DEVICE_ID_INTEL_UNC_R3QPI0 0x3c44
+#define PCI_DEVICE_ID_INTEL_UNC_R3QPI1 0x3c45
+#define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_RAS 0x3c71 /* 15.1 */
+#define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_ERR0 0x3c72 /* 16.2 */
+#define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_ERR1 0x3c73 /* 16.3 */
+#define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_ERR2 0x3c76 /* 16.6 */
+#define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_ERR3 0x3c77 /* 16.7 */
+#define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_HA0 0x3ca0 /* 14.0 */
+#define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TA 0x3ca8 /* 15.0 */
+#define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TAD0 0x3caa /* 15.2 */
+#define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TAD1 0x3cab /* 15.3 */
+#define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TAD2 0x3cac /* 15.4 */
+#define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TAD3 0x3cad /* 15.5 */
+#define PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_DDRIO 0x3cb8 /* 17.0 */
+#define PCI_DEVICE_ID_INTEL_JAKETOWN_UBOX 0x3ce0
+#define PCI_DEVICE_ID_INTEL_SBRIDGE_SAD0 0x3cf4 /* 12.6 */
+#define PCI_DEVICE_ID_INTEL_SBRIDGE_BR 0x3cf5 /* 13.6 */
+#define PCI_DEVICE_ID_INTEL_SBRIDGE_SAD1 0x3cf6 /* 12.7 */
+#define PCI_DEVICE_ID_INTEL_IOAT_SNB 0x402f
+#define PCI_DEVICE_ID_INTEL_5100_16 0x65f0
+#define PCI_DEVICE_ID_INTEL_5100_19 0x65f3
+#define PCI_DEVICE_ID_INTEL_5100_21 0x65f5
+#define PCI_DEVICE_ID_INTEL_5100_22 0x65f6
+#define PCI_DEVICE_ID_INTEL_5400_ERR 0x4030
+#define PCI_DEVICE_ID_INTEL_5400_FBD0 0x4035
+#define PCI_DEVICE_ID_INTEL_5400_FBD1 0x4036
+#define PCI_DEVICE_ID_INTEL_IOAT_SCNB 0x65ff
+#define PCI_DEVICE_ID_INTEL_EP80579_0 0x5031
+#define PCI_DEVICE_ID_INTEL_EP80579_1 0x5032
+#define PCI_DEVICE_ID_INTEL_82371SB_0 0x7000
+#define PCI_DEVICE_ID_INTEL_82371SB_1 0x7010
+#define PCI_DEVICE_ID_INTEL_82371SB_2 0x7020
+#define PCI_DEVICE_ID_INTEL_82437VX 0x7030
+#define PCI_DEVICE_ID_INTEL_82439TX 0x7100
+#define PCI_DEVICE_ID_INTEL_82371AB_0 0x7110
+#define PCI_DEVICE_ID_INTEL_82371AB 0x7111
+#define PCI_DEVICE_ID_INTEL_82371AB_2 0x7112
+#define PCI_DEVICE_ID_INTEL_82371AB_3 0x7113
+#define PCI_DEVICE_ID_INTEL_82810_MC1 0x7120
+#define PCI_DEVICE_ID_INTEL_82810_IG1 0x7121
+#define PCI_DEVICE_ID_INTEL_82810_MC3 0x7122
+#define PCI_DEVICE_ID_INTEL_82810_IG3 0x7123
+#define PCI_DEVICE_ID_INTEL_82810E_MC 0x7124
+#define PCI_DEVICE_ID_INTEL_82810E_IG 0x7125
+#define PCI_DEVICE_ID_INTEL_82443LX_0 0x7180
+#define PCI_DEVICE_ID_INTEL_82443LX_1 0x7181
+#define PCI_DEVICE_ID_INTEL_82443BX_0 0x7190
+#define PCI_DEVICE_ID_INTEL_82443BX_1 0x7191
+#define PCI_DEVICE_ID_INTEL_82443BX_2 0x7192
+#define PCI_DEVICE_ID_INTEL_440MX 0x7195
+#define PCI_DEVICE_ID_INTEL_440MX_6 0x7196
+#define PCI_DEVICE_ID_INTEL_82443MX_0 0x7198
+#define PCI_DEVICE_ID_INTEL_82443MX_1 0x7199
+#define PCI_DEVICE_ID_INTEL_82443MX_3 0x719b
+#define PCI_DEVICE_ID_INTEL_82443GX_0 0x71a0
+#define PCI_DEVICE_ID_INTEL_82443GX_2 0x71a2
+#define PCI_DEVICE_ID_INTEL_82372FB_1 0x7601
+#define PCI_DEVICE_ID_INTEL_SCH_LPC 0x8119
+#define PCI_DEVICE_ID_INTEL_SCH_IDE 0x811a
+#define PCI_DEVICE_ID_INTEL_E6XX_CU 0x8183
+#define PCI_DEVICE_ID_INTEL_ITC_LPC 0x8186
+#define PCI_DEVICE_ID_INTEL_82454GX 0x84c4
+#define PCI_DEVICE_ID_INTEL_82450GX 0x84c5
+#define PCI_DEVICE_ID_INTEL_82451NX 0x84ca
+#define PCI_DEVICE_ID_INTEL_82454NX 0x84cb
+#define PCI_DEVICE_ID_INTEL_84460GX 0x84ea
+#define PCI_DEVICE_ID_INTEL_IXP4XX 0x8500
+#define PCI_DEVICE_ID_INTEL_IXP2800 0x9004
+#define PCI_DEVICE_ID_INTEL_S21152BB 0xb152
+
+#define PCI_VENDOR_ID_SCALEMP 0x8686
+#define PCI_DEVICE_ID_SCALEMP_VSMP_CTL 0x1010
+
+#define PCI_VENDOR_ID_COMPUTONE 0x8e0e
+#define PCI_DEVICE_ID_COMPUTONE_PG 0x0302
+#define PCI_SUBVENDOR_ID_COMPUTONE 0x8e0e
+#define PCI_SUBDEVICE_ID_COMPUTONE_PG4 0x0001
+#define PCI_SUBDEVICE_ID_COMPUTONE_PG8 0x0002
+#define PCI_SUBDEVICE_ID_COMPUTONE_PG6 0x0003
+
+#define PCI_VENDOR_ID_KTI 0x8e2e
+
+#define PCI_VENDOR_ID_ADAPTEC 0x9004
+#define PCI_DEVICE_ID_ADAPTEC_7810 0x1078
+#define PCI_DEVICE_ID_ADAPTEC_7821 0x2178
+#define PCI_DEVICE_ID_ADAPTEC_38602 0x3860
+#define PCI_DEVICE_ID_ADAPTEC_7850 0x5078
+#define PCI_DEVICE_ID_ADAPTEC_7855 0x5578
+#define PCI_DEVICE_ID_ADAPTEC_3860 0x6038
+#define PCI_DEVICE_ID_ADAPTEC_1480A 0x6075
+#define PCI_DEVICE_ID_ADAPTEC_7860 0x6078
+#define PCI_DEVICE_ID_ADAPTEC_7861 0x6178
+#define PCI_DEVICE_ID_ADAPTEC_7870 0x7078
+#define PCI_DEVICE_ID_ADAPTEC_7871 0x7178
+#define PCI_DEVICE_ID_ADAPTEC_7872 0x7278
+#define PCI_DEVICE_ID_ADAPTEC_7873 0x7378
+#define PCI_DEVICE_ID_ADAPTEC_7874 0x7478
+#define PCI_DEVICE_ID_ADAPTEC_7895 0x7895
+#define PCI_DEVICE_ID_ADAPTEC_7880 0x8078
+#define PCI_DEVICE_ID_ADAPTEC_7881 0x8178
+#define PCI_DEVICE_ID_ADAPTEC_7882 0x8278
+#define PCI_DEVICE_ID_ADAPTEC_7883 0x8378
+#define PCI_DEVICE_ID_ADAPTEC_7884 0x8478
+#define PCI_DEVICE_ID_ADAPTEC_7885 0x8578
+#define PCI_DEVICE_ID_ADAPTEC_7886 0x8678
+#define PCI_DEVICE_ID_ADAPTEC_7887 0x8778
+#define PCI_DEVICE_ID_ADAPTEC_7888 0x8878
+
+#define PCI_VENDOR_ID_ADAPTEC2 0x9005
+#define PCI_DEVICE_ID_ADAPTEC2_2940U2 0x0010
+#define PCI_DEVICE_ID_ADAPTEC2_2930U2 0x0011
+#define PCI_DEVICE_ID_ADAPTEC2_7890B 0x0013
+#define PCI_DEVICE_ID_ADAPTEC2_7890 0x001f
+#define PCI_DEVICE_ID_ADAPTEC2_3940U2 0x0050
+#define PCI_DEVICE_ID_ADAPTEC2_3950U2D 0x0051
+#define PCI_DEVICE_ID_ADAPTEC2_7896 0x005f
+#define PCI_DEVICE_ID_ADAPTEC2_7892A 0x0080
+#define PCI_DEVICE_ID_ADAPTEC2_7892B 0x0081
+#define PCI_DEVICE_ID_ADAPTEC2_7892D 0x0083
+#define PCI_DEVICE_ID_ADAPTEC2_7892P 0x008f
+#define PCI_DEVICE_ID_ADAPTEC2_7899A 0x00c0
+#define PCI_DEVICE_ID_ADAPTEC2_7899B 0x00c1
+#define PCI_DEVICE_ID_ADAPTEC2_7899D 0x00c3
+#define PCI_DEVICE_ID_ADAPTEC2_7899P 0x00cf
+#define PCI_DEVICE_ID_ADAPTEC2_OBSIDIAN 0x0500
+#define PCI_DEVICE_ID_ADAPTEC2_SCAMP 0x0503
+
+#define PCI_VENDOR_ID_HOLTEK 0x9412
+#define PCI_DEVICE_ID_HOLTEK_6565 0x6565
+
+#define PCI_VENDOR_ID_NETMOS 0x9710
+#define PCI_DEVICE_ID_NETMOS_9705 0x9705
+#define PCI_DEVICE_ID_NETMOS_9715 0x9715
+#define PCI_DEVICE_ID_NETMOS_9735 0x9735
+#define PCI_DEVICE_ID_NETMOS_9745 0x9745
+#define PCI_DEVICE_ID_NETMOS_9755 0x9755
+#define PCI_DEVICE_ID_NETMOS_9805 0x9805
+#define PCI_DEVICE_ID_NETMOS_9815 0x9815
+#define PCI_DEVICE_ID_NETMOS_9835 0x9835
+#define PCI_DEVICE_ID_NETMOS_9845 0x9845
+#define PCI_DEVICE_ID_NETMOS_9855 0x9855
+#define PCI_DEVICE_ID_NETMOS_9865 0x9865
+#define PCI_DEVICE_ID_NETMOS_9900 0x9900
+#define PCI_DEVICE_ID_NETMOS_9901 0x9901
+#define PCI_DEVICE_ID_NETMOS_9904 0x9904
+#define PCI_DEVICE_ID_NETMOS_9912 0x9912
+#define PCI_DEVICE_ID_NETMOS_9922 0x9922
+
+#define PCI_VENDOR_ID_3COM_2 0xa727
+
+#define PCI_VENDOR_ID_DIGIUM 0xd161
+#define PCI_DEVICE_ID_DIGIUM_HFC4S 0xb410
+
+#define PCI_SUBVENDOR_ID_EXSYS 0xd84d
+#define PCI_SUBDEVICE_ID_EXSYS_4014 0x4014
+#define PCI_SUBDEVICE_ID_EXSYS_4055 0x4055
+
+#define PCI_VENDOR_ID_TIGERJET 0xe159
+#define PCI_DEVICE_ID_TIGERJET_300 0x0001
+#define PCI_DEVICE_ID_TIGERJET_100 0x0002
+
+#define PCI_VENDOR_ID_XILINX_RME 0xea60
+#define PCI_DEVICE_ID_RME_DIGI32 0x9896
+#define PCI_DEVICE_ID_RME_DIGI32_PRO 0x9897
+#define PCI_DEVICE_ID_RME_DIGI32_8 0x9898
+
+#define PCI_VENDOR_ID_XEN 0x5853
+#define PCI_DEVICE_ID_XEN_PLATFORM 0x0001
+
+#define PCI_VENDOR_ID_OCZ 0x1b85
+
+#endif /* _LINUX_PCI_IDS_H */
diff --git a/include/linux/platform_data/brcmfmac.h b/include/linux/platform_data/brcmfmac.h
new file mode 100644
index 0000000..1d30bf2
--- /dev/null
+++ b/include/linux/platform_data/brcmfmac.h
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 201 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _LINUX_BRCMFMAC_PLATFORM_H
+#define _LINUX_BRCMFMAC_PLATFORM_H
+
+
+#define BRCMFMAC_PDATA_NAME "brcmfmac"
+
+#define BRCMFMAC_COUNTRY_BUF_SZ 4
+
+
+/*
+ * Platform specific driver functions and data. Through the platform specific
+ * device data functions and data can be provided to help the brcmfmac driver to
+ * operate with the device in combination with the used platform.
+ */
+
+
+/**
+ * Note: the brcmfmac can be loaded as module or be statically built-in into
+ * the kernel. If built-in then do note that it uses module_init (and
+ * module_exit) routines which equal device_initcall. So if you intend to
+ * create a module with the platform specific data for the brcmfmac and have
+ * it built-in to the kernel then use a higher initcall then device_initcall
+ * (see init.h). If this is not done then brcmfmac will load without problems
+ * but will not pickup the platform data.
+ *
+ * When the driver does not "detect" platform driver data then it will continue
+ * without reporting anything and just assume there is no data needed. Which is
+ * probably true for most platforms.
+ */
+
+/**
+ * enum brcmf_bus_type - Bus type identifier. Currently SDIO, USB and PCIE are
+ * supported.
+ */
+enum brcmf_bus_type {
+ BRCMF_BUSTYPE_SDIO,
+ BRCMF_BUSTYPE_USB,
+ BRCMF_BUSTYPE_PCIE
+};
+
+
+/**
+ * struct brcmfmac_sdio_pd - SDIO Device specific platform data.
+ *
+ * @txglomsz: SDIO txglom size. Use 0 if default of driver is to be
+ * used.
+ * @drive_strength: is the preferred drive_strength to be used for the SDIO
+ * pins. If 0 then a default value will be used. This is
+ * the target drive strength, the exact drive strength
+ * which will be used depends on the capabilities of the
+ * device.
+ * @oob_irq_supported: does the board have support for OOB interrupts. SDIO
+ * in-band interrupts are relatively slow and for having
+ * less overhead on interrupt processing an out of band
+ * interrupt can be used. If the HW supports this then
+ * enable this by setting this field to true and configure
+ * the oob related fields.
+ * @oob_irq_nr,
+ * @oob_irq_flags: the OOB interrupt information. The values are used for
+ * registering the irq using request_irq function.
+ * @broken_sg_support: flag for broken sg list support of SDIO host controller.
+ * Set this to true if the SDIO host controller has higher
+ * align requirement than 32 bytes for each scatterlist
+ * item.
+ * @sd_head_align: alignment requirement for start of data buffer.
+ * @sd_sgentry_align: length alignment requirement for each sg entry.
+ * @reset: This function can get called if the device communication
+ * broke down. This functionality is particularly useful in
+ * case of SDIO type devices. It is possible to reset a
+ * dongle via sdio data interface, but it requires that
+ * this is fully functional. This function is chip/module
+ * specific and this function should return only after the
+ * complete reset has completed.
+ */
+struct brcmfmac_sdio_pd {
+ int txglomsz;
+ unsigned int drive_strength;
+ bool oob_irq_supported;
+ unsigned int oob_irq_nr;
+ unsigned long oob_irq_flags;
+ bool broken_sg_support;
+ unsigned short sd_head_align;
+ unsigned short sd_sgentry_align;
+ void (*reset)(void);
+};
+
+/**
+ * struct brcmfmac_pd_cc_entry - Struct for translating user space country code
+ * (iso3166) to firmware country code and
+ * revision.
+ *
+ * @iso3166: iso3166 alpha 2 country code string.
+ * @cc: firmware country code string.
+ * @rev: firmware country code revision.
+ */
+struct brcmfmac_pd_cc_entry {
+ char iso3166[BRCMFMAC_COUNTRY_BUF_SZ];
+ char cc[BRCMFMAC_COUNTRY_BUF_SZ];
+ s32 rev;
+};
+
+/**
+ * struct brcmfmac_pd_cc - Struct for translating country codes as set by user
+ * space to a country code and rev which can be used by
+ * firmware.
+ *
+ * @table_size: number of entries in table (> 0)
+ * @table: array of 1 or more elements with translation information.
+ */
+struct brcmfmac_pd_cc {
+ int table_size;
+ struct brcmfmac_pd_cc_entry table[0];
+};
+
+/**
+ * struct brcmfmac_pd_device - Device specific platform data. (id/rev/bus_type)
+ * is the unique identifier of the device.
+ *
+ * @id: ID of the device for which this data is. In case of SDIO
+ * or PCIE this is the chipid as identified by chip.c In
+ * case of USB this is the chipid as identified by the
+ * device query.
+ * @rev: chip revision, see id.
+ * @bus_type: The type of bus. Some chipid/rev exist for different bus
+ * types. Each bus type has its own set of settings.
+ * @feature_disable: Bitmask of features to disable (override), See feature.c
+ * in brcmfmac for details.
+ * @country_codes: If available, pointer to struct for translating country
+ * codes.
+ * @bus: Bus specific (union) device settings. Currently only
+ * SDIO.
+ */
+struct brcmfmac_pd_device {
+ unsigned int id;
+ unsigned int rev;
+ enum brcmf_bus_type bus_type;
+ unsigned int feature_disable;
+ struct brcmfmac_pd_cc *country_codes;
+ union {
+ struct brcmfmac_sdio_pd sdio;
+ } bus;
+};
+
+/**
+ * struct brcmfmac_platform_data - BRCMFMAC specific platform data.
+ *
+ * @power_on: This function is called by the brcmfmac driver when the module
+ * gets loaded. This can be particularly useful for low power
+ * devices. The platform spcific routine may for example decide to
+ * power up the complete device. If there is no use-case for this
+ * function then provide NULL.
+ * @power_off: This function is called by the brcmfmac when the module gets
+ * unloaded. At this point the devices can be powered down or
+ * otherwise be reset. So if an actual power_off is not supported
+ * but reset is supported by the devices then reset the devices
+ * when this function gets called. This can be particularly useful
+ * for low power devices. If there is no use-case for this
+ * function then provide NULL.
+ */
+struct brcmfmac_platform_data {
+ void (*power_on)(void);
+ void (*power_off)(void);
+ char *fw_alternative_path;
+ int device_count;
+ struct brcmfmac_pd_device devices[0];
+};
+
+
+#endif /* _LINUX_BRCMFMAC_PLATFORM_H */
diff --git a/include/linux/rhashtable.h b/include/linux/rhashtable.h
new file mode 100644
index 0000000..da79a7c
--- /dev/null
+++ b/include/linux/rhashtable.h
@@ -0,0 +1,34 @@
+/* Automatically created during backport process */
+#ifndef CPTCFG_BPAUTO_RHASHTABLE
+#include_next <linux/rhashtable.h>
+#else
+#undef lockdep_rht_mutex_is_held
+#define lockdep_rht_mutex_is_held LINUX_BACKPORT(lockdep_rht_mutex_is_held)
+#undef lockdep_rht_bucket_is_held
+#define lockdep_rht_bucket_is_held LINUX_BACKPORT(lockdep_rht_bucket_is_held)
+#undef rhashtable_insert_slow
+#define rhashtable_insert_slow LINUX_BACKPORT(rhashtable_insert_slow)
+#undef rhashtable_walk_enter
+#define rhashtable_walk_enter LINUX_BACKPORT(rhashtable_walk_enter)
+#undef rhashtable_walk_exit
+#define rhashtable_walk_exit LINUX_BACKPORT(rhashtable_walk_exit)
+#undef rhashtable_walk_start
+#define rhashtable_walk_start LINUX_BACKPORT(rhashtable_walk_start)
+#undef rhashtable_walk_next
+#define rhashtable_walk_next LINUX_BACKPORT(rhashtable_walk_next)
+#undef rhashtable_walk_stop
+#define rhashtable_walk_stop LINUX_BACKPORT(rhashtable_walk_stop)
+#undef rhashtable_init
+#define rhashtable_init LINUX_BACKPORT(rhashtable_init)
+#undef rhltable_init
+#define rhltable_init LINUX_BACKPORT(rhltable_init)
+#undef rhashtable_free_and_destroy
+#define rhashtable_free_and_destroy LINUX_BACKPORT(rhashtable_free_and_destroy)
+#undef rhashtable_destroy
+#define rhashtable_destroy LINUX_BACKPORT(rhashtable_destroy)
+#undef rht_bucket_nested
+#define rht_bucket_nested LINUX_BACKPORT(rht_bucket_nested)
+#undef rht_bucket_nested_insert
+#define rht_bucket_nested_insert LINUX_BACKPORT(rht_bucket_nested_insert)
+#include <linux/backport-rhashtable.h>
+#endif /* CPTCFG_BPAUTO_RHASHTABLE */
diff --git a/include/linux/unaligned/access_ok.h b/include/linux/unaligned/access_ok.h
new file mode 100644
index 0000000..33383ca
--- /dev/null
+++ b/include/linux/unaligned/access_ok.h
@@ -0,0 +1,67 @@
+#ifndef _LINUX_UNALIGNED_ACCESS_OK_H
+#define _LINUX_UNALIGNED_ACCESS_OK_H
+
+#include <linux/kernel.h>
+#include <asm/byteorder.h>
+
+static __always_inline u16 get_unaligned_le16(const void *p)
+{
+ return le16_to_cpup((__le16 *)p);
+}
+
+static __always_inline u32 get_unaligned_le32(const void *p)
+{
+ return le32_to_cpup((__le32 *)p);
+}
+
+static __always_inline u64 get_unaligned_le64(const void *p)
+{
+ return le64_to_cpup((__le64 *)p);
+}
+
+static __always_inline u16 get_unaligned_be16(const void *p)
+{
+ return be16_to_cpup((__be16 *)p);
+}
+
+static __always_inline u32 get_unaligned_be32(const void *p)
+{
+ return be32_to_cpup((__be32 *)p);
+}
+
+static __always_inline u64 get_unaligned_be64(const void *p)
+{
+ return be64_to_cpup((__be64 *)p);
+}
+
+static __always_inline void put_unaligned_le16(u16 val, void *p)
+{
+ *((__le16 *)p) = cpu_to_le16(val);
+}
+
+static __always_inline void put_unaligned_le32(u32 val, void *p)
+{
+ *((__le32 *)p) = cpu_to_le32(val);
+}
+
+static __always_inline void put_unaligned_le64(u64 val, void *p)
+{
+ *((__le64 *)p) = cpu_to_le64(val);
+}
+
+static __always_inline void put_unaligned_be16(u16 val, void *p)
+{
+ *((__be16 *)p) = cpu_to_be16(val);
+}
+
+static __always_inline void put_unaligned_be32(u32 val, void *p)
+{
+ *((__be32 *)p) = cpu_to_be32(val);
+}
+
+static __always_inline void put_unaligned_be64(u64 val, void *p)
+{
+ *((__be64 *)p) = cpu_to_be64(val);
+}
+
+#endif /* _LINUX_UNALIGNED_ACCESS_OK_H */
diff --git a/include/linux/unaligned/be_byteshift.h b/include/linux/unaligned/be_byteshift.h
new file mode 100644
index 0000000..9356b24
--- /dev/null
+++ b/include/linux/unaligned/be_byteshift.h
@@ -0,0 +1,70 @@
+#ifndef _LINUX_UNALIGNED_BE_BYTESHIFT_H
+#define _LINUX_UNALIGNED_BE_BYTESHIFT_H
+
+#include <linux/types.h>
+
+static inline u16 __get_unaligned_be16(const u8 *p)
+{
+ return p[0] << 8 | p[1];
+}
+
+static inline u32 __get_unaligned_be32(const u8 *p)
+{
+ return p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3];
+}
+
+static inline u64 __get_unaligned_be64(const u8 *p)
+{
+ return (u64)__get_unaligned_be32(p) << 32 |
+ __get_unaligned_be32(p + 4);
+}
+
+static inline void __put_unaligned_be16(u16 val, u8 *p)
+{
+ *p++ = val >> 8;
+ *p++ = val;
+}
+
+static inline void __put_unaligned_be32(u32 val, u8 *p)
+{
+ __put_unaligned_be16(val >> 16, p);
+ __put_unaligned_be16(val, p + 2);
+}
+
+static inline void __put_unaligned_be64(u64 val, u8 *p)
+{
+ __put_unaligned_be32(val >> 32, p);
+ __put_unaligned_be32(val, p + 4);
+}
+
+static inline u16 get_unaligned_be16(const void *p)
+{
+ return __get_unaligned_be16((const u8 *)p);
+}
+
+static inline u32 get_unaligned_be32(const void *p)
+{
+ return __get_unaligned_be32((const u8 *)p);
+}
+
+static inline u64 get_unaligned_be64(const void *p)
+{
+ return __get_unaligned_be64((const u8 *)p);
+}
+
+static inline void put_unaligned_be16(u16 val, void *p)
+{
+ __put_unaligned_be16(val, p);
+}
+
+static inline void put_unaligned_be32(u32 val, void *p)
+{
+ __put_unaligned_be32(val, p);
+}
+
+static inline void put_unaligned_be64(u64 val, void *p)
+{
+ __put_unaligned_be64(val, p);
+}
+
+#endif /* _LINUX_UNALIGNED_BE_BYTESHIFT_H */
diff --git a/include/linux/unaligned/be_memmove.h b/include/linux/unaligned/be_memmove.h
new file mode 100644
index 0000000..c2a76c5
--- /dev/null
+++ b/include/linux/unaligned/be_memmove.h
@@ -0,0 +1,36 @@
+#ifndef _LINUX_UNALIGNED_BE_MEMMOVE_H
+#define _LINUX_UNALIGNED_BE_MEMMOVE_H
+
+#include <linux/unaligned/memmove.h>
+
+static inline u16 get_unaligned_be16(const void *p)
+{
+ return __get_unaligned_memmove16((const u8 *)p);
+}
+
+static inline u32 get_unaligned_be32(const void *p)
+{
+ return __get_unaligned_memmove32((const u8 *)p);
+}
+
+static inline u64 get_unaligned_be64(const void *p)
+{
+ return __get_unaligned_memmove64((const u8 *)p);
+}
+
+static inline void put_unaligned_be16(u16 val, void *p)
+{
+ __put_unaligned_memmove16(val, p);
+}
+
+static inline void put_unaligned_be32(u32 val, void *p)
+{
+ __put_unaligned_memmove32(val, p);
+}
+
+static inline void put_unaligned_be64(u64 val, void *p)
+{
+ __put_unaligned_memmove64(val, p);
+}
+
+#endif /* _LINUX_UNALIGNED_LE_MEMMOVE_H */
diff --git a/include/linux/unaligned/be_struct.h b/include/linux/unaligned/be_struct.h
new file mode 100644
index 0000000..1324158
--- /dev/null
+++ b/include/linux/unaligned/be_struct.h
@@ -0,0 +1,36 @@
+#ifndef _LINUX_UNALIGNED_BE_STRUCT_H
+#define _LINUX_UNALIGNED_BE_STRUCT_H
+
+#include <linux/unaligned/packed_struct.h>
+
+static inline u16 get_unaligned_be16(const void *p)
+{
+ return __get_unaligned_cpu16((const u8 *)p);
+}
+
+static inline u32 get_unaligned_be32(const void *p)
+{
+ return __get_unaligned_cpu32((const u8 *)p);
+}
+
+static inline u64 get_unaligned_be64(const void *p)
+{
+ return __get_unaligned_cpu64((const u8 *)p);
+}
+
+static inline void put_unaligned_be16(u16 val, void *p)
+{
+ __put_unaligned_cpu16(val, p);
+}
+
+static inline void put_unaligned_be32(u32 val, void *p)
+{
+ __put_unaligned_cpu32(val, p);
+}
+
+static inline void put_unaligned_be64(u64 val, void *p)
+{
+ __put_unaligned_cpu64(val, p);
+}
+
+#endif /* _LINUX_UNALIGNED_BE_STRUCT_H */
diff --git a/include/linux/unaligned/generic.h b/include/linux/unaligned/generic.h
new file mode 100644
index 0000000..02d97ff
--- /dev/null
+++ b/include/linux/unaligned/generic.h
@@ -0,0 +1,68 @@
+#ifndef _LINUX_UNALIGNED_GENERIC_H
+#define _LINUX_UNALIGNED_GENERIC_H
+
+/*
+ * Cause a link-time error if we try an unaligned access other than
+ * 1,2,4 or 8 bytes long
+ */
+extern void __bad_unaligned_access_size(void);
+
+#define __get_unaligned_le(ptr) ((__force typeof(*(ptr)))({ \
+ __builtin_choose_expr(sizeof(*(ptr)) == 1, *(ptr), \
+ __builtin_choose_expr(sizeof(*(ptr)) == 2, get_unaligned_le16((ptr)), \
+ __builtin_choose_expr(sizeof(*(ptr)) == 4, get_unaligned_le32((ptr)), \
+ __builtin_choose_expr(sizeof(*(ptr)) == 8, get_unaligned_le64((ptr)), \
+ __bad_unaligned_access_size())))); \
+ }))
+
+#define __get_unaligned_be(ptr) ((__force typeof(*(ptr)))({ \
+ __builtin_choose_expr(sizeof(*(ptr)) == 1, *(ptr), \
+ __builtin_choose_expr(sizeof(*(ptr)) == 2, get_unaligned_be16((ptr)), \
+ __builtin_choose_expr(sizeof(*(ptr)) == 4, get_unaligned_be32((ptr)), \
+ __builtin_choose_expr(sizeof(*(ptr)) == 8, get_unaligned_be64((ptr)), \
+ __bad_unaligned_access_size())))); \
+ }))
+
+#define __put_unaligned_le(val, ptr) ({ \
+ void *__gu_p = (ptr); \
+ switch (sizeof(*(ptr))) { \
+ case 1: \
+ *(u8 *)__gu_p = (__force u8)(val); \
+ break; \
+ case 2: \
+ put_unaligned_le16((__force u16)(val), __gu_p); \
+ break; \
+ case 4: \
+ put_unaligned_le32((__force u32)(val), __gu_p); \
+ break; \
+ case 8: \
+ put_unaligned_le64((__force u64)(val), __gu_p); \
+ break; \
+ default: \
+ __bad_unaligned_access_size(); \
+ break; \
+ } \
+ (void)0; })
+
+#define __put_unaligned_be(val, ptr) ({ \
+ void *__gu_p = (ptr); \
+ switch (sizeof(*(ptr))) { \
+ case 1: \
+ *(u8 *)__gu_p = (__force u8)(val); \
+ break; \
+ case 2: \
+ put_unaligned_be16((__force u16)(val), __gu_p); \
+ break; \
+ case 4: \
+ put_unaligned_be32((__force u32)(val), __gu_p); \
+ break; \
+ case 8: \
+ put_unaligned_be64((__force u64)(val), __gu_p); \
+ break; \
+ default: \
+ __bad_unaligned_access_size(); \
+ break; \
+ } \
+ (void)0; })
+
+#endif /* _LINUX_UNALIGNED_GENERIC_H */
diff --git a/include/linux/unaligned/le_byteshift.h b/include/linux/unaligned/le_byteshift.h
new file mode 100644
index 0000000..be376fb
--- /dev/null
+++ b/include/linux/unaligned/le_byteshift.h
@@ -0,0 +1,70 @@
+#ifndef _LINUX_UNALIGNED_LE_BYTESHIFT_H
+#define _LINUX_UNALIGNED_LE_BYTESHIFT_H
+
+#include <linux/types.h>
+
+static inline u16 __get_unaligned_le16(const u8 *p)
+{
+ return p[0] | p[1] << 8;
+}
+
+static inline u32 __get_unaligned_le32(const u8 *p)
+{
+ return p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24;
+}
+
+static inline u64 __get_unaligned_le64(const u8 *p)
+{
+ return (u64)__get_unaligned_le32(p + 4) << 32 |
+ __get_unaligned_le32(p);
+}
+
+static inline void __put_unaligned_le16(u16 val, u8 *p)
+{
+ *p++ = val;
+ *p++ = val >> 8;
+}
+
+static inline void __put_unaligned_le32(u32 val, u8 *p)
+{
+ __put_unaligned_le16(val >> 16, p + 2);
+ __put_unaligned_le16(val, p);
+}
+
+static inline void __put_unaligned_le64(u64 val, u8 *p)
+{
+ __put_unaligned_le32(val >> 32, p + 4);
+ __put_unaligned_le32(val, p);
+}
+
+static inline u16 get_unaligned_le16(const void *p)
+{
+ return __get_unaligned_le16((const u8 *)p);
+}
+
+static inline u32 get_unaligned_le32(const void *p)
+{
+ return __get_unaligned_le32((const u8 *)p);
+}
+
+static inline u64 get_unaligned_le64(const void *p)
+{
+ return __get_unaligned_le64((const u8 *)p);
+}
+
+static inline void put_unaligned_le16(u16 val, void *p)
+{
+ __put_unaligned_le16(val, p);
+}
+
+static inline void put_unaligned_le32(u32 val, void *p)
+{
+ __put_unaligned_le32(val, p);
+}
+
+static inline void put_unaligned_le64(u64 val, void *p)
+{
+ __put_unaligned_le64(val, p);
+}
+
+#endif /* _LINUX_UNALIGNED_LE_BYTESHIFT_H */
diff --git a/include/linux/unaligned/le_memmove.h b/include/linux/unaligned/le_memmove.h
new file mode 100644
index 0000000..269849b
--- /dev/null
+++ b/include/linux/unaligned/le_memmove.h
@@ -0,0 +1,36 @@
+#ifndef _LINUX_UNALIGNED_LE_MEMMOVE_H
+#define _LINUX_UNALIGNED_LE_MEMMOVE_H
+
+#include <linux/unaligned/memmove.h>
+
+static inline u16 get_unaligned_le16(const void *p)
+{
+ return __get_unaligned_memmove16((const u8 *)p);
+}
+
+static inline u32 get_unaligned_le32(const void *p)
+{
+ return __get_unaligned_memmove32((const u8 *)p);
+}
+
+static inline u64 get_unaligned_le64(const void *p)
+{
+ return __get_unaligned_memmove64((const u8 *)p);
+}
+
+static inline void put_unaligned_le16(u16 val, void *p)
+{
+ __put_unaligned_memmove16(val, p);
+}
+
+static inline void put_unaligned_le32(u32 val, void *p)
+{
+ __put_unaligned_memmove32(val, p);
+}
+
+static inline void put_unaligned_le64(u64 val, void *p)
+{
+ __put_unaligned_memmove64(val, p);
+}
+
+#endif /* _LINUX_UNALIGNED_LE_MEMMOVE_H */
diff --git a/include/linux/unaligned/le_struct.h b/include/linux/unaligned/le_struct.h
new file mode 100644
index 0000000..088c457
--- /dev/null
+++ b/include/linux/unaligned/le_struct.h
@@ -0,0 +1,36 @@
+#ifndef _LINUX_UNALIGNED_LE_STRUCT_H
+#define _LINUX_UNALIGNED_LE_STRUCT_H
+
+#include <linux/unaligned/packed_struct.h>
+
+static inline u16 get_unaligned_le16(const void *p)
+{
+ return __get_unaligned_cpu16((const u8 *)p);
+}
+
+static inline u32 get_unaligned_le32(const void *p)
+{
+ return __get_unaligned_cpu32((const u8 *)p);
+}
+
+static inline u64 get_unaligned_le64(const void *p)
+{
+ return __get_unaligned_cpu64((const u8 *)p);
+}
+
+static inline void put_unaligned_le16(u16 val, void *p)
+{
+ __put_unaligned_cpu16(val, p);
+}
+
+static inline void put_unaligned_le32(u32 val, void *p)
+{
+ __put_unaligned_cpu32(val, p);
+}
+
+static inline void put_unaligned_le64(u64 val, void *p)
+{
+ __put_unaligned_cpu64(val, p);
+}
+
+#endif /* _LINUX_UNALIGNED_LE_STRUCT_H */
diff --git a/include/linux/unaligned/memmove.h b/include/linux/unaligned/memmove.h
new file mode 100644
index 0000000..eeb5a77
--- /dev/null
+++ b/include/linux/unaligned/memmove.h
@@ -0,0 +1,45 @@
+#ifndef _LINUX_UNALIGNED_MEMMOVE_H
+#define _LINUX_UNALIGNED_MEMMOVE_H
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+
+/* Use memmove here, so gcc does not insert a __builtin_memcpy. */
+
+static inline u16 __get_unaligned_memmove16(const void *p)
+{
+ u16 tmp;
+ memmove(&tmp, p, 2);
+ return tmp;
+}
+
+static inline u32 __get_unaligned_memmove32(const void *p)
+{
+ u32 tmp;
+ memmove(&tmp, p, 4);
+ return tmp;
+}
+
+static inline u64 __get_unaligned_memmove64(const void *p)
+{
+ u64 tmp;
+ memmove(&tmp, p, 8);
+ return tmp;
+}
+
+static inline void __put_unaligned_memmove16(u16 val, void *p)
+{
+ memmove(p, &val, 2);
+}
+
+static inline void __put_unaligned_memmove32(u32 val, void *p)
+{
+ memmove(p, &val, 4);
+}
+
+static inline void __put_unaligned_memmove64(u64 val, void *p)
+{
+ memmove(p, &val, 8);
+}
+
+#endif /* _LINUX_UNALIGNED_MEMMOVE_H */
diff --git a/include/linux/unaligned/packed_struct.h b/include/linux/unaligned/packed_struct.h
new file mode 100644
index 0000000..c0d817d
--- /dev/null
+++ b/include/linux/unaligned/packed_struct.h
@@ -0,0 +1,46 @@
+#ifndef _LINUX_UNALIGNED_PACKED_STRUCT_H
+#define _LINUX_UNALIGNED_PACKED_STRUCT_H
+
+#include <linux/kernel.h>
+
+struct __una_u16 { u16 x; } __packed;
+struct __una_u32 { u32 x; } __packed;
+struct __una_u64 { u64 x; } __packed;
+
+static inline u16 __get_unaligned_cpu16(const void *p)
+{
+ const struct __una_u16 *ptr = (const struct __una_u16 *)p;
+ return ptr->x;
+}
+
+static inline u32 __get_unaligned_cpu32(const void *p)
+{
+ const struct __una_u32 *ptr = (const struct __una_u32 *)p;
+ return ptr->x;
+}
+
+static inline u64 __get_unaligned_cpu64(const void *p)
+{
+ const struct __una_u64 *ptr = (const struct __una_u64 *)p;
+ return ptr->x;
+}
+
+static inline void __put_unaligned_cpu16(u16 val, void *p)
+{
+ struct __una_u16 *ptr = (struct __una_u16 *)p;
+ ptr->x = val;
+}
+
+static inline void __put_unaligned_cpu32(u32 val, void *p)
+{
+ struct __una_u32 *ptr = (struct __una_u32 *)p;
+ ptr->x = val;
+}
+
+static inline void __put_unaligned_cpu64(u64 val, void *p)
+{
+ struct __una_u64 *ptr = (struct __una_u64 *)p;
+ ptr->x = val;
+}
+
+#endif /* _LINUX_UNALIGNED_PACKED_STRUCT_H */
diff --git a/include/linux/wireless.h b/include/linux/wireless.h
new file mode 100644
index 0000000..4ea4c6e
--- /dev/null
+++ b/include/linux/wireless.h
@@ -0,0 +1,44 @@
+/*
+ * This file define a set of standard wireless extensions
+ *
+ * Version : 22 16.3.07
+ *
+ * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com>
+ * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved.
+ */
+#ifndef _LINUX_WIRELESS_H
+#define _LINUX_WIRELESS_H
+
+#include <uapi/linux/wireless.h>
+
+#ifdef CONFIG_COMPAT
+
+#include <linux/compat.h>
+
+struct compat_iw_point {
+ compat_caddr_t pointer;
+ __u16 length;
+ __u16 flags;
+};
+#endif
+#ifdef CONFIG_COMPAT
+struct __compat_iw_event {
+ __u16 len; /* Real length of this stuff */
+ __u16 cmd; /* Wireless IOCTL */
+ compat_caddr_t pointer;
+};
+#define IW_EV_COMPAT_LCP_LEN offsetof(struct __compat_iw_event, pointer)
+#define IW_EV_COMPAT_POINT_OFF offsetof(struct compat_iw_point, length)
+
+/* Size of the various events for compat */
+#define IW_EV_COMPAT_CHAR_LEN (IW_EV_COMPAT_LCP_LEN + IFNAMSIZ)
+#define IW_EV_COMPAT_UINT_LEN (IW_EV_COMPAT_LCP_LEN + sizeof(__u32))
+#define IW_EV_COMPAT_FREQ_LEN (IW_EV_COMPAT_LCP_LEN + sizeof(struct iw_freq))
+#define IW_EV_COMPAT_PARAM_LEN (IW_EV_COMPAT_LCP_LEN + sizeof(struct iw_param))
+#define IW_EV_COMPAT_ADDR_LEN (IW_EV_COMPAT_LCP_LEN + sizeof(struct sockaddr))
+#define IW_EV_COMPAT_QUAL_LEN (IW_EV_COMPAT_LCP_LEN + sizeof(struct iw_quality))
+#define IW_EV_COMPAT_POINT_LEN \
+ (IW_EV_COMPAT_LCP_LEN + sizeof(struct compat_iw_point) - \
+ IW_EV_COMPAT_POINT_OFF)
+#endif
+#endif /* _LINUX_WIRELESS_H */
diff --git a/include/net/cfg80211-wext.h b/include/net/cfg80211-wext.h
new file mode 100644
index 0000000..25baddc
--- /dev/null
+++ b/include/net/cfg80211-wext.h
@@ -0,0 +1,55 @@
+#ifndef __NET_CFG80211_WEXT_H
+#define __NET_CFG80211_WEXT_H
+/*
+ * 802.11 device and configuration interface -- wext handlers
+ *
+ * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/netdevice.h>
+#include <linux/wireless.h>
+#include <net/iw_handler.h>
+
+/*
+ * Temporary wext handlers & helper functions
+ *
+ * These are used only by drivers that aren't yet fully
+ * converted to cfg80211.
+ */
+int cfg80211_wext_giwname(struct net_device *dev,
+ struct iw_request_info *info,
+ char *name, char *extra);
+int cfg80211_wext_siwmode(struct net_device *dev, struct iw_request_info *info,
+ u32 *mode, char *extra);
+int cfg80211_wext_giwmode(struct net_device *dev, struct iw_request_info *info,
+ u32 *mode, char *extra);
+int cfg80211_wext_siwscan(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra);
+int cfg80211_wext_giwscan(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *data, char *extra);
+int cfg80211_wext_giwrange(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *data, char *extra);
+int cfg80211_wext_siwrts(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *rts, char *extra);
+int cfg80211_wext_giwrts(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *rts, char *extra);
+int cfg80211_wext_siwfrag(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *frag, char *extra);
+int cfg80211_wext_giwfrag(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *frag, char *extra);
+int cfg80211_wext_giwretry(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *retry, char *extra);
+
+#endif /* __NET_CFG80211_WEXT_H */
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
new file mode 100644
index 0000000..0c5da76
--- /dev/null
+++ b/include/net/cfg80211.h
@@ -0,0 +1,6248 @@
+#ifndef __NET_CFG80211_H
+#define __NET_CFG80211_H
+/*
+ * 802.11 device and configuration interface
+ *
+ * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2013-2014 Intel Mobile Communications GmbH
+ * Copyright 2015-2017 Intel Deutschland GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/netdevice.h>
+#include <linux/debugfs.h>
+#include <linux/list.h>
+#include <linux/bug.h>
+#include <linux/netlink.h>
+#include <linux/skbuff.h>
+#include <linux/nl80211.h>
+#include <linux/if_ether.h>
+#include <linux/ieee80211.h>
+#include <linux/net.h>
+#include <net/regulatory.h>
+
+/**
+ * DOC: Introduction
+ *
+ * cfg80211 is the configuration API for 802.11 devices in Linux. It bridges
+ * userspace and drivers, and offers some utility functionality associated
+ * with 802.11. cfg80211 must, directly or indirectly via mac80211, be used
+ * by all modern wireless drivers in Linux, so that they offer a consistent
+ * API through nl80211. For backward compatibility, cfg80211 also offers
+ * wireless extensions to userspace, but hides them from drivers completely.
+ *
+ * Additionally, cfg80211 contains code to help enforce regulatory spectrum
+ * use restrictions.
+ */
+
+
+/**
+ * DOC: Device registration
+ *
+ * In order for a driver to use cfg80211, it must register the hardware device
+ * with cfg80211. This happens through a number of hardware capability structs
+ * described below.
+ *
+ * The fundamental structure for each device is the 'wiphy', of which each
+ * instance describes a physical wireless device connected to the system. Each
+ * such wiphy can have zero, one, or many virtual interfaces associated with
+ * it, which need to be identified as such by pointing the network interface's
+ * @ieee80211_ptr pointer to a &struct wireless_dev which further describes
+ * the wireless part of the interface, normally this struct is embedded in the
+ * network interface's private data area. Drivers can optionally allow creating
+ * or destroying virtual interfaces on the fly, but without at least one or the
+ * ability to create some the wireless device isn't useful.
+ *
+ * Each wiphy structure contains device capability information, and also has
+ * a pointer to the various operations the driver offers. The definitions and
+ * structures here describe these capabilities in detail.
+ */
+
+struct wiphy;
+
+/*
+ * wireless hardware capability structures
+ */
+
+/**
+ * enum ieee80211_channel_flags - channel flags
+ *
+ * Channel flags set by the regulatory control code.
+ *
+ * @IEEE80211_CHAN_DISABLED: This channel is disabled.
+ * @IEEE80211_CHAN_NO_IR: do not initiate radiation, this includes
+ * sending probe requests or beaconing.
+ * @IEEE80211_CHAN_RADAR: Radar detection is required on this channel.
+ * @IEEE80211_CHAN_NO_HT40PLUS: extension channel above this channel
+ * is not permitted.
+ * @IEEE80211_CHAN_NO_HT40MINUS: extension channel below this channel
+ * is not permitted.
+ * @IEEE80211_CHAN_NO_OFDM: OFDM is not allowed on this channel.
+ * @IEEE80211_CHAN_NO_80MHZ: If the driver supports 80 MHz on the band,
+ * this flag indicates that an 80 MHz channel cannot use this
+ * channel as the control or any of the secondary channels.
+ * This may be due to the driver or due to regulatory bandwidth
+ * restrictions.
+ * @IEEE80211_CHAN_NO_160MHZ: If the driver supports 160 MHz on the band,
+ * this flag indicates that an 160 MHz channel cannot use this
+ * channel as the control or any of the secondary channels.
+ * This may be due to the driver or due to regulatory bandwidth
+ * restrictions.
+ * @IEEE80211_CHAN_INDOOR_ONLY: see %NL80211_FREQUENCY_ATTR_INDOOR_ONLY
+ * @IEEE80211_CHAN_IR_CONCURRENT: see %NL80211_FREQUENCY_ATTR_IR_CONCURRENT
+ * @IEEE80211_CHAN_NO_20MHZ: 20 MHz bandwidth is not permitted
+ * on this channel.
+ * @IEEE80211_CHAN_NO_10MHZ: 10 MHz bandwidth is not permitted
+ * on this channel.
+ *
+ */
+enum ieee80211_channel_flags {
+ IEEE80211_CHAN_DISABLED = 1<<0,
+ IEEE80211_CHAN_NO_IR = 1<<1,
+ /* hole at 1<<2 */
+ IEEE80211_CHAN_RADAR = 1<<3,
+ IEEE80211_CHAN_NO_HT40PLUS = 1<<4,
+ IEEE80211_CHAN_NO_HT40MINUS = 1<<5,
+ IEEE80211_CHAN_NO_OFDM = 1<<6,
+ IEEE80211_CHAN_NO_80MHZ = 1<<7,
+ IEEE80211_CHAN_NO_160MHZ = 1<<8,
+ IEEE80211_CHAN_INDOOR_ONLY = 1<<9,
+ IEEE80211_CHAN_IR_CONCURRENT = 1<<10,
+ IEEE80211_CHAN_NO_20MHZ = 1<<11,
+ IEEE80211_CHAN_NO_10MHZ = 1<<12,
+};
+
+#define IEEE80211_CHAN_NO_HT40 \
+ (IEEE80211_CHAN_NO_HT40PLUS | IEEE80211_CHAN_NO_HT40MINUS)
+
+#define IEEE80211_DFS_MIN_CAC_TIME_MS 60000
+#define IEEE80211_DFS_MIN_NOP_TIME_MS (30 * 60 * 1000)
+
+/**
+ * struct ieee80211_channel - channel definition
+ *
+ * This structure describes a single channel for use
+ * with cfg80211.
+ *
+ * @center_freq: center frequency in MHz
+ * @hw_value: hardware-specific value for the channel
+ * @flags: channel flags from &enum ieee80211_channel_flags.
+ * @orig_flags: channel flags at registration time, used by regulatory
+ * code to support devices with additional restrictions
+ * @band: band this channel belongs to.
+ * @max_antenna_gain: maximum antenna gain in dBi
+ * @max_power: maximum transmission power (in dBm)
+ * @max_reg_power: maximum regulatory transmission power (in dBm)
+ * @beacon_found: helper to regulatory code to indicate when a beacon
+ * has been found on this channel. Use regulatory_hint_found_beacon()
+ * to enable this, this is useful only on 5 GHz band.
+ * @orig_mag: internal use
+ * @orig_mpwr: internal use
+ * @dfs_state: current state of this channel. Only relevant if radar is required
+ * on this channel.
+ * @dfs_state_entered: timestamp (jiffies) when the dfs state was entered.
+ * @dfs_cac_ms: DFS CAC time in milliseconds, this is valid for DFS channels.
+ */
+struct ieee80211_channel {
+ enum nl80211_band band;
+ u16 center_freq;
+ u16 hw_value;
+ u32 flags;
+ int max_antenna_gain;
+ int max_power;
+ int max_reg_power;
+ bool beacon_found;
+ u32 orig_flags;
+ int orig_mag, orig_mpwr;
+ enum nl80211_dfs_state dfs_state;
+ unsigned long dfs_state_entered;
+ unsigned int dfs_cac_ms;
+};
+
+/**
+ * enum ieee80211_rate_flags - rate flags
+ *
+ * Hardware/specification flags for rates. These are structured
+ * in a way that allows using the same bitrate structure for
+ * different bands/PHY modes.
+ *
+ * @IEEE80211_RATE_SHORT_PREAMBLE: Hardware can send with short
+ * preamble on this bitrate; only relevant in 2.4GHz band and
+ * with CCK rates.
+ * @IEEE80211_RATE_MANDATORY_A: This bitrate is a mandatory rate
+ * when used with 802.11a (on the 5 GHz band); filled by the
+ * core code when registering the wiphy.
+ * @IEEE80211_RATE_MANDATORY_B: This bitrate is a mandatory rate
+ * when used with 802.11b (on the 2.4 GHz band); filled by the
+ * core code when registering the wiphy.
+ * @IEEE80211_RATE_MANDATORY_G: This bitrate is a mandatory rate
+ * when used with 802.11g (on the 2.4 GHz band); filled by the
+ * core code when registering the wiphy.
+ * @IEEE80211_RATE_ERP_G: This is an ERP rate in 802.11g mode.
+ * @IEEE80211_RATE_SUPPORTS_5MHZ: Rate can be used in 5 MHz mode
+ * @IEEE80211_RATE_SUPPORTS_10MHZ: Rate can be used in 10 MHz mode
+ */
+enum ieee80211_rate_flags {
+ IEEE80211_RATE_SHORT_PREAMBLE = 1<<0,
+ IEEE80211_RATE_MANDATORY_A = 1<<1,
+ IEEE80211_RATE_MANDATORY_B = 1<<2,
+ IEEE80211_RATE_MANDATORY_G = 1<<3,
+ IEEE80211_RATE_ERP_G = 1<<4,
+ IEEE80211_RATE_SUPPORTS_5MHZ = 1<<5,
+ IEEE80211_RATE_SUPPORTS_10MHZ = 1<<6,
+};
+
+/**
+ * enum ieee80211_bss_type - BSS type filter
+ *
+ * @IEEE80211_BSS_TYPE_ESS: Infrastructure BSS
+ * @IEEE80211_BSS_TYPE_PBSS: Personal BSS
+ * @IEEE80211_BSS_TYPE_IBSS: Independent BSS
+ * @IEEE80211_BSS_TYPE_MBSS: Mesh BSS
+ * @IEEE80211_BSS_TYPE_ANY: Wildcard value for matching any BSS type
+ */
+enum ieee80211_bss_type {
+ IEEE80211_BSS_TYPE_ESS,
+ IEEE80211_BSS_TYPE_PBSS,
+ IEEE80211_BSS_TYPE_IBSS,
+ IEEE80211_BSS_TYPE_MBSS,
+ IEEE80211_BSS_TYPE_ANY
+};
+
+/**
+ * enum ieee80211_privacy - BSS privacy filter
+ *
+ * @IEEE80211_PRIVACY_ON: privacy bit set
+ * @IEEE80211_PRIVACY_OFF: privacy bit clear
+ * @IEEE80211_PRIVACY_ANY: Wildcard value for matching any privacy setting
+ */
+enum ieee80211_privacy {
+ IEEE80211_PRIVACY_ON,
+ IEEE80211_PRIVACY_OFF,
+ IEEE80211_PRIVACY_ANY
+};
+
+#define IEEE80211_PRIVACY(x) \
+ ((x) ? IEEE80211_PRIVACY_ON : IEEE80211_PRIVACY_OFF)
+
+/**
+ * struct ieee80211_rate - bitrate definition
+ *
+ * This structure describes a bitrate that an 802.11 PHY can
+ * operate with. The two values @hw_value and @hw_value_short
+ * are only for driver use when pointers to this structure are
+ * passed around.
+ *
+ * @flags: rate-specific flags
+ * @bitrate: bitrate in units of 100 Kbps
+ * @hw_value: driver/hardware value for this rate
+ * @hw_value_short: driver/hardware value for this rate when
+ * short preamble is used
+ */
+struct ieee80211_rate {
+ u32 flags;
+ u16 bitrate;
+ u16 hw_value, hw_value_short;
+};
+
+/**
+ * struct ieee80211_sta_ht_cap - STA's HT capabilities
+ *
+ * This structure describes most essential parameters needed
+ * to describe 802.11n HT capabilities for an STA.
+ *
+ * @ht_supported: is HT supported by the STA
+ * @cap: HT capabilities map as described in 802.11n spec
+ * @ampdu_factor: Maximum A-MPDU length factor
+ * @ampdu_density: Minimum A-MPDU spacing
+ * @mcs: Supported MCS rates
+ */
+struct ieee80211_sta_ht_cap {
+ u16 cap; /* use IEEE80211_HT_CAP_ */
+ bool ht_supported;
+ u8 ampdu_factor;
+ u8 ampdu_density;
+ struct ieee80211_mcs_info mcs;
+};
+
+/**
+ * struct ieee80211_sta_vht_cap - STA's VHT capabilities
+ *
+ * This structure describes most essential parameters needed
+ * to describe 802.11ac VHT capabilities for an STA.
+ *
+ * @vht_supported: is VHT supported by the STA
+ * @cap: VHT capabilities map as described in 802.11ac spec
+ * @vht_mcs: Supported VHT MCS rates
+ */
+struct ieee80211_sta_vht_cap {
+ bool vht_supported;
+ u32 cap; /* use IEEE80211_VHT_CAP_ */
+ struct ieee80211_vht_mcs_info vht_mcs;
+};
+
+/**
+ * struct ieee80211_supported_band - frequency band definition
+ *
+ * This structure describes a frequency band a wiphy
+ * is able to operate in.
+ *
+ * @channels: Array of channels the hardware can operate in
+ * in this band.
+ * @band: the band this structure represents
+ * @n_channels: Number of channels in @channels
+ * @bitrates: Array of bitrates the hardware can operate with
+ * in this band. Must be sorted to give a valid "supported
+ * rates" IE, i.e. CCK rates first, then OFDM.
+ * @n_bitrates: Number of bitrates in @bitrates
+ * @ht_cap: HT capabilities in this band
+ * @vht_cap: VHT capabilities in this band
+ */
+struct ieee80211_supported_band {
+ struct ieee80211_channel *channels;
+ struct ieee80211_rate *bitrates;
+ enum nl80211_band band;
+ int n_channels;
+ int n_bitrates;
+ struct ieee80211_sta_ht_cap ht_cap;
+ struct ieee80211_sta_vht_cap vht_cap;
+};
+
+/**
+ * wiphy_read_of_freq_limits - read frequency limits from device tree
+ *
+ * @wiphy: the wireless device to get extra limits for
+ *
+ * Some devices may have extra limitations specified in DT. This may be useful
+ * for chipsets that normally support more bands but are limited due to board
+ * design (e.g. by antennas or external power amplifier).
+ *
+ * This function reads info from DT and uses it to *modify* channels (disable
+ * unavailable ones). It's usually a *bad* idea to use it in drivers with
+ * shared channel data as DT limitations are device specific. You should make
+ * sure to call it only if channels in wiphy are copied and can be modified
+ * without affecting other devices.
+ *
+ * As this function access device node it has to be called after set_wiphy_dev.
+ * It also modifies channels so they have to be set first.
+ * If using this helper, call it before wiphy_register().
+ */
+#ifdef CONFIG_OF
+void wiphy_read_of_freq_limits(struct wiphy *wiphy);
+#else /* CONFIG_OF */
+static inline void wiphy_read_of_freq_limits(struct wiphy *wiphy)
+{
+}
+#endif /* !CONFIG_OF */
+
+
+/*
+ * Wireless hardware/device configuration structures and methods
+ */
+
+/**
+ * DOC: Actions and configuration
+ *
+ * Each wireless device and each virtual interface offer a set of configuration
+ * operations and other actions that are invoked by userspace. Each of these
+ * actions is described in the operations structure, and the parameters these
+ * operations use are described separately.
+ *
+ * Additionally, some operations are asynchronous and expect to get status
+ * information via some functions that drivers need to call.
+ *
+ * Scanning and BSS list handling with its associated functionality is described
+ * in a separate chapter.
+ */
+
+#define VHT_MUMIMO_GROUPS_DATA_LEN (WLAN_MEMBERSHIP_LEN +\
+ WLAN_USER_POSITION_LEN)
+
+/**
+ * struct vif_params - describes virtual interface parameters
+ * @flags: monitor interface flags, unchanged if 0, otherwise
+ * %MONITOR_FLAG_CHANGED will be set
+ * @use_4addr: use 4-address frames
+ * @macaddr: address to use for this virtual interface.
+ * If this parameter is set to zero address the driver may
+ * determine the address as needed.
+ * This feature is only fully supported by drivers that enable the
+ * %NL80211_FEATURE_MAC_ON_CREATE flag. Others may support creating
+ ** only p2p devices with specified MAC.
+ * @vht_mumimo_groups: MU-MIMO groupID, used for monitoring MU-MIMO packets
+ * belonging to that MU-MIMO groupID; %NULL if not changed
+ * @vht_mumimo_follow_addr: MU-MIMO follow address, used for monitoring
+ * MU-MIMO packets going to the specified station; %NULL if not changed
+ */
+struct vif_params {
+ u32 flags;
+ int use_4addr;
+ u8 macaddr[ETH_ALEN];
+ const u8 *vht_mumimo_groups;
+ const u8 *vht_mumimo_follow_addr;
+};
+
+/**
+ * struct key_params - key information
+ *
+ * Information about a key
+ *
+ * @key: key material
+ * @key_len: length of key material
+ * @cipher: cipher suite selector
+ * @seq: sequence counter (IV/PN) for TKIP and CCMP keys, only used
+ * with the get_key() callback, must be in little endian,
+ * length given by @seq_len.
+ * @seq_len: length of @seq.
+ */
+struct key_params {
+ const u8 *key;
+ const u8 *seq;
+ int key_len;
+ int seq_len;
+ u32 cipher;
+};
+
+/**
+ * struct cfg80211_chan_def - channel definition
+ * @chan: the (control) channel
+ * @width: channel width
+ * @center_freq1: center frequency of first segment
+ * @center_freq2: center frequency of second segment
+ * (only with 80+80 MHz)
+ */
+struct cfg80211_chan_def {
+ struct ieee80211_channel *chan;
+ enum nl80211_chan_width width;
+ u32 center_freq1;
+ u32 center_freq2;
+};
+
+/**
+ * cfg80211_get_chandef_type - return old channel type from chandef
+ * @chandef: the channel definition
+ *
+ * Return: The old channel type (NOHT, HT20, HT40+/-) from a given
+ * chandef, which must have a bandwidth allowing this conversion.
+ */
+static inline enum nl80211_channel_type
+cfg80211_get_chandef_type(const struct cfg80211_chan_def *chandef)
+{
+ switch (chandef->width) {
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ return NL80211_CHAN_NO_HT;
+ case NL80211_CHAN_WIDTH_20:
+ return NL80211_CHAN_HT20;
+ case NL80211_CHAN_WIDTH_40:
+ if (chandef->center_freq1 > chandef->chan->center_freq)
+ return NL80211_CHAN_HT40PLUS;
+ return NL80211_CHAN_HT40MINUS;
+ default:
+ WARN_ON(1);
+ return NL80211_CHAN_NO_HT;
+ }
+}
+
+/**
+ * cfg80211_chandef_create - create channel definition using channel type
+ * @chandef: the channel definition struct to fill
+ * @channel: the control channel
+ * @chantype: the channel type
+ *
+ * Given a channel type, create a channel definition.
+ */
+void cfg80211_chandef_create(struct cfg80211_chan_def *chandef,
+ struct ieee80211_channel *channel,
+ enum nl80211_channel_type chantype);
+
+/**
+ * cfg80211_chandef_identical - check if two channel definitions are identical
+ * @chandef1: first channel definition
+ * @chandef2: second channel definition
+ *
+ * Return: %true if the channels defined by the channel definitions are
+ * identical, %false otherwise.
+ */
+static inline bool
+cfg80211_chandef_identical(const struct cfg80211_chan_def *chandef1,
+ const struct cfg80211_chan_def *chandef2)
+{
+ return (chandef1->chan == chandef2->chan &&
+ chandef1->width == chandef2->width &&
+ chandef1->center_freq1 == chandef2->center_freq1 &&
+ chandef1->center_freq2 == chandef2->center_freq2);
+}
+
+/**
+ * cfg80211_chandef_compatible - check if two channel definitions are compatible
+ * @chandef1: first channel definition
+ * @chandef2: second channel definition
+ *
+ * Return: %NULL if the given channel definitions are incompatible,
+ * chandef1 or chandef2 otherwise.
+ */
+const struct cfg80211_chan_def *
+cfg80211_chandef_compatible(const struct cfg80211_chan_def *chandef1,
+ const struct cfg80211_chan_def *chandef2);
+
+/**
+ * cfg80211_chandef_valid - check if a channel definition is valid
+ * @chandef: the channel definition to check
+ * Return: %true if the channel definition is valid. %false otherwise.
+ */
+bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef);
+
+/**
+ * cfg80211_chandef_usable - check if secondary channels can be used
+ * @wiphy: the wiphy to validate against
+ * @chandef: the channel definition to check
+ * @prohibited_flags: the regulatory channel flags that must not be set
+ * Return: %true if secondary channels are usable. %false otherwise.
+ */
+bool cfg80211_chandef_usable(struct wiphy *wiphy,
+ const struct cfg80211_chan_def *chandef,
+ u32 prohibited_flags);
+
+/**
+ * cfg80211_chandef_dfs_required - checks if radar detection is required
+ * @wiphy: the wiphy to validate against
+ * @chandef: the channel definition to check
+ * @iftype: the interface type as specified in &enum nl80211_iftype
+ * Returns:
+ * 1 if radar detection is required, 0 if it is not, < 0 on error
+ */
+int cfg80211_chandef_dfs_required(struct wiphy *wiphy,
+ const struct cfg80211_chan_def *chandef,
+ enum nl80211_iftype iftype);
+
+/**
+ * ieee80211_chandef_rate_flags - returns rate flags for a channel
+ *
+ * In some channel types, not all rates may be used - for example CCK
+ * rates may not be used in 5/10 MHz channels.
+ *
+ * @chandef: channel definition for the channel
+ *
+ * Returns: rate flags which apply for this channel
+ */
+static inline enum ieee80211_rate_flags
+ieee80211_chandef_rate_flags(struct cfg80211_chan_def *chandef)
+{
+ switch (chandef->width) {
+ case NL80211_CHAN_WIDTH_5:
+ return IEEE80211_RATE_SUPPORTS_5MHZ;
+ case NL80211_CHAN_WIDTH_10:
+ return IEEE80211_RATE_SUPPORTS_10MHZ;
+ default:
+ break;
+ }
+ return 0;
+}
+
+/**
+ * ieee80211_chandef_max_power - maximum transmission power for the chandef
+ *
+ * In some regulations, the transmit power may depend on the configured channel
+ * bandwidth which may be defined as dBm/MHz. This function returns the actual
+ * max_power for non-standard (20 MHz) channels.
+ *
+ * @chandef: channel definition for the channel
+ *
+ * Returns: maximum allowed transmission power in dBm for the chandef
+ */
+static inline int
+ieee80211_chandef_max_power(struct cfg80211_chan_def *chandef)
+{
+ switch (chandef->width) {
+ case NL80211_CHAN_WIDTH_5:
+ return min(chandef->chan->max_reg_power - 6,
+ chandef->chan->max_power);
+ case NL80211_CHAN_WIDTH_10:
+ return min(chandef->chan->max_reg_power - 3,
+ chandef->chan->max_power);
+ default:
+ break;
+ }
+ return chandef->chan->max_power;
+}
+
+/**
+ * enum survey_info_flags - survey information flags
+ *
+ * @SURVEY_INFO_NOISE_DBM: noise (in dBm) was filled in
+ * @SURVEY_INFO_IN_USE: channel is currently being used
+ * @SURVEY_INFO_TIME: active time (in ms) was filled in
+ * @SURVEY_INFO_TIME_BUSY: busy time was filled in
+ * @SURVEY_INFO_TIME_EXT_BUSY: extension channel busy time was filled in
+ * @SURVEY_INFO_TIME_RX: receive time was filled in
+ * @SURVEY_INFO_TIME_TX: transmit time was filled in
+ * @SURVEY_INFO_TIME_SCAN: scan time was filled in
+ *
+ * Used by the driver to indicate which info in &struct survey_info
+ * it has filled in during the get_survey().
+ */
+enum survey_info_flags {
+ SURVEY_INFO_NOISE_DBM = BIT(0),
+ SURVEY_INFO_IN_USE = BIT(1),
+ SURVEY_INFO_TIME = BIT(2),
+ SURVEY_INFO_TIME_BUSY = BIT(3),
+ SURVEY_INFO_TIME_EXT_BUSY = BIT(4),
+ SURVEY_INFO_TIME_RX = BIT(5),
+ SURVEY_INFO_TIME_TX = BIT(6),
+ SURVEY_INFO_TIME_SCAN = BIT(7),
+};
+
+/**
+ * struct survey_info - channel survey response
+ *
+ * @channel: the channel this survey record reports, may be %NULL for a single
+ * record to report global statistics
+ * @filled: bitflag of flags from &enum survey_info_flags
+ * @noise: channel noise in dBm. This and all following fields are
+ * optional
+ * @time: amount of time in ms the radio was turn on (on the channel)
+ * @time_busy: amount of time the primary channel was sensed busy
+ * @time_ext_busy: amount of time the extension channel was sensed busy
+ * @time_rx: amount of time the radio spent receiving data
+ * @time_tx: amount of time the radio spent transmitting data
+ * @time_scan: amount of time the radio spent for scanning
+ *
+ * Used by dump_survey() to report back per-channel survey information.
+ *
+ * This structure can later be expanded with things like
+ * channel duty cycle etc.
+ */
+struct survey_info {
+ struct ieee80211_channel *channel;
+ u64 time;
+ u64 time_busy;
+ u64 time_ext_busy;
+ u64 time_rx;
+ u64 time_tx;
+ u64 time_scan;
+ u32 filled;
+ s8 noise;
+};
+
+#define CFG80211_MAX_WEP_KEYS 4
+
+/**
+ * struct cfg80211_crypto_settings - Crypto settings
+ * @wpa_versions: indicates which, if any, WPA versions are enabled
+ * (from enum nl80211_wpa_versions)
+ * @cipher_group: group key cipher suite (or 0 if unset)
+ * @n_ciphers_pairwise: number of AP supported unicast ciphers
+ * @ciphers_pairwise: unicast key cipher suites
+ * @n_akm_suites: number of AKM suites
+ * @akm_suites: AKM suites
+ * @control_port: Whether user space controls IEEE 802.1X port, i.e.,
+ * sets/clears %NL80211_STA_FLAG_AUTHORIZED. If true, the driver is
+ * required to assume that the port is unauthorized until authorized by
+ * user space. Otherwise, port is marked authorized by default.
+ * @control_port_ethertype: the control port protocol that should be
+ * allowed through even on unauthorized ports
+ * @control_port_no_encrypt: TRUE to prevent encryption of control port
+ * protocol frames.
+ * @wep_keys: static WEP keys, if not NULL points to an array of
+ * CFG80211_MAX_WEP_KEYS WEP keys
+ * @wep_tx_key: key index (0..3) of the default TX static WEP key
+ * @psk: PSK (for devices supporting 4-way-handshake offload)
+ */
+struct cfg80211_crypto_settings {
+ u32 wpa_versions;
+ u32 cipher_group;
+ int n_ciphers_pairwise;
+ u32 ciphers_pairwise[NL80211_MAX_NR_CIPHER_SUITES];
+ int n_akm_suites;
+ u32 akm_suites[NL80211_MAX_NR_AKM_SUITES];
+ bool control_port;
+ __be16 control_port_ethertype;
+ bool control_port_no_encrypt;
+ struct key_params *wep_keys;
+ int wep_tx_key;
+ const u8 *psk;
+};
+
+/**
+ * struct cfg80211_beacon_data - beacon data
+ * @head: head portion of beacon (before TIM IE)
+ * or %NULL if not changed
+ * @tail: tail portion of beacon (after TIM IE)
+ * or %NULL if not changed
+ * @head_len: length of @head
+ * @tail_len: length of @tail
+ * @beacon_ies: extra information element(s) to add into Beacon frames or %NULL
+ * @beacon_ies_len: length of beacon_ies in octets
+ * @proberesp_ies: extra information element(s) to add into Probe Response
+ * frames or %NULL
+ * @proberesp_ies_len: length of proberesp_ies in octets
+ * @assocresp_ies: extra information element(s) to add into (Re)Association
+ * Response frames or %NULL
+ * @assocresp_ies_len: length of assocresp_ies in octets
+ * @probe_resp_len: length of probe response template (@probe_resp)
+ * @probe_resp: probe response template (AP mode only)
+ */
+struct cfg80211_beacon_data {
+ const u8 *head, *tail;
+ const u8 *beacon_ies;
+ const u8 *proberesp_ies;
+ const u8 *assocresp_ies;
+ const u8 *probe_resp;
+
+ size_t head_len, tail_len;
+ size_t beacon_ies_len;
+ size_t proberesp_ies_len;
+ size_t assocresp_ies_len;
+ size_t probe_resp_len;
+};
+
+struct mac_address {
+ u8 addr[ETH_ALEN];
+};
+
+/**
+ * struct cfg80211_acl_data - Access control list data
+ *
+ * @acl_policy: ACL policy to be applied on the station's
+ * entry specified by mac_addr
+ * @n_acl_entries: Number of MAC address entries passed
+ * @mac_addrs: List of MAC addresses of stations to be used for ACL
+ */
+struct cfg80211_acl_data {
+ enum nl80211_acl_policy acl_policy;
+ int n_acl_entries;
+
+ /* Keep it last */
+ struct mac_address mac_addrs[];
+};
+
+/*
+ * cfg80211_bitrate_mask - masks for bitrate control
+ */
+struct cfg80211_bitrate_mask {
+ struct {
+ u32 legacy;
+ u8 ht_mcs[IEEE80211_HT_MCS_MASK_LEN];
+ u16 vht_mcs[NL80211_VHT_NSS_MAX];
+ enum nl80211_txrate_gi gi;
+ } control[NUM_NL80211_BANDS];
+};
+
+/**
+ * struct cfg80211_ap_settings - AP configuration
+ *
+ * Used to configure an AP interface.
+ *
+ * @chandef: defines the channel to use
+ * @beacon: beacon data
+ * @beacon_interval: beacon interval
+ * @dtim_period: DTIM period
+ * @ssid: SSID to be used in the BSS (note: may be %NULL if not provided from
+ * user space)
+ * @ssid_len: length of @ssid
+ * @hidden_ssid: whether to hide the SSID in Beacon/Probe Response frames
+ * @crypto: crypto settings
+ * @privacy: the BSS uses privacy
+ * @auth_type: Authentication type (algorithm)
+ * @smps_mode: SMPS mode
+ * @inactivity_timeout: time in seconds to determine station's inactivity.
+ * @p2p_ctwindow: P2P CT Window
+ * @p2p_opp_ps: P2P opportunistic PS
+ * @acl: ACL configuration used by the drivers which has support for
+ * MAC address based access control
+ * @pbss: If set, start as a PCP instead of AP. Relevant for DMG
+ * networks.
+ * @beacon_rate: bitrate to be used for beacons
+ * @ht_cap: HT capabilities (or %NULL if HT isn't enabled)
+ * @vht_cap: VHT capabilities (or %NULL if VHT isn't enabled)
+ * @ht_required: stations must support HT
+ * @vht_required: stations must support VHT
+ */
+struct cfg80211_ap_settings {
+ struct cfg80211_chan_def chandef;
+
+ struct cfg80211_beacon_data beacon;
+
+ int beacon_interval, dtim_period;
+ const u8 *ssid;
+ size_t ssid_len;
+ enum nl80211_hidden_ssid hidden_ssid;
+ struct cfg80211_crypto_settings crypto;
+ bool privacy;
+ enum nl80211_auth_type auth_type;
+ enum nl80211_smps_mode smps_mode;
+ int inactivity_timeout;
+ u8 p2p_ctwindow;
+ bool p2p_opp_ps;
+ const struct cfg80211_acl_data *acl;
+ bool pbss;
+ struct cfg80211_bitrate_mask beacon_rate;
+
+ const struct ieee80211_ht_cap *ht_cap;
+ const struct ieee80211_vht_cap *vht_cap;
+ bool ht_required, vht_required;
+};
+
+/**
+ * struct cfg80211_csa_settings - channel switch settings
+ *
+ * Used for channel switch
+ *
+ * @chandef: defines the channel to use after the switch
+ * @beacon_csa: beacon data while performing the switch
+ * @counter_offsets_beacon: offsets of the counters within the beacon (tail)
+ * @counter_offsets_presp: offsets of the counters within the probe response
+ * @n_counter_offsets_beacon: number of csa counters the beacon (tail)
+ * @n_counter_offsets_presp: number of csa counters in the probe response
+ * @beacon_after: beacon data to be used on the new channel
+ * @radar_required: whether radar detection is required on the new channel
+ * @block_tx: whether transmissions should be blocked while changing
+ * @count: number of beacons until switch
+ */
+struct cfg80211_csa_settings {
+ struct cfg80211_chan_def chandef;
+ struct cfg80211_beacon_data beacon_csa;
+ const u16 *counter_offsets_beacon;
+ const u16 *counter_offsets_presp;
+ unsigned int n_counter_offsets_beacon;
+ unsigned int n_counter_offsets_presp;
+ struct cfg80211_beacon_data beacon_after;
+ bool radar_required;
+ bool block_tx;
+ u8 count;
+};
+
+/**
+ * struct iface_combination_params - input parameters for interface combinations
+ *
+ * Used to pass interface combination parameters
+ *
+ * @num_different_channels: the number of different channels we want
+ * to use for verification
+ * @radar_detect: a bitmap where each bit corresponds to a channel
+ * width where radar detection is needed, as in the definition of
+ * &struct ieee80211_iface_combination.@radar_detect_widths
+ * @iftype_num: array with the number of interfaces of each interface
+ * type. The index is the interface type as specified in &enum
+ * nl80211_iftype.
+ * @new_beacon_int: set this to the beacon interval of a new interface
+ * that's not operating yet, if such is to be checked as part of
+ * the verification
+ */
+struct iface_combination_params {
+ int num_different_channels;
+ u8 radar_detect;
+ int iftype_num[NUM_NL80211_IFTYPES];
+ u32 new_beacon_int;
+};
+
+/**
+ * enum station_parameters_apply_mask - station parameter values to apply
+ * @STATION_PARAM_APPLY_UAPSD: apply new uAPSD parameters (uapsd_queues, max_sp)
+ * @STATION_PARAM_APPLY_CAPABILITY: apply new capability
+ * @STATION_PARAM_APPLY_PLINK_STATE: apply new plink state
+ *
+ * Not all station parameters have in-band "no change" signalling,
+ * for those that don't these flags will are used.
+ */
+enum station_parameters_apply_mask {
+ STATION_PARAM_APPLY_UAPSD = BIT(0),
+ STATION_PARAM_APPLY_CAPABILITY = BIT(1),
+ STATION_PARAM_APPLY_PLINK_STATE = BIT(2),
+};
+
+/**
+ * struct station_parameters - station parameters
+ *
+ * Used to change and create a new station.
+ *
+ * @vlan: vlan interface station should belong to
+ * @supported_rates: supported rates in IEEE 802.11 format
+ * (or NULL for no change)
+ * @supported_rates_len: number of supported rates
+ * @sta_flags_mask: station flags that changed
+ * (bitmask of BIT(%NL80211_STA_FLAG_...))
+ * @sta_flags_set: station flags values
+ * (bitmask of BIT(%NL80211_STA_FLAG_...))
+ * @listen_interval: listen interval or -1 for no change
+ * @aid: AID or zero for no change
+ * @peer_aid: mesh peer AID or zero for no change
+ * @plink_action: plink action to take
+ * @plink_state: set the peer link state for a station
+ * @ht_capa: HT capabilities of station
+ * @vht_capa: VHT capabilities of station
+ * @uapsd_queues: bitmap of queues configured for uapsd. same format
+ * as the AC bitmap in the QoS info field
+ * @max_sp: max Service Period. same format as the MAX_SP in the
+ * QoS info field (but already shifted down)
+ * @sta_modify_mask: bitmap indicating which parameters changed
+ * (for those that don't have a natural "no change" value),
+ * see &enum station_parameters_apply_mask
+ * @local_pm: local link-specific mesh power save mode (no change when set
+ * to unknown)
+ * @capability: station capability
+ * @ext_capab: extended capabilities of the station
+ * @ext_capab_len: number of extended capabilities
+ * @supported_channels: supported channels in IEEE 802.11 format
+ * @supported_channels_len: number of supported channels
+ * @supported_oper_classes: supported oper classes in IEEE 802.11 format
+ * @supported_oper_classes_len: number of supported operating classes
+ * @opmode_notif: operating mode field from Operating Mode Notification
+ * @opmode_notif_used: information if operating mode field is used
+ * @support_p2p_ps: information if station supports P2P PS mechanism
+ */
+struct station_parameters {
+ const u8 *supported_rates;
+ struct net_device *vlan;
+ u32 sta_flags_mask, sta_flags_set;
+ u32 sta_modify_mask;
+ int listen_interval;
+ u16 aid;
+ u16 peer_aid;
+ u8 supported_rates_len;
+ u8 plink_action;
+ u8 plink_state;
+ const struct ieee80211_ht_cap *ht_capa;
+ const struct ieee80211_vht_cap *vht_capa;
+ u8 uapsd_queues;
+ u8 max_sp;
+ enum nl80211_mesh_power_mode local_pm;
+ u16 capability;
+ const u8 *ext_capab;
+ u8 ext_capab_len;
+ const u8 *supported_channels;
+ u8 supported_channels_len;
+ const u8 *supported_oper_classes;
+ u8 supported_oper_classes_len;
+ u8 opmode_notif;
+ bool opmode_notif_used;
+ int support_p2p_ps;
+};
+
+/**
+ * struct station_del_parameters - station deletion parameters
+ *
+ * Used to delete a station entry (or all stations).
+ *
+ * @mac: MAC address of the station to remove or NULL to remove all stations
+ * @subtype: Management frame subtype to use for indicating removal
+ * (10 = Disassociation, 12 = Deauthentication)
+ * @reason_code: Reason code for the Disassociation/Deauthentication frame
+ */
+struct station_del_parameters {
+ const u8 *mac;
+ u8 subtype;
+ u16 reason_code;
+};
+
+/**
+ * enum cfg80211_station_type - the type of station being modified
+ * @CFG80211_STA_AP_CLIENT: client of an AP interface
+ * @CFG80211_STA_AP_CLIENT_UNASSOC: client of an AP interface that is still
+ * unassociated (update properties for this type of client is permitted)
+ * @CFG80211_STA_AP_MLME_CLIENT: client of an AP interface that has
+ * the AP MLME in the device
+ * @CFG80211_STA_AP_STA: AP station on managed interface
+ * @CFG80211_STA_IBSS: IBSS station
+ * @CFG80211_STA_TDLS_PEER_SETUP: TDLS peer on managed interface (dummy entry
+ * while TDLS setup is in progress, it moves out of this state when
+ * being marked authorized; use this only if TDLS with external setup is
+ * supported/used)
+ * @CFG80211_STA_TDLS_PEER_ACTIVE: TDLS peer on managed interface (active
+ * entry that is operating, has been marked authorized by userspace)
+ * @CFG80211_STA_MESH_PEER_KERNEL: peer on mesh interface (kernel managed)
+ * @CFG80211_STA_MESH_PEER_USER: peer on mesh interface (user managed)
+ */
+enum cfg80211_station_type {
+ CFG80211_STA_AP_CLIENT,
+ CFG80211_STA_AP_CLIENT_UNASSOC,
+ CFG80211_STA_AP_MLME_CLIENT,
+ CFG80211_STA_AP_STA,
+ CFG80211_STA_IBSS,
+ CFG80211_STA_TDLS_PEER_SETUP,
+ CFG80211_STA_TDLS_PEER_ACTIVE,
+ CFG80211_STA_MESH_PEER_KERNEL,
+ CFG80211_STA_MESH_PEER_USER,
+};
+
+/**
+ * cfg80211_check_station_change - validate parameter changes
+ * @wiphy: the wiphy this operates on
+ * @params: the new parameters for a station
+ * @statype: the type of station being modified
+ *
+ * Utility function for the @change_station driver method. Call this function
+ * with the appropriate station type looking up the station (and checking that
+ * it exists). It will verify whether the station change is acceptable, and if
+ * not will return an error code. Note that it may modify the parameters for
+ * backward compatibility reasons, so don't use them before calling this.
+ */
+int cfg80211_check_station_change(struct wiphy *wiphy,
+ struct station_parameters *params,
+ enum cfg80211_station_type statype);
+
+/**
+ * enum station_info_rate_flags - bitrate info flags
+ *
+ * Used by the driver to indicate the specific rate transmission
+ * type for 802.11n transmissions.
+ *
+ * @RATE_INFO_FLAGS_MCS: mcs field filled with HT MCS
+ * @RATE_INFO_FLAGS_VHT_MCS: mcs field filled with VHT MCS
+ * @RATE_INFO_FLAGS_SHORT_GI: 400ns guard interval
+ * @RATE_INFO_FLAGS_60G: 60GHz MCS
+ */
+enum rate_info_flags {
+ RATE_INFO_FLAGS_MCS = BIT(0),
+ RATE_INFO_FLAGS_VHT_MCS = BIT(1),
+ RATE_INFO_FLAGS_SHORT_GI = BIT(2),
+ RATE_INFO_FLAGS_60G = BIT(3),
+};
+
+/**
+ * enum rate_info_bw - rate bandwidth information
+ *
+ * Used by the driver to indicate the rate bandwidth.
+ *
+ * @RATE_INFO_BW_5: 5 MHz bandwidth
+ * @RATE_INFO_BW_10: 10 MHz bandwidth
+ * @RATE_INFO_BW_20: 20 MHz bandwidth
+ * @RATE_INFO_BW_40: 40 MHz bandwidth
+ * @RATE_INFO_BW_80: 80 MHz bandwidth
+ * @RATE_INFO_BW_160: 160 MHz bandwidth
+ */
+enum rate_info_bw {
+ RATE_INFO_BW_20 = 0,
+ RATE_INFO_BW_5,
+ RATE_INFO_BW_10,
+ RATE_INFO_BW_40,
+ RATE_INFO_BW_80,
+ RATE_INFO_BW_160,
+};
+
+/**
+ * struct rate_info - bitrate information
+ *
+ * Information about a receiving or transmitting bitrate
+ *
+ * @flags: bitflag of flags from &enum rate_info_flags
+ * @mcs: mcs index if struct describes a 802.11n bitrate
+ * @legacy: bitrate in 100kbit/s for 802.11abg
+ * @nss: number of streams (VHT only)
+ * @bw: bandwidth (from &enum rate_info_bw)
+ */
+struct rate_info {
+ u8 flags;
+ u8 mcs;
+ u16 legacy;
+ u8 nss;
+ u8 bw;
+};
+
+/**
+ * enum station_info_rate_flags - bitrate info flags
+ *
+ * Used by the driver to indicate the specific rate transmission
+ * type for 802.11n transmissions.
+ *
+ * @BSS_PARAM_FLAGS_CTS_PROT: whether CTS protection is enabled
+ * @BSS_PARAM_FLAGS_SHORT_PREAMBLE: whether short preamble is enabled
+ * @BSS_PARAM_FLAGS_SHORT_SLOT_TIME: whether short slot time is enabled
+ */
+enum bss_param_flags {
+ BSS_PARAM_FLAGS_CTS_PROT = 1<<0,
+ BSS_PARAM_FLAGS_SHORT_PREAMBLE = 1<<1,
+ BSS_PARAM_FLAGS_SHORT_SLOT_TIME = 1<<2,
+};
+
+/**
+ * struct sta_bss_parameters - BSS parameters for the attached station
+ *
+ * Information about the currently associated BSS
+ *
+ * @flags: bitflag of flags from &enum bss_param_flags
+ * @dtim_period: DTIM period for the BSS
+ * @beacon_interval: beacon interval
+ */
+struct sta_bss_parameters {
+ u8 flags;
+ u8 dtim_period;
+ u16 beacon_interval;
+};
+
+/**
+ * struct cfg80211_tid_stats - per-TID statistics
+ * @filled: bitmap of flags using the bits of &enum nl80211_tid_stats to
+ * indicate the relevant values in this struct are filled
+ * @rx_msdu: number of received MSDUs
+ * @tx_msdu: number of (attempted) transmitted MSDUs
+ * @tx_msdu_retries: number of retries (not counting the first) for
+ * transmitted MSDUs
+ * @tx_msdu_failed: number of failed transmitted MSDUs
+ */
+struct cfg80211_tid_stats {
+ u32 filled;
+ u64 rx_msdu;
+ u64 tx_msdu;
+ u64 tx_msdu_retries;
+ u64 tx_msdu_failed;
+};
+
+#define IEEE80211_MAX_CHAINS 4
+
+/**
+ * struct station_info - station information
+ *
+ * Station information filled by driver for get_station() and dump_station.
+ *
+ * @filled: bitflag of flags using the bits of &enum nl80211_sta_info to
+ * indicate the relevant values in this struct for them
+ * @connected_time: time(in secs) since a station is last connected
+ * @inactive_time: time since last station activity (tx/rx) in milliseconds
+ * @rx_bytes: bytes (size of MPDUs) received from this station
+ * @tx_bytes: bytes (size of MPDUs) transmitted to this station
+ * @llid: mesh local link id
+ * @plid: mesh peer link id
+ * @plink_state: mesh peer link state
+ * @signal: The signal strength, type depends on the wiphy's signal_type.
+ * For CFG80211_SIGNAL_TYPE_MBM, value is expressed in _dBm_.
+ * @signal_avg: Average signal strength, type depends on the wiphy's signal_type.
+ * For CFG80211_SIGNAL_TYPE_MBM, value is expressed in _dBm_.
+ * @chains: bitmask for filled values in @chain_signal, @chain_signal_avg
+ * @chain_signal: per-chain signal strength of last received packet in dBm
+ * @chain_signal_avg: per-chain signal strength average in dBm
+ * @txrate: current unicast bitrate from this station
+ * @rxrate: current unicast bitrate to this station
+ * @rx_packets: packets (MSDUs & MMPDUs) received from this station
+ * @tx_packets: packets (MSDUs & MMPDUs) transmitted to this station
+ * @tx_retries: cumulative retry counts (MPDUs)
+ * @tx_failed: number of failed transmissions (MPDUs) (retries exceeded, no ACK)
+ * @rx_dropped_misc: Dropped for un-specified reason.
+ * @bss_param: current BSS parameters
+ * @generation: generation number for nl80211 dumps.
+ * This number should increase every time the list of stations
+ * changes, i.e. when a station is added or removed, so that
+ * userspace can tell whether it got a consistent snapshot.
+ * @assoc_req_ies: IEs from (Re)Association Request.
+ * This is used only when in AP mode with drivers that do not use
+ * user space MLME/SME implementation. The information is provided for
+ * the cfg80211_new_sta() calls to notify user space of the IEs.
+ * @assoc_req_ies_len: Length of assoc_req_ies buffer in octets.
+ * @sta_flags: station flags mask & values
+ * @beacon_loss_count: Number of times beacon loss event has triggered.
+ * @t_offset: Time offset of the station relative to this host.
+ * @local_pm: local mesh STA power save mode
+ * @peer_pm: peer mesh STA power save mode
+ * @nonpeer_pm: non-peer mesh STA power save mode
+ * @expected_throughput: expected throughput in kbps (including 802.11 headers)
+ * towards this station.
+ * @rx_beacon: number of beacons received from this peer
+ * @rx_beacon_signal_avg: signal strength average (in dBm) for beacons received
+ * from this peer
+ * @rx_duration: aggregate PPDU duration(usecs) for all the frames from a peer
+ * @pertid: per-TID statistics, see &struct cfg80211_tid_stats, using the last
+ * (IEEE80211_NUM_TIDS) index for MSDUs not encapsulated in QoS-MPDUs.
+ */
+struct station_info {
+ u64 filled;
+ u32 connected_time;
+ u32 inactive_time;
+ u64 rx_bytes;
+ u64 tx_bytes;
+ u16 llid;
+ u16 plid;
+ u8 plink_state;
+ s8 signal;
+ s8 signal_avg;
+
+ u8 chains;
+ s8 chain_signal[IEEE80211_MAX_CHAINS];
+ s8 chain_signal_avg[IEEE80211_MAX_CHAINS];
+
+ struct rate_info txrate;
+ struct rate_info rxrate;
+ u32 rx_packets;
+ u32 tx_packets;
+ u32 tx_retries;
+ u32 tx_failed;
+ u32 rx_dropped_misc;
+ struct sta_bss_parameters bss_param;
+ struct nl80211_sta_flag_update sta_flags;
+
+ int generation;
+
+ const u8 *assoc_req_ies;
+ size_t assoc_req_ies_len;
+
+ u32 beacon_loss_count;
+ s64 t_offset;
+ enum nl80211_mesh_power_mode local_pm;
+ enum nl80211_mesh_power_mode peer_pm;
+ enum nl80211_mesh_power_mode nonpeer_pm;
+
+ u32 expected_throughput;
+
+ u64 rx_beacon;
+ u64 rx_duration;
+ u8 rx_beacon_signal_avg;
+ struct cfg80211_tid_stats pertid[IEEE80211_NUM_TIDS + 1];
+};
+
+#if IS_ENABLED(CPTCFG_CFG80211)
+/**
+ * cfg80211_get_station - retrieve information about a given station
+ * @dev: the device where the station is supposed to be connected to
+ * @mac_addr: the mac address of the station of interest
+ * @sinfo: pointer to the structure to fill with the information
+ *
+ * Returns 0 on success and sinfo is filled with the available information
+ * otherwise returns a negative error code and the content of sinfo has to be
+ * considered undefined.
+ */
+int cfg80211_get_station(struct net_device *dev, const u8 *mac_addr,
+ struct station_info *sinfo);
+#else
+static inline int cfg80211_get_station(struct net_device *dev,
+ const u8 *mac_addr,
+ struct station_info *sinfo)
+{
+ return -ENOENT;
+}
+#endif
+
+/**
+ * enum monitor_flags - monitor flags
+ *
+ * Monitor interface configuration flags. Note that these must be the bits
+ * according to the nl80211 flags.
+ *
+ * @MONITOR_FLAG_CHANGED: set if the flags were changed
+ * @MONITOR_FLAG_FCSFAIL: pass frames with bad FCS
+ * @MONITOR_FLAG_PLCPFAIL: pass frames with bad PLCP
+ * @MONITOR_FLAG_CONTROL: pass control frames
+ * @MONITOR_FLAG_OTHER_BSS: disable BSSID filtering
+ * @MONITOR_FLAG_COOK_FRAMES: report frames after processing
+ * @MONITOR_FLAG_ACTIVE: active monitor, ACKs frames on its MAC address
+ */
+enum monitor_flags {
+ MONITOR_FLAG_CHANGED = 1<<__NL80211_MNTR_FLAG_INVALID,
+ MONITOR_FLAG_FCSFAIL = 1<<NL80211_MNTR_FLAG_FCSFAIL,
+ MONITOR_FLAG_PLCPFAIL = 1<<NL80211_MNTR_FLAG_PLCPFAIL,
+ MONITOR_FLAG_CONTROL = 1<<NL80211_MNTR_FLAG_CONTROL,
+ MONITOR_FLAG_OTHER_BSS = 1<<NL80211_MNTR_FLAG_OTHER_BSS,
+ MONITOR_FLAG_COOK_FRAMES = 1<<NL80211_MNTR_FLAG_COOK_FRAMES,
+ MONITOR_FLAG_ACTIVE = 1<<NL80211_MNTR_FLAG_ACTIVE,
+};
+
+/**
+ * enum mpath_info_flags - mesh path information flags
+ *
+ * Used by the driver to indicate which info in &struct mpath_info it has filled
+ * in during get_station() or dump_station().
+ *
+ * @MPATH_INFO_FRAME_QLEN: @frame_qlen filled
+ * @MPATH_INFO_SN: @sn filled
+ * @MPATH_INFO_METRIC: @metric filled
+ * @MPATH_INFO_EXPTIME: @exptime filled
+ * @MPATH_INFO_DISCOVERY_TIMEOUT: @discovery_timeout filled
+ * @MPATH_INFO_DISCOVERY_RETRIES: @discovery_retries filled
+ * @MPATH_INFO_FLAGS: @flags filled
+ */
+enum mpath_info_flags {
+ MPATH_INFO_FRAME_QLEN = BIT(0),
+ MPATH_INFO_SN = BIT(1),
+ MPATH_INFO_METRIC = BIT(2),
+ MPATH_INFO_EXPTIME = BIT(3),
+ MPATH_INFO_DISCOVERY_TIMEOUT = BIT(4),
+ MPATH_INFO_DISCOVERY_RETRIES = BIT(5),
+ MPATH_INFO_FLAGS = BIT(6),
+};
+
+/**
+ * struct mpath_info - mesh path information
+ *
+ * Mesh path information filled by driver for get_mpath() and dump_mpath().
+ *
+ * @filled: bitfield of flags from &enum mpath_info_flags
+ * @frame_qlen: number of queued frames for this destination
+ * @sn: target sequence number
+ * @metric: metric (cost) of this mesh path
+ * @exptime: expiration time for the mesh path from now, in msecs
+ * @flags: mesh path flags
+ * @discovery_timeout: total mesh path discovery timeout, in msecs
+ * @discovery_retries: mesh path discovery retries
+ * @generation: generation number for nl80211 dumps.
+ * This number should increase every time the list of mesh paths
+ * changes, i.e. when a station is added or removed, so that
+ * userspace can tell whether it got a consistent snapshot.
+ */
+struct mpath_info {
+ u32 filled;
+ u32 frame_qlen;
+ u32 sn;
+ u32 metric;
+ u32 exptime;
+ u32 discovery_timeout;
+ u8 discovery_retries;
+ u8 flags;
+
+ int generation;
+};
+
+/**
+ * struct bss_parameters - BSS parameters
+ *
+ * Used to change BSS parameters (mainly for AP mode).
+ *
+ * @use_cts_prot: Whether to use CTS protection
+ * (0 = no, 1 = yes, -1 = do not change)
+ * @use_short_preamble: Whether the use of short preambles is allowed
+ * (0 = no, 1 = yes, -1 = do not change)
+ * @use_short_slot_time: Whether the use of short slot time is allowed
+ * (0 = no, 1 = yes, -1 = do not change)
+ * @basic_rates: basic rates in IEEE 802.11 format
+ * (or NULL for no change)
+ * @basic_rates_len: number of basic rates
+ * @ap_isolate: do not forward packets between connected stations
+ * @ht_opmode: HT Operation mode
+ * (u16 = opmode, -1 = do not change)
+ * @p2p_ctwindow: P2P CT Window (-1 = no change)
+ * @p2p_opp_ps: P2P opportunistic PS (-1 = no change)
+ */
+struct bss_parameters {
+ int use_cts_prot;
+ int use_short_preamble;
+ int use_short_slot_time;
+ const u8 *basic_rates;
+ u8 basic_rates_len;
+ int ap_isolate;
+ int ht_opmode;
+ s8 p2p_ctwindow, p2p_opp_ps;
+};
+
+/**
+ * struct mesh_config - 802.11s mesh configuration
+ *
+ * These parameters can be changed while the mesh is active.
+ *
+ * @dot11MeshRetryTimeout: the initial retry timeout in millisecond units used
+ * by the Mesh Peering Open message
+ * @dot11MeshConfirmTimeout: the initial retry timeout in millisecond units
+ * used by the Mesh Peering Open message
+ * @dot11MeshHoldingTimeout: the confirm timeout in millisecond units used by
+ * the mesh peering management to close a mesh peering
+ * @dot11MeshMaxPeerLinks: the maximum number of peer links allowed on this
+ * mesh interface
+ * @dot11MeshMaxRetries: the maximum number of peer link open retries that can
+ * be sent to establish a new peer link instance in a mesh
+ * @dot11MeshTTL: the value of TTL field set at a source mesh STA
+ * @element_ttl: the value of TTL field set at a mesh STA for path selection
+ * elements
+ * @auto_open_plinks: whether we should automatically open peer links when we
+ * detect compatible mesh peers
+ * @dot11MeshNbrOffsetMaxNeighbor: the maximum number of neighbors to
+ * synchronize to for 11s default synchronization method
+ * @dot11MeshHWMPmaxPREQretries: the number of action frames containing a PREQ
+ * that an originator mesh STA can send to a particular path target
+ * @path_refresh_time: how frequently to refresh mesh paths in milliseconds
+ * @min_discovery_timeout: the minimum length of time to wait until giving up on
+ * a path discovery in milliseconds
+ * @dot11MeshHWMPactivePathTimeout: the time (in TUs) for which mesh STAs
+ * receiving a PREQ shall consider the forwarding information from the
+ * root to be valid. (TU = time unit)
+ * @dot11MeshHWMPpreqMinInterval: the minimum interval of time (in TUs) during
+ * which a mesh STA can send only one action frame containing a PREQ
+ * element
+ * @dot11MeshHWMPperrMinInterval: the minimum interval of time (in TUs) during
+ * which a mesh STA can send only one Action frame containing a PERR
+ * element
+ * @dot11MeshHWMPnetDiameterTraversalTime: the interval of time (in TUs) that
+ * it takes for an HWMP information element to propagate across the mesh
+ * @dot11MeshHWMPRootMode: the configuration of a mesh STA as root mesh STA
+ * @dot11MeshHWMPRannInterval: the interval of time (in TUs) between root
+ * announcements are transmitted
+ * @dot11MeshGateAnnouncementProtocol: whether to advertise that this mesh
+ * station has access to a broader network beyond the MBSS. (This is
+ * missnamed in draft 12.0: dot11MeshGateAnnouncementProtocol set to true
+ * only means that the station will announce others it's a mesh gate, but
+ * not necessarily using the gate announcement protocol. Still keeping the
+ * same nomenclature to be in sync with the spec)
+ * @dot11MeshForwarding: whether the Mesh STA is forwarding or non-forwarding
+ * entity (default is TRUE - forwarding entity)
+ * @rssi_threshold: the threshold for average signal strength of candidate
+ * station to establish a peer link
+ * @ht_opmode: mesh HT protection mode
+ *
+ * @dot11MeshHWMPactivePathToRootTimeout: The time (in TUs) for which mesh STAs
+ * receiving a proactive PREQ shall consider the forwarding information to
+ * the root mesh STA to be valid.
+ *
+ * @dot11MeshHWMProotInterval: The interval of time (in TUs) between proactive
+ * PREQs are transmitted.
+ * @dot11MeshHWMPconfirmationInterval: The minimum interval of time (in TUs)
+ * during which a mesh STA can send only one Action frame containing
+ * a PREQ element for root path confirmation.
+ * @power_mode: The default mesh power save mode which will be the initial
+ * setting for new peer links.
+ * @dot11MeshAwakeWindowDuration: The duration in TUs the STA will remain awake
+ * after transmitting its beacon.
+ * @plink_timeout: If no tx activity is seen from a STA we've established
+ * peering with for longer than this time (in seconds), then remove it
+ * from the STA's list of peers. Default is 30 minutes.
+ */
+struct mesh_config {
+ u16 dot11MeshRetryTimeout;
+ u16 dot11MeshConfirmTimeout;
+ u16 dot11MeshHoldingTimeout;
+ u16 dot11MeshMaxPeerLinks;
+ u8 dot11MeshMaxRetries;
+ u8 dot11MeshTTL;
+ u8 element_ttl;
+ bool auto_open_plinks;
+ u32 dot11MeshNbrOffsetMaxNeighbor;
+ u8 dot11MeshHWMPmaxPREQretries;
+ u32 path_refresh_time;
+ u16 min_discovery_timeout;
+ u32 dot11MeshHWMPactivePathTimeout;
+ u16 dot11MeshHWMPpreqMinInterval;
+ u16 dot11MeshHWMPperrMinInterval;
+ u16 dot11MeshHWMPnetDiameterTraversalTime;
+ u8 dot11MeshHWMPRootMode;
+ u16 dot11MeshHWMPRannInterval;
+ bool dot11MeshGateAnnouncementProtocol;
+ bool dot11MeshForwarding;
+ s32 rssi_threshold;
+ u16 ht_opmode;
+ u32 dot11MeshHWMPactivePathToRootTimeout;
+ u16 dot11MeshHWMProotInterval;
+ u16 dot11MeshHWMPconfirmationInterval;
+ enum nl80211_mesh_power_mode power_mode;
+ u16 dot11MeshAwakeWindowDuration;
+ u32 plink_timeout;
+};
+
+/**
+ * struct mesh_setup - 802.11s mesh setup configuration
+ * @chandef: defines the channel to use
+ * @mesh_id: the mesh ID
+ * @mesh_id_len: length of the mesh ID, at least 1 and at most 32 bytes
+ * @sync_method: which synchronization method to use
+ * @path_sel_proto: which path selection protocol to use
+ * @path_metric: which metric to use
+ * @auth_id: which authentication method this mesh is using
+ * @ie: vendor information elements (optional)
+ * @ie_len: length of vendor information elements
+ * @is_authenticated: this mesh requires authentication
+ * @is_secure: this mesh uses security
+ * @user_mpm: userspace handles all MPM functions
+ * @dtim_period: DTIM period to use
+ * @beacon_interval: beacon interval to use
+ * @mcast_rate: multicat rate for Mesh Node [6Mbps is the default for 802.11a]
+ * @basic_rates: basic rates to use when creating the mesh
+ * @beacon_rate: bitrate to be used for beacons
+ *
+ * These parameters are fixed when the mesh is created.
+ */
+struct mesh_setup {
+ struct cfg80211_chan_def chandef;
+ const u8 *mesh_id;
+ u8 mesh_id_len;
+ u8 sync_method;
+ u8 path_sel_proto;
+ u8 path_metric;
+ u8 auth_id;
+ const u8 *ie;
+ u8 ie_len;
+ bool is_authenticated;
+ bool is_secure;
+ bool user_mpm;
+ u8 dtim_period;
+ u16 beacon_interval;
+ int mcast_rate[NUM_NL80211_BANDS];
+ u32 basic_rates;
+ struct cfg80211_bitrate_mask beacon_rate;
+};
+
+/**
+ * struct ocb_setup - 802.11p OCB mode setup configuration
+ * @chandef: defines the channel to use
+ *
+ * These parameters are fixed when connecting to the network
+ */
+struct ocb_setup {
+ struct cfg80211_chan_def chandef;
+};
+
+/**
+ * struct ieee80211_txq_params - TX queue parameters
+ * @ac: AC identifier
+ * @txop: Maximum burst time in units of 32 usecs, 0 meaning disabled
+ * @cwmin: Minimum contention window [a value of the form 2^n-1 in the range
+ * 1..32767]
+ * @cwmax: Maximum contention window [a value of the form 2^n-1 in the range
+ * 1..32767]
+ * @aifs: Arbitration interframe space [0..255]
+ */
+struct ieee80211_txq_params {
+ enum nl80211_ac ac;
+ u16 txop;
+ u16 cwmin;
+ u16 cwmax;
+ u8 aifs;
+};
+
+/**
+ * DOC: Scanning and BSS list handling
+ *
+ * The scanning process itself is fairly simple, but cfg80211 offers quite
+ * a bit of helper functionality. To start a scan, the scan operation will
+ * be invoked with a scan definition. This scan definition contains the
+ * channels to scan, and the SSIDs to send probe requests for (including the
+ * wildcard, if desired). A passive scan is indicated by having no SSIDs to
+ * probe. Additionally, a scan request may contain extra information elements
+ * that should be added to the probe request. The IEs are guaranteed to be
+ * well-formed, and will not exceed the maximum length the driver advertised
+ * in the wiphy structure.
+ *
+ * When scanning finds a BSS, cfg80211 needs to be notified of that, because
+ * it is responsible for maintaining the BSS list; the driver should not
+ * maintain a list itself. For this notification, various functions exist.
+ *
+ * Since drivers do not maintain a BSS list, there are also a number of
+ * functions to search for a BSS and obtain information about it from the
+ * BSS structure cfg80211 maintains. The BSS list is also made available
+ * to userspace.
+ */
+
+/**
+ * struct cfg80211_ssid - SSID description
+ * @ssid: the SSID
+ * @ssid_len: length of the ssid
+ */
+struct cfg80211_ssid {
+ u8 ssid[IEEE80211_MAX_SSID_LEN];
+ u8 ssid_len;
+};
+
+/**
+ * struct cfg80211_scan_info - information about completed scan
+ * @scan_start_tsf: scan start time in terms of the TSF of the BSS that the
+ * wireless device that requested the scan is connected to. If this
+ * information is not available, this field is left zero.
+ * @tsf_bssid: the BSSID according to which %scan_start_tsf is set.
+ * @aborted: set to true if the scan was aborted for any reason,
+ * userspace will be notified of that
+ */
+struct cfg80211_scan_info {
+ u64 scan_start_tsf;
+ u8 tsf_bssid[ETH_ALEN] __aligned(2);
+ bool aborted;
+};
+
+/**
+ * struct cfg80211_scan_request - scan request description
+ *
+ * @ssids: SSIDs to scan for (active scan only)
+ * @n_ssids: number of SSIDs
+ * @channels: channels to scan on.
+ * @n_channels: total number of channels to scan
+ * @scan_width: channel width for scanning
+ * @ie: optional information element(s) to add into Probe Request or %NULL
+ * @ie_len: length of ie in octets
+ * @duration: how long to listen on each channel, in TUs. If
+ * %duration_mandatory is not set, this is the maximum dwell time and
+ * the actual dwell time may be shorter.
+ * @duration_mandatory: if set, the scan duration must be as specified by the
+ * %duration field.
+ * @flags: bit field of flags controlling operation
+ * @rates: bitmap of rates to advertise for each band
+ * @wiphy: the wiphy this was for
+ * @scan_start: time (in jiffies) when the scan started
+ * @wdev: the wireless device to scan for
+ * @info: (internal) information about completed scan
+ * @notified: (internal) scan request was notified as done or aborted
+ * @no_cck: used to send probe requests at non CCK rate in 2GHz band
+ * @mac_addr: MAC address used with randomisation
+ * @mac_addr_mask: MAC address mask used with randomisation, bits that
+ * are 0 in the mask should be randomised, bits that are 1 should
+ * be taken from the @mac_addr
+ * @bssid: BSSID to scan for (most commonly, the wildcard BSSID)
+ */
+struct cfg80211_scan_request {
+ struct cfg80211_ssid *ssids;
+ int n_ssids;
+ u32 n_channels;
+ enum nl80211_bss_scan_width scan_width;
+ const u8 *ie;
+ size_t ie_len;
+ u16 duration;
+ bool duration_mandatory;
+ u32 flags;
+
+ u32 rates[NUM_NL80211_BANDS];
+
+ struct wireless_dev *wdev;
+
+ u8 mac_addr[ETH_ALEN] __aligned(2);
+ u8 mac_addr_mask[ETH_ALEN] __aligned(2);
+ u8 bssid[ETH_ALEN] __aligned(2);
+
+ /* internal */
+ struct wiphy *wiphy;
+ unsigned long scan_start;
+ struct cfg80211_scan_info info;
+ bool notified;
+ bool no_cck;
+
+ /* keep last */
+ struct ieee80211_channel *channels[0];
+};
+
+static inline void get_random_mask_addr(u8 *buf, const u8 *addr, const u8 *mask)
+{
+ int i;
+
+ get_random_bytes(buf, ETH_ALEN);
+ for (i = 0; i < ETH_ALEN; i++) {
+ buf[i] &= ~mask[i];
+ buf[i] |= addr[i] & mask[i];
+ }
+}
+
+/**
+ * struct cfg80211_match_set - sets of attributes to match
+ *
+ * @ssid: SSID to be matched; may be zero-length in case of BSSID match
+ * or no match (RSSI only)
+ * @bssid: BSSID to be matched; may be all-zero BSSID in case of SSID match
+ * or no match (RSSI only)
+ * @rssi_thold: don't report scan results below this threshold (in s32 dBm)
+ */
+struct cfg80211_match_set {
+ struct cfg80211_ssid ssid;
+ u8 bssid[ETH_ALEN];
+ s32 rssi_thold;
+};
+
+/**
+ * struct cfg80211_sched_scan_plan - scan plan for scheduled scan
+ *
+ * @interval: interval between scheduled scan iterations. In seconds.
+ * @iterations: number of scan iterations in this scan plan. Zero means
+ * infinite loop.
+ * The last scan plan will always have this parameter set to zero,
+ * all other scan plans will have a finite number of iterations.
+ */
+struct cfg80211_sched_scan_plan {
+ u32 interval;
+ u32 iterations;
+};
+
+/**
+ * struct cfg80211_bss_select_adjust - BSS selection with RSSI adjustment.
+ *
+ * @band: band of BSS which should match for RSSI level adjustment.
+ * @delta: value of RSSI level adjustment.
+ */
+struct cfg80211_bss_select_adjust {
+ enum nl80211_band band;
+ s8 delta;
+};
+
+/**
+ * struct cfg80211_sched_scan_request - scheduled scan request description
+ *
+ * @reqid: identifies this request.
+ * @ssids: SSIDs to scan for (passed in the probe_reqs in active scans)
+ * @n_ssids: number of SSIDs
+ * @n_channels: total number of channels to scan
+ * @scan_width: channel width for scanning
+ * @ie: optional information element(s) to add into Probe Request or %NULL
+ * @ie_len: length of ie in octets
+ * @flags: bit field of flags controlling operation
+ * @match_sets: sets of parameters to be matched for a scan result
+ * entry to be considered valid and to be passed to the host
+ * (others are filtered out).
+ * If ommited, all results are passed.
+ * @n_match_sets: number of match sets
+ * @report_results: indicates that results were reported for this request
+ * @wiphy: the wiphy this was for
+ * @dev: the interface
+ * @scan_start: start time of the scheduled scan
+ * @channels: channels to scan
+ * @min_rssi_thold: for drivers only supporting a single threshold, this
+ * contains the minimum over all matchsets
+ * @mac_addr: MAC address used with randomisation
+ * @mac_addr_mask: MAC address mask used with randomisation, bits that
+ * are 0 in the mask should be randomised, bits that are 1 should
+ * be taken from the @mac_addr
+ * @scan_plans: scan plans to be executed in this scheduled scan. Lowest
+ * index must be executed first.
+ * @n_scan_plans: number of scan plans, at least 1.
+ * @rcu_head: RCU callback used to free the struct
+ * @owner_nlportid: netlink portid of owner (if this should is a request
+ * owned by a particular socket)
+ * @nl_owner_dead: netlink owner socket was closed - this request be freed
+ * @list: for keeping list of requests.
+ * @delay: delay in seconds to use before starting the first scan
+ * cycle. The driver may ignore this parameter and start
+ * immediately (or at any other time), if this feature is not
+ * supported.
+ * @relative_rssi_set: Indicates whether @relative_rssi is set or not.
+ * @relative_rssi: Relative RSSI threshold in dB to restrict scan result
+ * reporting in connected state to cases where a matching BSS is determined
+ * to have better or slightly worse RSSI than the current connected BSS.
+ * The relative RSSI threshold values are ignored in disconnected state.
+ * @rssi_adjust: delta dB of RSSI preference to be given to the BSSs that belong
+ * to the specified band while deciding whether a better BSS is reported
+ * using @relative_rssi. If delta is a negative number, the BSSs that
+ * belong to the specified band will be penalized by delta dB in relative
+ * comparisions.
+ */
+struct cfg80211_sched_scan_request {
+ u64 reqid;
+ struct cfg80211_ssid *ssids;
+ int n_ssids;
+ u32 n_channels;
+ enum nl80211_bss_scan_width scan_width;
+ const u8 *ie;
+ size_t ie_len;
+ u32 flags;
+ struct cfg80211_match_set *match_sets;
+ int n_match_sets;
+ s32 min_rssi_thold;
+ u32 delay;
+ struct cfg80211_sched_scan_plan *scan_plans;
+ int n_scan_plans;
+
+ u8 mac_addr[ETH_ALEN] __aligned(2);
+ u8 mac_addr_mask[ETH_ALEN] __aligned(2);
+
+ bool relative_rssi_set;
+ s8 relative_rssi;
+ struct cfg80211_bss_select_adjust rssi_adjust;
+
+ /* internal */
+ struct wiphy *wiphy;
+ struct net_device *dev;
+ unsigned long scan_start;
+ bool report_results;
+ struct rcu_head rcu_head;
+ u32 owner_nlportid;
+ bool nl_owner_dead;
+ struct list_head list;
+
+ /* keep last */
+ struct ieee80211_channel *channels[0];
+};
+
+/**
+ * enum cfg80211_signal_type - signal type
+ *
+ * @CFG80211_SIGNAL_TYPE_NONE: no signal strength information available
+ * @CFG80211_SIGNAL_TYPE_MBM: signal strength in mBm (100*dBm)
+ * @CFG80211_SIGNAL_TYPE_UNSPEC: signal strength, increasing from 0 through 100
+ */
+enum cfg80211_signal_type {
+ CFG80211_SIGNAL_TYPE_NONE,
+ CFG80211_SIGNAL_TYPE_MBM,
+ CFG80211_SIGNAL_TYPE_UNSPEC,
+};
+
+/**
+ * struct cfg80211_inform_bss - BSS inform data
+ * @chan: channel the frame was received on
+ * @scan_width: scan width that was used
+ * @signal: signal strength value, according to the wiphy's
+ * signal type
+ * @boottime_ns: timestamp (CLOCK_BOOTTIME) when the information was
+ * received; should match the time when the frame was actually
+ * received by the device (not just by the host, in case it was
+ * buffered on the device) and be accurate to about 10ms.
+ * If the frame isn't buffered, just passing the return value of
+ * ktime_get_boot_ns() is likely appropriate.
+ * @parent_tsf: the time at the start of reception of the first octet of the
+ * timestamp field of the frame. The time is the TSF of the BSS specified
+ * by %parent_bssid.
+ * @parent_bssid: the BSS according to which %parent_tsf is set. This is set to
+ * the BSS that requested the scan in which the beacon/probe was received.
+ */
+struct cfg80211_inform_bss {
+ struct ieee80211_channel *chan;
+ enum nl80211_bss_scan_width scan_width;
+ s32 signal;
+ u64 boottime_ns;
+ u64 parent_tsf;
+ u8 parent_bssid[ETH_ALEN] __aligned(2);
+};
+
+/**
+ * struct cfg80211_bss_ies - BSS entry IE data
+ * @tsf: TSF contained in the frame that carried these IEs
+ * @rcu_head: internal use, for freeing
+ * @len: length of the IEs
+ * @from_beacon: these IEs are known to come from a beacon
+ * @data: IE data
+ */
+struct cfg80211_bss_ies {
+ u64 tsf;
+ struct rcu_head rcu_head;
+ int len;
+ bool from_beacon;
+ u8 data[];
+};
+
+/**
+ * struct cfg80211_bss - BSS description
+ *
+ * This structure describes a BSS (which may also be a mesh network)
+ * for use in scan results and similar.
+ *
+ * @channel: channel this BSS is on
+ * @scan_width: width of the control channel
+ * @bssid: BSSID of the BSS
+ * @beacon_interval: the beacon interval as from the frame
+ * @capability: the capability field in host byte order
+ * @ies: the information elements (Note that there is no guarantee that these
+ * are well-formed!); this is a pointer to either the beacon_ies or
+ * proberesp_ies depending on whether Probe Response frame has been
+ * received. It is always non-%NULL.
+ * @beacon_ies: the information elements from the last Beacon frame
+ * (implementation note: if @hidden_beacon_bss is set this struct doesn't
+ * own the beacon_ies, but they're just pointers to the ones from the
+ * @hidden_beacon_bss struct)
+ * @proberesp_ies: the information elements from the last Probe Response frame
+ * @hidden_beacon_bss: in case this BSS struct represents a probe response from
+ * a BSS that hides the SSID in its beacon, this points to the BSS struct
+ * that holds the beacon data. @beacon_ies is still valid, of course, and
+ * points to the same data as hidden_beacon_bss->beacon_ies in that case.
+ * @signal: signal strength value (type depends on the wiphy's signal_type)
+ * @priv: private area for driver use, has at least wiphy->bss_priv_size bytes
+ */
+struct cfg80211_bss {
+ struct ieee80211_channel *channel;
+ enum nl80211_bss_scan_width scan_width;
+
+ const struct cfg80211_bss_ies __rcu *ies;
+ const struct cfg80211_bss_ies __rcu *beacon_ies;
+ const struct cfg80211_bss_ies __rcu *proberesp_ies;
+
+ struct cfg80211_bss *hidden_beacon_bss;
+
+ s32 signal;
+
+ u16 beacon_interval;
+ u16 capability;
+
+ u8 bssid[ETH_ALEN];
+
+ u8 priv[0] __aligned(sizeof(void *));
+};
+
+/**
+ * ieee80211_bss_get_ie - find IE with given ID
+ * @bss: the bss to search
+ * @ie: the IE ID
+ *
+ * Note that the return value is an RCU-protected pointer, so
+ * rcu_read_lock() must be held when calling this function.
+ * Return: %NULL if not found.
+ */
+const u8 *ieee80211_bss_get_ie(struct cfg80211_bss *bss, u8 ie);
+
+
+/**
+ * struct cfg80211_auth_request - Authentication request data
+ *
+ * This structure provides information needed to complete IEEE 802.11
+ * authentication.
+ *
+ * @bss: The BSS to authenticate with, the callee must obtain a reference
+ * to it if it needs to keep it.
+ * @auth_type: Authentication type (algorithm)
+ * @ie: Extra IEs to add to Authentication frame or %NULL
+ * @ie_len: Length of ie buffer in octets
+ * @key_len: length of WEP key for shared key authentication
+ * @key_idx: index of WEP key for shared key authentication
+ * @key: WEP key for shared key authentication
+ * @auth_data: Fields and elements in Authentication frames. This contains
+ * the authentication frame body (non-IE and IE data), excluding the
+ * Authentication algorithm number, i.e., starting at the Authentication
+ * transaction sequence number field.
+ * @auth_data_len: Length of auth_data buffer in octets
+ */
+struct cfg80211_auth_request {
+ struct cfg80211_bss *bss;
+ const u8 *ie;
+ size_t ie_len;
+ enum nl80211_auth_type auth_type;
+ const u8 *key;
+ u8 key_len, key_idx;
+ const u8 *auth_data;
+ size_t auth_data_len;
+};
+
+/**
+ * enum cfg80211_assoc_req_flags - Over-ride default behaviour in association.
+ *
+ * @ASSOC_REQ_DISABLE_HT: Disable HT (802.11n)
+ * @ASSOC_REQ_DISABLE_VHT: Disable VHT
+ * @ASSOC_REQ_USE_RRM: Declare RRM capability in this association
+ */
+enum cfg80211_assoc_req_flags {
+ ASSOC_REQ_DISABLE_HT = BIT(0),
+ ASSOC_REQ_DISABLE_VHT = BIT(1),
+ ASSOC_REQ_USE_RRM = BIT(2),
+};
+
+/**
+ * struct cfg80211_assoc_request - (Re)Association request data
+ *
+ * This structure provides information needed to complete IEEE 802.11
+ * (re)association.
+ * @bss: The BSS to associate with. If the call is successful the driver is
+ * given a reference that it must give back to cfg80211_send_rx_assoc()
+ * or to cfg80211_assoc_timeout(). To ensure proper refcounting, new
+ * association requests while already associating must be rejected.
+ * @ie: Extra IEs to add to (Re)Association Request frame or %NULL
+ * @ie_len: Length of ie buffer in octets
+ * @use_mfp: Use management frame protection (IEEE 802.11w) in this association
+ * @crypto: crypto settings
+ * @prev_bssid: previous BSSID, if not %NULL use reassociate frame. This is used
+ * to indicate a request to reassociate within the ESS instead of a request
+ * do the initial association with the ESS. When included, this is set to
+ * the BSSID of the current association, i.e., to the value that is
+ * included in the Current AP address field of the Reassociation Request
+ * frame.
+ * @flags: See &enum cfg80211_assoc_req_flags
+ * @ht_capa: HT Capabilities over-rides. Values set in ht_capa_mask
+ * will be used in ht_capa. Un-supported values will be ignored.
+ * @ht_capa_mask: The bits of ht_capa which are to be used.
+ * @vht_capa: VHT capability override
+ * @vht_capa_mask: VHT capability mask indicating which fields to use
+ * @fils_kek: FILS KEK for protecting (Re)Association Request/Response frame or
+ * %NULL if FILS is not used.
+ * @fils_kek_len: Length of fils_kek in octets
+ * @fils_nonces: FILS nonces (part of AAD) for protecting (Re)Association
+ * Request/Response frame or %NULL if FILS is not used. This field starts
+ * with 16 octets of STA Nonce followed by 16 octets of AP Nonce.
+ */
+struct cfg80211_assoc_request {
+ struct cfg80211_bss *bss;
+ const u8 *ie, *prev_bssid;
+ size_t ie_len;
+ struct cfg80211_crypto_settings crypto;
+ bool use_mfp;
+ u32 flags;
+ struct ieee80211_ht_cap ht_capa;
+ struct ieee80211_ht_cap ht_capa_mask;
+ struct ieee80211_vht_cap vht_capa, vht_capa_mask;
+ const u8 *fils_kek;
+ size_t fils_kek_len;
+ const u8 *fils_nonces;
+};
+
+/**
+ * struct cfg80211_deauth_request - Deauthentication request data
+ *
+ * This structure provides information needed to complete IEEE 802.11
+ * deauthentication.
+ *
+ * @bssid: the BSSID of the BSS to deauthenticate from
+ * @ie: Extra IEs to add to Deauthentication frame or %NULL
+ * @ie_len: Length of ie buffer in octets
+ * @reason_code: The reason code for the deauthentication
+ * @local_state_change: if set, change local state only and
+ * do not set a deauth frame
+ */
+struct cfg80211_deauth_request {
+ const u8 *bssid;
+ const u8 *ie;
+ size_t ie_len;
+ u16 reason_code;
+ bool local_state_change;
+};
+
+/**
+ * struct cfg80211_disassoc_request - Disassociation request data
+ *
+ * This structure provides information needed to complete IEEE 802.11
+ * disassociation.
+ *
+ * @bss: the BSS to disassociate from
+ * @ie: Extra IEs to add to Disassociation frame or %NULL
+ * @ie_len: Length of ie buffer in octets
+ * @reason_code: The reason code for the disassociation
+ * @local_state_change: This is a request for a local state only, i.e., no
+ * Disassociation frame is to be transmitted.
+ */
+struct cfg80211_disassoc_request {
+ struct cfg80211_bss *bss;
+ const u8 *ie;
+ size_t ie_len;
+ u16 reason_code;
+ bool local_state_change;
+};
+
+/**
+ * struct cfg80211_ibss_params - IBSS parameters
+ *
+ * This structure defines the IBSS parameters for the join_ibss()
+ * method.
+ *
+ * @ssid: The SSID, will always be non-null.
+ * @ssid_len: The length of the SSID, will always be non-zero.
+ * @bssid: Fixed BSSID requested, maybe be %NULL, if set do not
+ * search for IBSSs with a different BSSID.
+ * @chandef: defines the channel to use if no other IBSS to join can be found
+ * @channel_fixed: The channel should be fixed -- do not search for
+ * IBSSs to join on other channels.
+ * @ie: information element(s) to include in the beacon
+ * @ie_len: length of that
+ * @beacon_interval: beacon interval to use
+ * @privacy: this is a protected network, keys will be configured
+ * after joining
+ * @control_port: whether user space controls IEEE 802.1X port, i.e.,
+ * sets/clears %NL80211_STA_FLAG_AUTHORIZED. If true, the driver is
+ * required to assume that the port is unauthorized until authorized by
+ * user space. Otherwise, port is marked authorized by default.
+ * @userspace_handles_dfs: whether user space controls DFS operation, i.e.
+ * changes the channel when a radar is detected. This is required
+ * to operate on DFS channels.
+ * @basic_rates: bitmap of basic rates to use when creating the IBSS
+ * @mcast_rate: per-band multicast rate index + 1 (0: disabled)
+ * @ht_capa: HT Capabilities over-rides. Values set in ht_capa_mask
+ * will be used in ht_capa. Un-supported values will be ignored.
+ * @ht_capa_mask: The bits of ht_capa which are to be used.
+ */
+struct cfg80211_ibss_params {
+ const u8 *ssid;
+ const u8 *bssid;
+ struct cfg80211_chan_def chandef;
+ const u8 *ie;
+ u8 ssid_len, ie_len;
+ u16 beacon_interval;
+ u32 basic_rates;
+ bool channel_fixed;
+ bool privacy;
+ bool control_port;
+ bool userspace_handles_dfs;
+ int mcast_rate[NUM_NL80211_BANDS];
+ struct ieee80211_ht_cap ht_capa;
+ struct ieee80211_ht_cap ht_capa_mask;
+};
+
+/**
+ * struct cfg80211_bss_selection - connection parameters for BSS selection.
+ *
+ * @behaviour: requested BSS selection behaviour.
+ * @param: parameters for requestion behaviour.
+ * @band_pref: preferred band for %NL80211_BSS_SELECT_ATTR_BAND_PREF.
+ * @adjust: parameters for %NL80211_BSS_SELECT_ATTR_RSSI_ADJUST.
+ */
+struct cfg80211_bss_selection {
+ enum nl80211_bss_select_attr behaviour;
+ union {
+ enum nl80211_band band_pref;
+ struct cfg80211_bss_select_adjust adjust;
+ } param;
+};
+
+/**
+ * struct cfg80211_connect_params - Connection parameters
+ *
+ * This structure provides information needed to complete IEEE 802.11
+ * authentication and association.
+ *
+ * @channel: The channel to use or %NULL if not specified (auto-select based
+ * on scan results)
+ * @channel_hint: The channel of the recommended BSS for initial connection or
+ * %NULL if not specified
+ * @bssid: The AP BSSID or %NULL if not specified (auto-select based on scan
+ * results)
+ * @bssid_hint: The recommended AP BSSID for initial connection to the BSS or
+ * %NULL if not specified. Unlike the @bssid parameter, the driver is
+ * allowed to ignore this @bssid_hint if it has knowledge of a better BSS
+ * to use.
+ * @ssid: SSID
+ * @ssid_len: Length of ssid in octets
+ * @auth_type: Authentication type (algorithm)
+ * @ie: IEs for association request
+ * @ie_len: Length of assoc_ie in octets
+ * @privacy: indicates whether privacy-enabled APs should be used
+ * @mfp: indicate whether management frame protection is used
+ * @crypto: crypto settings
+ * @key_len: length of WEP key for shared key authentication
+ * @key_idx: index of WEP key for shared key authentication
+ * @key: WEP key for shared key authentication
+ * @flags: See &enum cfg80211_assoc_req_flags
+ * @bg_scan_period: Background scan period in seconds
+ * or -1 to indicate that default value is to be used.
+ * @ht_capa: HT Capabilities over-rides. Values set in ht_capa_mask
+ * will be used in ht_capa. Un-supported values will be ignored.
+ * @ht_capa_mask: The bits of ht_capa which are to be used.
+ * @vht_capa: VHT Capability overrides
+ * @vht_capa_mask: The bits of vht_capa which are to be used.
+ * @pbss: if set, connect to a PCP instead of AP. Valid for DMG
+ * networks.
+ * @bss_select: criteria to be used for BSS selection.
+ * @prev_bssid: previous BSSID, if not %NULL use reassociate frame. This is used
+ * to indicate a request to reassociate within the ESS instead of a request
+ * do the initial association with the ESS. When included, this is set to
+ * the BSSID of the current association, i.e., to the value that is
+ * included in the Current AP address field of the Reassociation Request
+ * frame.
+ * @fils_erp_username: EAP re-authentication protocol (ERP) username part of the
+ * NAI or %NULL if not specified. This is used to construct FILS wrapped
+ * data IE.
+ * @fils_erp_username_len: Length of @fils_erp_username in octets.
+ * @fils_erp_realm: EAP re-authentication protocol (ERP) realm part of NAI or
+ * %NULL if not specified. This specifies the domain name of ER server and
+ * is used to construct FILS wrapped data IE.
+ * @fils_erp_realm_len: Length of @fils_erp_realm in octets.
+ * @fils_erp_next_seq_num: The next sequence number to use in the FILS ERP
+ * messages. This is also used to construct FILS wrapped data IE.
+ * @fils_erp_rrk: ERP re-authentication Root Key (rRK) used to derive additional
+ * keys in FILS or %NULL if not specified.
+ * @fils_erp_rrk_len: Length of @fils_erp_rrk in octets.
+ * @want_1x: indicates user-space supports and wants to use 802.1X driver
+ * offload of 4-way handshake.
+ */
+struct cfg80211_connect_params {
+ struct ieee80211_channel *channel;
+ struct ieee80211_channel *channel_hint;
+ const u8 *bssid;
+ const u8 *bssid_hint;
+ const u8 *ssid;
+ size_t ssid_len;
+ enum nl80211_auth_type auth_type;
+ const u8 *ie;
+ size_t ie_len;
+ bool privacy;
+ enum nl80211_mfp mfp;
+ struct cfg80211_crypto_settings crypto;
+ const u8 *key;
+ u8 key_len, key_idx;
+ u32 flags;
+ int bg_scan_period;
+ struct ieee80211_ht_cap ht_capa;
+ struct ieee80211_ht_cap ht_capa_mask;
+ struct ieee80211_vht_cap vht_capa;
+ struct ieee80211_vht_cap vht_capa_mask;
+ bool pbss;
+ struct cfg80211_bss_selection bss_select;
+ const u8 *prev_bssid;
+ const u8 *fils_erp_username;
+ size_t fils_erp_username_len;
+ const u8 *fils_erp_realm;
+ size_t fils_erp_realm_len;
+ u16 fils_erp_next_seq_num;
+ const u8 *fils_erp_rrk;
+ size_t fils_erp_rrk_len;
+ bool want_1x;
+};
+
+/**
+ * enum cfg80211_connect_params_changed - Connection parameters being updated
+ *
+ * This enum provides information of all connect parameters that
+ * have to be updated as part of update_connect_params() call.
+ *
+ * @UPDATE_ASSOC_IES: Indicates whether association request IEs are updated
+ */
+enum cfg80211_connect_params_changed {
+ UPDATE_ASSOC_IES = BIT(0),
+};
+
+/**
+ * enum wiphy_params_flags - set_wiphy_params bitfield values
+ * @WIPHY_PARAM_RETRY_SHORT: wiphy->retry_short has changed
+ * @WIPHY_PARAM_RETRY_LONG: wiphy->retry_long has changed
+ * @WIPHY_PARAM_FRAG_THRESHOLD: wiphy->frag_threshold has changed
+ * @WIPHY_PARAM_RTS_THRESHOLD: wiphy->rts_threshold has changed
+ * @WIPHY_PARAM_COVERAGE_CLASS: coverage class changed
+ * @WIPHY_PARAM_DYN_ACK: dynack has been enabled
+ */
+enum wiphy_params_flags {
+ WIPHY_PARAM_RETRY_SHORT = 1 << 0,
+ WIPHY_PARAM_RETRY_LONG = 1 << 1,
+ WIPHY_PARAM_FRAG_THRESHOLD = 1 << 2,
+ WIPHY_PARAM_RTS_THRESHOLD = 1 << 3,
+ WIPHY_PARAM_COVERAGE_CLASS = 1 << 4,
+ WIPHY_PARAM_DYN_ACK = 1 << 5,
+};
+
+/**
+ * struct cfg80211_pmksa - PMK Security Association
+ *
+ * This structure is passed to the set/del_pmksa() method for PMKSA
+ * caching.
+ *
+ * @bssid: The AP's BSSID (may be %NULL).
+ * @pmkid: The identifier to refer a PMKSA.
+ * @pmk: The PMK for the PMKSA identified by @pmkid. This is used for key
+ * derivation by a FILS STA. Otherwise, %NULL.
+ * @pmk_len: Length of the @pmk. The length of @pmk can differ depending on
+ * the hash algorithm used to generate this.
+ * @ssid: SSID to specify the ESS within which a PMKSA is valid when using FILS
+ * cache identifier (may be %NULL).
+ * @ssid_len: Length of the @ssid in octets.
+ * @cache_id: 2-octet cache identifier advertized by a FILS AP identifying the
+ * scope of PMKSA. This is valid only if @ssid_len is non-zero (may be
+ * %NULL).
+ */
+struct cfg80211_pmksa {
+ const u8 *bssid;
+ const u8 *pmkid;
+ const u8 *pmk;
+ size_t pmk_len;
+ const u8 *ssid;
+ size_t ssid_len;
+ const u8 *cache_id;
+};
+
+/**
+ * struct cfg80211_pkt_pattern - packet pattern
+ * @mask: bitmask where to match pattern and where to ignore bytes,
+ * one bit per byte, in same format as nl80211
+ * @pattern: bytes to match where bitmask is 1
+ * @pattern_len: length of pattern (in bytes)
+ * @pkt_offset: packet offset (in bytes)
+ * @action: whether to drop or allow this pattern
+ *
+ * Internal note: @mask and @pattern are allocated in one chunk of
+ * memory, free @mask only!
+ */
+struct cfg80211_pkt_pattern {
+ const u8 *mask, *pattern;
+ int pattern_len;
+ int pkt_offset;
+ u8 action;
+};
+
+/**
+ * struct cfg80211_wowlan_tcp - TCP connection parameters
+ *
+ * @sock: (internal) socket for source port allocation
+ * @src: source IP address
+ * @dst: destination IP address
+ * @dst_mac: destination MAC address
+ * @src_port: source port
+ * @dst_port: destination port
+ * @payload_len: data payload length
+ * @payload: data payload buffer
+ * @payload_seq: payload sequence stamping configuration
+ * @data_interval: interval at which to send data packets
+ * @wake_len: wakeup payload match length
+ * @wake_data: wakeup payload match data
+ * @wake_mask: wakeup payload match mask
+ * @tokens_size: length of the tokens buffer
+ * @payload_tok: payload token usage configuration
+ */
+struct cfg80211_wowlan_tcp {
+ struct socket *sock;
+ __be32 src, dst;
+ u16 src_port, dst_port;
+ u8 dst_mac[ETH_ALEN];
+ int payload_len;
+ const u8 *payload;
+ struct nl80211_wowlan_tcp_data_seq payload_seq;
+ u32 data_interval;
+ u32 wake_len;
+ const u8 *wake_data, *wake_mask;
+ u32 tokens_size;
+ /* must be last, variable member */
+ struct nl80211_wowlan_tcp_data_token payload_tok;
+};
+
+/**
+ * struct cfg80211_wowlan - Wake on Wireless-LAN support info
+ *
+ * This structure defines the enabled WoWLAN triggers for the device.
+ * @any: wake up on any activity -- special trigger if device continues
+ * operating as normal during suspend
+ * @disconnect: wake up if getting disconnected
+ * @magic_pkt: wake up on receiving magic packet
+ * @patterns: wake up on receiving packet matching a pattern
+ * @n_patterns: number of patterns
+ * @gtk_rekey_failure: wake up on GTK rekey failure
+ * @eap_identity_req: wake up on EAP identity request packet
+ * @four_way_handshake: wake up on 4-way handshake
+ * @rfkill_release: wake up when rfkill is released
+ * @tcp: TCP connection establishment/wakeup parameters, see nl80211.h.
+ * NULL if not configured.
+ * @nd_config: configuration for the scan to be used for net detect wake.
+ */
+struct cfg80211_wowlan {
+ bool any, disconnect, magic_pkt, gtk_rekey_failure,
+ eap_identity_req, four_way_handshake,
+ rfkill_release;
+ struct cfg80211_pkt_pattern *patterns;
+ struct cfg80211_wowlan_tcp *tcp;
+ int n_patterns;
+ struct cfg80211_sched_scan_request *nd_config;
+};
+
+/**
+ * struct cfg80211_coalesce_rules - Coalesce rule parameters
+ *
+ * This structure defines coalesce rule for the device.
+ * @delay: maximum coalescing delay in msecs.
+ * @condition: condition for packet coalescence.
+ * see &enum nl80211_coalesce_condition.
+ * @patterns: array of packet patterns
+ * @n_patterns: number of patterns
+ */
+struct cfg80211_coalesce_rules {
+ int delay;
+ enum nl80211_coalesce_condition condition;
+ struct cfg80211_pkt_pattern *patterns;
+ int n_patterns;
+};
+
+/**
+ * struct cfg80211_coalesce - Packet coalescing settings
+ *
+ * This structure defines coalescing settings.
+ * @rules: array of coalesce rules
+ * @n_rules: number of rules
+ */
+struct cfg80211_coalesce {
+ struct cfg80211_coalesce_rules *rules;
+ int n_rules;
+};
+
+/**
+ * struct cfg80211_wowlan_nd_match - information about the match
+ *
+ * @ssid: SSID of the match that triggered the wake up
+ * @n_channels: Number of channels where the match occurred. This
+ * value may be zero if the driver can't report the channels.
+ * @channels: center frequencies of the channels where a match
+ * occurred (in MHz)
+ */
+struct cfg80211_wowlan_nd_match {
+ struct cfg80211_ssid ssid;
+ int n_channels;
+ u32 channels[];
+};
+
+/**
+ * struct cfg80211_wowlan_nd_info - net detect wake up information
+ *
+ * @n_matches: Number of match information instances provided in
+ * @matches. This value may be zero if the driver can't provide
+ * match information.
+ * @matches: Array of pointers to matches containing information about
+ * the matches that triggered the wake up.
+ */
+struct cfg80211_wowlan_nd_info {
+ int n_matches;
+ struct cfg80211_wowlan_nd_match *matches[];
+};
+
+/**
+ * struct cfg80211_wowlan_wakeup - wakeup report
+ * @disconnect: woke up by getting disconnected
+ * @magic_pkt: woke up by receiving magic packet
+ * @gtk_rekey_failure: woke up by GTK rekey failure
+ * @eap_identity_req: woke up by EAP identity request packet
+ * @four_way_handshake: woke up by 4-way handshake
+ * @rfkill_release: woke up by rfkill being released
+ * @pattern_idx: pattern that caused wakeup, -1 if not due to pattern
+ * @packet_present_len: copied wakeup packet data
+ * @packet_len: original wakeup packet length
+ * @packet: The packet causing the wakeup, if any.
+ * @packet_80211: For pattern match, magic packet and other data
+ * frame triggers an 802.3 frame should be reported, for
+ * disconnect due to deauth 802.11 frame. This indicates which
+ * it is.
+ * @tcp_match: TCP wakeup packet received
+ * @tcp_connlost: TCP connection lost or failed to establish
+ * @tcp_nomoretokens: TCP data ran out of tokens
+ * @net_detect: if not %NULL, woke up because of net detect
+ */
+struct cfg80211_wowlan_wakeup {
+ bool disconnect, magic_pkt, gtk_rekey_failure,
+ eap_identity_req, four_way_handshake,
+ rfkill_release, packet_80211,
+ tcp_match, tcp_connlost, tcp_nomoretokens;
+ s32 pattern_idx;
+ u32 packet_present_len, packet_len;
+ const void *packet;
+ struct cfg80211_wowlan_nd_info *net_detect;
+};
+
+#define CFG80211_KEEP_ALIVE_PAYLOAD_MAX_LENGTH 64
+
+struct cfg80211_keepalive_request {
+ u32 interval;
+ u8 cmd; /* 0: ADD; 1: DEL: 2: START; 3: STOP */
+ u8 index;
+ u8 trig;
+ u8 dst_macaddr[ETH_ALEN];
+ u8 payload_len;
+ u8 payload[CFG80211_KEEP_ALIVE_PAYLOAD_MAX_LENGTH];
+};
+
+/**
+ * struct cfg80211_gtk_rekey_data - rekey data
+ * @kek: key encryption key (NL80211_KEK_LEN bytes)
+ * @kck: key confirmation key (NL80211_KCK_LEN bytes)
+ * @replay_ctr: replay counter (NL80211_REPLAY_CTR_LEN bytes)
+ */
+struct cfg80211_gtk_rekey_data {
+ const u8 *kek, *kck, *replay_ctr;
+};
+
+/**
+ * struct cfg80211_update_ft_ies_params - FT IE Information
+ *
+ * This structure provides information needed to update the fast transition IE
+ *
+ * @md: The Mobility Domain ID, 2 Octet value
+ * @ie: Fast Transition IEs
+ * @ie_len: Length of ft_ie in octets
+ */
+struct cfg80211_update_ft_ies_params {
+ u16 md;
+ const u8 *ie;
+ size_t ie_len;
+};
+
+/**
+ * struct cfg80211_mgmt_tx_params - mgmt tx parameters
+ *
+ * This structure provides information needed to transmit a mgmt frame
+ *
+ * @chan: channel to use
+ * @offchan: indicates wether off channel operation is required
+ * @wait: duration for ROC
+ * @buf: buffer to transmit
+ * @len: buffer length
+ * @no_cck: don't use cck rates for this frame
+ * @dont_wait_for_ack: tells the low level not to wait for an ack
+ * @n_csa_offsets: length of csa_offsets array
+ * @csa_offsets: array of all the csa offsets in the frame
+ */
+struct cfg80211_mgmt_tx_params {
+ struct ieee80211_channel *chan;
+ bool offchan;
+ unsigned int wait;
+ const u8 *buf;
+ size_t len;
+ bool no_cck;
+ bool dont_wait_for_ack;
+ int n_csa_offsets;
+ const u16 *csa_offsets;
+};
+
+/**
+ * struct cfg80211_dscp_exception - DSCP exception
+ *
+ * @dscp: DSCP value that does not adhere to the user priority range definition
+ * @up: user priority value to which the corresponding DSCP value belongs
+ */
+struct cfg80211_dscp_exception {
+ u8 dscp;
+ u8 up;
+};
+
+/**
+ * struct cfg80211_dscp_range - DSCP range definition for user priority
+ *
+ * @low: lowest DSCP value of this user priority range, inclusive
+ * @high: highest DSCP value of this user priority range, inclusive
+ */
+struct cfg80211_dscp_range {
+ u8 low;
+ u8 high;
+};
+
+/* QoS Map Set element length defined in IEEE Std 802.11-2012, 8.4.2.97 */
+#define IEEE80211_QOS_MAP_MAX_EX 21
+#define IEEE80211_QOS_MAP_LEN_MIN 16
+#define IEEE80211_QOS_MAP_LEN_MAX \
+ (IEEE80211_QOS_MAP_LEN_MIN + 2 * IEEE80211_QOS_MAP_MAX_EX)
+
+/**
+ * struct cfg80211_qos_map - QoS Map Information
+ *
+ * This struct defines the Interworking QoS map setting for DSCP values
+ *
+ * @num_des: number of DSCP exceptions (0..21)
+ * @dscp_exception: optionally up to maximum of 21 DSCP exceptions from
+ * the user priority DSCP range definition
+ * @up: DSCP range definition for a particular user priority
+ */
+struct cfg80211_qos_map {
+ u8 num_des;
+ struct cfg80211_dscp_exception dscp_exception[IEEE80211_QOS_MAP_MAX_EX];
+ struct cfg80211_dscp_range up[8];
+};
+
+/**
+ * struct cfg80211_nan_conf - NAN configuration
+ *
+ * This struct defines NAN configuration parameters
+ *
+ * @master_pref: master preference (1 - 255)
+ * @bands: operating bands, a bitmap of &enum nl80211_band values.
+ * For instance, for NL80211_BAND_2GHZ, bit 0 would be set
+ * (i.e. BIT(NL80211_BAND_2GHZ)).
+ */
+struct cfg80211_nan_conf {
+ u8 master_pref;
+ u8 bands;
+};
+
+/**
+ * enum cfg80211_nan_conf_changes - indicates changed fields in NAN
+ * configuration
+ *
+ * @CFG80211_NAN_CONF_CHANGED_PREF: master preference
+ * @CFG80211_NAN_CONF_CHANGED_BANDS: operating bands
+ */
+enum cfg80211_nan_conf_changes {
+ CFG80211_NAN_CONF_CHANGED_PREF = BIT(0),
+ CFG80211_NAN_CONF_CHANGED_BANDS = BIT(1),
+};
+
+/**
+ * struct cfg80211_nan_func_filter - a NAN function Rx / Tx filter
+ *
+ * @filter: the content of the filter
+ * @len: the length of the filter
+ */
+struct cfg80211_nan_func_filter {
+ const u8 *filter;
+ u8 len;
+};
+
+/**
+ * struct cfg80211_nan_func - a NAN function
+ *
+ * @type: &enum nl80211_nan_function_type
+ * @service_id: the service ID of the function
+ * @publish_type: &nl80211_nan_publish_type
+ * @close_range: if true, the range should be limited. Threshold is
+ * implementation specific.
+ * @publish_bcast: if true, the solicited publish should be broadcasted
+ * @subscribe_active: if true, the subscribe is active
+ * @followup_id: the instance ID for follow up
+ * @followup_reqid: the requestor instance ID for follow up
+ * @followup_dest: MAC address of the recipient of the follow up
+ * @ttl: time to live counter in DW.
+ * @serv_spec_info: Service Specific Info
+ * @serv_spec_info_len: Service Specific Info length
+ * @srf_include: if true, SRF is inclusive
+ * @srf_bf: Bloom Filter
+ * @srf_bf_len: Bloom Filter length
+ * @srf_bf_idx: Bloom Filter index
+ * @srf_macs: SRF MAC addresses
+ * @srf_num_macs: number of MAC addresses in SRF
+ * @rx_filters: rx filters that are matched with corresponding peer's tx_filter
+ * @tx_filters: filters that should be transmitted in the SDF.
+ * @num_rx_filters: length of &rx_filters.
+ * @num_tx_filters: length of &tx_filters.
+ * @instance_id: driver allocated id of the function.
+ * @cookie: unique NAN function identifier.
+ */
+struct cfg80211_nan_func {
+ enum nl80211_nan_function_type type;
+ u8 service_id[NL80211_NAN_FUNC_SERVICE_ID_LEN];
+ u8 publish_type;
+ bool close_range;
+ bool publish_bcast;
+ bool subscribe_active;
+ u8 followup_id;
+ u8 followup_reqid;
+ struct mac_address followup_dest;
+ u32 ttl;
+ const u8 *serv_spec_info;
+ u8 serv_spec_info_len;
+ bool srf_include;
+ const u8 *srf_bf;
+ u8 srf_bf_len;
+ u8 srf_bf_idx;
+ struct mac_address *srf_macs;
+ int srf_num_macs;
+ struct cfg80211_nan_func_filter *rx_filters;
+ struct cfg80211_nan_func_filter *tx_filters;
+ u8 num_tx_filters;
+ u8 num_rx_filters;
+ u8 instance_id;
+ u64 cookie;
+};
+
+/**
+ * struct cfg80211_pmk_conf - PMK configuration
+ *
+ * @aa: authenticator address
+ * @pmk_len: PMK length in bytes.
+ * @pmk: the PMK material
+ * @pmk_r0_name: PMK-R0 Name. NULL if not applicable (i.e., the PMK
+ * is not PMK-R0). When pmk_r0_name is not NULL, the pmk field
+ * holds PMK-R0.
+ */
+struct cfg80211_pmk_conf {
+ const u8 *aa;
+ u8 pmk_len;
+ const u8 *pmk;
+ const u8 *pmk_r0_name;
+};
+
+/**
+ * struct cfg80211_ops - backend description for wireless configuration
+ *
+ * This struct is registered by fullmac card drivers and/or wireless stacks
+ * in order to handle configuration requests on their interfaces.
+ *
+ * All callbacks except where otherwise noted should return 0
+ * on success or a negative error code.
+ *
+ * All operations are currently invoked under rtnl for consistency with the
+ * wireless extensions but this is subject to reevaluation as soon as this
+ * code is used more widely and we have a first user without wext.
+ *
+ * @suspend: wiphy device needs to be suspended. The variable @wow will
+ * be %NULL or contain the enabled Wake-on-Wireless triggers that are
+ * configured for the device.
+ * @resume: wiphy device needs to be resumed
+ * @set_wakeup: Called when WoWLAN is enabled/disabled, use this callback
+ * to call device_set_wakeup_enable() to enable/disable wakeup from
+ * the device.
+ *
+ * @add_virtual_intf: create a new virtual interface with the given name,
+ * must set the struct wireless_dev's iftype. Beware: You must create
+ * the new netdev in the wiphy's network namespace! Returns the struct
+ * wireless_dev, or an ERR_PTR. For P2P device wdevs, the driver must
+ * also set the address member in the wdev.
+ *
+ * @del_virtual_intf: remove the virtual interface
+ *
+ * @change_virtual_intf: change type/configuration of virtual interface,
+ * keep the struct wireless_dev's iftype updated.
+ *
+ * @add_key: add a key with the given parameters. @mac_addr will be %NULL
+ * when adding a group key.
+ *
+ * @get_key: get information about the key with the given parameters.
+ * @mac_addr will be %NULL when requesting information for a group
+ * key. All pointers given to the @callback function need not be valid
+ * after it returns. This function should return an error if it is
+ * not possible to retrieve the key, -ENOENT if it doesn't exist.
+ *
+ * @del_key: remove a key given the @mac_addr (%NULL for a group key)
+ * and @key_index, return -ENOENT if the key doesn't exist.
+ *
+ * @set_default_key: set the default key on an interface
+ *
+ * @set_default_mgmt_key: set the default management frame key on an interface
+ *
+ * @set_rekey_data: give the data necessary for GTK rekeying to the driver
+ *
+ * @start_ap: Start acting in AP mode defined by the parameters.
+ * @change_beacon: Change the beacon parameters for an access point mode
+ * interface. This should reject the call when AP mode wasn't started.
+ * @stop_ap: Stop being an AP, including stopping beaconing.
+ *
+ * @add_station: Add a new station.
+ * @del_station: Remove a station
+ * @change_station: Modify a given station. Note that flags changes are not much
+ * validated in cfg80211, in particular the auth/assoc/authorized flags
+ * might come to the driver in invalid combinations -- make sure to check
+ * them, also against the existing state! Drivers must call
+ * cfg80211_check_station_change() to validate the information.
+ * @get_station: get station information for the station identified by @mac
+ * @dump_station: dump station callback -- resume dump at index @idx
+ *
+ * @add_mpath: add a fixed mesh path
+ * @del_mpath: delete a given mesh path
+ * @change_mpath: change a given mesh path
+ * @get_mpath: get a mesh path for the given parameters
+ * @dump_mpath: dump mesh path callback -- resume dump at index @idx
+ * @get_mpp: get a mesh proxy path for the given parameters
+ * @dump_mpp: dump mesh proxy path callback -- resume dump at index @idx
+ * @join_mesh: join the mesh network with the specified parameters
+ * (invoked with the wireless_dev mutex held)
+ * @leave_mesh: leave the current mesh network
+ * (invoked with the wireless_dev mutex held)
+ *
+ * @get_mesh_config: Get the current mesh configuration
+ *
+ * @update_mesh_config: Update mesh parameters on a running mesh.
+ * The mask is a bitfield which tells us which parameters to
+ * set, and which to leave alone.
+ *
+ * @change_bss: Modify parameters for a given BSS.
+ *
+ * @set_txq_params: Set TX queue parameters
+ *
+ * @libertas_set_mesh_channel: Only for backward compatibility for libertas,
+ * as it doesn't implement join_mesh and needs to set the channel to
+ * join the mesh instead.
+ *
+ * @set_monitor_channel: Set the monitor mode channel for the device. If other
+ * interfaces are active this callback should reject the configuration.
+ * If no interfaces are active or the device is down, the channel should
+ * be stored for when a monitor interface becomes active.
+ *
+ * @scan: Request to do a scan. If returning zero, the scan request is given
+ * the driver, and will be valid until passed to cfg80211_scan_done().
+ * For scan results, call cfg80211_inform_bss(); you can call this outside
+ * the scan/scan_done bracket too.
+ * @abort_scan: Tell the driver to abort an ongoing scan. The driver shall
+ * indicate the status of the scan through cfg80211_scan_done().
+ *
+ * @auth: Request to authenticate with the specified peer
+ * (invoked with the wireless_dev mutex held)
+ * @assoc: Request to (re)associate with the specified peer
+ * (invoked with the wireless_dev mutex held)
+ * @deauth: Request to deauthenticate from the specified peer
+ * (invoked with the wireless_dev mutex held)
+ * @disassoc: Request to disassociate from the specified peer
+ * (invoked with the wireless_dev mutex held)
+ *
+ * @connect: Connect to the ESS with the specified parameters. When connected,
+ * call cfg80211_connect_result()/cfg80211_connect_bss() with status code
+ * %WLAN_STATUS_SUCCESS. If the connection fails for some reason, call
+ * cfg80211_connect_result()/cfg80211_connect_bss() with the status code
+ * from the AP or cfg80211_connect_timeout() if no frame with status code
+ * was received.
+ * The driver is allowed to roam to other BSSes within the ESS when the
+ * other BSS matches the connect parameters. When such roaming is initiated
+ * by the driver, the driver is expected to verify that the target matches
+ * the configured security parameters and to use Reassociation Request
+ * frame instead of Association Request frame.
+ * The connect function can also be used to request the driver to perform a
+ * specific roam when connected to an ESS. In that case, the prev_bssid
+ * parameter is set to the BSSID of the currently associated BSS as an
+ * indication of requesting reassociation.
+ * In both the driver-initiated and new connect() call initiated roaming
+ * cases, the result of roaming is indicated with a call to
+ * cfg80211_roamed(). (invoked with the wireless_dev mutex held)
+ * @update_connect_params: Update the connect parameters while connected to a
+ * BSS. The updated parameters can be used by driver/firmware for
+ * subsequent BSS selection (roaming) decisions and to form the
+ * Authentication/(Re)Association Request frames. This call does not
+ * request an immediate disassociation or reassociation with the current
+ * BSS, i.e., this impacts only subsequent (re)associations. The bits in
+ * changed are defined in &enum cfg80211_connect_params_changed.
+ * (invoked with the wireless_dev mutex held)
+ * @disconnect: Disconnect from the BSS/ESS or stop connection attempts if
+ * connection is in progress. Once done, call cfg80211_disconnected() in
+ * case connection was already established (invoked with the
+ * wireless_dev mutex held), otherwise call cfg80211_connect_timeout().
+ *
+ * @join_ibss: Join the specified IBSS (or create if necessary). Once done, call
+ * cfg80211_ibss_joined(), also call that function when changing BSSID due
+ * to a merge.
+ * (invoked with the wireless_dev mutex held)
+ * @leave_ibss: Leave the IBSS.
+ * (invoked with the wireless_dev mutex held)
+ *
+ * @set_mcast_rate: Set the specified multicast rate (only if vif is in ADHOC or
+ * MESH mode)
+ *
+ * @set_wiphy_params: Notify that wiphy parameters have changed;
+ * @changed bitfield (see &enum wiphy_params_flags) describes which values
+ * have changed. The actual parameter values are available in
+ * struct wiphy. If returning an error, no value should be changed.
+ *
+ * @set_tx_power: set the transmit power according to the parameters,
+ * the power passed is in mBm, to get dBm use MBM_TO_DBM(). The
+ * wdev may be %NULL if power was set for the wiphy, and will
+ * always be %NULL unless the driver supports per-vif TX power
+ * (as advertised by the nl80211 feature flag.)
+ * @get_tx_power: store the current TX power into the dbm variable;
+ * return 0 if successful
+ *
+ * @set_wds_peer: set the WDS peer for a WDS interface
+ *
+ * @rfkill_poll: polls the hw rfkill line, use cfg80211 reporting
+ * functions to adjust rfkill hw state
+ *
+ * @dump_survey: get site survey information.
+ *
+ * @remain_on_channel: Request the driver to remain awake on the specified
+ * channel for the specified duration to complete an off-channel
+ * operation (e.g., public action frame exchange). When the driver is
+ * ready on the requested channel, it must indicate this with an event
+ * notification by calling cfg80211_ready_on_channel().
+ * @cancel_remain_on_channel: Cancel an on-going remain-on-channel operation.
+ * This allows the operation to be terminated prior to timeout based on
+ * the duration value.
+ * @mgmt_tx: Transmit a management frame.
+ * @mgmt_tx_cancel_wait: Cancel the wait time from transmitting a management
+ * frame on another channel
+ *
+ * @testmode_cmd: run a test mode command; @wdev may be %NULL
+ * @testmode_dump: Implement a test mode dump. The cb->args[2] and up may be
+ * used by the function, but 0 and 1 must not be touched. Additionally,
+ * return error codes other than -ENOBUFS and -ENOENT will terminate the
+ * dump and return to userspace with an error, so be careful. If any data
+ * was passed in from userspace then the data/len arguments will be present
+ * and point to the data contained in %NL80211_ATTR_TESTDATA.
+ *
+ * @set_bitrate_mask: set the bitrate mask configuration
+ *
+ * @set_pmksa: Cache a PMKID for a BSSID. This is mostly useful for fullmac
+ * devices running firmwares capable of generating the (re) association
+ * RSN IE. It allows for faster roaming between WPA2 BSSIDs.
+ * @del_pmksa: Delete a cached PMKID.
+ * @flush_pmksa: Flush all cached PMKIDs.
+ * @set_power_mgmt: Configure WLAN power management. A timeout value of -1
+ * allows the driver to adjust the dynamic ps timeout value.
+ * @set_cqm_rssi_config: Configure connection quality monitor RSSI threshold.
+ * After configuration, the driver should (soon) send an event indicating
+ * the current level is above/below the configured threshold; this may
+ * need some care when the configuration is changed (without first being
+ * disabled.)
+ * @set_cqm_rssi_range_config: Configure two RSSI thresholds in the
+ * connection quality monitor. An event is to be sent only when the
+ * signal level is found to be outside the two values. The driver should
+ * set %NL80211_EXT_FEATURE_CQM_RSSI_LIST if this method is implemented.
+ * If it is provided then there's no point providing @set_cqm_rssi_config.
+ * @set_cqm_txe_config: Configure connection quality monitor TX error
+ * thresholds.
+ * @sched_scan_start: Tell the driver to start a scheduled scan.
+ * @sched_scan_stop: Tell the driver to stop an ongoing scheduled scan with
+ * given request id. This call must stop the scheduled scan and be ready
+ * for starting a new one before it returns, i.e. @sched_scan_start may be
+ * called immediately after that again and should not fail in that case.
+ * The driver should not call cfg80211_sched_scan_stopped() for a requested
+ * stop (when this method returns 0).
+ *
+ * @mgmt_frame_register: Notify driver that a management frame type was
+ * registered. The callback is allowed to sleep.
+ *
+ * @set_antenna: Set antenna configuration (tx_ant, rx_ant) on the device.
+ * Parameters are bitmaps of allowed antennas to use for TX/RX. Drivers may
+ * reject TX/RX mask combinations they cannot support by returning -EINVAL
+ * (also see nl80211.h @NL80211_ATTR_WIPHY_ANTENNA_TX).
+ *
+ * @get_antenna: Get current antenna configuration from device (tx_ant, rx_ant).
+ *
+ * @tdls_mgmt: Transmit a TDLS management frame.
+ * @tdls_oper: Perform a high-level TDLS operation (e.g. TDLS link setup).
+ *
+ * @probe_client: probe an associated client, must return a cookie that it
+ * later passes to cfg80211_probe_status().
+ *
+ * @set_noack_map: Set the NoAck Map for the TIDs.
+ *
+ * @get_channel: Get the current operating channel for the virtual interface.
+ * For monitor interfaces, it should return %NULL unless there's a single
+ * current monitoring channel.
+ *
+ * @start_p2p_device: Start the given P2P device.
+ * @stop_p2p_device: Stop the given P2P device.
+ *
+ * @set_mac_acl: Sets MAC address control list in AP and P2P GO mode.
+ * Parameters include ACL policy, an array of MAC address of stations
+ * and the number of MAC addresses. If there is already a list in driver
+ * this new list replaces the existing one. Driver has to clear its ACL
+ * when number of MAC addresses entries is passed as 0. Drivers which
+ * advertise the support for MAC based ACL have to implement this callback.
+ *
+ * @start_radar_detection: Start radar detection in the driver.
+ *
+ * @update_ft_ies: Provide updated Fast BSS Transition information to the
+ * driver. If the SME is in the driver/firmware, this information can be
+ * used in building Authentication and Reassociation Request frames.
+ *
+ * @crit_proto_start: Indicates a critical protocol needs more link reliability
+ * for a given duration (milliseconds). The protocol is provided so the
+ * driver can take the most appropriate actions.
+ * @crit_proto_stop: Indicates critical protocol no longer needs increased link
+ * reliability. This operation can not fail.
+ * @set_coalesce: Set coalesce parameters.
+ *
+ * @channel_switch: initiate channel-switch procedure (with CSA). Driver is
+ * responsible for veryfing if the switch is possible. Since this is
+ * inherently tricky driver may decide to disconnect an interface later
+ * with cfg80211_stop_iface(). This doesn't mean driver can accept
+ * everything. It should do it's best to verify requests and reject them
+ * as soon as possible.
+ *
+ * @set_qos_map: Set QoS mapping information to the driver
+ *
+ * @set_ap_chanwidth: Set the AP (including P2P GO) mode channel width for the
+ * given interface This is used e.g. for dynamic HT 20/40 MHz channel width
+ * changes during the lifetime of the BSS.
+ *
+ * @add_tx_ts: validate (if admitted_time is 0) or add a TX TS to the device
+ * with the given parameters; action frame exchange has been handled by
+ * userspace so this just has to modify the TX path to take the TS into
+ * account.
+ * If the admitted time is 0 just validate the parameters to make sure
+ * the session can be created at all; it is valid to just always return
+ * success for that but that may result in inefficient behaviour (handshake
+ * with the peer followed by immediate teardown when the addition is later
+ * rejected)
+ * @del_tx_ts: remove an existing TX TS
+ *
+ * @join_ocb: join the OCB network with the specified parameters
+ * (invoked with the wireless_dev mutex held)
+ * @leave_ocb: leave the current OCB network
+ * (invoked with the wireless_dev mutex held)
+ *
+ * @tdls_channel_switch: Start channel-switching with a TDLS peer. The driver
+ * is responsible for continually initiating channel-switching operations
+ * and returning to the base channel for communication with the AP.
+ * @tdls_cancel_channel_switch: Stop channel-switching with a TDLS peer. Both
+ * peers must be on the base channel when the call completes.
+ * @start_nan: Start the NAN interface.
+ * @stop_nan: Stop the NAN interface.
+ * @add_nan_func: Add a NAN function. Returns negative value on failure.
+ * On success @nan_func ownership is transferred to the driver and
+ * it may access it outside of the scope of this function. The driver
+ * should free the @nan_func when no longer needed by calling
+ * cfg80211_free_nan_func().
+ * On success the driver should assign an instance_id in the
+ * provided @nan_func.
+ * @del_nan_func: Delete a NAN function.
+ * @nan_change_conf: changes NAN configuration. The changed parameters must
+ * be specified in @changes (using &enum cfg80211_nan_conf_changes);
+ * All other parameters must be ignored.
+ *
+ * @set_multicast_to_unicast: configure multicast to unicast conversion for BSS
+ *
+ * @set_pmk: configure the PMK to be used for offloaded 802.1X 4-Way handshake.
+ * If not deleted through @del_pmk the PMK remains valid until disconnect
+ * upon which the driver should clear it.
+ * (invoked with the wireless_dev mutex held)
+ * @del_pmk: delete the previously configured PMK for the given authenticator.
+ * (invoked with the wireless_dev mutex held)
+ */
+struct cfg80211_ops {
+ int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
+ int (*resume)(struct wiphy *wiphy);
+ void (*set_wakeup)(struct wiphy *wiphy, bool enabled);
+
+ struct wireless_dev * (*add_virtual_intf)(struct wiphy *wiphy,
+ const char *name,
+ unsigned char name_assign_type,
+ enum nl80211_iftype type,
+ struct vif_params *params);
+ int (*del_virtual_intf)(struct wiphy *wiphy,
+ struct wireless_dev *wdev);
+ int (*change_virtual_intf)(struct wiphy *wiphy,
+ struct net_device *dev,
+ enum nl80211_iftype type,
+ struct vif_params *params);
+
+ int (*add_key)(struct wiphy *wiphy, struct net_device *netdev,
+ u8 key_index, bool pairwise, const u8 *mac_addr,
+ struct key_params *params);
+ int (*get_key)(struct wiphy *wiphy, struct net_device *netdev,
+ u8 key_index, bool pairwise, const u8 *mac_addr,
+ void *cookie,
+ void (*callback)(void *cookie, struct key_params*));
+ int (*del_key)(struct wiphy *wiphy, struct net_device *netdev,
+ u8 key_index, bool pairwise, const u8 *mac_addr);
+ int (*set_default_key)(struct wiphy *wiphy,
+ struct net_device *netdev,
+ u8 key_index, bool unicast, bool multicast);
+ int (*set_default_mgmt_key)(struct wiphy *wiphy,
+ struct net_device *netdev,
+ u8 key_index);
+
+ int (*start_ap)(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_ap_settings *settings);
+ int (*change_beacon)(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_beacon_data *info);
+ int (*stop_ap)(struct wiphy *wiphy, struct net_device *dev);
+
+
+ int (*add_station)(struct wiphy *wiphy, struct net_device *dev,
+ const u8 *mac,
+ struct station_parameters *params);
+ int (*del_station)(struct wiphy *wiphy, struct net_device *dev,
+ struct station_del_parameters *params);
+ int (*change_station)(struct wiphy *wiphy, struct net_device *dev,
+ const u8 *mac,
+ struct station_parameters *params);
+ int (*get_station)(struct wiphy *wiphy, struct net_device *dev,
+ const u8 *mac, struct station_info *sinfo);
+ int (*dump_station)(struct wiphy *wiphy, struct net_device *dev,
+ int idx, u8 *mac, struct station_info *sinfo);
+
+ int (*add_mpath)(struct wiphy *wiphy, struct net_device *dev,
+ const u8 *dst, const u8 *next_hop);
+ int (*del_mpath)(struct wiphy *wiphy, struct net_device *dev,
+ const u8 *dst);
+ int (*change_mpath)(struct wiphy *wiphy, struct net_device *dev,
+ const u8 *dst, const u8 *next_hop);
+ int (*get_mpath)(struct wiphy *wiphy, struct net_device *dev,
+ u8 *dst, u8 *next_hop, struct mpath_info *pinfo);
+ int (*dump_mpath)(struct wiphy *wiphy, struct net_device *dev,
+ int idx, u8 *dst, u8 *next_hop,
+ struct mpath_info *pinfo);
+ int (*get_mpp)(struct wiphy *wiphy, struct net_device *dev,
+ u8 *dst, u8 *mpp, struct mpath_info *pinfo);
+ int (*dump_mpp)(struct wiphy *wiphy, struct net_device *dev,
+ int idx, u8 *dst, u8 *mpp,
+ struct mpath_info *pinfo);
+ int (*get_mesh_config)(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct mesh_config *conf);
+ int (*update_mesh_config)(struct wiphy *wiphy,
+ struct net_device *dev, u32 mask,
+ const struct mesh_config *nconf);
+ int (*join_mesh)(struct wiphy *wiphy, struct net_device *dev,
+ const struct mesh_config *conf,
+ const struct mesh_setup *setup);
+ int (*leave_mesh)(struct wiphy *wiphy, struct net_device *dev);
+
+ int (*join_ocb)(struct wiphy *wiphy, struct net_device *dev,
+ struct ocb_setup *setup);
+ int (*leave_ocb)(struct wiphy *wiphy, struct net_device *dev);
+
+ int (*change_bss)(struct wiphy *wiphy, struct net_device *dev,
+ struct bss_parameters *params);
+
+ int (*set_txq_params)(struct wiphy *wiphy, struct net_device *dev,
+ struct ieee80211_txq_params *params);
+
+ int (*libertas_set_mesh_channel)(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct ieee80211_channel *chan);
+
+ int (*set_monitor_channel)(struct wiphy *wiphy,
+ struct cfg80211_chan_def *chandef);
+
+ int (*scan)(struct wiphy *wiphy,
+ struct cfg80211_scan_request *request);
+ void (*abort_scan)(struct wiphy *wiphy, struct wireless_dev *wdev);
+
+ int (*auth)(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_auth_request *req);
+ int (*assoc)(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_assoc_request *req);
+ int (*deauth)(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_deauth_request *req);
+ int (*disassoc)(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_disassoc_request *req);
+
+ int (*connect)(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_connect_params *sme);
+ int (*update_connect_params)(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct cfg80211_connect_params *sme,
+ u32 changed);
+ int (*disconnect)(struct wiphy *wiphy, struct net_device *dev,
+ u16 reason_code);
+
+ int (*join_ibss)(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_ibss_params *params);
+ int (*leave_ibss)(struct wiphy *wiphy, struct net_device *dev);
+
+ int (*set_mcast_rate)(struct wiphy *wiphy, struct net_device *dev,
+ int rate[NUM_NL80211_BANDS]);
+
+ int (*set_wiphy_params)(struct wiphy *wiphy, u32 changed);
+
+ int (*set_tx_power)(struct wiphy *wiphy, struct wireless_dev *wdev,
+ enum nl80211_tx_power_setting type, int mbm);
+ int (*get_tx_power)(struct wiphy *wiphy, struct wireless_dev *wdev,
+ int *dbm);
+
+ int (*set_wds_peer)(struct wiphy *wiphy, struct net_device *dev,
+ const u8 *addr);
+
+ void (*rfkill_poll)(struct wiphy *wiphy);
+
+#ifdef CPTCFG_NL80211_TESTMODE
+ int (*testmode_cmd)(struct wiphy *wiphy, struct wireless_dev *wdev,
+ void *data, int len);
+ int (*testmode_dump)(struct wiphy *wiphy, struct sk_buff *skb,
+ struct netlink_callback *cb,
+ void *data, int len);
+#endif
+
+ int (*set_bitrate_mask)(struct wiphy *wiphy,
+ struct net_device *dev,
+ const u8 *peer,
+ const struct cfg80211_bitrate_mask *mask);
+
+ int (*dump_survey)(struct wiphy *wiphy, struct net_device *netdev,
+ int idx, struct survey_info *info);
+
+ int (*set_pmksa)(struct wiphy *wiphy, struct net_device *netdev,
+ struct cfg80211_pmksa *pmksa);
+ int (*del_pmksa)(struct wiphy *wiphy, struct net_device *netdev,
+ struct cfg80211_pmksa *pmksa);
+ int (*flush_pmksa)(struct wiphy *wiphy, struct net_device *netdev);
+
+ int (*remain_on_channel)(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ struct ieee80211_channel *chan,
+ unsigned int duration,
+ u64 *cookie);
+ int (*cancel_remain_on_channel)(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ u64 cookie);
+
+ int (*mgmt_tx)(struct wiphy *wiphy, struct wireless_dev *wdev,
+ struct cfg80211_mgmt_tx_params *params,
+ u64 *cookie);
+ int (*mgmt_tx_cancel_wait)(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ u64 cookie);
+
+ int (*set_power_mgmt)(struct wiphy *wiphy, struct net_device *dev,
+ bool enabled, int timeout);
+
+ int (*set_cqm_rssi_config)(struct wiphy *wiphy,
+ struct net_device *dev,
+ s32 rssi_thold, u32 rssi_hyst);
+
+ int (*set_cqm_rssi_range_config)(struct wiphy *wiphy,
+ struct net_device *dev,
+ s32 rssi_low, s32 rssi_high);
+
+ int (*set_cqm_txe_config)(struct wiphy *wiphy,
+ struct net_device *dev,
+ u32 rate, u32 pkts, u32 intvl);
+
+ void (*mgmt_frame_register)(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ u16 frame_type, bool reg);
+
+ int (*set_antenna)(struct wiphy *wiphy, u32 tx_ant, u32 rx_ant);
+ int (*get_antenna)(struct wiphy *wiphy, u32 *tx_ant, u32 *rx_ant);
+
+ int (*sched_scan_start)(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct cfg80211_sched_scan_request *request);
+ int (*sched_scan_stop)(struct wiphy *wiphy, struct net_device *dev,
+ u64 reqid);
+
+ int (*set_rekey_data)(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_gtk_rekey_data *data);
+
+ int (*tdls_mgmt)(struct wiphy *wiphy, struct net_device *dev,
+ const u8 *peer, u8 action_code, u8 dialog_token,
+ u16 status_code, u32 peer_capability,
+ bool initiator, const u8 *buf, size_t len);
+ int (*tdls_oper)(struct wiphy *wiphy, struct net_device *dev,
+ const u8 *peer, enum nl80211_tdls_operation oper);
+
+ int (*probe_client)(struct wiphy *wiphy, struct net_device *dev,
+ const u8 *peer, u64 *cookie);
+
+ int (*set_noack_map)(struct wiphy *wiphy,
+ struct net_device *dev,
+ u16 noack_map);
+
+ int (*get_channel)(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ struct cfg80211_chan_def *chandef);
+
+ int (*start_p2p_device)(struct wiphy *wiphy,
+ struct wireless_dev *wdev);
+ void (*stop_p2p_device)(struct wiphy *wiphy,
+ struct wireless_dev *wdev);
+
+ int (*set_mac_acl)(struct wiphy *wiphy, struct net_device *dev,
+ const struct cfg80211_acl_data *params);
+
+ int (*start_radar_detection)(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct cfg80211_chan_def *chandef,
+ u32 cac_time_ms);
+ int (*update_ft_ies)(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_update_ft_ies_params *ftie);
+ int (*crit_proto_start)(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ enum nl80211_crit_proto_id protocol,
+ u16 duration);
+ void (*crit_proto_stop)(struct wiphy *wiphy,
+ struct wireless_dev *wdev);
+ int (*set_coalesce)(struct wiphy *wiphy,
+ struct cfg80211_coalesce *coalesce);
+
+ int (*channel_switch)(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct cfg80211_csa_settings *params);
+
+ int (*set_qos_map)(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct cfg80211_qos_map *qos_map);
+
+ int (*set_ap_chanwidth)(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_chan_def *chandef);
+
+ int (*add_tx_ts)(struct wiphy *wiphy, struct net_device *dev,
+ u8 tsid, const u8 *peer, u8 user_prio,
+ u16 admitted_time);
+ int (*del_tx_ts)(struct wiphy *wiphy, struct net_device *dev,
+ u8 tsid, const u8 *peer);
+
+ int (*tdls_channel_switch)(struct wiphy *wiphy,
+ struct net_device *dev,
+ const u8 *addr, u8 oper_class,
+ struct cfg80211_chan_def *chandef);
+ void (*tdls_cancel_channel_switch)(struct wiphy *wiphy,
+ struct net_device *dev,
+ const u8 *addr);
+ int (*start_nan)(struct wiphy *wiphy, struct wireless_dev *wdev,
+ struct cfg80211_nan_conf *conf);
+ void (*stop_nan)(struct wiphy *wiphy, struct wireless_dev *wdev);
+ int (*add_nan_func)(struct wiphy *wiphy, struct wireless_dev *wdev,
+ struct cfg80211_nan_func *nan_func);
+ void (*del_nan_func)(struct wiphy *wiphy, struct wireless_dev *wdev,
+ u64 cookie);
+ int (*nan_change_conf)(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ struct cfg80211_nan_conf *conf,
+ u32 changes);
+
+ int (*set_multicast_to_unicast)(struct wiphy *wiphy,
+ struct net_device *dev,
+ const bool enabled);
+
+ int (*set_pmk)(struct wiphy *wiphy, struct net_device *dev,
+ const struct cfg80211_pmk_conf *conf);
+ int (*del_pmk)(struct wiphy *wiphy, struct net_device *dev,
+ const u8 *aa);
+ int (*change_keepalive)(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct cfg80211_keepalive_request *klv,
+ bool show);
+};
+
+/*
+ * wireless hardware and networking interfaces structures
+ * and registration/helper functions
+ */
+
+/**
+ * enum wiphy_flags - wiphy capability flags
+ *
+ * @WIPHY_FLAG_NETNS_OK: if not set, do not allow changing the netns of this
+ * wiphy at all
+ * @WIPHY_FLAG_PS_ON_BY_DEFAULT: if set to true, powersave will be enabled
+ * by default -- this flag will be set depending on the kernel's default
+ * on wiphy_new(), but can be changed by the driver if it has a good
+ * reason to override the default
+ * @WIPHY_FLAG_4ADDR_AP: supports 4addr mode even on AP (with a single station
+ * on a VLAN interface)
+ * @WIPHY_FLAG_4ADDR_STATION: supports 4addr mode even as a station
+ * @WIPHY_FLAG_CONTROL_PORT_PROTOCOL: This device supports setting the
+ * control port protocol ethertype. The device also honours the
+ * control_port_no_encrypt flag.
+ * @WIPHY_FLAG_IBSS_RSN: The device supports IBSS RSN.
+ * @WIPHY_FLAG_MESH_AUTH: The device supports mesh authentication by routing
+ * auth frames to userspace. See @NL80211_MESH_SETUP_USERSPACE_AUTH.
+ * @WIPHY_FLAG_SUPPORTS_SCHED_SCAN: The device supports scheduled scans.
+ * @WIPHY_FLAG_SUPPORTS_FW_ROAM: The device supports roaming feature in the
+ * firmware.
+ * @WIPHY_FLAG_AP_UAPSD: The device supports uapsd on AP.
+ * @WIPHY_FLAG_SUPPORTS_TDLS: The device supports TDLS (802.11z) operation.
+ * @WIPHY_FLAG_TDLS_EXTERNAL_SETUP: The device does not handle TDLS (802.11z)
+ * link setup/discovery operations internally. Setup, discovery and
+ * teardown packets should be sent through the @NL80211_CMD_TDLS_MGMT
+ * command. When this flag is not set, @NL80211_CMD_TDLS_OPER should be
+ * used for asking the driver/firmware to perform a TDLS operation.
+ * @WIPHY_FLAG_HAVE_AP_SME: device integrates AP SME
+ * @WIPHY_FLAG_REPORTS_OBSS: the device will report beacons from other BSSes
+ * when there are virtual interfaces in AP mode by calling
+ * cfg80211_report_obss_beacon().
+ * @WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD: When operating as an AP, the device
+ * responds to probe-requests in hardware.
+ * @WIPHY_FLAG_OFFCHAN_TX: Device supports direct off-channel TX.
+ * @WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL: Device supports remain-on-channel call.
+ * @WIPHY_FLAG_SUPPORTS_5_10_MHZ: Device supports 5 MHz and 10 MHz channels.
+ * @WIPHY_FLAG_HAS_CHANNEL_SWITCH: Device supports channel switch in
+ * beaconing mode (AP, IBSS, Mesh, ...).
+ * @WIPHY_FLAG_HAS_STATIC_WEP: The device supports static WEP key installation
+ * before connection.
+ */
+enum wiphy_flags {
+ /* use hole at 0 */
+ /* use hole at 1 */
+ /* use hole at 2 */
+ WIPHY_FLAG_NETNS_OK = BIT(3),
+ WIPHY_FLAG_PS_ON_BY_DEFAULT = BIT(4),
+ WIPHY_FLAG_4ADDR_AP = BIT(5),
+ WIPHY_FLAG_4ADDR_STATION = BIT(6),
+ WIPHY_FLAG_CONTROL_PORT_PROTOCOL = BIT(7),
+ WIPHY_FLAG_IBSS_RSN = BIT(8),
+ WIPHY_FLAG_MESH_AUTH = BIT(10),
+ /* use hole at 11 */
+ /* use hole at 12 */
+ WIPHY_FLAG_SUPPORTS_FW_ROAM = BIT(13),
+ WIPHY_FLAG_AP_UAPSD = BIT(14),
+ WIPHY_FLAG_SUPPORTS_TDLS = BIT(15),
+ WIPHY_FLAG_TDLS_EXTERNAL_SETUP = BIT(16),
+ WIPHY_FLAG_HAVE_AP_SME = BIT(17),
+ WIPHY_FLAG_REPORTS_OBSS = BIT(18),
+ WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD = BIT(19),
+ WIPHY_FLAG_OFFCHAN_TX = BIT(20),
+ WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL = BIT(21),
+ WIPHY_FLAG_SUPPORTS_5_10_MHZ = BIT(22),
+ WIPHY_FLAG_HAS_CHANNEL_SWITCH = BIT(23),
+ WIPHY_FLAG_HAS_STATIC_WEP = BIT(24),
+};
+
+/**
+ * struct ieee80211_iface_limit - limit on certain interface types
+ * @max: maximum number of interfaces of these types
+ * @types: interface types (bits)
+ */
+struct ieee80211_iface_limit {
+ u16 max;
+ u16 types;
+};
+
+/**
+ * struct ieee80211_iface_combination - possible interface combination
+ *
+ * With this structure the driver can describe which interface
+ * combinations it supports concurrently.
+ *
+ * Examples:
+ *
+ * 1. Allow #STA <= 1, #AP <= 1, matching BI, channels = 1, 2 total:
+ *
+ * .. code-block:: c
+ *
+ * struct ieee80211_iface_limit limits1[] = {
+ * { .max = 1, .types = BIT(NL80211_IFTYPE_STATION), },
+ * { .max = 1, .types = BIT(NL80211_IFTYPE_AP}, },
+ * };
+ * struct ieee80211_iface_combination combination1 = {
+ * .limits = limits1,
+ * .n_limits = ARRAY_SIZE(limits1),
+ * .max_interfaces = 2,
+ * .beacon_int_infra_match = true,
+ * };
+ *
+ *
+ * 2. Allow #{AP, P2P-GO} <= 8, channels = 1, 8 total:
+ *
+ * .. code-block:: c
+ *
+ * struct ieee80211_iface_limit limits2[] = {
+ * { .max = 8, .types = BIT(NL80211_IFTYPE_AP) |
+ * BIT(NL80211_IFTYPE_P2P_GO), },
+ * };
+ * struct ieee80211_iface_combination combination2 = {
+ * .limits = limits2,
+ * .n_limits = ARRAY_SIZE(limits2),
+ * .max_interfaces = 8,
+ * .num_different_channels = 1,
+ * };
+ *
+ *
+ * 3. Allow #STA <= 1, #{P2P-client,P2P-GO} <= 3 on two channels, 4 total.
+ *
+ * This allows for an infrastructure connection and three P2P connections.
+ *
+ * .. code-block:: c
+ *
+ * struct ieee80211_iface_limit limits3[] = {
+ * { .max = 1, .types = BIT(NL80211_IFTYPE_STATION), },
+ * { .max = 3, .types = BIT(NL80211_IFTYPE_P2P_GO) |
+ * BIT(NL80211_IFTYPE_P2P_CLIENT), },
+ * };
+ * struct ieee80211_iface_combination combination3 = {
+ * .limits = limits3,
+ * .n_limits = ARRAY_SIZE(limits3),
+ * .max_interfaces = 4,
+ * .num_different_channels = 2,
+ * };
+ *
+ */
+struct ieee80211_iface_combination {
+ /**
+ * @limits:
+ * limits for the given interface types
+ */
+ const struct ieee80211_iface_limit *limits;
+
+ /**
+ * @num_different_channels:
+ * can use up to this many different channels
+ */
+ u32 num_different_channels;
+
+ /**
+ * @max_interfaces:
+ * maximum number of interfaces in total allowed in this group
+ */
+ u16 max_interfaces;
+
+ /**
+ * @n_limits:
+ * number of limitations
+ */
+ u8 n_limits;
+
+ /**
+ * @beacon_int_infra_match:
+ * In this combination, the beacon intervals between infrastructure
+ * and AP types must match. This is required only in special cases.
+ */
+ bool beacon_int_infra_match;
+
+ /**
+ * @radar_detect_widths:
+ * bitmap of channel widths supported for radar detection
+ */
+ u8 radar_detect_widths;
+
+ /**
+ * @radar_detect_regions:
+ * bitmap of regions supported for radar detection
+ */
+ u8 radar_detect_regions;
+
+ /**
+ * @beacon_int_min_gcd:
+ * This interface combination supports different beacon intervals.
+ *
+ * = 0
+ * all beacon intervals for different interface must be same.
+ * > 0
+ * any beacon interval for the interface part of this combination AND
+ * GCD of all beacon intervals from beaconing interfaces of this
+ * combination must be greater or equal to this value.
+ */
+ u32 beacon_int_min_gcd;
+};
+
+struct ieee80211_txrx_stypes {
+ u16 tx, rx;
+};
+
+/**
+ * enum wiphy_wowlan_support_flags - WoWLAN support flags
+ * @WIPHY_WOWLAN_ANY: supports wakeup for the special "any"
+ * trigger that keeps the device operating as-is and
+ * wakes up the host on any activity, for example a
+ * received packet that passed filtering; note that the
+ * packet should be preserved in that case
+ * @WIPHY_WOWLAN_MAGIC_PKT: supports wakeup on magic packet
+ * (see nl80211.h)
+ * @WIPHY_WOWLAN_DISCONNECT: supports wakeup on disconnect
+ * @WIPHY_WOWLAN_SUPPORTS_GTK_REKEY: supports GTK rekeying while asleep
+ * @WIPHY_WOWLAN_GTK_REKEY_FAILURE: supports wakeup on GTK rekey failure
+ * @WIPHY_WOWLAN_EAP_IDENTITY_REQ: supports wakeup on EAP identity request
+ * @WIPHY_WOWLAN_4WAY_HANDSHAKE: supports wakeup on 4-way handshake failure
+ * @WIPHY_WOWLAN_RFKILL_RELEASE: supports wakeup on RF-kill release
+ * @WIPHY_WOWLAN_NET_DETECT: supports wakeup on network detection
+ */
+enum wiphy_wowlan_support_flags {
+ WIPHY_WOWLAN_ANY = BIT(0),
+ WIPHY_WOWLAN_MAGIC_PKT = BIT(1),
+ WIPHY_WOWLAN_DISCONNECT = BIT(2),
+ WIPHY_WOWLAN_SUPPORTS_GTK_REKEY = BIT(3),
+ WIPHY_WOWLAN_GTK_REKEY_FAILURE = BIT(4),
+ WIPHY_WOWLAN_EAP_IDENTITY_REQ = BIT(5),
+ WIPHY_WOWLAN_4WAY_HANDSHAKE = BIT(6),
+ WIPHY_WOWLAN_RFKILL_RELEASE = BIT(7),
+ WIPHY_WOWLAN_NET_DETECT = BIT(8),
+};
+
+struct wiphy_wowlan_tcp_support {
+ const struct nl80211_wowlan_tcp_data_token_feature *tok;
+ u32 data_payload_max;
+ u32 data_interval_max;
+ u32 wake_payload_max;
+ bool seq;
+};
+
+/**
+ * struct wiphy_wowlan_support - WoWLAN support data
+ * @flags: see &enum wiphy_wowlan_support_flags
+ * @n_patterns: number of supported wakeup patterns
+ * (see nl80211.h for the pattern definition)
+ * @pattern_max_len: maximum length of each pattern
+ * @pattern_min_len: minimum length of each pattern
+ * @max_pkt_offset: maximum Rx packet offset
+ * @max_nd_match_sets: maximum number of matchsets for net-detect,
+ * similar, but not necessarily identical, to max_match_sets for
+ * scheduled scans.
+ * See &struct cfg80211_sched_scan_request.@match_sets for more
+ * details.
+ * @tcp: TCP wakeup support information
+ */
+struct wiphy_wowlan_support {
+ u32 flags;
+ int n_patterns;
+ int pattern_max_len;
+ int pattern_min_len;
+ int max_pkt_offset;
+ int max_nd_match_sets;
+ const struct wiphy_wowlan_tcp_support *tcp;
+};
+
+/**
+ * struct wiphy_coalesce_support - coalesce support data
+ * @n_rules: maximum number of coalesce rules
+ * @max_delay: maximum supported coalescing delay in msecs
+ * @n_patterns: number of supported patterns in a rule
+ * (see nl80211.h for the pattern definition)
+ * @pattern_max_len: maximum length of each pattern
+ * @pattern_min_len: minimum length of each pattern
+ * @max_pkt_offset: maximum Rx packet offset
+ */
+struct wiphy_coalesce_support {
+ int n_rules;
+ int max_delay;
+ int n_patterns;
+ int pattern_max_len;
+ int pattern_min_len;
+ int max_pkt_offset;
+};
+
+/**
+ * enum wiphy_vendor_command_flags - validation flags for vendor commands
+ * @WIPHY_VENDOR_CMD_NEED_WDEV: vendor command requires wdev
+ * @WIPHY_VENDOR_CMD_NEED_NETDEV: vendor command requires netdev
+ * @WIPHY_VENDOR_CMD_NEED_RUNNING: interface/wdev must be up & running
+ * (must be combined with %_WDEV or %_NETDEV)
+ */
+enum wiphy_vendor_command_flags {
+ WIPHY_VENDOR_CMD_NEED_WDEV = BIT(0),
+ WIPHY_VENDOR_CMD_NEED_NETDEV = BIT(1),
+ WIPHY_VENDOR_CMD_NEED_RUNNING = BIT(2),
+};
+
+/**
+ * struct wiphy_vendor_command - vendor command definition
+ * @info: vendor command identifying information, as used in nl80211
+ * @flags: flags, see &enum wiphy_vendor_command_flags
+ * @doit: callback for the operation, note that wdev is %NULL if the
+ * flags didn't ask for a wdev and non-%NULL otherwise; the data
+ * pointer may be %NULL if userspace provided no data at all
+ * @dumpit: dump callback, for transferring bigger/multiple items. The
+ * @storage points to cb->args[5], ie. is preserved over the multiple
+ * dumpit calls.
+ * It's recommended to not have the same sub command with both @doit and
+ * @dumpit, so that userspace can assume certain ones are get and others
+ * are used with dump requests.
+ */
+struct wiphy_vendor_command {
+ struct nl80211_vendor_cmd_info info;
+ u32 flags;
+ int (*doit)(struct wiphy *wiphy, struct wireless_dev *wdev,
+ const void *data, int data_len);
+ int (*dumpit)(struct wiphy *wiphy, struct wireless_dev *wdev,
+ struct sk_buff *skb, const void *data, int data_len,
+ unsigned long *storage);
+};
+
+/**
+ * struct wiphy_iftype_ext_capab - extended capabilities per interface type
+ * @iftype: interface type
+ * @extended_capabilities: extended capabilities supported by the driver,
+ * additional capabilities might be supported by userspace; these are the
+ * 802.11 extended capabilities ("Extended Capabilities element") and are
+ * in the same format as in the information element. See IEEE Std
+ * 802.11-2012 8.4.2.29 for the defined fields.
+ * @extended_capabilities_mask: mask of the valid values
+ * @extended_capabilities_len: length of the extended capabilities
+ */
+struct wiphy_iftype_ext_capab {
+ enum nl80211_iftype iftype;
+ const u8 *extended_capabilities;
+ const u8 *extended_capabilities_mask;
+ u8 extended_capabilities_len;
+};
+
+/**
+ * struct wiphy - wireless hardware description
+ * @reg_notifier: the driver's regulatory notification callback,
+ * note that if your driver uses wiphy_apply_custom_regulatory()
+ * the reg_notifier's request can be passed as NULL
+ * @regd: the driver's regulatory domain, if one was requested via
+ * the regulatory_hint() API. This can be used by the driver
+ * on the reg_notifier() if it chooses to ignore future
+ * regulatory domain changes caused by other drivers.
+ * @signal_type: signal type reported in &struct cfg80211_bss.
+ * @cipher_suites: supported cipher suites
+ * @n_cipher_suites: number of supported cipher suites
+ * @retry_short: Retry limit for short frames (dot11ShortRetryLimit)
+ * @retry_long: Retry limit for long frames (dot11LongRetryLimit)
+ * @frag_threshold: Fragmentation threshold (dot11FragmentationThreshold);
+ * -1 = fragmentation disabled, only odd values >= 256 used
+ * @rts_threshold: RTS threshold (dot11RTSThreshold); -1 = RTS/CTS disabled
+ * @_net: the network namespace this wiphy currently lives in
+ * @perm_addr: permanent MAC address of this device
+ * @addr_mask: If the device supports multiple MAC addresses by masking,
+ * set this to a mask with variable bits set to 1, e.g. if the last
+ * four bits are variable then set it to 00-00-00-00-00-0f. The actual
+ * variable bits shall be determined by the interfaces added, with
+ * interfaces not matching the mask being rejected to be brought up.
+ * @n_addresses: number of addresses in @addresses.
+ * @addresses: If the device has more than one address, set this pointer
+ * to a list of addresses (6 bytes each). The first one will be used
+ * by default for perm_addr. In this case, the mask should be set to
+ * all-zeroes. In this case it is assumed that the device can handle
+ * the same number of arbitrary MAC addresses.
+ * @registered: protects ->resume and ->suspend sysfs callbacks against
+ * unregister hardware
+ * @debugfsdir: debugfs directory used for this wiphy, will be renamed
+ * automatically on wiphy renames
+ * @dev: (virtual) struct device for this wiphy
+ * @registered: helps synchronize suspend/resume with wiphy unregister
+ * @wext: wireless extension handlers
+ * @priv: driver private data (sized according to wiphy_new() parameter)
+ * @interface_modes: bitmask of interfaces types valid for this wiphy,
+ * must be set by driver
+ * @iface_combinations: Valid interface combinations array, should not
+ * list single interface types.
+ * @n_iface_combinations: number of entries in @iface_combinations array.
+ * @software_iftypes: bitmask of software interface types, these are not
+ * subject to any restrictions since they are purely managed in SW.
+ * @flags: wiphy flags, see &enum wiphy_flags
+ * @regulatory_flags: wiphy regulatory flags, see
+ * &enum ieee80211_regulatory_flags
+ * @features: features advertised to nl80211, see &enum nl80211_feature_flags.
+ * @ext_features: extended features advertised to nl80211, see
+ * &enum nl80211_ext_feature_index.
+ * @bss_priv_size: each BSS struct has private data allocated with it,
+ * this variable determines its size
+ * @max_scan_ssids: maximum number of SSIDs the device can scan for in
+ * any given scan
+ * @max_sched_scan_reqs: maximum number of scheduled scan requests that
+ * the device can run concurrently.
+ * @max_sched_scan_ssids: maximum number of SSIDs the device can scan
+ * for in any given scheduled scan
+ * @max_match_sets: maximum number of match sets the device can handle
+ * when performing a scheduled scan, 0 if filtering is not
+ * supported.
+ * @max_scan_ie_len: maximum length of user-controlled IEs device can
+ * add to probe request frames transmitted during a scan, must not
+ * include fixed IEs like supported rates
+ * @max_sched_scan_ie_len: same as max_scan_ie_len, but for scheduled
+ * scans
+ * @max_sched_scan_plans: maximum number of scan plans (scan interval and number
+ * of iterations) for scheduled scan supported by the device.
+ * @max_sched_scan_plan_interval: maximum interval (in seconds) for a
+ * single scan plan supported by the device.
+ * @max_sched_scan_plan_iterations: maximum number of iterations for a single
+ * scan plan supported by the device.
+ * @coverage_class: current coverage class
+ * @fw_version: firmware version for ethtool reporting
+ * @hw_version: hardware version for ethtool reporting
+ * @max_num_pmkids: maximum number of PMKIDs supported by device
+ * @privid: a pointer that drivers can use to identify if an arbitrary
+ * wiphy is theirs, e.g. in global notifiers
+ * @bands: information about bands/channels supported by this device
+ *
+ * @mgmt_stypes: bitmasks of frame subtypes that can be subscribed to or
+ * transmitted through nl80211, points to an array indexed by interface
+ * type
+ *
+ * @available_antennas_tx: bitmap of antennas which are available to be
+ * configured as TX antennas. Antenna configuration commands will be
+ * rejected unless this or @available_antennas_rx is set.
+ *
+ * @available_antennas_rx: bitmap of antennas which are available to be
+ * configured as RX antennas. Antenna configuration commands will be
+ * rejected unless this or @available_antennas_tx is set.
+ *
+ * @probe_resp_offload:
+ * Bitmap of supported protocols for probe response offloading.
+ * See &enum nl80211_probe_resp_offload_support_attr. Only valid
+ * when the wiphy flag @WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD is set.
+ *
+ * @max_remain_on_channel_duration: Maximum time a remain-on-channel operation
+ * may request, if implemented.
+ *
+ * @wowlan: WoWLAN support information
+ * @wowlan_config: current WoWLAN configuration; this should usually not be
+ * used since access to it is necessarily racy, use the parameter passed
+ * to the suspend() operation instead.
+ *
+ * @ap_sme_capa: AP SME capabilities, flags from &enum nl80211_ap_sme_features.
+ * @ht_capa_mod_mask: Specify what ht_cap values can be over-ridden.
+ * If null, then none can be over-ridden.
+ * @vht_capa_mod_mask: Specify what VHT capabilities can be over-ridden.
+ * If null, then none can be over-ridden.
+ *
+ * @wdev_list: the list of associated (virtual) interfaces; this list must
+ * not be modified by the driver, but can be read with RTNL/RCU protection.
+ *
+ * @max_acl_mac_addrs: Maximum number of MAC addresses that the device
+ * supports for ACL.
+ *
+ * @extended_capabilities: extended capabilities supported by the driver,
+ * additional capabilities might be supported by userspace; these are
+ * the 802.11 extended capabilities ("Extended Capabilities element")
+ * and are in the same format as in the information element. See
+ * 802.11-2012 8.4.2.29 for the defined fields. These are the default
+ * extended capabilities to be used if the capabilities are not specified
+ * for a specific interface type in iftype_ext_capab.
+ * @extended_capabilities_mask: mask of the valid values
+ * @extended_capabilities_len: length of the extended capabilities
+ * @iftype_ext_capab: array of extended capabilities per interface type
+ * @num_iftype_ext_capab: number of interface types for which extended
+ * capabilities are specified separately.
+ * @coalesce: packet coalescing support information
+ *
+ * @vendor_commands: array of vendor commands supported by the hardware
+ * @n_vendor_commands: number of vendor commands
+ * @vendor_events: array of vendor events supported by the hardware
+ * @n_vendor_events: number of vendor events
+ *
+ * @max_ap_assoc_sta: maximum number of associated stations supported in AP mode
+ * (including P2P GO) or 0 to indicate no such limit is advertised. The
+ * driver is allowed to advertise a theoretical limit that it can reach in
+ * some cases, but may not always reach.
+ *
+ * @max_num_csa_counters: Number of supported csa_counters in beacons
+ * and probe responses. This value should be set if the driver
+ * wishes to limit the number of csa counters. Default (0) means
+ * infinite.
+ * @max_adj_channel_rssi_comp: max offset of between the channel on which the
+ * frame was sent and the channel on which the frame was heard for which
+ * the reported rssi is still valid. If a driver is able to compensate the
+ * low rssi when a frame is heard on different channel, then it should set
+ * this variable to the maximal offset for which it can compensate.
+ * This value should be set in MHz.
+ * @bss_select_support: bitmask indicating the BSS selection criteria supported
+ * by the driver in the .connect() callback. The bit position maps to the
+ * attribute indices defined in &enum nl80211_bss_select_attr.
+ *
+ * @cookie_counter: unique generic cookie counter, used to identify objects.
+ * @nan_supported_bands: bands supported by the device in NAN mode, a
+ * bitmap of &enum nl80211_band values. For instance, for
+ * NL80211_BAND_2GHZ, bit 0 would be set
+ * (i.e. BIT(NL80211_BAND_2GHZ)).
+ */
+struct wiphy {
+ /* assign these fields before you register the wiphy */
+
+#define WIPHY_COMPAT_PAD_SIZE 2048
+ u8 padding[WIPHY_COMPAT_PAD_SIZE];
+
+ /* permanent MAC address(es) */
+ u8 perm_addr[ETH_ALEN];
+ u8 addr_mask[ETH_ALEN];
+
+ struct mac_address *addresses;
+
+ const struct ieee80211_txrx_stypes *mgmt_stypes;
+
+ const struct ieee80211_iface_combination *iface_combinations;
+ int n_iface_combinations;
+ u16 software_iftypes;
+
+ u16 n_addresses;
+
+ /* Supported interface modes, OR together BIT(NL80211_IFTYPE_...) */
+ u16 interface_modes;
+
+ u16 max_acl_mac_addrs;
+
+ u32 flags, regulatory_flags, features;
+ u8 ext_features[DIV_ROUND_UP(NUM_NL80211_EXT_FEATURES, 8)];
+
+ u32 ap_sme_capa;
+
+ enum cfg80211_signal_type signal_type;
+
+ int bss_priv_size;
+ u8 max_scan_ssids;
+ u8 max_sched_scan_reqs;
+ u8 max_sched_scan_ssids;
+ u8 max_match_sets;
+ u16 max_scan_ie_len;
+ u16 max_sched_scan_ie_len;
+ u32 max_sched_scan_plans;
+ u32 max_sched_scan_plan_interval;
+ u32 max_sched_scan_plan_iterations;
+
+ int n_cipher_suites;
+ const u32 *cipher_suites;
+
+ u8 retry_short;
+ u8 retry_long;
+ u32 frag_threshold;
+ u32 rts_threshold;
+ u8 coverage_class;
+
+ char fw_version[ETHTOOL_FWVERS_LEN];
+ u32 hw_version;
+
+#ifdef CONFIG_PM
+ const struct wiphy_wowlan_support *wowlan;
+ struct cfg80211_wowlan *wowlan_config;
+#endif
+
+ u16 max_remain_on_channel_duration;
+
+ u8 max_num_pmkids;
+
+ u32 available_antennas_tx;
+ u32 available_antennas_rx;
+
+ /*
+ * Bitmap of supported protocols for probe response offloading
+ * see &enum nl80211_probe_resp_offload_support_attr. Only valid
+ * when the wiphy flag @WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD is set.
+ */
+ u32 probe_resp_offload;
+
+ const u8 *extended_capabilities, *extended_capabilities_mask;
+ u8 extended_capabilities_len;
+
+ const struct wiphy_iftype_ext_capab *iftype_ext_capab;
+ unsigned int num_iftype_ext_capab;
+
+ /* If multiple wiphys are registered and you're handed e.g.
+ * a regular netdev with assigned ieee80211_ptr, you won't
+ * know whether it points to a wiphy your driver has registered
+ * or not. Assign this to something global to your driver to
+ * help determine whether you own this wiphy or not. */
+ const void *privid;
+
+ struct ieee80211_supported_band *bands[NUM_NL80211_BANDS];
+
+ /* Lets us get back the wiphy on the callback */
+ void (*reg_notifier)(struct wiphy *wiphy,
+ struct regulatory_request *request);
+
+ /* fields below are read-only, assigned by cfg80211 */
+
+ const struct ieee80211_regdomain __rcu *regd;
+
+ /* the item in /sys/class/ieee80211/ points to this,
+ * you need use set_wiphy_dev() (see below) */
+ struct device dev;
+
+ /* protects ->resume, ->suspend sysfs callbacks against unregister hw */
+ bool registered;
+
+ /* dir in debugfs: ieee80211/<wiphyname> */
+ struct dentry *debugfsdir;
+
+ const struct ieee80211_ht_cap *ht_capa_mod_mask;
+ const struct ieee80211_vht_cap *vht_capa_mod_mask;
+
+ struct list_head wdev_list;
+
+ /* the network namespace this phy lives in currently */
+ possible_net_t _net;
+
+#ifdef CPTCFG_CFG80211_WEXT
+ const struct iw_handler_def *wext;
+#endif
+
+ const struct wiphy_coalesce_support *coalesce;
+
+ const struct wiphy_vendor_command *vendor_commands;
+ const struct nl80211_vendor_cmd_info *vendor_events;
+ int n_vendor_commands, n_vendor_events;
+
+ u16 max_ap_assoc_sta;
+
+ u8 max_num_csa_counters;
+ u8 max_adj_channel_rssi_comp;
+
+ u32 bss_select_support;
+
+ u64 cookie_counter;
+
+ u8 nan_supported_bands;
+
+ char priv[0] __aligned(NETDEV_ALIGN);
+};
+
+static inline struct net *wiphy_net(struct wiphy *wiphy)
+{
+ return possible_read_pnet(&wiphy->_net);
+}
+
+static inline void wiphy_net_set(struct wiphy *wiphy, struct net *net)
+{
+ possible_write_pnet(&wiphy->_net, net);
+}
+
+/**
+ * wiphy_priv - return priv from wiphy
+ *
+ * @wiphy: the wiphy whose priv pointer to return
+ * Return: The priv of @wiphy.
+ */
+static inline void *wiphy_priv(struct wiphy *wiphy)
+{
+ BUG_ON(!wiphy);
+ return &wiphy->priv;
+}
+
+/**
+ * priv_to_wiphy - return the wiphy containing the priv
+ *
+ * @priv: a pointer previously returned by wiphy_priv
+ * Return: The wiphy of @priv.
+ */
+static inline struct wiphy *priv_to_wiphy(void *priv)
+{
+ BUG_ON(!priv);
+ return container_of(priv, struct wiphy, priv);
+}
+
+/**
+ * set_wiphy_dev - set device pointer for wiphy
+ *
+ * @wiphy: The wiphy whose device to bind
+ * @dev: The device to parent it to
+ */
+static inline void set_wiphy_dev(struct wiphy *wiphy, struct device *dev)
+{
+ wiphy->dev.parent = dev;
+}
+
+/**
+ * wiphy_dev - get wiphy dev pointer
+ *
+ * @wiphy: The wiphy whose device struct to look up
+ * Return: The dev of @wiphy.
+ */
+static inline struct device *wiphy_dev(struct wiphy *wiphy)
+{
+ return wiphy->dev.parent;
+}
+
+/**
+ * wiphy_name - get wiphy name
+ *
+ * @wiphy: The wiphy whose name to return
+ * Return: The name of @wiphy.
+ */
+static inline const char *wiphy_name(const struct wiphy *wiphy)
+{
+ return dev_name(&wiphy->dev);
+}
+
+/**
+ * wiphy_new_nm - create a new wiphy for use with cfg80211
+ *
+ * @ops: The configuration operations for this device
+ * @sizeof_priv: The size of the private area to allocate
+ * @requested_name: Request a particular name.
+ * NULL is valid value, and means use the default phy%d naming.
+ *
+ * Create a new wiphy and associate the given operations with it.
+ * @sizeof_priv bytes are allocated for private use.
+ *
+ * Return: A pointer to the new wiphy. This pointer must be
+ * assigned to each netdev's ieee80211_ptr for proper operation.
+ */
+struct wiphy *wiphy_new_nm(const struct cfg80211_ops *ops, int sizeof_priv,
+ const char *requested_name);
+
+/**
+ * wiphy_new - create a new wiphy for use with cfg80211
+ *
+ * @ops: The configuration operations for this device
+ * @sizeof_priv: The size of the private area to allocate
+ *
+ * Create a new wiphy and associate the given operations with it.
+ * @sizeof_priv bytes are allocated for private use.
+ *
+ * Return: A pointer to the new wiphy. This pointer must be
+ * assigned to each netdev's ieee80211_ptr for proper operation.
+ */
+static inline struct wiphy *wiphy_new(const struct cfg80211_ops *ops,
+ int sizeof_priv)
+{
+ return wiphy_new_nm(ops, sizeof_priv, NULL);
+}
+
+/**
+ * wiphy_register - register a wiphy with cfg80211
+ *
+ * @wiphy: The wiphy to register.
+ *
+ * Return: A non-negative wiphy index or a negative error code.
+ */
+int wiphy_register(struct wiphy *wiphy);
+
+/**
+ * wiphy_unregister - deregister a wiphy from cfg80211
+ *
+ * @wiphy: The wiphy to unregister.
+ *
+ * After this call, no more requests can be made with this priv
+ * pointer, but the call may sleep to wait for an outstanding
+ * request that is being handled.
+ */
+void wiphy_unregister(struct wiphy *wiphy);
+
+/**
+ * wiphy_free - free wiphy
+ *
+ * @wiphy: The wiphy to free
+ */
+void wiphy_free(struct wiphy *wiphy);
+
+/* internal structs */
+struct cfg80211_conn;
+struct cfg80211_internal_bss;
+struct cfg80211_cached_keys;
+struct cfg80211_cqm_config;
+
+/**
+ * struct wireless_dev - wireless device state
+ *
+ * For netdevs, this structure must be allocated by the driver
+ * that uses the ieee80211_ptr field in struct net_device (this
+ * is intentional so it can be allocated along with the netdev.)
+ * It need not be registered then as netdev registration will
+ * be intercepted by cfg80211 to see the new wireless device.
+ *
+ * For non-netdev uses, it must also be allocated by the driver
+ * in response to the cfg80211 callbacks that require it, as
+ * there's no netdev registration in that case it may not be
+ * allocated outside of callback operations that return it.
+ *
+ * @wiphy: pointer to hardware description
+ * @iftype: interface type
+ * @list: (private) Used to collect the interfaces
+ * @netdev: (private) Used to reference back to the netdev, may be %NULL
+ * @identifier: (private) Identifier used in nl80211 to identify this
+ * wireless device if it has no netdev
+ * @current_bss: (private) Used by the internal configuration code
+ * @chandef: (private) Used by the internal configuration code to track
+ * the user-set channel definition.
+ * @preset_chandef: (private) Used by the internal configuration code to
+ * track the channel to be used for AP later
+ * @bssid: (private) Used by the internal configuration code
+ * @ssid: (private) Used by the internal configuration code
+ * @ssid_len: (private) Used by the internal configuration code
+ * @mesh_id_len: (private) Used by the internal configuration code
+ * @mesh_id_up_len: (private) Used by the internal configuration code
+ * @wext: (private) Used by the internal wireless extensions compat code
+ * @use_4addr: indicates 4addr mode is used on this interface, must be
+ * set by driver (if supported) on add_interface BEFORE registering the
+ * netdev and may otherwise be used by driver read-only, will be update
+ * by cfg80211 on change_interface
+ * @mgmt_registrations: list of registrations for management frames
+ * @mgmt_registrations_lock: lock for the list
+ * @mtx: mutex used to lock data in this struct, may be used by drivers
+ * and some API functions require it held
+ * @beacon_interval: beacon interval used on this device for transmitting
+ * beacons, 0 when not valid
+ * @address: The address for this device, valid only if @netdev is %NULL
+ * @is_running: true if this is a non-netdev device that has been started, e.g.
+ * the P2P Device.
+ * @cac_started: true if DFS channel availability check has been started
+ * @cac_start_time: timestamp (jiffies) when the dfs state was entered.
+ * @cac_time_ms: CAC time in ms
+ * @ps: powersave mode is enabled
+ * @ps_timeout: dynamic powersave timeout
+ * @ap_unexpected_nlportid: (private) netlink port ID of application
+ * registered for unexpected class 3 frames (AP mode)
+ * @conn: (private) cfg80211 software SME connection state machine data
+ * @connect_keys: (private) keys to set after connection is established
+ * @conn_bss_type: connecting/connected BSS type
+ * @conn_owner_nlportid: (private) connection owner socket port ID
+ * @disconnect_wk: (private) auto-disconnect work
+ * @disconnect_bssid: (private) the BSSID to use for auto-disconnect
+ * @ibss_fixed: (private) IBSS is using fixed BSSID
+ * @ibss_dfs_possible: (private) IBSS may change to a DFS channel
+ * @event_list: (private) list for internal event processing
+ * @event_lock: (private) lock for event list
+ * @owner_nlportid: (private) owner socket port ID
+ * @nl_owner_dead: (private) owner socket went away
+ * @cqm_config: (private) nl80211 RSSI monitor state
+ */
+struct wireless_dev {
+ struct wiphy *wiphy;
+ enum nl80211_iftype iftype;
+
+ /* the remainder of this struct should be private to cfg80211 */
+ struct list_head list;
+ struct net_device *netdev;
+
+ u32 identifier;
+
+ struct list_head mgmt_registrations;
+ spinlock_t mgmt_registrations_lock;
+
+ struct mutex mtx;
+
+ bool use_4addr, is_running;
+
+ u8 address[ETH_ALEN] __aligned(sizeof(u16));
+
+ /* currently used for IBSS and SME - might be rearranged later */
+ u8 ssid[IEEE80211_MAX_SSID_LEN];
+ u8 ssid_len, mesh_id_len, mesh_id_up_len;
+ struct cfg80211_conn *conn;
+ struct cfg80211_cached_keys *connect_keys;
+ enum ieee80211_bss_type conn_bss_type;
+ u32 conn_owner_nlportid;
+
+ struct work_struct disconnect_wk;
+ u8 disconnect_bssid[ETH_ALEN];
+
+ struct list_head event_list;
+ spinlock_t event_lock;
+
+ struct cfg80211_internal_bss *current_bss; /* associated / joined */
+ struct cfg80211_chan_def preset_chandef;
+ struct cfg80211_chan_def chandef;
+
+ bool ibss_fixed;
+ bool ibss_dfs_possible;
+
+ bool ps;
+ int ps_timeout;
+
+ int beacon_interval;
+
+ u32 ap_unexpected_nlportid;
+
+ u32 owner_nlportid;
+ bool nl_owner_dead;
+
+ bool cac_started;
+ unsigned long cac_start_time;
+ unsigned int cac_time_ms;
+
+#ifdef CPTCFG_CFG80211_WEXT
+ /* wext data */
+ struct {
+ struct cfg80211_ibss_params ibss;
+ struct cfg80211_connect_params connect;
+ struct cfg80211_cached_keys *keys;
+ const u8 *ie;
+ size_t ie_len;
+ u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN];
+ u8 ssid[IEEE80211_MAX_SSID_LEN];
+ s8 default_key, default_mgmt_key;
+ bool prev_bssid_valid;
+ } wext;
+#endif
+
+ struct cfg80211_cqm_config *cqm_config;
+};
+
+static inline u8 *wdev_address(struct wireless_dev *wdev)
+{
+ if (wdev->netdev)
+ return wdev->netdev->dev_addr;
+ return wdev->address;
+}
+
+static inline bool wdev_running(struct wireless_dev *wdev)
+{
+ if (wdev->netdev)
+ return netif_running(wdev->netdev);
+ return wdev->is_running;
+}
+
+/**
+ * wdev_priv - return wiphy priv from wireless_dev
+ *
+ * @wdev: The wireless device whose wiphy's priv pointer to return
+ * Return: The wiphy priv of @wdev.
+ */
+static inline void *wdev_priv(struct wireless_dev *wdev)
+{
+ BUG_ON(!wdev);
+ return wiphy_priv(wdev->wiphy);
+}
+
+/**
+ * DOC: Utility functions
+ *
+ * cfg80211 offers a number of utility functions that can be useful.
+ */
+
+/**
+ * ieee80211_channel_to_frequency - convert channel number to frequency
+ * @chan: channel number
+ * @band: band, necessary due to channel number overlap
+ * Return: The corresponding frequency (in MHz), or 0 if the conversion failed.
+ */
+int ieee80211_channel_to_frequency(int chan, enum nl80211_band band);
+
+/**
+ * ieee80211_frequency_to_channel - convert frequency to channel number
+ * @freq: center frequency
+ * Return: The corresponding channel, or 0 if the conversion failed.
+ */
+int ieee80211_frequency_to_channel(int freq);
+
+/**
+ * ieee80211_get_channel - get channel struct from wiphy for specified frequency
+ *
+ * @wiphy: the struct wiphy to get the channel for
+ * @freq: the center frequency of the channel
+ *
+ * Return: The channel struct from @wiphy at @freq.
+ */
+struct ieee80211_channel *ieee80211_get_channel(struct wiphy *wiphy, int freq);
+
+/**
+ * ieee80211_get_response_rate - get basic rate for a given rate
+ *
+ * @sband: the band to look for rates in
+ * @basic_rates: bitmap of basic rates
+ * @bitrate: the bitrate for which to find the basic rate
+ *
+ * Return: The basic rate corresponding to a given bitrate, that
+ * is the next lower bitrate contained in the basic rate map,
+ * which is, for this function, given as a bitmap of indices of
+ * rates in the band's bitrate table.
+ */
+struct ieee80211_rate *
+ieee80211_get_response_rate(struct ieee80211_supported_band *sband,
+ u32 basic_rates, int bitrate);
+
+/**
+ * ieee80211_mandatory_rates - get mandatory rates for a given band
+ * @sband: the band to look for rates in
+ * @scan_width: width of the control channel
+ *
+ * This function returns a bitmap of the mandatory rates for the given
+ * band, bits are set according to the rate position in the bitrates array.
+ */
+u32 ieee80211_mandatory_rates(struct ieee80211_supported_band *sband,
+ enum nl80211_bss_scan_width scan_width);
+
+/*
+ * Radiotap parsing functions -- for controlled injection support
+ *
+ * Implemented in net/wireless/radiotap.c
+ * Documentation in Documentation/networking/radiotap-headers.txt
+ */
+
+struct radiotap_align_size {
+ uint8_t align:4, size:4;
+};
+
+struct ieee80211_radiotap_namespace {
+ const struct radiotap_align_size *align_size;
+ int n_bits;
+ uint32_t oui;
+ uint8_t subns;
+};
+
+struct ieee80211_radiotap_vendor_namespaces {
+ const struct ieee80211_radiotap_namespace *ns;
+ int n_ns;
+};
+
+/**
+ * struct ieee80211_radiotap_iterator - tracks walk thru present radiotap args
+ * @this_arg_index: index of current arg, valid after each successful call
+ * to ieee80211_radiotap_iterator_next()
+ * @this_arg: pointer to current radiotap arg; it is valid after each
+ * call to ieee80211_radiotap_iterator_next() but also after
+ * ieee80211_radiotap_iterator_init() where it will point to
+ * the beginning of the actual data portion
+ * @this_arg_size: length of the current arg, for convenience
+ * @current_namespace: pointer to the current namespace definition
+ * (or internally %NULL if the current namespace is unknown)
+ * @is_radiotap_ns: indicates whether the current namespace is the default
+ * radiotap namespace or not
+ *
+ * @_rtheader: pointer to the radiotap header we are walking through
+ * @_max_length: length of radiotap header in cpu byte ordering
+ * @_arg_index: next argument index
+ * @_arg: next argument pointer
+ * @_next_bitmap: internal pointer to next present u32
+ * @_bitmap_shifter: internal shifter for curr u32 bitmap, b0 set == arg present
+ * @_vns: vendor namespace definitions
+ * @_next_ns_data: beginning of the next namespace's data
+ * @_reset_on_ext: internal; reset the arg index to 0 when going to the
+ * next bitmap word
+ *
+ * Describes the radiotap parser state. Fields prefixed with an underscore
+ * must not be used by users of the parser, only by the parser internally.
+ */
+
+struct ieee80211_radiotap_iterator {
+ struct ieee80211_radiotap_header *_rtheader;
+ const struct ieee80211_radiotap_vendor_namespaces *_vns;
+ const struct ieee80211_radiotap_namespace *current_namespace;
+
+ unsigned char *_arg, *_next_ns_data;
+ __le32 *_next_bitmap;
+
+ unsigned char *this_arg;
+ int this_arg_index;
+ int this_arg_size;
+
+ int is_radiotap_ns;
+
+ int _max_length;
+ int _arg_index;
+ uint32_t _bitmap_shifter;
+ int _reset_on_ext;
+};
+
+int
+ieee80211_radiotap_iterator_init(struct ieee80211_radiotap_iterator *iterator,
+ struct ieee80211_radiotap_header *radiotap_header,
+ int max_length,
+ const struct ieee80211_radiotap_vendor_namespaces *vns);
+
+int
+ieee80211_radiotap_iterator_next(struct ieee80211_radiotap_iterator *iterator);
+
+
+extern const unsigned char rfc1042_header[6];
+extern const unsigned char bridge_tunnel_header[6];
+
+/**
+ * ieee80211_get_hdrlen_from_skb - get header length from data
+ *
+ * @skb: the frame
+ *
+ * Given an skb with a raw 802.11 header at the data pointer this function
+ * returns the 802.11 header length.
+ *
+ * Return: The 802.11 header length in bytes (not including encryption
+ * headers). Or 0 if the data in the sk_buff is too short to contain a valid
+ * 802.11 header.
+ */
+unsigned int ieee80211_get_hdrlen_from_skb(const struct sk_buff *skb);
+
+/**
+ * ieee80211_hdrlen - get header length in bytes from frame control
+ * @fc: frame control field in little-endian format
+ * Return: The header length in bytes.
+ */
+unsigned int __attribute_const__ ieee80211_hdrlen(__le16 fc);
+
+/**
+ * ieee80211_get_mesh_hdrlen - get mesh extension header length
+ * @meshhdr: the mesh extension header, only the flags field
+ * (first byte) will be accessed
+ * Return: The length of the extension header, which is always at
+ * least 6 bytes and at most 18 if address 5 and 6 are present.
+ */
+unsigned int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr);
+
+/**
+ * DOC: Data path helpers
+ *
+ * In addition to generic utilities, cfg80211 also offers
+ * functions that help implement the data path for devices
+ * that do not do the 802.11/802.3 conversion on the device.
+ */
+
+/**
+ * ieee80211_data_to_8023_exthdr - convert an 802.11 data frame to 802.3
+ * @skb: the 802.11 data frame
+ * @ehdr: pointer to a &struct ethhdr that will get the header, instead
+ * of it being pushed into the SKB
+ * @addr: the device MAC address
+ * @iftype: the virtual interface type
+ * Return: 0 on success. Non-zero on error.
+ */
+int ieee80211_data_to_8023_exthdr(struct sk_buff *skb, struct ethhdr *ehdr,
+ const u8 *addr, enum nl80211_iftype iftype);
+
+/**
+ * ieee80211_data_to_8023 - convert an 802.11 data frame to 802.3
+ * @skb: the 802.11 data frame
+ * @addr: the device MAC address
+ * @iftype: the virtual interface type
+ * Return: 0 on success. Non-zero on error.
+ */
+static inline int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr,
+ enum nl80211_iftype iftype)
+{
+ return ieee80211_data_to_8023_exthdr(skb, NULL, addr, iftype);
+}
+
+/**
+ * ieee80211_data_from_8023 - convert an 802.3 frame to 802.11
+ * @skb: the 802.3 frame
+ * @addr: the device MAC address
+ * @iftype: the virtual interface type
+ * @bssid: the network bssid (used only for iftype STATION and ADHOC)
+ * @qos: build 802.11 QoS data frame
+ * Return: 0 on success, or a negative error code.
+ */
+int ieee80211_data_from_8023(struct sk_buff *skb, const u8 *addr,
+ enum nl80211_iftype iftype, const u8 *bssid,
+ bool qos);
+
+/**
+ * ieee80211_amsdu_to_8023s - decode an IEEE 802.11n A-MSDU frame
+ *
+ * Decode an IEEE 802.11 A-MSDU and convert it to a list of 802.3 frames.
+ * The @list will be empty if the decode fails. The @skb must be fully
+ * header-less before being passed in here; it is freed in this function.
+ *
+ * @skb: The input A-MSDU frame without any headers.
+ * @list: The output list of 802.3 frames. It must be allocated and
+ * initialized by by the caller.
+ * @addr: The device MAC address.
+ * @iftype: The device interface type.
+ * @extra_headroom: The hardware extra headroom for SKBs in the @list.
+ * @check_da: DA to check in the inner ethernet header, or NULL
+ * @check_sa: SA to check in the inner ethernet header, or NULL
+ */
+void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list,
+ const u8 *addr, enum nl80211_iftype iftype,
+ const unsigned int extra_headroom,
+ const u8 *check_da, const u8 *check_sa);
+
+/**
+ * cfg80211_classify8021d - determine the 802.1p/1d tag for a data frame
+ * @skb: the data frame
+ * @qos_map: Interworking QoS mapping or %NULL if not in use
+ * Return: The 802.1p/1d tag.
+ */
+unsigned int cfg80211_classify8021d(struct sk_buff *skb,
+ struct cfg80211_qos_map *qos_map);
+
+/**
+ * cfg80211_find_ie_match - match information element and byte array in data
+ *
+ * @eid: element ID
+ * @ies: data consisting of IEs
+ * @len: length of data
+ * @match: byte array to match
+ * @match_len: number of bytes in the match array
+ * @match_offset: offset in the IE where the byte array should match.
+ * If match_len is zero, this must also be set to zero.
+ * Otherwise this must be set to 2 or more, because the first
+ * byte is the element id, which is already compared to eid, and
+ * the second byte is the IE length.
+ *
+ * Return: %NULL if the element ID could not be found or if
+ * the element is invalid (claims to be longer than the given
+ * data) or if the byte array doesn't match, or a pointer to the first
+ * byte of the requested element, that is the byte containing the
+ * element ID.
+ *
+ * Note: There are no checks on the element length other than
+ * having to fit into the given data and being large enough for the
+ * byte array to match.
+ */
+const u8 *cfg80211_find_ie_match(u8 eid, const u8 *ies, int len,
+ const u8 *match, int match_len,
+ int match_offset);
+
+/**
+ * cfg80211_find_ie - find information element in data
+ *
+ * @eid: element ID
+ * @ies: data consisting of IEs
+ * @len: length of data
+ *
+ * Return: %NULL if the element ID could not be found or if
+ * the element is invalid (claims to be longer than the given
+ * data), or a pointer to the first byte of the requested
+ * element, that is the byte containing the element ID.
+ *
+ * Note: There are no checks on the element length other than
+ * having to fit into the given data.
+ */
+static inline const u8 *cfg80211_find_ie(u8 eid, const u8 *ies, int len)
+{
+ return cfg80211_find_ie_match(eid, ies, len, NULL, 0, 0);
+}
+
+/**
+ * cfg80211_find_ext_ie - find information element with EID Extension in data
+ *
+ * @ext_eid: element ID Extension
+ * @ies: data consisting of IEs
+ * @len: length of data
+ *
+ * Return: %NULL if the extended element ID could not be found or if
+ * the element is invalid (claims to be longer than the given
+ * data), or a pointer to the first byte of the requested
+ * element, that is the byte containing the element ID.
+ *
+ * Note: There are no checks on the element length other than
+ * having to fit into the given data.
+ */
+static inline const u8 *cfg80211_find_ext_ie(u8 ext_eid, const u8 *ies, int len)
+{
+ return cfg80211_find_ie_match(WLAN_EID_EXTENSION, ies, len,
+ &ext_eid, 1, 2);
+}
+
+/**
+ * cfg80211_find_vendor_ie - find vendor specific information element in data
+ *
+ * @oui: vendor OUI
+ * @oui_type: vendor-specific OUI type (must be < 0xff), negative means any
+ * @ies: data consisting of IEs
+ * @len: length of data
+ *
+ * Return: %NULL if the vendor specific element ID could not be found or if the
+ * element is invalid (claims to be longer than the given data), or a pointer to
+ * the first byte of the requested element, that is the byte containing the
+ * element ID.
+ *
+ * Note: There are no checks on the element length other than having to fit into
+ * the given data.
+ */
+const u8 *cfg80211_find_vendor_ie(unsigned int oui, int oui_type,
+ const u8 *ies, int len);
+
+/**
+ * DOC: Regulatory enforcement infrastructure
+ *
+ * TODO
+ */
+
+/**
+ * regulatory_hint - driver hint to the wireless core a regulatory domain
+ * @wiphy: the wireless device giving the hint (used only for reporting
+ * conflicts)
+ * @alpha2: the ISO/IEC 3166 alpha2 the driver claims its regulatory domain
+ * should be in. If @rd is set this should be NULL. Note that if you
+ * set this to NULL you should still set rd->alpha2 to some accepted
+ * alpha2.
+ *
+ * Wireless drivers can use this function to hint to the wireless core
+ * what it believes should be the current regulatory domain by
+ * giving it an ISO/IEC 3166 alpha2 country code it knows its regulatory
+ * domain should be in or by providing a completely build regulatory domain.
+ * If the driver provides an ISO/IEC 3166 alpha2 userspace will be queried
+ * for a regulatory domain structure for the respective country.
+ *
+ * The wiphy must have been registered to cfg80211 prior to this call.
+ * For cfg80211 drivers this means you must first use wiphy_register(),
+ * for mac80211 drivers you must first use ieee80211_register_hw().
+ *
+ * Drivers should check the return value, its possible you can get
+ * an -ENOMEM.
+ *
+ * Return: 0 on success. -ENOMEM.
+ */
+int regulatory_hint(struct wiphy *wiphy, const char *alpha2);
+
+/**
+ * regulatory_set_wiphy_regd - set regdom info for self managed drivers
+ * @wiphy: the wireless device we want to process the regulatory domain on
+ * @rd: the regulatory domain informatoin to use for this wiphy
+ *
+ * Set the regulatory domain information for self-managed wiphys, only they
+ * may use this function. See %REGULATORY_WIPHY_SELF_MANAGED for more
+ * information.
+ *
+ * Return: 0 on success. -EINVAL, -EPERM
+ */
+int regulatory_set_wiphy_regd(struct wiphy *wiphy,
+ struct ieee80211_regdomain *rd);
+
+/**
+ * regulatory_set_wiphy_regd_sync_rtnl - set regdom for self-managed drivers
+ * @wiphy: the wireless device we want to process the regulatory domain on
+ * @rd: the regulatory domain information to use for this wiphy
+ *
+ * This functions requires the RTNL to be held and applies the new regdomain
+ * synchronously to this wiphy. For more details see
+ * regulatory_set_wiphy_regd().
+ *
+ * Return: 0 on success. -EINVAL, -EPERM
+ */
+int regulatory_set_wiphy_regd_sync_rtnl(struct wiphy *wiphy,
+ struct ieee80211_regdomain *rd);
+
+/**
+ * wiphy_apply_custom_regulatory - apply a custom driver regulatory domain
+ * @wiphy: the wireless device we want to process the regulatory domain on
+ * @regd: the custom regulatory domain to use for this wiphy
+ *
+ * Drivers can sometimes have custom regulatory domains which do not apply
+ * to a specific country. Drivers can use this to apply such custom regulatory
+ * domains. This routine must be called prior to wiphy registration. The
+ * custom regulatory domain will be trusted completely and as such previous
+ * default channel settings will be disregarded. If no rule is found for a
+ * channel on the regulatory domain the channel will be disabled.
+ * Drivers using this for a wiphy should also set the wiphy flag
+ * REGULATORY_CUSTOM_REG or cfg80211 will set it for the wiphy
+ * that called this helper.
+ */
+void wiphy_apply_custom_regulatory(struct wiphy *wiphy,
+ const struct ieee80211_regdomain *regd);
+
+/**
+ * freq_reg_info - get regulatory information for the given frequency
+ * @wiphy: the wiphy for which we want to process this rule for
+ * @center_freq: Frequency in KHz for which we want regulatory information for
+ *
+ * Use this function to get the regulatory rule for a specific frequency on
+ * a given wireless device. If the device has a specific regulatory domain
+ * it wants to follow we respect that unless a country IE has been received
+ * and processed already.
+ *
+ * Return: A valid pointer, or, when an error occurs, for example if no rule
+ * can be found, the return value is encoded using ERR_PTR(). Use IS_ERR() to
+ * check and PTR_ERR() to obtain the numeric return value. The numeric return
+ * value will be -ERANGE if we determine the given center_freq does not even
+ * have a regulatory rule for a frequency range in the center_freq's band.
+ * See freq_in_rule_band() for our current definition of a band -- this is
+ * purely subjective and right now it's 802.11 specific.
+ */
+const struct ieee80211_reg_rule *freq_reg_info(struct wiphy *wiphy,
+ u32 center_freq);
+
+/**
+ * reg_initiator_name - map regulatory request initiator enum to name
+ * @initiator: the regulatory request initiator
+ *
+ * You can use this to map the regulatory request initiator enum to a
+ * proper string representation.
+ */
+const char *reg_initiator_name(enum nl80211_reg_initiator initiator);
+
+/*
+ * callbacks for asynchronous cfg80211 methods, notification
+ * functions and BSS handling helpers
+ */
+
+/**
+ * cfg80211_scan_done - notify that scan finished
+ *
+ * @request: the corresponding scan request
+ * @info: information about the completed scan
+ */
+void cfg80211_scan_done(struct cfg80211_scan_request *request,
+ struct cfg80211_scan_info *info);
+
+/**
+ * cfg80211_sched_scan_results - notify that new scan results are available
+ *
+ * @wiphy: the wiphy which got scheduled scan results
+ * @reqid: identifier for the related scheduled scan request
+ */
+void cfg80211_sched_scan_results(struct wiphy *wiphy, u64 reqid);
+
+/**
+ * cfg80211_sched_scan_stopped - notify that the scheduled scan has stopped
+ *
+ * @wiphy: the wiphy on which the scheduled scan stopped
+ * @reqid: identifier for the related scheduled scan request
+ *
+ * The driver can call this function to inform cfg80211 that the
+ * scheduled scan had to be stopped, for whatever reason. The driver
+ * is then called back via the sched_scan_stop operation when done.
+ */
+void cfg80211_sched_scan_stopped(struct wiphy *wiphy, u64 reqid);
+
+/**
+ * cfg80211_sched_scan_stopped_rtnl - notify that the scheduled scan has stopped
+ *
+ * @wiphy: the wiphy on which the scheduled scan stopped
+ * @reqid: identifier for the related scheduled scan request
+ *
+ * The driver can call this function to inform cfg80211 that the
+ * scheduled scan had to be stopped, for whatever reason. The driver
+ * is then called back via the sched_scan_stop operation when done.
+ * This function should be called with rtnl locked.
+ */
+void cfg80211_sched_scan_stopped_rtnl(struct wiphy *wiphy, u64 reqid);
+
+/**
+ * cfg80211_inform_bss_frame_data - inform cfg80211 of a received BSS frame
+ * @wiphy: the wiphy reporting the BSS
+ * @data: the BSS metadata
+ * @mgmt: the management frame (probe response or beacon)
+ * @len: length of the management frame
+ * @gfp: context flags
+ *
+ * This informs cfg80211 that BSS information was found and
+ * the BSS should be updated/added.
+ *
+ * Return: A referenced struct, must be released with cfg80211_put_bss()!
+ * Or %NULL on error.
+ */
+struct cfg80211_bss * __must_check
+cfg80211_inform_bss_frame_data(struct wiphy *wiphy,
+ struct cfg80211_inform_bss *data,
+ struct ieee80211_mgmt *mgmt, size_t len,
+ gfp_t gfp);
+
+static inline struct cfg80211_bss * __must_check
+cfg80211_inform_bss_width_frame(struct wiphy *wiphy,
+ struct ieee80211_channel *rx_channel,
+ enum nl80211_bss_scan_width scan_width,
+ struct ieee80211_mgmt *mgmt, size_t len,
+ s32 signal, gfp_t gfp)
+{
+ struct cfg80211_inform_bss data = {
+ .chan = rx_channel,
+ .scan_width = scan_width,
+ .signal = signal,
+ };
+
+ return cfg80211_inform_bss_frame_data(wiphy, &data, mgmt, len, gfp);
+}
+
+static inline struct cfg80211_bss * __must_check
+cfg80211_inform_bss_frame(struct wiphy *wiphy,
+ struct ieee80211_channel *rx_channel,
+ struct ieee80211_mgmt *mgmt, size_t len,
+ s32 signal, gfp_t gfp)
+{
+ struct cfg80211_inform_bss data = {
+ .chan = rx_channel,
+ .scan_width = NL80211_BSS_CHAN_WIDTH_20,
+ .signal = signal,
+ };
+
+ return cfg80211_inform_bss_frame_data(wiphy, &data, mgmt, len, gfp);
+}
+
+/**
+ * enum cfg80211_bss_frame_type - frame type that the BSS data came from
+ * @CFG80211_BSS_FTYPE_UNKNOWN: driver doesn't know whether the data is
+ * from a beacon or probe response
+ * @CFG80211_BSS_FTYPE_BEACON: data comes from a beacon
+ * @CFG80211_BSS_FTYPE_PRESP: data comes from a probe response
+ */
+enum cfg80211_bss_frame_type {
+ CFG80211_BSS_FTYPE_UNKNOWN,
+ CFG80211_BSS_FTYPE_BEACON,
+ CFG80211_BSS_FTYPE_PRESP,
+};
+
+/**
+ * cfg80211_inform_bss_data - inform cfg80211 of a new BSS
+ *
+ * @wiphy: the wiphy reporting the BSS
+ * @data: the BSS metadata
+ * @ftype: frame type (if known)
+ * @bssid: the BSSID of the BSS
+ * @tsf: the TSF sent by the peer in the beacon/probe response (or 0)
+ * @capability: the capability field sent by the peer
+ * @beacon_interval: the beacon interval announced by the peer
+ * @ie: additional IEs sent by the peer
+ * @ielen: length of the additional IEs
+ * @gfp: context flags
+ *
+ * This informs cfg80211 that BSS information was found and
+ * the BSS should be updated/added.
+ *
+ * Return: A referenced struct, must be released with cfg80211_put_bss()!
+ * Or %NULL on error.
+ */
+struct cfg80211_bss * __must_check
+cfg80211_inform_bss_data(struct wiphy *wiphy,
+ struct cfg80211_inform_bss *data,
+ enum cfg80211_bss_frame_type ftype,
+ const u8 *bssid, u64 tsf, u16 capability,
+ u16 beacon_interval, const u8 *ie, size_t ielen,
+ gfp_t gfp);
+
+static inline struct cfg80211_bss * __must_check
+cfg80211_inform_bss_width(struct wiphy *wiphy,
+ struct ieee80211_channel *rx_channel,
+ enum nl80211_bss_scan_width scan_width,
+ enum cfg80211_bss_frame_type ftype,
+ const u8 *bssid, u64 tsf, u16 capability,
+ u16 beacon_interval, const u8 *ie, size_t ielen,
+ s32 signal, gfp_t gfp)
+{
+ struct cfg80211_inform_bss data = {
+ .chan = rx_channel,
+ .scan_width = scan_width,
+ .signal = signal,
+ };
+
+ return cfg80211_inform_bss_data(wiphy, &data, ftype, bssid, tsf,
+ capability, beacon_interval, ie, ielen,
+ gfp);
+}
+
+static inline struct cfg80211_bss * __must_check
+cfg80211_inform_bss(struct wiphy *wiphy,
+ struct ieee80211_channel *rx_channel,
+ enum cfg80211_bss_frame_type ftype,
+ const u8 *bssid, u64 tsf, u16 capability,
+ u16 beacon_interval, const u8 *ie, size_t ielen,
+ s32 signal, gfp_t gfp)
+{
+ struct cfg80211_inform_bss data = {
+ .chan = rx_channel,
+ .scan_width = NL80211_BSS_CHAN_WIDTH_20,
+ .signal = signal,
+ };
+
+ return cfg80211_inform_bss_data(wiphy, &data, ftype, bssid, tsf,
+ capability, beacon_interval, ie, ielen,
+ gfp);
+}
+
+/**
+ * cfg80211_get_bss - get a BSS reference
+ * @wiphy: the wiphy this BSS struct belongs to
+ * @channel: the channel to search on (or %NULL)
+ * @bssid: the desired BSSID (or %NULL)
+ * @ssid: the desired SSID (or %NULL)
+ * @ssid_len: length of the SSID (or 0)
+ * @bss_type: type of BSS, see &enum ieee80211_bss_type
+ * @privacy: privacy filter, see &enum ieee80211_privacy
+ */
+struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy,
+ struct ieee80211_channel *channel,
+ const u8 *bssid,
+ const u8 *ssid, size_t ssid_len,
+ enum ieee80211_bss_type bss_type,
+ enum ieee80211_privacy privacy);
+static inline struct cfg80211_bss *
+cfg80211_get_ibss(struct wiphy *wiphy,
+ struct ieee80211_channel *channel,
+ const u8 *ssid, size_t ssid_len)
+{
+ return cfg80211_get_bss(wiphy, channel, NULL, ssid, ssid_len,
+ IEEE80211_BSS_TYPE_IBSS,
+ IEEE80211_PRIVACY_ANY);
+}
+
+/**
+ * cfg80211_ref_bss - reference BSS struct
+ * @wiphy: the wiphy this BSS struct belongs to
+ * @bss: the BSS struct to reference
+ *
+ * Increments the refcount of the given BSS struct.
+ */
+void cfg80211_ref_bss(struct wiphy *wiphy, struct cfg80211_bss *bss);
+
+/**
+ * cfg80211_put_bss - unref BSS struct
+ * @wiphy: the wiphy this BSS struct belongs to
+ * @bss: the BSS struct
+ *
+ * Decrements the refcount of the given BSS struct.
+ */
+void cfg80211_put_bss(struct wiphy *wiphy, struct cfg80211_bss *bss);
+
+/**
+ * cfg80211_unlink_bss - unlink BSS from internal data structures
+ * @wiphy: the wiphy
+ * @bss: the bss to remove
+ *
+ * This function removes the given BSS from the internal data structures
+ * thereby making it no longer show up in scan results etc. Use this
+ * function when you detect a BSS is gone. Normally BSSes will also time
+ * out, so it is not necessary to use this function at all.
+ */
+void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *bss);
+
+static inline enum nl80211_bss_scan_width
+cfg80211_chandef_to_scan_width(const struct cfg80211_chan_def *chandef)
+{
+ switch (chandef->width) {
+ case NL80211_CHAN_WIDTH_5:
+ return NL80211_BSS_CHAN_WIDTH_5;
+ case NL80211_CHAN_WIDTH_10:
+ return NL80211_BSS_CHAN_WIDTH_10;
+ default:
+ return NL80211_BSS_CHAN_WIDTH_20;
+ }
+}
+
+/**
+ * cfg80211_rx_mlme_mgmt - notification of processed MLME management frame
+ * @dev: network device
+ * @buf: authentication frame (header + body)
+ * @len: length of the frame data
+ *
+ * This function is called whenever an authentication, disassociation or
+ * deauthentication frame has been received and processed in station mode.
+ * After being asked to authenticate via cfg80211_ops::auth() the driver must
+ * call either this function or cfg80211_auth_timeout().
+ * After being asked to associate via cfg80211_ops::assoc() the driver must
+ * call either this function or cfg80211_auth_timeout().
+ * While connected, the driver must calls this for received and processed
+ * disassociation and deauthentication frames. If the frame couldn't be used
+ * because it was unprotected, the driver must call the function
+ * cfg80211_rx_unprot_mlme_mgmt() instead.
+ *
+ * This function may sleep. The caller must hold the corresponding wdev's mutex.
+ */
+void cfg80211_rx_mlme_mgmt(struct net_device *dev, const u8 *buf, size_t len);
+
+/**
+ * cfg80211_auth_timeout - notification of timed out authentication
+ * @dev: network device
+ * @addr: The MAC address of the device with which the authentication timed out
+ *
+ * This function may sleep. The caller must hold the corresponding wdev's
+ * mutex.
+ */
+void cfg80211_auth_timeout(struct net_device *dev, const u8 *addr);
+
+/**
+ * cfg80211_rx_assoc_resp - notification of processed association response
+ * @dev: network device
+ * @bss: the BSS that association was requested with, ownership of the pointer
+ * moves to cfg80211 in this call
+ * @buf: authentication frame (header + body)
+ * @len: length of the frame data
+ * @uapsd_queues: bitmap of queues configured for uapsd. Same format
+ * as the AC bitmap in the QoS info field
+ *
+ * After being asked to associate via cfg80211_ops::assoc() the driver must
+ * call either this function or cfg80211_auth_timeout().
+ *
+ * This function may sleep. The caller must hold the corresponding wdev's mutex.
+ */
+void cfg80211_rx_assoc_resp(struct net_device *dev,
+ struct cfg80211_bss *bss,
+ const u8 *buf, size_t len,
+ int uapsd_queues);
+
+/**
+ * cfg80211_assoc_timeout - notification of timed out association
+ * @dev: network device
+ * @bss: The BSS entry with which association timed out.
+ *
+ * This function may sleep. The caller must hold the corresponding wdev's mutex.
+ */
+void cfg80211_assoc_timeout(struct net_device *dev, struct cfg80211_bss *bss);
+
+/**
+ * cfg80211_abandon_assoc - notify cfg80211 of abandoned association attempt
+ * @dev: network device
+ * @bss: The BSS entry with which association was abandoned.
+ *
+ * Call this whenever - for reasons reported through other API, like deauth RX,
+ * an association attempt was abandoned.
+ * This function may sleep. The caller must hold the corresponding wdev's mutex.
+ */
+void cfg80211_abandon_assoc(struct net_device *dev, struct cfg80211_bss *bss);
+
+/**
+ * cfg80211_tx_mlme_mgmt - notification of transmitted deauth/disassoc frame
+ * @dev: network device
+ * @buf: 802.11 frame (header + body)
+ * @len: length of the frame data
+ *
+ * This function is called whenever deauthentication has been processed in
+ * station mode. This includes both received deauthentication frames and
+ * locally generated ones. This function may sleep. The caller must hold the
+ * corresponding wdev's mutex.
+ */
+void cfg80211_tx_mlme_mgmt(struct net_device *dev, const u8 *buf, size_t len);
+
+/**
+ * cfg80211_rx_unprot_mlme_mgmt - notification of unprotected mlme mgmt frame
+ * @dev: network device
+ * @buf: deauthentication frame (header + body)
+ * @len: length of the frame data
+ *
+ * This function is called whenever a received deauthentication or dissassoc
+ * frame has been dropped in station mode because of MFP being used but the
+ * frame was not protected. This function may sleep.
+ */
+void cfg80211_rx_unprot_mlme_mgmt(struct net_device *dev,
+ const u8 *buf, size_t len);
+
+/**
+ * cfg80211_michael_mic_failure - notification of Michael MIC failure (TKIP)
+ * @dev: network device
+ * @addr: The source MAC address of the frame
+ * @key_type: The key type that the received frame used
+ * @key_id: Key identifier (0..3). Can be -1 if missing.
+ * @tsc: The TSC value of the frame that generated the MIC failure (6 octets)
+ * @gfp: allocation flags
+ *
+ * This function is called whenever the local MAC detects a MIC failure in a
+ * received frame. This matches with MLME-MICHAELMICFAILURE.indication()
+ * primitive.
+ */
+void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr,
+ enum nl80211_key_type key_type, int key_id,
+ const u8 *tsc, gfp_t gfp);
+
+/**
+ * cfg80211_ibss_joined - notify cfg80211 that device joined an IBSS
+ *
+ * @dev: network device
+ * @bssid: the BSSID of the IBSS joined
+ * @channel: the channel of the IBSS joined
+ * @gfp: allocation flags
+ *
+ * This function notifies cfg80211 that the device joined an IBSS or
+ * switched to a different BSSID. Before this function can be called,
+ * either a beacon has to have been received from the IBSS, or one of
+ * the cfg80211_inform_bss{,_frame} functions must have been called
+ * with the locally generated beacon -- this guarantees that there is
+ * always a scan result for this IBSS. cfg80211 will handle the rest.
+ */
+void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid,
+ struct ieee80211_channel *channel, gfp_t gfp);
+
+/**
+ * cfg80211_notify_new_candidate - notify cfg80211 of a new mesh peer candidate
+ *
+ * @dev: network device
+ * @macaddr: the MAC address of the new candidate
+ * @ie: information elements advertised by the peer candidate
+ * @ie_len: lenght of the information elements buffer
+ * @gfp: allocation flags
+ *
+ * This function notifies cfg80211 that the mesh peer candidate has been
+ * detected, most likely via a beacon or, less likely, via a probe response.
+ * cfg80211 then sends a notification to userspace.
+ */
+void cfg80211_notify_new_peer_candidate(struct net_device *dev,
+ const u8 *macaddr, const u8 *ie, u8 ie_len, gfp_t gfp);
+
+/**
+ * DOC: RFkill integration
+ *
+ * RFkill integration in cfg80211 is almost invisible to drivers,
+ * as cfg80211 automatically registers an rfkill instance for each
+ * wireless device it knows about. Soft kill is also translated
+ * into disconnecting and turning all interfaces off, drivers are
+ * expected to turn off the device when all interfaces are down.
+ *
+ * However, devices may have a hard RFkill line, in which case they
+ * also need to interact with the rfkill subsystem, via cfg80211.
+ * They can do this with a few helper functions documented here.
+ */
+
+/**
+ * wiphy_rfkill_set_hw_state - notify cfg80211 about hw block state
+ * @wiphy: the wiphy
+ * @blocked: block status
+ */
+void wiphy_rfkill_set_hw_state(struct wiphy *wiphy, bool blocked);
+
+/**
+ * wiphy_rfkill_start_polling - start polling rfkill
+ * @wiphy: the wiphy
+ */
+void wiphy_rfkill_start_polling(struct wiphy *wiphy);
+
+/**
+ * wiphy_rfkill_stop_polling - stop polling rfkill
+ * @wiphy: the wiphy
+ */
+void wiphy_rfkill_stop_polling(struct wiphy *wiphy);
+
+/**
+ * DOC: Vendor commands
+ *
+ * Occasionally, there are special protocol or firmware features that
+ * can't be implemented very openly. For this and similar cases, the
+ * vendor command functionality allows implementing the features with
+ * (typically closed-source) userspace and firmware, using nl80211 as
+ * the configuration mechanism.
+ *
+ * A driver supporting vendor commands must register them as an array
+ * in struct wiphy, with handlers for each one, each command has an
+ * OUI and sub command ID to identify it.
+ *
+ * Note that this feature should not be (ab)used to implement protocol
+ * features that could openly be shared across drivers. In particular,
+ * it must never be required to use vendor commands to implement any
+ * "normal" functionality that higher-level userspace like connection
+ * managers etc. need.
+ */
+
+struct sk_buff *__cfg80211_alloc_reply_skb(struct wiphy *wiphy,
+ enum nl80211_commands cmd,
+ enum nl80211_attrs attr,
+ int approxlen);
+
+struct sk_buff *__cfg80211_alloc_event_skb(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ enum nl80211_commands cmd,
+ enum nl80211_attrs attr,
+ int vendor_event_idx,
+ int approxlen, gfp_t gfp);
+
+void __cfg80211_send_event_skb(struct sk_buff *skb, gfp_t gfp);
+
+/**
+ * cfg80211_vendor_cmd_alloc_reply_skb - allocate vendor command reply
+ * @wiphy: the wiphy
+ * @approxlen: an upper bound of the length of the data that will
+ * be put into the skb
+ *
+ * This function allocates and pre-fills an skb for a reply to
+ * a vendor command. Since it is intended for a reply, calling
+ * it outside of a vendor command's doit() operation is invalid.
+ *
+ * The returned skb is pre-filled with some identifying data in
+ * a way that any data that is put into the skb (with skb_put(),
+ * nla_put() or similar) will end up being within the
+ * %NL80211_ATTR_VENDOR_DATA attribute, so all that needs to be done
+ * with the skb is adding data for the corresponding userspace tool
+ * which can then read that data out of the vendor data attribute.
+ * You must not modify the skb in any other way.
+ *
+ * When done, call cfg80211_vendor_cmd_reply() with the skb and return
+ * its error code as the result of the doit() operation.
+ *
+ * Return: An allocated and pre-filled skb. %NULL if any errors happen.
+ */
+static inline struct sk_buff *
+cfg80211_vendor_cmd_alloc_reply_skb(struct wiphy *wiphy, int approxlen)
+{
+ return __cfg80211_alloc_reply_skb(wiphy, NL80211_CMD_VENDOR,
+ NL80211_ATTR_VENDOR_DATA, approxlen);
+}
+
+/**
+ * cfg80211_vendor_cmd_reply - send the reply skb
+ * @skb: The skb, must have been allocated with
+ * cfg80211_vendor_cmd_alloc_reply_skb()
+ *
+ * Since calling this function will usually be the last thing
+ * before returning from the vendor command doit() you should
+ * return the error code. Note that this function consumes the
+ * skb regardless of the return value.
+ *
+ * Return: An error code or 0 on success.
+ */
+int cfg80211_vendor_cmd_reply(struct sk_buff *skb);
+
+/**
+ * cfg80211_vendor_event_alloc - allocate vendor-specific event skb
+ * @wiphy: the wiphy
+ * @wdev: the wireless device
+ * @event_idx: index of the vendor event in the wiphy's vendor_events
+ * @approxlen: an upper bound of the length of the data that will
+ * be put into the skb
+ * @gfp: allocation flags
+ *
+ * This function allocates and pre-fills an skb for an event on the
+ * vendor-specific multicast group.
+ *
+ * If wdev != NULL, both the ifindex and identifier of the specified
+ * wireless device are added to the event message before the vendor data
+ * attribute.
+ *
+ * When done filling the skb, call cfg80211_vendor_event() with the
+ * skb to send the event.
+ *
+ * Return: An allocated and pre-filled skb. %NULL if any errors happen.
+ */
+static inline struct sk_buff *
+cfg80211_vendor_event_alloc(struct wiphy *wiphy, struct wireless_dev *wdev,
+ int approxlen, int event_idx, gfp_t gfp)
+{
+ return __cfg80211_alloc_event_skb(wiphy, wdev, NL80211_CMD_VENDOR,
+ NL80211_ATTR_VENDOR_DATA,
+ event_idx, approxlen, gfp);
+}
+
+/**
+ * cfg80211_vendor_event - send the event
+ * @skb: The skb, must have been allocated with cfg80211_vendor_event_alloc()
+ * @gfp: allocation flags
+ *
+ * This function sends the given @skb, which must have been allocated
+ * by cfg80211_vendor_event_alloc(), as an event. It always consumes it.
+ */
+static inline void cfg80211_vendor_event(struct sk_buff *skb, gfp_t gfp)
+{
+ __cfg80211_send_event_skb(skb, gfp);
+}
+
+#ifdef CPTCFG_NL80211_TESTMODE
+/**
+ * DOC: Test mode
+ *
+ * Test mode is a set of utility functions to allow drivers to
+ * interact with driver-specific tools to aid, for instance,
+ * factory programming.
+ *
+ * This chapter describes how drivers interact with it, for more
+ * information see the nl80211 book's chapter on it.
+ */
+
+/**
+ * cfg80211_testmode_alloc_reply_skb - allocate testmode reply
+ * @wiphy: the wiphy
+ * @approxlen: an upper bound of the length of the data that will
+ * be put into the skb
+ *
+ * This function allocates and pre-fills an skb for a reply to
+ * the testmode command. Since it is intended for a reply, calling
+ * it outside of the @testmode_cmd operation is invalid.
+ *
+ * The returned skb is pre-filled with the wiphy index and set up in
+ * a way that any data that is put into the skb (with skb_put(),
+ * nla_put() or similar) will end up being within the
+ * %NL80211_ATTR_TESTDATA attribute, so all that needs to be done
+ * with the skb is adding data for the corresponding userspace tool
+ * which can then read that data out of the testdata attribute. You
+ * must not modify the skb in any other way.
+ *
+ * When done, call cfg80211_testmode_reply() with the skb and return
+ * its error code as the result of the @testmode_cmd operation.
+ *
+ * Return: An allocated and pre-filled skb. %NULL if any errors happen.
+ */
+static inline struct sk_buff *
+cfg80211_testmode_alloc_reply_skb(struct wiphy *wiphy, int approxlen)
+{
+ return __cfg80211_alloc_reply_skb(wiphy, NL80211_CMD_TESTMODE,
+ NL80211_ATTR_TESTDATA, approxlen);
+}
+
+/**
+ * cfg80211_testmode_reply - send the reply skb
+ * @skb: The skb, must have been allocated with
+ * cfg80211_testmode_alloc_reply_skb()
+ *
+ * Since calling this function will usually be the last thing
+ * before returning from the @testmode_cmd you should return
+ * the error code. Note that this function consumes the skb
+ * regardless of the return value.
+ *
+ * Return: An error code or 0 on success.
+ */
+static inline int cfg80211_testmode_reply(struct sk_buff *skb)
+{
+ return cfg80211_vendor_cmd_reply(skb);
+}
+
+/**
+ * cfg80211_testmode_alloc_event_skb - allocate testmode event
+ * @wiphy: the wiphy
+ * @approxlen: an upper bound of the length of the data that will
+ * be put into the skb
+ * @gfp: allocation flags
+ *
+ * This function allocates and pre-fills an skb for an event on the
+ * testmode multicast group.
+ *
+ * The returned skb is set up in the same way as with
+ * cfg80211_testmode_alloc_reply_skb() but prepared for an event. As
+ * there, you should simply add data to it that will then end up in the
+ * %NL80211_ATTR_TESTDATA attribute. Again, you must not modify the skb
+ * in any other way.
+ *
+ * When done filling the skb, call cfg80211_testmode_event() with the
+ * skb to send the event.
+ *
+ * Return: An allocated and pre-filled skb. %NULL if any errors happen.
+ */
+static inline struct sk_buff *
+cfg80211_testmode_alloc_event_skb(struct wiphy *wiphy, int approxlen, gfp_t gfp)
+{
+ return __cfg80211_alloc_event_skb(wiphy, NULL, NL80211_CMD_TESTMODE,
+ NL80211_ATTR_TESTDATA, -1,
+ approxlen, gfp);
+}
+
+/**
+ * cfg80211_testmode_event - send the event
+ * @skb: The skb, must have been allocated with
+ * cfg80211_testmode_alloc_event_skb()
+ * @gfp: allocation flags
+ *
+ * This function sends the given @skb, which must have been allocated
+ * by cfg80211_testmode_alloc_event_skb(), as an event. It always
+ * consumes it.
+ */
+static inline void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp)
+{
+ __cfg80211_send_event_skb(skb, gfp);
+}
+
+#define CFG80211_TESTMODE_CMD(cmd) .testmode_cmd = (cmd),
+#define CFG80211_TESTMODE_DUMP(cmd) .testmode_dump = (cmd),
+#else
+#define CFG80211_TESTMODE_CMD(cmd)
+#define CFG80211_TESTMODE_DUMP(cmd)
+#endif
+
+/**
+ * struct cfg80211_connect_resp_params - Connection response params
+ * @status: Status code, %WLAN_STATUS_SUCCESS for successful connection, use
+ * %WLAN_STATUS_UNSPECIFIED_FAILURE if your device cannot give you
+ * the real status code for failures. If this call is used to report a
+ * failure due to a timeout (e.g., not receiving an Authentication frame
+ * from the AP) instead of an explicit rejection by the AP, -1 is used to
+ * indicate that this is a failure, but without a status code.
+ * @timeout_reason is used to report the reason for the timeout in that
+ * case.
+ * @bssid: The BSSID of the AP (may be %NULL)
+ * @bss: Entry of bss to which STA got connected to, can be obtained through
+ * cfg80211_get_bss() (may be %NULL). Only one parameter among @bssid and
+ * @bss needs to be specified.
+ * @req_ie: Association request IEs (may be %NULL)
+ * @req_ie_len: Association request IEs length
+ * @resp_ie: Association response IEs (may be %NULL)
+ * @resp_ie_len: Association response IEs length
+ * @fils_kek: KEK derived from a successful FILS connection (may be %NULL)
+ * @fils_kek_len: Length of @fils_kek in octets
+ * @update_erp_next_seq_num: Boolean value to specify whether the value in
+ * @fils_erp_next_seq_num is valid.
+ * @fils_erp_next_seq_num: The next sequence number to use in ERP message in
+ * FILS Authentication. This value should be specified irrespective of the
+ * status for a FILS connection.
+ * @pmk: A new PMK if derived from a successful FILS connection (may be %NULL).
+ * @pmk_len: Length of @pmk in octets
+ * @pmkid: A new PMKID if derived from a successful FILS connection or the PMKID
+ * used for this FILS connection (may be %NULL).
+ * @timeout_reason: Reason for connection timeout. This is used when the
+ * connection fails due to a timeout instead of an explicit rejection from
+ * the AP. %NL80211_TIMEOUT_UNSPECIFIED is used when the timeout reason is
+ * not known. This value is used only if @status < 0 to indicate that the
+ * failure is due to a timeout and not due to explicit rejection by the AP.
+ * This value is ignored in other cases (@status >= 0).
+ */
+struct cfg80211_connect_resp_params {
+ int status;
+ const u8 *bssid;
+ struct cfg80211_bss *bss;
+ const u8 *req_ie;
+ size_t req_ie_len;
+ const u8 *resp_ie;
+ size_t resp_ie_len;
+ const u8 *fils_kek;
+ size_t fils_kek_len;
+ bool update_erp_next_seq_num;
+ u16 fils_erp_next_seq_num;
+ const u8 *pmk;
+ size_t pmk_len;
+ const u8 *pmkid;
+ enum nl80211_timeout_reason timeout_reason;
+};
+
+/**
+ * cfg80211_connect_done - notify cfg80211 of connection result
+ *
+ * @dev: network device
+ * @params: connection response parameters
+ * @gfp: allocation flags
+ *
+ * It should be called by the underlying driver once execution of the connection
+ * request from connect() has been completed. This is similar to
+ * cfg80211_connect_bss(), but takes a structure pointer for connection response
+ * parameters. Only one of the functions among cfg80211_connect_bss(),
+ * cfg80211_connect_result(), cfg80211_connect_timeout(),
+ * and cfg80211_connect_done() should be called.
+ */
+void cfg80211_connect_done(struct net_device *dev,
+ struct cfg80211_connect_resp_params *params,
+ gfp_t gfp);
+
+/**
+ * cfg80211_connect_bss - notify cfg80211 of connection result
+ *
+ * @dev: network device
+ * @bssid: the BSSID of the AP
+ * @bss: entry of bss to which STA got connected to, can be obtained
+ * through cfg80211_get_bss (may be %NULL)
+ * @req_ie: association request IEs (maybe be %NULL)
+ * @req_ie_len: association request IEs length
+ * @resp_ie: association response IEs (may be %NULL)
+ * @resp_ie_len: assoc response IEs length
+ * @status: status code, %WLAN_STATUS_SUCCESS for successful connection, use
+ * %WLAN_STATUS_UNSPECIFIED_FAILURE if your device cannot give you
+ * the real status code for failures. If this call is used to report a
+ * failure due to a timeout (e.g., not receiving an Authentication frame
+ * from the AP) instead of an explicit rejection by the AP, -1 is used to
+ * indicate that this is a failure, but without a status code.
+ * @timeout_reason is used to report the reason for the timeout in that
+ * case.
+ * @gfp: allocation flags
+ * @timeout_reason: reason for connection timeout. This is used when the
+ * connection fails due to a timeout instead of an explicit rejection from
+ * the AP. %NL80211_TIMEOUT_UNSPECIFIED is used when the timeout reason is
+ * not known. This value is used only if @status < 0 to indicate that the
+ * failure is due to a timeout and not due to explicit rejection by the AP.
+ * This value is ignored in other cases (@status >= 0).
+ *
+ * It should be called by the underlying driver once execution of the connection
+ * request from connect() has been completed. This is similar to
+ * cfg80211_connect_result(), but with the option of identifying the exact bss
+ * entry for the connection. Only one of the functions among
+ * cfg80211_connect_bss(), cfg80211_connect_result(),
+ * cfg80211_connect_timeout(), and cfg80211_connect_done() should be called.
+ */
+static inline void
+cfg80211_connect_bss(struct net_device *dev, const u8 *bssid,
+ struct cfg80211_bss *bss, const u8 *req_ie,
+ size_t req_ie_len, const u8 *resp_ie,
+ size_t resp_ie_len, int status, gfp_t gfp,
+ enum nl80211_timeout_reason timeout_reason)
+{
+ struct cfg80211_connect_resp_params params;
+
+ memset(¶ms, 0, sizeof(params));
+ params.status = status;
+ params.bssid = bssid;
+ params.bss = bss;
+ params.req_ie = req_ie;
+ params.req_ie_len = req_ie_len;
+ params.resp_ie = resp_ie;
+ params.resp_ie_len = resp_ie_len;
+ params.timeout_reason = timeout_reason;
+
+ cfg80211_connect_done(dev, ¶ms, gfp);
+}
+
+/**
+ * cfg80211_connect_result - notify cfg80211 of connection result
+ *
+ * @dev: network device
+ * @bssid: the BSSID of the AP
+ * @req_ie: association request IEs (maybe be %NULL)
+ * @req_ie_len: association request IEs length
+ * @resp_ie: association response IEs (may be %NULL)
+ * @resp_ie_len: assoc response IEs length
+ * @status: status code, %WLAN_STATUS_SUCCESS for successful connection, use
+ * %WLAN_STATUS_UNSPECIFIED_FAILURE if your device cannot give you
+ * the real status code for failures.
+ * @gfp: allocation flags
+ *
+ * It should be called by the underlying driver once execution of the connection
+ * request from connect() has been completed. This is similar to
+ * cfg80211_connect_bss() which allows the exact bss entry to be specified. Only
+ * one of the functions among cfg80211_connect_bss(), cfg80211_connect_result(),
+ * cfg80211_connect_timeout(), and cfg80211_connect_done() should be called.
+ */
+static inline void
+cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
+ const u8 *req_ie, size_t req_ie_len,
+ const u8 *resp_ie, size_t resp_ie_len,
+ u16 status, gfp_t gfp)
+{
+ cfg80211_connect_bss(dev, bssid, NULL, req_ie, req_ie_len, resp_ie,
+ resp_ie_len, status, gfp,
+ NL80211_TIMEOUT_UNSPECIFIED);
+}
+
+/**
+ * cfg80211_connect_timeout - notify cfg80211 of connection timeout
+ *
+ * @dev: network device
+ * @bssid: the BSSID of the AP
+ * @req_ie: association request IEs (maybe be %NULL)
+ * @req_ie_len: association request IEs length
+ * @gfp: allocation flags
+ * @timeout_reason: reason for connection timeout.
+ *
+ * It should be called by the underlying driver whenever connect() has failed
+ * in a sequence where no explicit authentication/association rejection was
+ * received from the AP. This could happen, e.g., due to not being able to send
+ * out the Authentication or Association Request frame or timing out while
+ * waiting for the response. Only one of the functions among
+ * cfg80211_connect_bss(), cfg80211_connect_result(),
+ * cfg80211_connect_timeout(), and cfg80211_connect_done() should be called.
+ */
+static inline void
+cfg80211_connect_timeout(struct net_device *dev, const u8 *bssid,
+ const u8 *req_ie, size_t req_ie_len, gfp_t gfp,
+ enum nl80211_timeout_reason timeout_reason)
+{
+ cfg80211_connect_bss(dev, bssid, NULL, req_ie, req_ie_len, NULL, 0, -1,
+ gfp, timeout_reason);
+}
+
+/**
+ * struct cfg80211_roam_info - driver initiated roaming information
+ *
+ * @channel: the channel of the new AP
+ * @bss: entry of bss to which STA got roamed (may be %NULL if %bssid is set)
+ * @bssid: the BSSID of the new AP (may be %NULL if %bss is set)
+ * @req_ie: association request IEs (maybe be %NULL)
+ * @req_ie_len: association request IEs length
+ * @resp_ie: association response IEs (may be %NULL)
+ * @resp_ie_len: assoc response IEs length
+ */
+struct cfg80211_roam_info {
+ struct ieee80211_channel *channel;
+ struct cfg80211_bss *bss;
+ const u8 *bssid;
+ const u8 *req_ie;
+ size_t req_ie_len;
+ const u8 *resp_ie;
+ size_t resp_ie_len;
+};
+
+/**
+ * cfg80211_roamed - notify cfg80211 of roaming
+ *
+ * @dev: network device
+ * @info: information about the new BSS. struct &cfg80211_roam_info.
+ * @gfp: allocation flags
+ *
+ * This function may be called with the driver passing either the BSSID of the
+ * new AP or passing the bss entry to avoid a race in timeout of the bss entry.
+ * It should be called by the underlying driver whenever it roamed from one AP
+ * to another while connected. Drivers which have roaming implemented in
+ * firmware should pass the bss entry to avoid a race in bss entry timeout where
+ * the bss entry of the new AP is seen in the driver, but gets timed out by the
+ * time it is accessed in __cfg80211_roamed() due to delay in scheduling
+ * rdev->event_work. In case of any failures, the reference is released
+ * either in cfg80211_roamed() or in __cfg80211_romed(), Otherwise, it will be
+ * released while diconneting from the current bss.
+ */
+void cfg80211_roamed(struct net_device *dev, struct cfg80211_roam_info *info,
+ gfp_t gfp);
+
+/**
+ * cfg80211_disconnected - notify cfg80211 that connection was dropped
+ *
+ * @dev: network device
+ * @ie: information elements of the deauth/disassoc frame (may be %NULL)
+ * @ie_len: length of IEs
+ * @reason: reason code for the disconnection, set it to 0 if unknown
+ * @locally_generated: disconnection was requested locally
+ * @gfp: allocation flags
+ *
+ * After it calls this function, the driver should enter an idle state
+ * and not try to connect to any AP any more.
+ */
+void cfg80211_disconnected(struct net_device *dev, u16 reason,
+ const u8 *ie, size_t ie_len,
+ bool locally_generated, gfp_t gfp);
+
+/**
+ * cfg80211_ready_on_channel - notification of remain_on_channel start
+ * @wdev: wireless device
+ * @cookie: the request cookie
+ * @chan: The current channel (from remain_on_channel request)
+ * @duration: Duration in milliseconds that the driver intents to remain on the
+ * channel
+ * @gfp: allocation flags
+ */
+void cfg80211_ready_on_channel(struct wireless_dev *wdev, u64 cookie,
+ struct ieee80211_channel *chan,
+ unsigned int duration, gfp_t gfp);
+
+/**
+ * cfg80211_remain_on_channel_expired - remain_on_channel duration expired
+ * @wdev: wireless device
+ * @cookie: the request cookie
+ * @chan: The current channel (from remain_on_channel request)
+ * @gfp: allocation flags
+ */
+void cfg80211_remain_on_channel_expired(struct wireless_dev *wdev, u64 cookie,
+ struct ieee80211_channel *chan,
+ gfp_t gfp);
+
+
+/**
+ * cfg80211_new_sta - notify userspace about station
+ *
+ * @dev: the netdev
+ * @mac_addr: the station's address
+ * @sinfo: the station information
+ * @gfp: allocation flags
+ */
+void cfg80211_new_sta(struct net_device *dev, const u8 *mac_addr,
+ struct station_info *sinfo, gfp_t gfp);
+
+/**
+ * cfg80211_del_sta_sinfo - notify userspace about deletion of a station
+ * @dev: the netdev
+ * @mac_addr: the station's address
+ * @sinfo: the station information/statistics
+ * @gfp: allocation flags
+ */
+void cfg80211_del_sta_sinfo(struct net_device *dev, const u8 *mac_addr,
+ struct station_info *sinfo, gfp_t gfp);
+
+/**
+ * cfg80211_del_sta - notify userspace about deletion of a station
+ *
+ * @dev: the netdev
+ * @mac_addr: the station's address
+ * @gfp: allocation flags
+ */
+static inline void cfg80211_del_sta(struct net_device *dev,
+ const u8 *mac_addr, gfp_t gfp)
+{
+ cfg80211_del_sta_sinfo(dev, mac_addr, NULL, gfp);
+}
+
+/**
+ * cfg80211_conn_failed - connection request failed notification
+ *
+ * @dev: the netdev
+ * @mac_addr: the station's address
+ * @reason: the reason for connection failure
+ * @gfp: allocation flags
+ *
+ * Whenever a station tries to connect to an AP and if the station
+ * could not connect to the AP as the AP has rejected the connection
+ * for some reasons, this function is called.
+ *
+ * The reason for connection failure can be any of the value from
+ * nl80211_connect_failed_reason enum
+ */
+void cfg80211_conn_failed(struct net_device *dev, const u8 *mac_addr,
+ enum nl80211_connect_failed_reason reason,
+ gfp_t gfp);
+
+/**
+ * cfg80211_rx_mgmt - notification of received, unprocessed management frame
+ * @wdev: wireless device receiving the frame
+ * @freq: Frequency on which the frame was received in MHz
+ * @sig_dbm: signal strength in mBm, or 0 if unknown
+ * @buf: Management frame (header + body)
+ * @len: length of the frame data
+ * @flags: flags, as defined in enum nl80211_rxmgmt_flags
+ *
+ * This function is called whenever an Action frame is received for a station
+ * mode interface, but is not processed in kernel.
+ *
+ * Return: %true if a user space application has registered for this frame.
+ * For action frames, that makes it responsible for rejecting unrecognized
+ * action frames; %false otherwise, in which case for action frames the
+ * driver is responsible for rejecting the frame.
+ */
+bool cfg80211_rx_mgmt(struct wireless_dev *wdev, int freq, int sig_dbm,
+ const u8 *buf, size_t len, u32 flags);
+
+/**
+ * cfg80211_mgmt_tx_status - notification of TX status for management frame
+ * @wdev: wireless device receiving the frame
+ * @cookie: Cookie returned by cfg80211_ops::mgmt_tx()
+ * @buf: Management frame (header + body)
+ * @len: length of the frame data
+ * @ack: Whether frame was acknowledged
+ * @gfp: context flags
+ *
+ * This function is called whenever a management frame was requested to be
+ * transmitted with cfg80211_ops::mgmt_tx() to report the TX status of the
+ * transmission attempt.
+ */
+void cfg80211_mgmt_tx_status(struct wireless_dev *wdev, u64 cookie,
+ const u8 *buf, size_t len, bool ack, gfp_t gfp);
+
+
+/**
+ * cfg80211_cqm_rssi_notify - connection quality monitoring rssi event
+ * @dev: network device
+ * @rssi_event: the triggered RSSI event
+ * @rssi_level: new RSSI level value or 0 if not available
+ * @gfp: context flags
+ *
+ * This function is called when a configured connection quality monitoring
+ * rssi threshold reached event occurs.
+ */
+void cfg80211_cqm_rssi_notify(struct net_device *dev,
+ enum nl80211_cqm_rssi_threshold_event rssi_event,
+ s32 rssi_level, gfp_t gfp);
+
+/**
+ * cfg80211_cqm_pktloss_notify - notify userspace about packetloss to peer
+ * @dev: network device
+ * @peer: peer's MAC address
+ * @num_packets: how many packets were lost -- should be a fixed threshold
+ * but probably no less than maybe 50, or maybe a throughput dependent
+ * threshold (to account for temporary interference)
+ * @gfp: context flags
+ */
+void cfg80211_cqm_pktloss_notify(struct net_device *dev,
+ const u8 *peer, u32 num_packets, gfp_t gfp);
+
+/**
+ * cfg80211_cqm_txe_notify - TX error rate event
+ * @dev: network device
+ * @peer: peer's MAC address
+ * @num_packets: how many packets were lost
+ * @rate: % of packets which failed transmission
+ * @intvl: interval (in s) over which the TX failure threshold was breached.
+ * @gfp: context flags
+ *
+ * Notify userspace when configured % TX failures over number of packets in a
+ * given interval is exceeded.
+ */
+void cfg80211_cqm_txe_notify(struct net_device *dev, const u8 *peer,
+ u32 num_packets, u32 rate, u32 intvl, gfp_t gfp);
+
+/**
+ * cfg80211_cqm_beacon_loss_notify - beacon loss event
+ * @dev: network device
+ * @gfp: context flags
+ *
+ * Notify userspace about beacon loss from the connected AP.
+ */
+void cfg80211_cqm_beacon_loss_notify(struct net_device *dev, gfp_t gfp);
+
+/**
+ * cfg80211_radar_event - radar detection event
+ * @wiphy: the wiphy
+ * @chandef: chandef for the current channel
+ * @gfp: context flags
+ *
+ * This function is called when a radar is detected on the current chanenl.
+ */
+void cfg80211_radar_event(struct wiphy *wiphy,
+ struct cfg80211_chan_def *chandef, gfp_t gfp);
+
+/**
+ * cfg80211_cac_event - Channel availability check (CAC) event
+ * @netdev: network device
+ * @chandef: chandef for the current channel
+ * @event: type of event
+ * @gfp: context flags
+ *
+ * This function is called when a Channel availability check (CAC) is finished
+ * or aborted. This must be called to notify the completion of a CAC process,
+ * also by full-MAC drivers.
+ */
+void cfg80211_cac_event(struct net_device *netdev,
+ const struct cfg80211_chan_def *chandef,
+ enum nl80211_radar_event event, gfp_t gfp);
+
+
+/**
+ * cfg80211_gtk_rekey_notify - notify userspace about driver rekeying
+ * @dev: network device
+ * @bssid: BSSID of AP (to avoid races)
+ * @replay_ctr: new replay counter
+ * @gfp: allocation flags
+ */
+void cfg80211_gtk_rekey_notify(struct net_device *dev, const u8 *bssid,
+ const u8 *replay_ctr, gfp_t gfp);
+
+/**
+ * cfg80211_pmksa_candidate_notify - notify about PMKSA caching candidate
+ * @dev: network device
+ * @index: candidate index (the smaller the index, the higher the priority)
+ * @bssid: BSSID of AP
+ * @preauth: Whether AP advertises support for RSN pre-authentication
+ * @gfp: allocation flags
+ */
+void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index,
+ const u8 *bssid, bool preauth, gfp_t gfp);
+
+/**
+ * cfg80211_rx_spurious_frame - inform userspace about a spurious frame
+ * @dev: The device the frame matched to
+ * @addr: the transmitter address
+ * @gfp: context flags
+ *
+ * This function is used in AP mode (only!) to inform userspace that
+ * a spurious class 3 frame was received, to be able to deauth the
+ * sender.
+ * Return: %true if the frame was passed to userspace (or this failed
+ * for a reason other than not having a subscription.)
+ */
+bool cfg80211_rx_spurious_frame(struct net_device *dev,
+ const u8 *addr, gfp_t gfp);
+
+/**
+ * cfg80211_rx_unexpected_4addr_frame - inform about unexpected WDS frame
+ * @dev: The device the frame matched to
+ * @addr: the transmitter address
+ * @gfp: context flags
+ *
+ * This function is used in AP mode (only!) to inform userspace that
+ * an associated station sent a 4addr frame but that wasn't expected.
+ * It is allowed and desirable to send this event only once for each
+ * station to avoid event flooding.
+ * Return: %true if the frame was passed to userspace (or this failed
+ * for a reason other than not having a subscription.)
+ */
+bool cfg80211_rx_unexpected_4addr_frame(struct net_device *dev,
+ const u8 *addr, gfp_t gfp);
+
+/**
+ * cfg80211_probe_status - notify userspace about probe status
+ * @dev: the device the probe was sent on
+ * @addr: the address of the peer
+ * @cookie: the cookie filled in @probe_client previously
+ * @acked: indicates whether probe was acked or not
+ * @gfp: allocation flags
+ */
+void cfg80211_probe_status(struct net_device *dev, const u8 *addr,
+ u64 cookie, bool acked, gfp_t gfp);
+
+/**
+ * cfg80211_report_obss_beacon - report beacon from other APs
+ * @wiphy: The wiphy that received the beacon
+ * @frame: the frame
+ * @len: length of the frame
+ * @freq: frequency the frame was received on
+ * @sig_dbm: signal strength in mBm, or 0 if unknown
+ *
+ * Use this function to report to userspace when a beacon was
+ * received. It is not useful to call this when there is no
+ * netdev that is in AP/GO mode.
+ */
+void cfg80211_report_obss_beacon(struct wiphy *wiphy,
+ const u8 *frame, size_t len,
+ int freq, int sig_dbm);
+
+/**
+ * cfg80211_reg_can_beacon - check if beaconing is allowed
+ * @wiphy: the wiphy
+ * @chandef: the channel definition
+ * @iftype: interface type
+ *
+ * Return: %true if there is no secondary channel or the secondary channel(s)
+ * can be used for beaconing (i.e. is not a radar channel etc.)
+ */
+bool cfg80211_reg_can_beacon(struct wiphy *wiphy,
+ struct cfg80211_chan_def *chandef,
+ enum nl80211_iftype iftype);
+
+/**
+ * cfg80211_reg_can_beacon_relax - check if beaconing is allowed with relaxation
+ * @wiphy: the wiphy
+ * @chandef: the channel definition
+ * @iftype: interface type
+ *
+ * Return: %true if there is no secondary channel or the secondary channel(s)
+ * can be used for beaconing (i.e. is not a radar channel etc.). This version
+ * also checks if IR-relaxation conditions apply, to allow beaconing under
+ * more permissive conditions.
+ *
+ * Requires the RTNL to be held.
+ */
+bool cfg80211_reg_can_beacon_relax(struct wiphy *wiphy,
+ struct cfg80211_chan_def *chandef,
+ enum nl80211_iftype iftype);
+
+/*
+ * cfg80211_ch_switch_notify - update wdev channel and notify userspace
+ * @dev: the device which switched channels
+ * @chandef: the new channel definition
+ *
+ * Caller must acquire wdev_lock, therefore must only be called from sleepable
+ * driver context!
+ */
+void cfg80211_ch_switch_notify(struct net_device *dev,
+ struct cfg80211_chan_def *chandef);
+
+/*
+ * cfg80211_ch_switch_started_notify - notify channel switch start
+ * @dev: the device on which the channel switch started
+ * @chandef: the future channel definition
+ * @count: the number of TBTTs until the channel switch happens
+ *
+ * Inform the userspace about the channel switch that has just
+ * started, so that it can take appropriate actions (eg. starting
+ * channel switch on other vifs), if necessary.
+ */
+void cfg80211_ch_switch_started_notify(struct net_device *dev,
+ struct cfg80211_chan_def *chandef,
+ u8 count);
+
+/**
+ * ieee80211_operating_class_to_band - convert operating class to band
+ *
+ * @operating_class: the operating class to convert
+ * @band: band pointer to fill
+ *
+ * Returns %true if the conversion was successful, %false otherwise.
+ */
+bool ieee80211_operating_class_to_band(u8 operating_class,
+ enum nl80211_band *band);
+
+/**
+ * ieee80211_chandef_to_operating_class - convert chandef to operation class
+ *
+ * @chandef: the chandef to convert
+ * @op_class: a pointer to the resulting operating class
+ *
+ * Returns %true if the conversion was successful, %false otherwise.
+ */
+bool ieee80211_chandef_to_operating_class(struct cfg80211_chan_def *chandef,
+ u8 *op_class);
+
+/*
+ * cfg80211_tdls_oper_request - request userspace to perform TDLS operation
+ * @dev: the device on which the operation is requested
+ * @peer: the MAC address of the peer device
+ * @oper: the requested TDLS operation (NL80211_TDLS_SETUP or
+ * NL80211_TDLS_TEARDOWN)
+ * @reason_code: the reason code for teardown request
+ * @gfp: allocation flags
+ *
+ * This function is used to request userspace to perform TDLS operation that
+ * requires knowledge of keys, i.e., link setup or teardown when the AP
+ * connection uses encryption. This is optional mechanism for the driver to use
+ * if it can automatically determine when a TDLS link could be useful (e.g.,
+ * based on traffic and signal strength for a peer).
+ */
+void cfg80211_tdls_oper_request(struct net_device *dev, const u8 *peer,
+ enum nl80211_tdls_operation oper,
+ u16 reason_code, gfp_t gfp);
+
+/*
+ * cfg80211_calculate_bitrate - calculate actual bitrate (in 100Kbps units)
+ * @rate: given rate_info to calculate bitrate from
+ *
+ * return 0 if MCS index >= 32
+ */
+u32 cfg80211_calculate_bitrate(struct rate_info *rate);
+
+/**
+ * cfg80211_unregister_wdev - remove the given wdev
+ * @wdev: struct wireless_dev to remove
+ *
+ * Call this function only for wdevs that have no netdev assigned,
+ * e.g. P2P Devices. It removes the device from the list so that
+ * it can no longer be used. It is necessary to call this function
+ * even when cfg80211 requests the removal of the interface by
+ * calling the del_virtual_intf() callback. The function must also
+ * be called when the driver wishes to unregister the wdev, e.g.
+ * when the device is unbound from the driver.
+ *
+ * Requires the RTNL to be held.
+ */
+void cfg80211_unregister_wdev(struct wireless_dev *wdev);
+
+/**
+ * struct cfg80211_ft_event - FT Information Elements
+ * @ies: FT IEs
+ * @ies_len: length of the FT IE in bytes
+ * @target_ap: target AP's MAC address
+ * @ric_ies: RIC IE
+ * @ric_ies_len: length of the RIC IE in bytes
+ */
+struct cfg80211_ft_event_params {
+ const u8 *ies;
+ size_t ies_len;
+ const u8 *target_ap;
+ const u8 *ric_ies;
+ size_t ric_ies_len;
+};
+
+/**
+ * cfg80211_ft_event - notify userspace about FT IE and RIC IE
+ * @netdev: network device
+ * @ft_event: IE information
+ */
+void cfg80211_ft_event(struct net_device *netdev,
+ struct cfg80211_ft_event_params *ft_event);
+
+/**
+ * cfg80211_get_p2p_attr - find and copy a P2P attribute from IE buffer
+ * @ies: the input IE buffer
+ * @len: the input length
+ * @attr: the attribute ID to find
+ * @buf: output buffer, can be %NULL if the data isn't needed, e.g.
+ * if the function is only called to get the needed buffer size
+ * @bufsize: size of the output buffer
+ *
+ * The function finds a given P2P attribute in the (vendor) IEs and
+ * copies its contents to the given buffer.
+ *
+ * Return: A negative error code (-%EILSEQ or -%ENOENT) if the data is
+ * malformed or the attribute can't be found (respectively), or the
+ * length of the found attribute (which can be zero).
+ */
+int cfg80211_get_p2p_attr(const u8 *ies, unsigned int len,
+ enum ieee80211_p2p_attr_id attr,
+ u8 *buf, unsigned int bufsize);
+
+/**
+ * ieee80211_ie_split_ric - split an IE buffer according to ordering (with RIC)
+ * @ies: the IE buffer
+ * @ielen: the length of the IE buffer
+ * @ids: an array with element IDs that are allowed before
+ * the split
+ * @n_ids: the size of the element ID array
+ * @after_ric: array IE types that come after the RIC element
+ * @n_after_ric: size of the @after_ric array
+ * @offset: offset where to start splitting in the buffer
+ *
+ * This function splits an IE buffer by updating the @offset
+ * variable to point to the location where the buffer should be
+ * split.
+ *
+ * It assumes that the given IE buffer is well-formed, this
+ * has to be guaranteed by the caller!
+ *
+ * It also assumes that the IEs in the buffer are ordered
+ * correctly, if not the result of using this function will not
+ * be ordered correctly either, i.e. it does no reordering.
+ *
+ * The function returns the offset where the next part of the
+ * buffer starts, which may be @ielen if the entire (remainder)
+ * of the buffer should be used.
+ */
+size_t ieee80211_ie_split_ric(const u8 *ies, size_t ielen,
+ const u8 *ids, int n_ids,
+ const u8 *after_ric, int n_after_ric,
+ size_t offset);
+
+/**
+ * ieee80211_ie_split - split an IE buffer according to ordering
+ * @ies: the IE buffer
+ * @ielen: the length of the IE buffer
+ * @ids: an array with element IDs that are allowed before
+ * the split
+ * @n_ids: the size of the element ID array
+ * @offset: offset where to start splitting in the buffer
+ *
+ * This function splits an IE buffer by updating the @offset
+ * variable to point to the location where the buffer should be
+ * split.
+ *
+ * It assumes that the given IE buffer is well-formed, this
+ * has to be guaranteed by the caller!
+ *
+ * It also assumes that the IEs in the buffer are ordered
+ * correctly, if not the result of using this function will not
+ * be ordered correctly either, i.e. it does no reordering.
+ *
+ * The function returns the offset where the next part of the
+ * buffer starts, which may be @ielen if the entire (remainder)
+ * of the buffer should be used.
+ */
+static inline size_t ieee80211_ie_split(const u8 *ies, size_t ielen,
+ const u8 *ids, int n_ids, size_t offset)
+{
+ return ieee80211_ie_split_ric(ies, ielen, ids, n_ids, NULL, 0, offset);
+}
+
+/**
+ * cfg80211_report_wowlan_wakeup - report wakeup from WoWLAN
+ * @wdev: the wireless device reporting the wakeup
+ * @wakeup: the wakeup report
+ * @gfp: allocation flags
+ *
+ * This function reports that the given device woke up. If it
+ * caused the wakeup, report the reason(s), otherwise you may
+ * pass %NULL as the @wakeup parameter to advertise that something
+ * else caused the wakeup.
+ */
+void cfg80211_report_wowlan_wakeup(struct wireless_dev *wdev,
+ struct cfg80211_wowlan_wakeup *wakeup,
+ gfp_t gfp);
+
+/**
+ * cfg80211_crit_proto_stopped() - indicate critical protocol stopped by driver.
+ *
+ * @wdev: the wireless device for which critical protocol is stopped.
+ * @gfp: allocation flags
+ *
+ * This function can be called by the driver to indicate it has reverted
+ * operation back to normal. One reason could be that the duration given
+ * by .crit_proto_start() has expired.
+ */
+void cfg80211_crit_proto_stopped(struct wireless_dev *wdev, gfp_t gfp);
+
+/**
+ * ieee80211_get_num_supported_channels - get number of channels device has
+ * @wiphy: the wiphy
+ *
+ * Return: the number of channels supported by the device.
+ */
+unsigned int ieee80211_get_num_supported_channels(struct wiphy *wiphy);
+
+/**
+ * cfg80211_check_combinations - check interface combinations
+ *
+ * @wiphy: the wiphy
+ * @params: the interface combinations parameter
+ *
+ * This function can be called by the driver to check whether a
+ * combination of interfaces and their types are allowed according to
+ * the interface combinations.
+ */
+int cfg80211_check_combinations(struct wiphy *wiphy,
+ struct iface_combination_params *params);
+
+/**
+ * cfg80211_iter_combinations - iterate over matching combinations
+ *
+ * @wiphy: the wiphy
+ * @params: the interface combinations parameter
+ * @iter: function to call for each matching combination
+ * @data: pointer to pass to iter function
+ *
+ * This function can be called by the driver to check what possible
+ * combinations it fits in at a given moment, e.g. for channel switching
+ * purposes.
+ */
+int cfg80211_iter_combinations(struct wiphy *wiphy,
+ struct iface_combination_params *params,
+ void (*iter)(const struct ieee80211_iface_combination *c,
+ void *data),
+ void *data);
+
+/*
+ * cfg80211_stop_iface - trigger interface disconnection
+ *
+ * @wiphy: the wiphy
+ * @wdev: wireless device
+ * @gfp: context flags
+ *
+ * Trigger interface to be stopped as if AP was stopped, IBSS/mesh left, STA
+ * disconnected.
+ *
+ * Note: This doesn't need any locks and is asynchronous.
+ */
+void cfg80211_stop_iface(struct wiphy *wiphy, struct wireless_dev *wdev,
+ gfp_t gfp);
+
+/**
+ * cfg80211_shutdown_all_interfaces - shut down all interfaces for a wiphy
+ * @wiphy: the wiphy to shut down
+ *
+ * This function shuts down all interfaces belonging to this wiphy by
+ * calling dev_close() (and treating non-netdev interfaces as needed).
+ * It shouldn't really be used unless there are some fatal device errors
+ * that really can't be recovered in any other way.
+ *
+ * Callers must hold the RTNL and be able to deal with callbacks into
+ * the driver while the function is running.
+ */
+void cfg80211_shutdown_all_interfaces(struct wiphy *wiphy);
+
+/**
+ * wiphy_ext_feature_set - set the extended feature flag
+ *
+ * @wiphy: the wiphy to modify.
+ * @ftidx: extended feature bit index.
+ *
+ * The extended features are flagged in multiple bytes (see
+ * &struct wiphy.@ext_features)
+ */
+static inline void wiphy_ext_feature_set(struct wiphy *wiphy,
+ enum nl80211_ext_feature_index ftidx)
+{
+ u8 *ft_byte;
+
+ ft_byte = &wiphy->ext_features[ftidx / 8];
+ *ft_byte |= BIT(ftidx % 8);
+}
+
+/**
+ * wiphy_ext_feature_isset - check the extended feature flag
+ *
+ * @wiphy: the wiphy to modify.
+ * @ftidx: extended feature bit index.
+ *
+ * The extended features are flagged in multiple bytes (see
+ * &struct wiphy.@ext_features)
+ */
+static inline bool
+wiphy_ext_feature_isset(struct wiphy *wiphy,
+ enum nl80211_ext_feature_index ftidx)
+{
+ u8 ft_byte;
+
+ ft_byte = wiphy->ext_features[ftidx / 8];
+ return (ft_byte & BIT(ftidx % 8)) != 0;
+}
+
+/**
+ * cfg80211_free_nan_func - free NAN function
+ * @f: NAN function that should be freed
+ *
+ * Frees all the NAN function and all it's allocated members.
+ */
+void cfg80211_free_nan_func(struct cfg80211_nan_func *f);
+
+/**
+ * struct cfg80211_nan_match_params - NAN match parameters
+ * @type: the type of the function that triggered a match. If it is
+ * %NL80211_NAN_FUNC_SUBSCRIBE it means that we replied to a subscriber.
+ * If it is %NL80211_NAN_FUNC_PUBLISH, it means that we got a discovery
+ * result.
+ * If it is %NL80211_NAN_FUNC_FOLLOW_UP, we received a follow up.
+ * @inst_id: the local instance id
+ * @peer_inst_id: the instance id of the peer's function
+ * @addr: the MAC address of the peer
+ * @info_len: the length of the &info
+ * @info: the Service Specific Info from the peer (if any)
+ * @cookie: unique identifier of the corresponding function
+ */
+struct cfg80211_nan_match_params {
+ enum nl80211_nan_function_type type;
+ u8 inst_id;
+ u8 peer_inst_id;
+ const u8 *addr;
+ u8 info_len;
+ const u8 *info;
+ u64 cookie;
+};
+
+/**
+ * cfg80211_nan_match - report a match for a NAN function.
+ * @wdev: the wireless device reporting the match
+ * @match: match notification parameters
+ * @gfp: allocation flags
+ *
+ * This function reports that the a NAN function had a match. This
+ * can be a subscribe that had a match or a solicited publish that
+ * was sent. It can also be a follow up that was received.
+ */
+void cfg80211_nan_match(struct wireless_dev *wdev,
+ struct cfg80211_nan_match_params *match, gfp_t gfp);
+
+/**
+ * cfg80211_nan_func_terminated - notify about NAN function termination.
+ *
+ * @wdev: the wireless device reporting the match
+ * @inst_id: the local instance id
+ * @reason: termination reason (one of the NL80211_NAN_FUNC_TERM_REASON_*)
+ * @cookie: unique NAN function identifier
+ * @gfp: allocation flags
+ *
+ * This function reports that the a NAN function is terminated.
+ */
+void cfg80211_nan_func_terminated(struct wireless_dev *wdev,
+ u8 inst_id,
+ enum nl80211_nan_func_term_reason reason,
+ u64 cookie, gfp_t gfp);
+
+/* ethtool helper */
+void cfg80211_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info);
+
+/* Logging, debugging and troubleshooting/diagnostic helpers. */
+
+/* wiphy_printk helpers, similar to dev_printk */
+
+#define wiphy_printk(level, wiphy, format, args...) \
+ dev_printk(level, &(wiphy)->dev, format, ##args)
+#define wiphy_emerg(wiphy, format, args...) \
+ dev_emerg(&(wiphy)->dev, format, ##args)
+#define wiphy_alert(wiphy, format, args...) \
+ dev_alert(&(wiphy)->dev, format, ##args)
+#define wiphy_crit(wiphy, format, args...) \
+ dev_crit(&(wiphy)->dev, format, ##args)
+#define wiphy_err(wiphy, format, args...) \
+ dev_err(&(wiphy)->dev, format, ##args)
+#define wiphy_warn(wiphy, format, args...) \
+ dev_warn(&(wiphy)->dev, format, ##args)
+#define wiphy_notice(wiphy, format, args...) \
+ dev_notice(&(wiphy)->dev, format, ##args)
+#define wiphy_info(wiphy, format, args...) \
+ dev_info(&(wiphy)->dev, format, ##args)
+
+#define wiphy_debug(wiphy, format, args...) \
+ wiphy_printk(KERN_DEBUG, wiphy, format, ##args)
+
+#define wiphy_dbg(wiphy, format, args...) \
+ dev_dbg(&(wiphy)->dev, format, ##args)
+
+#if defined(VERBOSE_DEBUG)
+#define wiphy_vdbg wiphy_dbg
+#else
+#define wiphy_vdbg(wiphy, format, args...) \
+({ \
+ if (0) \
+ wiphy_printk(KERN_DEBUG, wiphy, format, ##args); \
+ 0; \
+})
+#endif
+
+/*
+ * wiphy_WARN() acts like wiphy_printk(), but with the key difference
+ * of using a WARN/WARN_ON to get the message out, including the
+ * file/line information and a backtrace.
+ */
+#define wiphy_WARN(wiphy, format, args...) \
+ WARN(1, "wiphy: %s\n" format, wiphy_name(wiphy), ##args);
+
+#endif /* __NET_CFG80211_H */
diff --git a/include/net/codel.h b/include/net/codel.h
new file mode 100644
index 0000000..a6e428f
--- /dev/null
+++ b/include/net/codel.h
@@ -0,0 +1,164 @@
+#ifndef __NET_SCHED_CODEL_H
+#define __NET_SCHED_CODEL_H
+
+/*
+ * Codel - The Controlled-Delay Active Queue Management algorithm
+ *
+ * Copyright (C) 2011-2012 Kathleen Nichols <nichols@pollere.com>
+ * Copyright (C) 2011-2012 Van Jacobson <van@pollere.net>
+ * Copyright (C) 2012 Michael D. Taht <dave.taht@bufferbloat.net>
+ * Copyright (C) 2012,2015 Eric Dumazet <edumazet@google.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The names of the authors may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/ktime.h>
+#include <linux/skbuff.h>
+#include <net/pkt_sched.h>
+#include <net/inet_ecn.h>
+
+/* Controlling Queue Delay (CoDel) algorithm
+ * =========================================
+ * Source : Kathleen Nichols and Van Jacobson
+ * http://queue.acm.org/detail.cfm?id=2209336
+ *
+ * Implemented on linux by Dave Taht and Eric Dumazet
+ */
+
+
+/* CoDel uses a 1024 nsec clock, encoded in u32
+ * This gives a range of 2199 seconds, because of signed compares
+ */
+typedef u32 codel_time_t;
+typedef s32 codel_tdiff_t;
+#define CODEL_SHIFT 10
+#define MS2TIME(a) ((a * NSEC_PER_MSEC) >> CODEL_SHIFT)
+
+static inline codel_time_t codel_get_time(void)
+{
+ u64 ns = ktime_get_ns();
+
+ return ns >> CODEL_SHIFT;
+}
+
+/* Dealing with timer wrapping, according to RFC 1982, as desc in wikipedia:
+ * https://en.wikipedia.org/wiki/Serial_number_arithmetic#General_Solution
+ * codel_time_after(a,b) returns true if the time a is after time b.
+ */
+#define codel_time_after(a, b) \
+ (typecheck(codel_time_t, a) && \
+ typecheck(codel_time_t, b) && \
+ ((s32)((a) - (b)) > 0))
+#define codel_time_before(a, b) codel_time_after(b, a)
+
+#define codel_time_after_eq(a, b) \
+ (typecheck(codel_time_t, a) && \
+ typecheck(codel_time_t, b) && \
+ ((s32)((a) - (b)) >= 0))
+#define codel_time_before_eq(a, b) codel_time_after_eq(b, a)
+
+static inline u32 codel_time_to_us(codel_time_t val)
+{
+ u64 valns = ((u64)val << CODEL_SHIFT);
+
+ do_div(valns, NSEC_PER_USEC);
+ return (u32)valns;
+}
+
+/**
+ * struct codel_params - contains codel parameters
+ * @target: target queue size (in time units)
+ * @ce_threshold: threshold for marking packets with ECN CE
+ * @interval: width of moving time window
+ * @mtu: device mtu, or minimal queue backlog in bytes.
+ * @ecn: is Explicit Congestion Notification enabled
+ */
+struct codel_params {
+ codel_time_t target;
+ codel_time_t ce_threshold;
+ codel_time_t interval;
+ u32 mtu;
+ bool ecn;
+};
+
+/**
+ * struct codel_vars - contains codel variables
+ * @count: how many drops we've done since the last time we
+ * entered dropping state
+ * @lastcount: count at entry to dropping state
+ * @dropping: set to true if in dropping state
+ * @rec_inv_sqrt: reciprocal value of sqrt(count) >> 1
+ * @first_above_time: when we went (or will go) continuously above target
+ * for interval
+ * @drop_next: time to drop next packet, or when we dropped last
+ * @ldelay: sojourn time of last dequeued packet
+ */
+struct codel_vars {
+ u32 count;
+ u32 lastcount;
+ bool dropping;
+ u16 rec_inv_sqrt;
+ codel_time_t first_above_time;
+ codel_time_t drop_next;
+ codel_time_t ldelay;
+};
+
+#define REC_INV_SQRT_BITS (8 * sizeof(u16)) /* or sizeof_in_bits(rec_inv_sqrt) */
+/* needed shift to get a Q0.32 number from rec_inv_sqrt */
+#define REC_INV_SQRT_SHIFT (32 - REC_INV_SQRT_BITS)
+
+/**
+ * struct codel_stats - contains codel shared variables and stats
+ * @maxpacket: largest packet we've seen so far
+ * @drop_count: temp count of dropped packets in dequeue()
+ * @drop_len: bytes of dropped packets in dequeue()
+ * ecn_mark: number of packets we ECN marked instead of dropping
+ * ce_mark: number of packets CE marked because sojourn time was above ce_threshold
+ */
+struct codel_stats {
+ u32 maxpacket;
+ u32 drop_count;
+ u32 drop_len;
+ u32 ecn_mark;
+ u32 ce_mark;
+};
+
+#define CODEL_DISABLED_THRESHOLD INT_MAX
+
+typedef u32 (*codel_skb_len_t)(const struct sk_buff *skb);
+typedef codel_time_t (*codel_skb_time_t)(const struct sk_buff *skb);
+typedef void (*codel_skb_drop_t)(struct sk_buff *skb, void *ctx);
+typedef struct sk_buff * (*codel_skb_dequeue_t)(struct codel_vars *vars,
+ void *ctx);
+
+#endif
diff --git a/include/net/codel_impl.h b/include/net/codel_impl.h
new file mode 100644
index 0000000..d289b91
--- /dev/null
+++ b/include/net/codel_impl.h
@@ -0,0 +1,255 @@
+#ifndef __NET_SCHED_CODEL_IMPL_H
+#define __NET_SCHED_CODEL_IMPL_H
+
+/*
+ * Codel - The Controlled-Delay Active Queue Management algorithm
+ *
+ * Copyright (C) 2011-2012 Kathleen Nichols <nichols@pollere.com>
+ * Copyright (C) 2011-2012 Van Jacobson <van@pollere.net>
+ * Copyright (C) 2012 Michael D. Taht <dave.taht@bufferbloat.net>
+ * Copyright (C) 2012,2015 Eric Dumazet <edumazet@google.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The names of the authors may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+/* Controlling Queue Delay (CoDel) algorithm
+ * =========================================
+ * Source : Kathleen Nichols and Van Jacobson
+ * http://queue.acm.org/detail.cfm?id=2209336
+ *
+ * Implemented on linux by Dave Taht and Eric Dumazet
+ */
+
+static void codel_params_init(struct codel_params *params)
+{
+ params->interval = MS2TIME(100);
+ params->target = MS2TIME(5);
+ params->ce_threshold = CODEL_DISABLED_THRESHOLD;
+ params->ecn = false;
+}
+
+static void codel_vars_init(struct codel_vars *vars)
+{
+ memset(vars, 0, sizeof(*vars));
+}
+
+static void codel_stats_init(struct codel_stats *stats)
+{
+ stats->maxpacket = 0;
+}
+
+/*
+ * http://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Iterative_methods_for_reciprocal_square_roots
+ * new_invsqrt = (invsqrt / 2) * (3 - count * invsqrt^2)
+ *
+ * Here, invsqrt is a fixed point number (< 1.0), 32bit mantissa, aka Q0.32
+ */
+static void codel_Newton_step(struct codel_vars *vars)
+{
+ u32 invsqrt = ((u32)vars->rec_inv_sqrt) << REC_INV_SQRT_SHIFT;
+ u32 invsqrt2 = ((u64)invsqrt * invsqrt) >> 32;
+ u64 val = (3LL << 32) - ((u64)vars->count * invsqrt2);
+
+ val >>= 2; /* avoid overflow in following multiply */
+ val = (val * invsqrt) >> (32 - 2 + 1);
+
+ vars->rec_inv_sqrt = val >> REC_INV_SQRT_SHIFT;
+}
+
+/*
+ * CoDel control_law is t + interval/sqrt(count)
+ * We maintain in rec_inv_sqrt the reciprocal value of sqrt(count) to avoid
+ * both sqrt() and divide operation.
+ */
+static codel_time_t codel_control_law(codel_time_t t,
+ codel_time_t interval,
+ u32 rec_inv_sqrt)
+{
+ return t + reciprocal_scale(interval, rec_inv_sqrt << REC_INV_SQRT_SHIFT);
+}
+
+static bool codel_should_drop(const struct sk_buff *skb,
+ void *ctx,
+ struct codel_vars *vars,
+ struct codel_params *params,
+ struct codel_stats *stats,
+ codel_skb_len_t skb_len_func,
+ codel_skb_time_t skb_time_func,
+ u32 *backlog,
+ codel_time_t now)
+{
+ bool ok_to_drop;
+ u32 skb_len;
+
+ if (!skb) {
+ vars->first_above_time = 0;
+ return false;
+ }
+
+ skb_len = skb_len_func(skb);
+ vars->ldelay = now - skb_time_func(skb);
+
+ if (unlikely(skb_len > stats->maxpacket))
+ stats->maxpacket = skb_len;
+
+ if (codel_time_before(vars->ldelay, params->target) ||
+ *backlog <= params->mtu) {
+ /* went below - stay below for at least interval */
+ vars->first_above_time = 0;
+ return false;
+ }
+ ok_to_drop = false;
+ if (vars->first_above_time == 0) {
+ /* just went above from below. If we stay above
+ * for at least interval we'll say it's ok to drop
+ */
+ vars->first_above_time = now + params->interval;
+ } else if (codel_time_after(now, vars->first_above_time)) {
+ ok_to_drop = true;
+ }
+ return ok_to_drop;
+}
+
+static struct sk_buff *codel_dequeue(void *ctx,
+ u32 *backlog,
+ struct codel_params *params,
+ struct codel_vars *vars,
+ struct codel_stats *stats,
+ codel_skb_len_t skb_len_func,
+ codel_skb_time_t skb_time_func,
+ codel_skb_drop_t drop_func,
+ codel_skb_dequeue_t dequeue_func)
+{
+ struct sk_buff *skb = dequeue_func(vars, ctx);
+ codel_time_t now;
+ bool drop;
+
+ if (!skb) {
+ vars->dropping = false;
+ return skb;
+ }
+ now = codel_get_time();
+ drop = codel_should_drop(skb, ctx, vars, params, stats,
+ skb_len_func, skb_time_func, backlog, now);
+ if (vars->dropping) {
+ if (!drop) {
+ /* sojourn time below target - leave dropping state */
+ vars->dropping = false;
+ } else if (codel_time_after_eq(now, vars->drop_next)) {
+ /* It's time for the next drop. Drop the current
+ * packet and dequeue the next. The dequeue might
+ * take us out of dropping state.
+ * If not, schedule the next drop.
+ * A large backlog might result in drop rates so high
+ * that the next drop should happen now,
+ * hence the while loop.
+ */
+ while (vars->dropping &&
+ codel_time_after_eq(now, vars->drop_next)) {
+ vars->count++; /* dont care of possible wrap
+ * since there is no more divide
+ */
+ codel_Newton_step(vars);
+ if (params->ecn && INET_ECN_set_ce(skb)) {
+ stats->ecn_mark++;
+ vars->drop_next =
+ codel_control_law(vars->drop_next,
+ params->interval,
+ vars->rec_inv_sqrt);
+ goto end;
+ }
+ stats->drop_len += skb_len_func(skb);
+ drop_func(skb, ctx);
+ stats->drop_count++;
+ skb = dequeue_func(vars, ctx);
+ if (!codel_should_drop(skb, ctx,
+ vars, params, stats,
+ skb_len_func,
+ skb_time_func,
+ backlog, now)) {
+ /* leave dropping state */
+ vars->dropping = false;
+ } else {
+ /* and schedule the next drop */
+ vars->drop_next =
+ codel_control_law(vars->drop_next,
+ params->interval,
+ vars->rec_inv_sqrt);
+ }
+ }
+ }
+ } else if (drop) {
+ u32 delta;
+
+ if (params->ecn && INET_ECN_set_ce(skb)) {
+ stats->ecn_mark++;
+ } else {
+ stats->drop_len += skb_len_func(skb);
+ drop_func(skb, ctx);
+ stats->drop_count++;
+
+ skb = dequeue_func(vars, ctx);
+ drop = codel_should_drop(skb, ctx, vars, params,
+ stats, skb_len_func,
+ skb_time_func, backlog, now);
+ }
+ vars->dropping = true;
+ /* if min went above target close to when we last went below it
+ * assume that the drop rate that controlled the queue on the
+ * last cycle is a good starting point to control it now.
+ */
+ delta = vars->count - vars->lastcount;
+ if (delta > 1 &&
+ codel_time_before(now - vars->drop_next,
+ 16 * params->interval)) {
+ vars->count = delta;
+ /* we dont care if rec_inv_sqrt approximation
+ * is not very precise :
+ * Next Newton steps will correct it quadratically.
+ */
+ codel_Newton_step(vars);
+ } else {
+ vars->count = 1;
+ vars->rec_inv_sqrt = ~0U >> REC_INV_SQRT_SHIFT;
+ }
+ vars->lastcount = vars->count;
+ vars->drop_next = codel_control_law(now, params->interval,
+ vars->rec_inv_sqrt);
+ }
+end:
+ if (skb && codel_time_after(vars->ldelay, params->ce_threshold) &&
+ INET_ECN_set_ce(skb))
+ stats->ce_mark++;
+ return skb;
+}
+
+#endif
diff --git a/include/net/codel_qdisc.h b/include/net/codel_qdisc.h
new file mode 100644
index 0000000..098630f
--- /dev/null
+++ b/include/net/codel_qdisc.h
@@ -0,0 +1,74 @@
+#ifndef __NET_SCHED_CODEL_QDISC_H
+#define __NET_SCHED_CODEL_QDISC_H
+
+/*
+ * Codel - The Controlled-Delay Active Queue Management algorithm
+ *
+ * Copyright (C) 2011-2012 Kathleen Nichols <nichols@pollere.com>
+ * Copyright (C) 2011-2012 Van Jacobson <van@pollere.net>
+ * Copyright (C) 2012 Michael D. Taht <dave.taht@bufferbloat.net>
+ * Copyright (C) 2012,2015 Eric Dumazet <edumazet@google.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The names of the authors may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+/* Controlling Queue Delay (CoDel) algorithm
+ * =========================================
+ * Source : Kathleen Nichols and Van Jacobson
+ * http://queue.acm.org/detail.cfm?id=2209336
+ *
+ * Implemented on linux by Dave Taht and Eric Dumazet
+ */
+
+/* Qdiscs using codel plugin must use codel_skb_cb in their own cb[] */
+struct codel_skb_cb {
+ codel_time_t enqueue_time;
+ unsigned int mem_usage;
+};
+
+static struct codel_skb_cb *get_codel_cb(const struct sk_buff *skb)
+{
+ qdisc_cb_private_validate(skb, sizeof(struct codel_skb_cb));
+ return (struct codel_skb_cb *)qdisc_skb_cb(skb)->data;
+}
+
+static codel_time_t codel_get_enqueue_time(const struct sk_buff *skb)
+{
+ return get_codel_cb(skb)->enqueue_time;
+}
+
+static void codel_set_enqueue_time(struct sk_buff *skb)
+{
+ get_codel_cb(skb)->enqueue_time = codel_get_time();
+}
+
+#endif
diff --git a/include/net/fq.h b/include/net/fq.h
new file mode 100644
index 0000000..6d8521a
--- /dev/null
+++ b/include/net/fq.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2016 Qualcomm Atheros, Inc
+ *
+ * GPL v2
+ *
+ * Based on net/sched/sch_fq_codel.c
+ */
+#ifndef __NET_SCHED_FQ_H
+#define __NET_SCHED_FQ_H
+
+struct fq_tin;
+
+/**
+ * struct fq_flow - per traffic flow queue
+ *
+ * @tin: owner of this flow. Used to manage collisions, i.e. when a packet
+ * hashes to an index which points to a flow that is already owned by a
+ * different tin the packet is destined to. In such case the implementer
+ * must provide a fallback flow
+ * @flowchain: can be linked to fq_tin's new_flows or old_flows. Used for DRR++
+ * (deficit round robin) based round robin queuing similar to the one
+ * found in net/sched/sch_fq_codel.c
+ * @backlogchain: can be linked to other fq_flow and fq. Used to keep track of
+ * fat flows and efficient head-dropping if packet limit is reached
+ * @queue: sk_buff queue to hold packets
+ * @backlog: number of bytes pending in the queue. The number of packets can be
+ * found in @queue.qlen
+ * @deficit: used for DRR++
+ */
+struct fq_flow {
+ struct fq_tin *tin;
+ struct list_head flowchain;
+ struct list_head backlogchain;
+ struct sk_buff_head queue;
+ u32 backlog;
+ int deficit;
+};
+
+/**
+ * struct fq_tin - a logical container of fq_flows
+ *
+ * Used to group fq_flows into a logical aggregate. DRR++ scheme is used to
+ * pull interleaved packets out of the associated flows.
+ *
+ * @new_flows: linked list of fq_flow
+ * @old_flows: linked list of fq_flow
+ */
+struct fq_tin {
+ struct list_head new_flows;
+ struct list_head old_flows;
+ u32 backlog_bytes;
+ u32 backlog_packets;
+ u32 overlimit;
+ u32 collisions;
+ u32 flows;
+ u32 tx_bytes;
+ u32 tx_packets;
+};
+
+/**
+ * struct fq - main container for fair queuing purposes
+ *
+ * @backlogs: linked to fq_flows. Used to maintain fat flows for efficient
+ * head-dropping when @backlog reaches @limit
+ * @limit: max number of packets that can be queued across all flows
+ * @backlog: number of packets queued across all flows
+ */
+struct fq {
+ struct fq_flow *flows;
+ struct list_head backlogs;
+ spinlock_t lock;
+ u32 flows_cnt;
+ u32 perturbation;
+ u32 limit;
+ u32 memory_limit;
+ u32 memory_usage;
+ u32 quantum;
+ u32 backlog;
+ u32 overlimit;
+ u32 overmemory;
+ u32 collisions;
+};
+
+typedef struct sk_buff *fq_tin_dequeue_t(struct fq *,
+ struct fq_tin *,
+ struct fq_flow *flow);
+
+typedef void fq_skb_free_t(struct fq *,
+ struct fq_tin *,
+ struct fq_flow *,
+ struct sk_buff *);
+
+typedef struct fq_flow *fq_flow_get_default_t(struct fq *,
+ struct fq_tin *,
+ int idx,
+ struct sk_buff *);
+
+#endif
diff --git a/include/net/fq_impl.h b/include/net/fq_impl.h
new file mode 100644
index 0000000..4e6131c
--- /dev/null
+++ b/include/net/fq_impl.h
@@ -0,0 +1,282 @@
+/*
+ * Copyright (c) 2016 Qualcomm Atheros, Inc
+ *
+ * GPL v2
+ *
+ * Based on net/sched/sch_fq_codel.c
+ */
+#ifndef __NET_SCHED_FQ_IMPL_H
+#define __NET_SCHED_FQ_IMPL_H
+
+#include <net/fq.h>
+
+/* functions that are embedded into includer */
+
+static struct sk_buff *fq_flow_dequeue(struct fq *fq,
+ struct fq_flow *flow)
+{
+ struct fq_tin *tin = flow->tin;
+ struct fq_flow *i;
+ struct sk_buff *skb;
+
+ lockdep_assert_held(&fq->lock);
+
+ skb = __skb_dequeue(&flow->queue);
+ if (!skb)
+ return NULL;
+
+ tin->backlog_bytes -= skb->len;
+ tin->backlog_packets--;
+ flow->backlog -= skb->len;
+ fq->backlog--;
+ fq->memory_usage -= skb->truesize;
+
+ if (flow->backlog == 0) {
+ list_del_init(&flow->backlogchain);
+ } else {
+ i = flow;
+
+ list_for_each_entry_continue(i, &fq->backlogs, backlogchain)
+ if (i->backlog < flow->backlog)
+ break;
+
+ list_move_tail(&flow->backlogchain,
+ &i->backlogchain);
+ }
+
+ return skb;
+}
+
+static struct sk_buff *fq_tin_dequeue(struct fq *fq,
+ struct fq_tin *tin,
+ fq_tin_dequeue_t dequeue_func)
+{
+ struct fq_flow *flow;
+ struct list_head *head;
+ struct sk_buff *skb;
+
+ lockdep_assert_held(&fq->lock);
+
+begin:
+ head = &tin->new_flows;
+ if (list_empty(head)) {
+ head = &tin->old_flows;
+ if (list_empty(head))
+ return NULL;
+ }
+
+ flow = list_first_entry(head, struct fq_flow, flowchain);
+
+ if (flow->deficit <= 0) {
+ flow->deficit += fq->quantum;
+ list_move_tail(&flow->flowchain,
+ &tin->old_flows);
+ goto begin;
+ }
+
+ skb = dequeue_func(fq, tin, flow);
+ if (!skb) {
+ /* force a pass through old_flows to prevent starvation */
+ if ((head == &tin->new_flows) &&
+ !list_empty(&tin->old_flows)) {
+ list_move_tail(&flow->flowchain, &tin->old_flows);
+ } else {
+ list_del_init(&flow->flowchain);
+ flow->tin = NULL;
+ }
+ goto begin;
+ }
+
+ flow->deficit -= skb->len;
+ tin->tx_bytes += skb->len;
+ tin->tx_packets++;
+
+ return skb;
+}
+
+static struct fq_flow *fq_flow_classify(struct fq *fq,
+ struct fq_tin *tin,
+ struct sk_buff *skb,
+ fq_flow_get_default_t get_default_func)
+{
+ struct fq_flow *flow;
+ u32 hash;
+ u32 idx;
+
+ lockdep_assert_held(&fq->lock);
+
+ hash = skb_get_hash_perturb(skb, fq->perturbation);
+ idx = reciprocal_scale(hash, fq->flows_cnt);
+ flow = &fq->flows[idx];
+
+ if (flow->tin && flow->tin != tin) {
+ flow = get_default_func(fq, tin, idx, skb);
+ tin->collisions++;
+ fq->collisions++;
+ }
+
+ if (!flow->tin)
+ tin->flows++;
+
+ return flow;
+}
+
+static void fq_recalc_backlog(struct fq *fq,
+ struct fq_tin *tin,
+ struct fq_flow *flow)
+{
+ struct fq_flow *i;
+
+ if (list_empty(&flow->backlogchain))
+ list_add_tail(&flow->backlogchain, &fq->backlogs);
+
+ i = flow;
+ list_for_each_entry_continue_reverse(i, &fq->backlogs,
+ backlogchain)
+ if (i->backlog > flow->backlog)
+ break;
+
+ list_move(&flow->backlogchain, &i->backlogchain);
+}
+
+static void fq_tin_enqueue(struct fq *fq,
+ struct fq_tin *tin,
+ struct sk_buff *skb,
+ fq_skb_free_t free_func,
+ fq_flow_get_default_t get_default_func)
+{
+ struct fq_flow *flow;
+
+ lockdep_assert_held(&fq->lock);
+
+ flow = fq_flow_classify(fq, tin, skb, get_default_func);
+
+ flow->tin = tin;
+ flow->backlog += skb->len;
+ tin->backlog_bytes += skb->len;
+ tin->backlog_packets++;
+ fq->memory_usage += skb->truesize;
+ fq->backlog++;
+
+ fq_recalc_backlog(fq, tin, flow);
+
+ if (list_empty(&flow->flowchain)) {
+ flow->deficit = fq->quantum;
+ list_add_tail(&flow->flowchain,
+ &tin->new_flows);
+ }
+
+ __skb_queue_tail(&flow->queue, skb);
+
+ if (fq->backlog > fq->limit || fq->memory_usage > fq->memory_limit) {
+ flow = list_first_entry_or_null(&fq->backlogs,
+ struct fq_flow,
+ backlogchain);
+ if (!flow)
+ return;
+
+ skb = fq_flow_dequeue(fq, flow);
+ if (!skb)
+ return;
+
+ free_func(fq, flow->tin, flow, skb);
+
+ flow->tin->overlimit++;
+ fq->overlimit++;
+ if (fq->memory_usage > fq->memory_limit)
+ fq->overmemory++;
+ }
+}
+
+static void fq_flow_reset(struct fq *fq,
+ struct fq_flow *flow,
+ fq_skb_free_t free_func)
+{
+ struct sk_buff *skb;
+
+ while ((skb = fq_flow_dequeue(fq, flow)))
+ free_func(fq, flow->tin, flow, skb);
+
+ if (!list_empty(&flow->flowchain))
+ list_del_init(&flow->flowchain);
+
+ if (!list_empty(&flow->backlogchain))
+ list_del_init(&flow->backlogchain);
+
+ flow->tin = NULL;
+
+ WARN_ON_ONCE(flow->backlog);
+}
+
+static void fq_tin_reset(struct fq *fq,
+ struct fq_tin *tin,
+ fq_skb_free_t free_func)
+{
+ struct list_head *head;
+ struct fq_flow *flow;
+
+ for (;;) {
+ head = &tin->new_flows;
+ if (list_empty(head)) {
+ head = &tin->old_flows;
+ if (list_empty(head))
+ break;
+ }
+
+ flow = list_first_entry(head, struct fq_flow, flowchain);
+ fq_flow_reset(fq, flow, free_func);
+ }
+
+ WARN_ON_ONCE(tin->backlog_bytes);
+ WARN_ON_ONCE(tin->backlog_packets);
+}
+
+static void fq_flow_init(struct fq_flow *flow)
+{
+ INIT_LIST_HEAD(&flow->flowchain);
+ INIT_LIST_HEAD(&flow->backlogchain);
+ __skb_queue_head_init(&flow->queue);
+}
+
+static void fq_tin_init(struct fq_tin *tin)
+{
+ INIT_LIST_HEAD(&tin->new_flows);
+ INIT_LIST_HEAD(&tin->old_flows);
+}
+
+static int fq_init(struct fq *fq, int flows_cnt)
+{
+ int i;
+
+ memset(fq, 0, sizeof(fq[0]));
+ INIT_LIST_HEAD(&fq->backlogs);
+ spin_lock_init(&fq->lock);
+ fq->flows_cnt = max_t(u32, flows_cnt, 1);
+ fq->perturbation = prandom_u32();
+ fq->quantum = 300;
+ fq->limit = 8192;
+ fq->memory_limit = 16 << 20; /* 16 MBytes */
+
+ fq->flows = kcalloc(fq->flows_cnt, sizeof(fq->flows[0]), GFP_KERNEL);
+ if (!fq->flows)
+ return -ENOMEM;
+
+ for (i = 0; i < fq->flows_cnt; i++)
+ fq_flow_init(&fq->flows[i]);
+
+ return 0;
+}
+
+static void fq_reset(struct fq *fq,
+ fq_skb_free_t free_func)
+{
+ int i;
+
+ for (i = 0; i < fq->flows_cnt; i++)
+ fq_flow_reset(fq, &fq->flows[i], free_func);
+
+ kfree(fq->flows);
+ fq->flows = NULL;
+}
+
+#endif
diff --git a/include/net/ieee80211_radiotap.h b/include/net/ieee80211_radiotap.h
new file mode 100644
index 0000000..d91f9e7
--- /dev/null
+++ b/include/net/ieee80211_radiotap.h
@@ -0,0 +1,213 @@
+/*
+ * Copyright (c) 2017 Intel Deutschland GmbH
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#ifndef __RADIOTAP_H
+#define __RADIOTAP_H
+
+#include <linux/kernel.h>
+#include <asm/unaligned.h>
+
+/**
+ * struct ieee82011_radiotap_header - base radiotap header
+ */
+struct ieee80211_radiotap_header {
+ /**
+ * @it_version: radiotap version, always 0
+ */
+ uint8_t it_version;
+
+ /**
+ * @it_pad: padding (or alignment)
+ */
+ uint8_t it_pad;
+
+ /**
+ * @it_len: overall radiotap header length
+ */
+ __le16 it_len;
+
+ /**
+ * @it_present: (first) present word
+ */
+ __le32 it_present;
+} __packed;
+
+/* version is always 0 */
+#define PKTHDR_RADIOTAP_VERSION 0
+
+/* see the radiotap website for the descriptions */
+enum ieee80211_radiotap_presence {
+ IEEE80211_RADIOTAP_TSFT = 0,
+ IEEE80211_RADIOTAP_FLAGS = 1,
+ IEEE80211_RADIOTAP_RATE = 2,
+ IEEE80211_RADIOTAP_CHANNEL = 3,
+ IEEE80211_RADIOTAP_FHSS = 4,
+ IEEE80211_RADIOTAP_DBM_ANTSIGNAL = 5,
+ IEEE80211_RADIOTAP_DBM_ANTNOISE = 6,
+ IEEE80211_RADIOTAP_LOCK_QUALITY = 7,
+ IEEE80211_RADIOTAP_TX_ATTENUATION = 8,
+ IEEE80211_RADIOTAP_DB_TX_ATTENUATION = 9,
+ IEEE80211_RADIOTAP_DBM_TX_POWER = 10,
+ IEEE80211_RADIOTAP_ANTENNA = 11,
+ IEEE80211_RADIOTAP_DB_ANTSIGNAL = 12,
+ IEEE80211_RADIOTAP_DB_ANTNOISE = 13,
+ IEEE80211_RADIOTAP_RX_FLAGS = 14,
+ IEEE80211_RADIOTAP_TX_FLAGS = 15,
+ IEEE80211_RADIOTAP_RTS_RETRIES = 16,
+ IEEE80211_RADIOTAP_DATA_RETRIES = 17,
+ /* 18 is XChannel, but it's not defined yet */
+ IEEE80211_RADIOTAP_MCS = 19,
+ IEEE80211_RADIOTAP_AMPDU_STATUS = 20,
+ IEEE80211_RADIOTAP_VHT = 21,
+ IEEE80211_RADIOTAP_TIMESTAMP = 22,
+
+ /* valid in every it_present bitmap, even vendor namespaces */
+ IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE = 29,
+ IEEE80211_RADIOTAP_VENDOR_NAMESPACE = 30,
+ IEEE80211_RADIOTAP_EXT = 31
+};
+
+/* for IEEE80211_RADIOTAP_FLAGS */
+enum ieee80211_radiotap_flags {
+ IEEE80211_RADIOTAP_F_CFP = 0x01,
+ IEEE80211_RADIOTAP_F_SHORTPRE = 0x02,
+ IEEE80211_RADIOTAP_F_WEP = 0x04,
+ IEEE80211_RADIOTAP_F_FRAG = 0x08,
+ IEEE80211_RADIOTAP_F_FCS = 0x10,
+ IEEE80211_RADIOTAP_F_DATAPAD = 0x20,
+ IEEE80211_RADIOTAP_F_BADFCS = 0x40,
+};
+
+/* for IEEE80211_RADIOTAP_CHANNEL */
+enum ieee80211_radiotap_channel_flags {
+ IEEE80211_CHAN_CCK = 0x0020,
+ IEEE80211_CHAN_OFDM = 0x0040,
+ IEEE80211_CHAN_2GHZ = 0x0080,
+ IEEE80211_CHAN_5GHZ = 0x0100,
+ IEEE80211_CHAN_DYN = 0x0400,
+ IEEE80211_CHAN_HALF = 0x4000,
+ IEEE80211_CHAN_QUARTER = 0x8000,
+};
+
+/* for IEEE80211_RADIOTAP_RX_FLAGS */
+enum ieee80211_radiotap_rx_flags {
+ IEEE80211_RADIOTAP_F_RX_BADPLCP = 0x0002,
+};
+
+/* for IEEE80211_RADIOTAP_TX_FLAGS */
+enum ieee80211_radiotap_tx_flags {
+ IEEE80211_RADIOTAP_F_TX_FAIL = 0x0001,
+ IEEE80211_RADIOTAP_F_TX_CTS = 0x0002,
+ IEEE80211_RADIOTAP_F_TX_RTS = 0x0004,
+ IEEE80211_RADIOTAP_F_TX_NOACK = 0x0008,
+};
+
+/* for IEEE80211_RADIOTAP_MCS "have" flags */
+enum ieee80211_radiotap_mcs_have {
+ IEEE80211_RADIOTAP_MCS_HAVE_BW = 0x01,
+ IEEE80211_RADIOTAP_MCS_HAVE_MCS = 0x02,
+ IEEE80211_RADIOTAP_MCS_HAVE_GI = 0x04,
+ IEEE80211_RADIOTAP_MCS_HAVE_FMT = 0x08,
+ IEEE80211_RADIOTAP_MCS_HAVE_FEC = 0x10,
+ IEEE80211_RADIOTAP_MCS_HAVE_STBC = 0x20,
+};
+
+enum ieee80211_radiotap_mcs_flags {
+ IEEE80211_RADIOTAP_MCS_BW_MASK = 0x03,
+ IEEE80211_RADIOTAP_MCS_BW_20 = 0,
+ IEEE80211_RADIOTAP_MCS_BW_40 = 1,
+ IEEE80211_RADIOTAP_MCS_BW_20L = 2,
+ IEEE80211_RADIOTAP_MCS_BW_20U = 3,
+
+ IEEE80211_RADIOTAP_MCS_SGI = 0x04,
+ IEEE80211_RADIOTAP_MCS_FMT_GF = 0x08,
+ IEEE80211_RADIOTAP_MCS_FEC_LDPC = 0x10,
+ IEEE80211_RADIOTAP_MCS_STBC_MASK = 0x60,
+ IEEE80211_RADIOTAP_MCS_STBC_1 = 1,
+ IEEE80211_RADIOTAP_MCS_STBC_2 = 2,
+ IEEE80211_RADIOTAP_MCS_STBC_3 = 3,
+ IEEE80211_RADIOTAP_MCS_STBC_SHIFT = 5,
+};
+
+/* for IEEE80211_RADIOTAP_AMPDU_STATUS */
+enum ieee80211_radiotap_ampdu_flags {
+ IEEE80211_RADIOTAP_AMPDU_REPORT_ZEROLEN = 0x0001,
+ IEEE80211_RADIOTAP_AMPDU_IS_ZEROLEN = 0x0002,
+ IEEE80211_RADIOTAP_AMPDU_LAST_KNOWN = 0x0004,
+ IEEE80211_RADIOTAP_AMPDU_IS_LAST = 0x0008,
+ IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_ERR = 0x0010,
+ IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_KNOWN = 0x0020,
+};
+
+/* for IEEE80211_RADIOTAP_VHT */
+enum ieee80211_radiotap_vht_known {
+ IEEE80211_RADIOTAP_VHT_KNOWN_STBC = 0x0001,
+ IEEE80211_RADIOTAP_VHT_KNOWN_TXOP_PS_NA = 0x0002,
+ IEEE80211_RADIOTAP_VHT_KNOWN_GI = 0x0004,
+ IEEE80211_RADIOTAP_VHT_KNOWN_SGI_NSYM_DIS = 0x0008,
+ IEEE80211_RADIOTAP_VHT_KNOWN_LDPC_EXTRA_OFDM_SYM = 0x0010,
+ IEEE80211_RADIOTAP_VHT_KNOWN_BEAMFORMED = 0x0020,
+ IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH = 0x0040,
+ IEEE80211_RADIOTAP_VHT_KNOWN_GROUP_ID = 0x0080,
+ IEEE80211_RADIOTAP_VHT_KNOWN_PARTIAL_AID = 0x0100,
+};
+
+enum ieee80211_radiotap_vht_flags {
+ IEEE80211_RADIOTAP_VHT_FLAG_STBC = 0x01,
+ IEEE80211_RADIOTAP_VHT_FLAG_TXOP_PS_NA = 0x02,
+ IEEE80211_RADIOTAP_VHT_FLAG_SGI = 0x04,
+ IEEE80211_RADIOTAP_VHT_FLAG_SGI_NSYM_M10_9 = 0x08,
+ IEEE80211_RADIOTAP_VHT_FLAG_LDPC_EXTRA_OFDM_SYM = 0x10,
+ IEEE80211_RADIOTAP_VHT_FLAG_BEAMFORMED = 0x20,
+};
+
+enum ieee80211_radiotap_vht_coding {
+ IEEE80211_RADIOTAP_CODING_LDPC_USER0 = 0x01,
+ IEEE80211_RADIOTAP_CODING_LDPC_USER1 = 0x02,
+ IEEE80211_RADIOTAP_CODING_LDPC_USER2 = 0x04,
+ IEEE80211_RADIOTAP_CODING_LDPC_USER3 = 0x08,
+};
+
+/* for IEEE80211_RADIOTAP_TIMESTAMP */
+enum ieee80211_radiotap_timestamp_unit_spos {
+ IEEE80211_RADIOTAP_TIMESTAMP_UNIT_MASK = 0x000F,
+ IEEE80211_RADIOTAP_TIMESTAMP_UNIT_MS = 0x0000,
+ IEEE80211_RADIOTAP_TIMESTAMP_UNIT_US = 0x0001,
+ IEEE80211_RADIOTAP_TIMESTAMP_UNIT_NS = 0x0003,
+ IEEE80211_RADIOTAP_TIMESTAMP_SPOS_MASK = 0x00F0,
+ IEEE80211_RADIOTAP_TIMESTAMP_SPOS_BEGIN_MDPU = 0x0000,
+ IEEE80211_RADIOTAP_TIMESTAMP_SPOS_PLCP_SIG_ACQ = 0x0010,
+ IEEE80211_RADIOTAP_TIMESTAMP_SPOS_EO_PPDU = 0x0020,
+ IEEE80211_RADIOTAP_TIMESTAMP_SPOS_EO_MPDU = 0x0030,
+ IEEE80211_RADIOTAP_TIMESTAMP_SPOS_UNKNOWN = 0x00F0,
+};
+
+enum ieee80211_radiotap_timestamp_flags {
+ IEEE80211_RADIOTAP_TIMESTAMP_FLAG_64BIT = 0x00,
+ IEEE80211_RADIOTAP_TIMESTAMP_FLAG_32BIT = 0x01,
+ IEEE80211_RADIOTAP_TIMESTAMP_FLAG_ACCURACY = 0x02,
+};
+
+/**
+ * ieee80211_get_radiotap_len - get radiotap header length
+ */
+static inline u16 ieee80211_get_radiotap_len(const char *data)
+{
+ struct ieee80211_radiotap_header *hdr = (void *)data;
+
+ return get_unaligned_le16(&hdr->it_len);
+}
+
+#endif /* __RADIOTAP_H */
diff --git a/include/net/lib80211.h b/include/net/lib80211.h
new file mode 100644
index 0000000..aab0f42
--- /dev/null
+++ b/include/net/lib80211.h
@@ -0,0 +1,121 @@
+/*
+ * lib80211.h -- common bits for IEEE802.11 wireless drivers
+ *
+ * Copyright (c) 2008, John W. Linville <linville@tuxdriver.com>
+ *
+ * Some bits copied from old ieee80211 component, w/ original copyright
+ * notices below:
+ *
+ * Original code based on Host AP (software wireless LAN access point) driver
+ * for Intersil Prism2/2.5/3.
+ *
+ * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
+ * <j@w1.fi>
+ * Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi>
+ *
+ * Adaption to a generic IEEE 802.11 stack by James Ketrenos
+ * <jketreno@linux.intel.com>
+ *
+ * Copyright (c) 2004, Intel Corporation
+ *
+ */
+
+#ifndef LIB80211_H
+#define LIB80211_H
+
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/atomic.h>
+#include <linux/if.h>
+#include <linux/skbuff.h>
+#include <linux/ieee80211.h>
+#include <linux/timer.h>
+#include <linux/seq_file.h>
+
+#define NUM_WEP_KEYS 4
+
+enum {
+ IEEE80211_CRYPTO_TKIP_COUNTERMEASURES = (1 << 0),
+};
+
+struct module;
+
+struct lib80211_crypto_ops {
+ const char *name;
+ struct list_head list;
+
+ /* init new crypto context (e.g., allocate private data space,
+ * select IV, etc.); returns NULL on failure or pointer to allocated
+ * private data on success */
+ void *(*init) (int keyidx);
+
+ /* deinitialize crypto context and free allocated private data */
+ void (*deinit) (void *priv);
+
+ /* encrypt/decrypt return < 0 on error or >= 0 on success. The return
+ * value from decrypt_mpdu is passed as the keyidx value for
+ * decrypt_msdu. skb must have enough head and tail room for the
+ * encryption; if not, error will be returned; these functions are
+ * called for all MPDUs (i.e., fragments).
+ */
+ int (*encrypt_mpdu) (struct sk_buff * skb, int hdr_len, void *priv);
+ int (*decrypt_mpdu) (struct sk_buff * skb, int hdr_len, void *priv);
+
+ /* These functions are called for full MSDUs, i.e. full frames.
+ * These can be NULL if full MSDU operations are not needed. */
+ int (*encrypt_msdu) (struct sk_buff * skb, int hdr_len, void *priv);
+ int (*decrypt_msdu) (struct sk_buff * skb, int keyidx, int hdr_len,
+ void *priv);
+
+ int (*set_key) (void *key, int len, u8 * seq, void *priv);
+ int (*get_key) (void *key, int len, u8 * seq, void *priv);
+
+ /* procfs handler for printing out key information and possible
+ * statistics */
+ void (*print_stats) (struct seq_file *m, void *priv);
+
+ /* Crypto specific flag get/set for configuration settings */
+ unsigned long (*get_flags) (void *priv);
+ unsigned long (*set_flags) (unsigned long flags, void *priv);
+
+ /* maximum number of bytes added by encryption; encrypt buf is
+ * allocated with extra_prefix_len bytes, copy of in_buf, and
+ * extra_postfix_len; encrypt need not use all this space, but
+ * the result must start at the beginning of the buffer and correct
+ * length must be returned */
+ int extra_mpdu_prefix_len, extra_mpdu_postfix_len;
+ int extra_msdu_prefix_len, extra_msdu_postfix_len;
+
+ struct module *owner;
+};
+
+struct lib80211_crypt_data {
+ struct list_head list; /* delayed deletion list */
+ struct lib80211_crypto_ops *ops;
+ void *priv;
+ atomic_t refcnt;
+};
+
+struct lib80211_crypt_info {
+ char *name;
+ /* Most clients will already have a lock,
+ so just point to that. */
+ spinlock_t *lock;
+
+ struct lib80211_crypt_data *crypt[NUM_WEP_KEYS];
+ int tx_keyidx; /* default TX key index (crypt[tx_keyidx]) */
+ struct list_head crypt_deinit_list;
+ struct timer_list crypt_deinit_timer;
+ int crypt_quiesced;
+};
+
+int lib80211_crypt_info_init(struct lib80211_crypt_info *info, char *name,
+ spinlock_t *lock);
+void lib80211_crypt_info_free(struct lib80211_crypt_info *info);
+int lib80211_register_crypto_ops(struct lib80211_crypto_ops *ops);
+int lib80211_unregister_crypto_ops(struct lib80211_crypto_ops *ops);
+struct lib80211_crypto_ops *lib80211_get_crypto_ops(const char *name);
+void lib80211_crypt_delayed_deinit(struct lib80211_crypt_info *info,
+ struct lib80211_crypt_data **crypt);
+
+#endif /* LIB80211_H */
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
new file mode 100644
index 0000000..130d29f
--- /dev/null
+++ b/include/net/mac80211.h
@@ -0,0 +1,5886 @@
+/*
+ * mac80211 <-> driver interface
+ *
+ * Copyright 2002-2005, Devicescape Software, Inc.
+ * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
+ * Copyright 2007-2010 Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2013-2014 Intel Mobile Communications GmbH
+ * Copyright (C) 2015 - 2017 Intel Deutschland GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef MAC80211_H
+#define MAC80211_H
+
+#include <linux/bug.h>
+#include <linux/kernel.h>
+#include <linux/if_ether.h>
+#include <linux/skbuff.h>
+#include <linux/ieee80211.h>
+#include <net/cfg80211.h>
+#include <net/codel.h>
+#include <asm/unaligned.h>
+
+/**
+ * DOC: Introduction
+ *
+ * mac80211 is the Linux stack for 802.11 hardware that implements
+ * only partial functionality in hard- or firmware. This document
+ * defines the interface between mac80211 and low-level hardware
+ * drivers.
+ */
+
+/**
+ * DOC: Calling mac80211 from interrupts
+ *
+ * Only ieee80211_tx_status_irqsafe() and ieee80211_rx_irqsafe() can be
+ * called in hardware interrupt context. The low-level driver must not call any
+ * other functions in hardware interrupt context. If there is a need for such
+ * call, the low-level driver should first ACK the interrupt and perform the
+ * IEEE 802.11 code call after this, e.g. from a scheduled workqueue or even
+ * tasklet function.
+ *
+ * NOTE: If the driver opts to use the _irqsafe() functions, it may not also
+ * use the non-IRQ-safe functions!
+ */
+
+/**
+ * DOC: Warning
+ *
+ * If you're reading this document and not the header file itself, it will
+ * be incomplete because not all documentation has been converted yet.
+ */
+
+/**
+ * DOC: Frame format
+ *
+ * As a general rule, when frames are passed between mac80211 and the driver,
+ * they start with the IEEE 802.11 header and include the same octets that are
+ * sent over the air except for the FCS which should be calculated by the
+ * hardware.
+ *
+ * There are, however, various exceptions to this rule for advanced features:
+ *
+ * The first exception is for hardware encryption and decryption offload
+ * where the IV/ICV may or may not be generated in hardware.
+ *
+ * Secondly, when the hardware handles fragmentation, the frame handed to
+ * the driver from mac80211 is the MSDU, not the MPDU.
+ */
+
+/**
+ * DOC: mac80211 workqueue
+ *
+ * mac80211 provides its own workqueue for drivers and internal mac80211 use.
+ * The workqueue is a single threaded workqueue and can only be accessed by
+ * helpers for sanity checking. Drivers must ensure all work added onto the
+ * mac80211 workqueue should be cancelled on the driver stop() callback.
+ *
+ * mac80211 will flushed the workqueue upon interface removal and during
+ * suspend.
+ *
+ * All work performed on the mac80211 workqueue must not acquire the RTNL lock.
+ *
+ */
+
+/**
+ * DOC: mac80211 software tx queueing
+ *
+ * mac80211 provides an optional intermediate queueing implementation designed
+ * to allow the driver to keep hardware queues short and provide some fairness
+ * between different stations/interfaces.
+ * In this model, the driver pulls data frames from the mac80211 queue instead
+ * of letting mac80211 push them via drv_tx().
+ * Other frames (e.g. control or management) are still pushed using drv_tx().
+ *
+ * Drivers indicate that they use this model by implementing the .wake_tx_queue
+ * driver operation.
+ *
+ * Intermediate queues (struct ieee80211_txq) are kept per-sta per-tid, with a
+ * single per-vif queue for multicast data frames.
+ *
+ * The driver is expected to initialize its private per-queue data for stations
+ * and interfaces in the .add_interface and .sta_add ops.
+ *
+ * The driver can't access the queue directly. To dequeue a frame, it calls
+ * ieee80211_tx_dequeue(). Whenever mac80211 adds a new frame to a queue, it
+ * calls the .wake_tx_queue driver op.
+ *
+ * For AP powersave TIM handling, the driver only needs to indicate if it has
+ * buffered packets in the driver specific data structures by calling
+ * ieee80211_sta_set_buffered(). For frames buffered in the ieee80211_txq
+ * struct, mac80211 sets the appropriate TIM PVB bits and calls
+ * .release_buffered_frames().
+ * In that callback the driver is therefore expected to release its own
+ * buffered frames and afterwards also frames from the ieee80211_txq (obtained
+ * via the usual ieee80211_tx_dequeue).
+ */
+
+struct device;
+
+/**
+ * enum ieee80211_max_queues - maximum number of queues
+ *
+ * @IEEE80211_MAX_QUEUES: Maximum number of regular device queues.
+ * @IEEE80211_MAX_QUEUE_MAP: bitmap with maximum queues set
+ */
+enum ieee80211_max_queues {
+ IEEE80211_MAX_QUEUES = 16,
+ IEEE80211_MAX_QUEUE_MAP = BIT(IEEE80211_MAX_QUEUES) - 1,
+};
+
+#define IEEE80211_INVAL_HW_QUEUE 0xff
+
+/**
+ * enum ieee80211_ac_numbers - AC numbers as used in mac80211
+ * @IEEE80211_AC_VO: voice
+ * @IEEE80211_AC_VI: video
+ * @IEEE80211_AC_BE: best effort
+ * @IEEE80211_AC_BK: background
+ */
+enum ieee80211_ac_numbers {
+ IEEE80211_AC_VO = 0,
+ IEEE80211_AC_VI = 1,
+ IEEE80211_AC_BE = 2,
+ IEEE80211_AC_BK = 3,
+};
+
+/**
+ * struct ieee80211_tx_queue_params - transmit queue configuration
+ *
+ * The information provided in this structure is required for QoS
+ * transmit queue configuration. Cf. IEEE 802.11 7.3.2.29.
+ *
+ * @aifs: arbitration interframe space [0..255]
+ * @cw_min: minimum contention window [a value of the form
+ * 2^n-1 in the range 1..32767]
+ * @cw_max: maximum contention window [like @cw_min]
+ * @txop: maximum burst time in units of 32 usecs, 0 meaning disabled
+ * @acm: is mandatory admission control required for the access category
+ * @uapsd: is U-APSD mode enabled for the queue
+ */
+struct ieee80211_tx_queue_params {
+ u16 txop;
+ u16 cw_min;
+ u16 cw_max;
+ u8 aifs;
+ bool acm;
+ bool uapsd;
+};
+
+struct ieee80211_low_level_stats {
+ unsigned int dot11ACKFailureCount;
+ unsigned int dot11RTSFailureCount;
+ unsigned int dot11FCSErrorCount;
+ unsigned int dot11RTSSuccessCount;
+};
+
+/**
+ * enum ieee80211_chanctx_change - change flag for channel context
+ * @IEEE80211_CHANCTX_CHANGE_WIDTH: The channel width changed
+ * @IEEE80211_CHANCTX_CHANGE_RX_CHAINS: The number of RX chains changed
+ * @IEEE80211_CHANCTX_CHANGE_RADAR: radar detection flag changed
+ * @IEEE80211_CHANCTX_CHANGE_CHANNEL: switched to another operating channel,
+ * this is used only with channel switching with CSA
+ * @IEEE80211_CHANCTX_CHANGE_MIN_WIDTH: The min required channel width changed
+ */
+enum ieee80211_chanctx_change {
+ IEEE80211_CHANCTX_CHANGE_WIDTH = BIT(0),
+ IEEE80211_CHANCTX_CHANGE_RX_CHAINS = BIT(1),
+ IEEE80211_CHANCTX_CHANGE_RADAR = BIT(2),
+ IEEE80211_CHANCTX_CHANGE_CHANNEL = BIT(3),
+ IEEE80211_CHANCTX_CHANGE_MIN_WIDTH = BIT(4),
+};
+
+/**
+ * struct ieee80211_chanctx_conf - channel context that vifs may be tuned to
+ *
+ * This is the driver-visible part. The ieee80211_chanctx
+ * that contains it is visible in mac80211 only.
+ *
+ * @def: the channel definition
+ * @min_def: the minimum channel definition currently required.
+ * @rx_chains_static: The number of RX chains that must always be
+ * active on the channel to receive MIMO transmissions
+ * @rx_chains_dynamic: The number of RX chains that must be enabled
+ * after RTS/CTS handshake to receive SMPS MIMO transmissions;
+ * this will always be >= @rx_chains_static.
+ * @radar_enabled: whether radar detection is enabled on this channel.
+ * @drv_priv: data area for driver use, will always be aligned to
+ * sizeof(void *), size is determined in hw information.
+ */
+struct ieee80211_chanctx_conf {
+ struct cfg80211_chan_def def;
+ struct cfg80211_chan_def min_def;
+
+ u8 rx_chains_static, rx_chains_dynamic;
+
+ bool radar_enabled;
+
+ u8 drv_priv[0] __aligned(sizeof(void *));
+};
+
+/**
+ * enum ieee80211_chanctx_switch_mode - channel context switch mode
+ * @CHANCTX_SWMODE_REASSIGN_VIF: Both old and new contexts already
+ * exist (and will continue to exist), but the virtual interface
+ * needs to be switched from one to the other.
+ * @CHANCTX_SWMODE_SWAP_CONTEXTS: The old context exists but will stop
+ * to exist with this call, the new context doesn't exist but
+ * will be active after this call, the virtual interface switches
+ * from the old to the new (note that the driver may of course
+ * implement this as an on-the-fly chandef switch of the existing
+ * hardware context, but the mac80211 pointer for the old context
+ * will cease to exist and only the new one will later be used
+ * for changes/removal.)
+ */
+enum ieee80211_chanctx_switch_mode {
+ CHANCTX_SWMODE_REASSIGN_VIF,
+ CHANCTX_SWMODE_SWAP_CONTEXTS,
+};
+
+/**
+ * struct ieee80211_vif_chanctx_switch - vif chanctx switch information
+ *
+ * This is structure is used to pass information about a vif that
+ * needs to switch from one chanctx to another. The
+ * &ieee80211_chanctx_switch_mode defines how the switch should be
+ * done.
+ *
+ * @vif: the vif that should be switched from old_ctx to new_ctx
+ * @old_ctx: the old context to which the vif was assigned
+ * @new_ctx: the new context to which the vif must be assigned
+ */
+struct ieee80211_vif_chanctx_switch {
+ struct ieee80211_vif *vif;
+ struct ieee80211_chanctx_conf *old_ctx;
+ struct ieee80211_chanctx_conf *new_ctx;
+};
+
+/**
+ * enum ieee80211_bss_change - BSS change notification flags
+ *
+ * These flags are used with the bss_info_changed() callback
+ * to indicate which BSS parameter changed.
+ *
+ * @BSS_CHANGED_ASSOC: association status changed (associated/disassociated),
+ * also implies a change in the AID.
+ * @BSS_CHANGED_ERP_CTS_PROT: CTS protection changed
+ * @BSS_CHANGED_ERP_PREAMBLE: preamble changed
+ * @BSS_CHANGED_ERP_SLOT: slot timing changed
+ * @BSS_CHANGED_HT: 802.11n parameters changed
+ * @BSS_CHANGED_BASIC_RATES: Basic rateset changed
+ * @BSS_CHANGED_BEACON_INT: Beacon interval changed
+ * @BSS_CHANGED_BSSID: BSSID changed, for whatever
+ * reason (IBSS and managed mode)
+ * @BSS_CHANGED_BEACON: Beacon data changed, retrieve
+ * new beacon (beaconing modes)
+ * @BSS_CHANGED_BEACON_ENABLED: Beaconing should be
+ * enabled/disabled (beaconing modes)
+ * @BSS_CHANGED_CQM: Connection quality monitor config changed
+ * @BSS_CHANGED_IBSS: IBSS join status changed
+ * @BSS_CHANGED_ARP_FILTER: Hardware ARP filter address list or state changed.
+ * @BSS_CHANGED_QOS: QoS for this association was enabled/disabled. Note
+ * that it is only ever disabled for station mode.
+ * @BSS_CHANGED_IDLE: Idle changed for this BSS/interface.
+ * @BSS_CHANGED_SSID: SSID changed for this BSS (AP and IBSS mode)
+ * @BSS_CHANGED_AP_PROBE_RESP: Probe Response changed for this BSS (AP mode)
+ * @BSS_CHANGED_PS: PS changed for this BSS (STA mode)
+ * @BSS_CHANGED_TXPOWER: TX power setting changed for this interface
+ * @BSS_CHANGED_P2P_PS: P2P powersave settings (CTWindow, opportunistic PS)
+ * changed
+ * @BSS_CHANGED_BEACON_INFO: Data from the AP's beacon became available:
+ * currently dtim_period only is under consideration.
+ * @BSS_CHANGED_BANDWIDTH: The bandwidth used by this interface changed,
+ * note that this is only called when it changes after the channel
+ * context had been assigned.
+ * @BSS_CHANGED_OCB: OCB join status changed
+ * @BSS_CHANGED_MU_GROUPS: VHT MU-MIMO group id or user position changed
+ * @BSS_CHANGED_KEEP_ALIVE: keep alive options (idle period or protected
+ * keep alive) changed.
+ */
+enum ieee80211_bss_change {
+ BSS_CHANGED_ASSOC = 1<<0,
+ BSS_CHANGED_ERP_CTS_PROT = 1<<1,
+ BSS_CHANGED_ERP_PREAMBLE = 1<<2,
+ BSS_CHANGED_ERP_SLOT = 1<<3,
+ BSS_CHANGED_HT = 1<<4,
+ BSS_CHANGED_BASIC_RATES = 1<<5,
+ BSS_CHANGED_BEACON_INT = 1<<6,
+ BSS_CHANGED_BSSID = 1<<7,
+ BSS_CHANGED_BEACON = 1<<8,
+ BSS_CHANGED_BEACON_ENABLED = 1<<9,
+ BSS_CHANGED_CQM = 1<<10,
+ BSS_CHANGED_IBSS = 1<<11,
+ BSS_CHANGED_ARP_FILTER = 1<<12,
+ BSS_CHANGED_QOS = 1<<13,
+ BSS_CHANGED_IDLE = 1<<14,
+ BSS_CHANGED_SSID = 1<<15,
+ BSS_CHANGED_AP_PROBE_RESP = 1<<16,
+ BSS_CHANGED_PS = 1<<17,
+ BSS_CHANGED_TXPOWER = 1<<18,
+ BSS_CHANGED_P2P_PS = 1<<19,
+ BSS_CHANGED_BEACON_INFO = 1<<20,
+ BSS_CHANGED_BANDWIDTH = 1<<21,
+ BSS_CHANGED_OCB = 1<<22,
+ BSS_CHANGED_MU_GROUPS = 1<<23,
+ BSS_CHANGED_KEEP_ALIVE = 1<<24,
+
+ /* when adding here, make sure to change ieee80211_reconfig */
+};
+
+/*
+ * The maximum number of IPv4 addresses listed for ARP filtering. If the number
+ * of addresses for an interface increase beyond this value, hardware ARP
+ * filtering will be disabled.
+ */
+#define IEEE80211_BSS_ARP_ADDR_LIST_LEN 4
+
+/**
+ * enum ieee80211_event_type - event to be notified to the low level driver
+ * @RSSI_EVENT: AP's rssi crossed the a threshold set by the driver.
+ * @MLME_EVENT: event related to MLME
+ * @BAR_RX_EVENT: a BAR was received
+ * @BA_FRAME_TIMEOUT: Frames were released from the reordering buffer because
+ * they timed out. This won't be called for each frame released, but only
+ * once each time the timeout triggers.
+ */
+enum ieee80211_event_type {
+ RSSI_EVENT,
+ MLME_EVENT,
+ BAR_RX_EVENT,
+ BA_FRAME_TIMEOUT,
+};
+
+/**
+ * enum ieee80211_rssi_event_data - relevant when event type is %RSSI_EVENT
+ * @RSSI_EVENT_HIGH: AP's rssi went below the threshold set by the driver.
+ * @RSSI_EVENT_LOW: AP's rssi went above the threshold set by the driver.
+ */
+enum ieee80211_rssi_event_data {
+ RSSI_EVENT_HIGH,
+ RSSI_EVENT_LOW,
+};
+
+/**
+ * struct ieee80211_rssi_event - data attached to an %RSSI_EVENT
+ * @data: See &enum ieee80211_rssi_event_data
+ */
+struct ieee80211_rssi_event {
+ enum ieee80211_rssi_event_data data;
+};
+
+/**
+ * enum ieee80211_mlme_event_data - relevant when event type is %MLME_EVENT
+ * @AUTH_EVENT: the MLME operation is authentication
+ * @ASSOC_EVENT: the MLME operation is association
+ * @DEAUTH_RX_EVENT: deauth received..
+ * @DEAUTH_TX_EVENT: deauth sent.
+ */
+enum ieee80211_mlme_event_data {
+ AUTH_EVENT,
+ ASSOC_EVENT,
+ DEAUTH_RX_EVENT,
+ DEAUTH_TX_EVENT,
+};
+
+/**
+ * enum ieee80211_mlme_event_status - relevant when event type is %MLME_EVENT
+ * @MLME_SUCCESS: the MLME operation completed successfully.
+ * @MLME_DENIED: the MLME operation was denied by the peer.
+ * @MLME_TIMEOUT: the MLME operation timed out.
+ */
+enum ieee80211_mlme_event_status {
+ MLME_SUCCESS,
+ MLME_DENIED,
+ MLME_TIMEOUT,
+};
+
+/**
+ * struct ieee80211_mlme_event - data attached to an %MLME_EVENT
+ * @data: See &enum ieee80211_mlme_event_data
+ * @status: See &enum ieee80211_mlme_event_status
+ * @reason: the reason code if applicable
+ */
+struct ieee80211_mlme_event {
+ enum ieee80211_mlme_event_data data;
+ enum ieee80211_mlme_event_status status;
+ u16 reason;
+};
+
+/**
+ * struct ieee80211_ba_event - data attached for BlockAck related events
+ * @sta: pointer to the &ieee80211_sta to which this event relates
+ * @tid: the tid
+ * @ssn: the starting sequence number (for %BAR_RX_EVENT)
+ */
+struct ieee80211_ba_event {
+ struct ieee80211_sta *sta;
+ u16 tid;
+ u16 ssn;
+};
+
+/**
+ * struct ieee80211_event - event to be sent to the driver
+ * @type: The event itself. See &enum ieee80211_event_type.
+ * @rssi: relevant if &type is %RSSI_EVENT
+ * @mlme: relevant if &type is %AUTH_EVENT
+ * @ba: relevant if &type is %BAR_RX_EVENT or %BA_FRAME_TIMEOUT
+ * @u:union holding the fields above
+ */
+struct ieee80211_event {
+ enum ieee80211_event_type type;
+ union {
+ struct ieee80211_rssi_event rssi;
+ struct ieee80211_mlme_event mlme;
+ struct ieee80211_ba_event ba;
+ } u;
+};
+
+/**
+ * struct ieee80211_mu_group_data - STA's VHT MU-MIMO group data
+ *
+ * This structure describes the group id data of VHT MU-MIMO
+ *
+ * @membership: 64 bits array - a bit is set if station is member of the group
+ * @position: 2 bits per group id indicating the position in the group
+ */
+struct ieee80211_mu_group_data {
+ u8 membership[WLAN_MEMBERSHIP_LEN];
+ u8 position[WLAN_USER_POSITION_LEN];
+};
+
+/**
+ * struct ieee80211_bss_conf - holds the BSS's changing parameters
+ *
+ * This structure keeps information about a BSS (and an association
+ * to that BSS) that can change during the lifetime of the BSS.
+ *
+ * @assoc: association status
+ * @ibss_joined: indicates whether this station is part of an IBSS
+ * or not
+ * @ibss_creator: indicates if a new IBSS network is being created
+ * @aid: association ID number, valid only when @assoc is true
+ * @use_cts_prot: use CTS protection
+ * @use_short_preamble: use 802.11b short preamble
+ * @use_short_slot: use short slot time (only relevant for ERP)
+ * @dtim_period: num of beacons before the next DTIM, for beaconing,
+ * valid in station mode only if after the driver was notified
+ * with the %BSS_CHANGED_BEACON_INFO flag, will be non-zero then.
+ * @sync_tsf: last beacon's/probe response's TSF timestamp (could be old
+ * as it may have been received during scanning long ago). If the
+ * HW flag %IEEE80211_HW_TIMING_BEACON_ONLY is set, then this can
+ * only come from a beacon, but might not become valid until after
+ * association when a beacon is received (which is notified with the
+ * %BSS_CHANGED_DTIM flag.). See also sync_dtim_count important notice.
+ * @sync_device_ts: the device timestamp corresponding to the sync_tsf,
+ * the driver/device can use this to calculate synchronisation
+ * (see @sync_tsf). See also sync_dtim_count important notice.
+ * @sync_dtim_count: Only valid when %IEEE80211_HW_TIMING_BEACON_ONLY
+ * is requested, see @sync_tsf/@sync_device_ts.
+ * IMPORTANT: These three sync_* parameters would possibly be out of sync
+ * by the time the driver will use them. The synchronized view is currently
+ * guaranteed only in certain callbacks.
+ * @beacon_int: beacon interval
+ * @assoc_capability: capabilities taken from assoc resp
+ * @basic_rates: bitmap of basic rates, each bit stands for an
+ * index into the rate table configured by the driver in
+ * the current band.
+ * @beacon_rate: associated AP's beacon TX rate
+ * @mcast_rate: per-band multicast rate index + 1 (0: disabled)
+ * @bssid: The BSSID for this BSS
+ * @enable_beacon: whether beaconing should be enabled or not
+ * @chandef: Channel definition for this BSS -- the hardware might be
+ * configured a higher bandwidth than this BSS uses, for example.
+ * @mu_group: VHT MU-MIMO group membership data
+ * @ht_operation_mode: HT operation mode like in &struct ieee80211_ht_operation.
+ * This field is only valid when the channel is a wide HT/VHT channel.
+ * Note that with TDLS this can be the case (channel is HT, protection must
+ * be used from this field) even when the BSS association isn't using HT.
+ * @cqm_rssi_thold: Connection quality monitor RSSI threshold, a zero value
+ * implies disabled. As with the cfg80211 callback, a change here should
+ * cause an event to be sent indicating where the current value is in
+ * relation to the newly configured threshold.
+ * @cqm_rssi_low: Connection quality monitor RSSI lower threshold, a zero value
+ * implies disabled. This is an alternative mechanism to the single
+ * threshold event and can't be enabled simultaneously with it.
+ * @cqm_rssi_high: Connection quality monitor RSSI upper threshold.
+ * @cqm_rssi_hyst: Connection quality monitor RSSI hysteresis
+ * @arp_addr_list: List of IPv4 addresses for hardware ARP filtering. The
+ * may filter ARP queries targeted for other addresses than listed here.
+ * The driver must allow ARP queries targeted for all address listed here
+ * to pass through. An empty list implies no ARP queries need to pass.
+ * @arp_addr_cnt: Number of addresses currently on the list. Note that this
+ * may be larger than %IEEE80211_BSS_ARP_ADDR_LIST_LEN (the arp_addr_list
+ * array size), it's up to the driver what to do in that case.
+ * @qos: This is a QoS-enabled BSS.
+ * @idle: This interface is idle. There's also a global idle flag in the
+ * hardware config which may be more appropriate depending on what
+ * your driver/device needs to do.
+ * @ps: power-save mode (STA only). This flag is NOT affected by
+ * offchannel/dynamic_ps operations.
+ * @ssid: The SSID of the current vif. Valid in AP and IBSS mode.
+ * @ssid_len: Length of SSID given in @ssid.
+ * @hidden_ssid: The SSID of the current vif is hidden. Only valid in AP-mode.
+ * @txpower: TX power in dBm
+ * @txpower_type: TX power adjustment used to control per packet Transmit
+ * Power Control (TPC) in lower driver for the current vif. In particular
+ * TPC is enabled if value passed in %txpower_type is
+ * NL80211_TX_POWER_LIMITED (allow using less than specified from
+ * userspace), whereas TPC is disabled if %txpower_type is set to
+ * NL80211_TX_POWER_FIXED (use value configured from userspace)
+ * @p2p_noa_attr: P2P NoA attribute for P2P powersave
+ * @allow_p2p_go_ps: indication for AP or P2P GO interface, whether it's allowed
+ * to use P2P PS mechanism or not. AP/P2P GO is not allowed to use P2P PS
+ * if it has associated clients without P2P PS support.
+ * @max_idle_period: the time period during which the station can refrain from
+ * transmitting frames to its associated AP without being disassociated.
+ * In units of 1000 TUs. Zero value indicates that the AP did not include
+ * a (valid) BSS Max Idle Period Element.
+ * @protected_keep_alive: if set, indicates that the station should send an RSN
+ * protected frame to the AP to reset the idle timer at the AP for the
+ * station.
+ */
+struct ieee80211_bss_conf {
+ const u8 *bssid;
+ /* association related data */
+ bool assoc, ibss_joined;
+ bool ibss_creator;
+ u16 aid;
+ /* erp related data */
+ bool use_cts_prot;
+ bool use_short_preamble;
+ bool use_short_slot;
+ bool enable_beacon;
+ u8 dtim_period;
+ u16 beacon_int;
+ u16 assoc_capability;
+ u64 sync_tsf;
+ u32 sync_device_ts;
+ u8 sync_dtim_count;
+ u32 basic_rates;
+ struct ieee80211_rate *beacon_rate;
+ int mcast_rate[NUM_NL80211_BANDS];
+ u16 ht_operation_mode;
+ s32 cqm_rssi_thold;
+ u32 cqm_rssi_hyst;
+ s32 cqm_rssi_low;
+ s32 cqm_rssi_high;
+ struct cfg80211_chan_def chandef;
+ struct ieee80211_mu_group_data mu_group;
+ __be32 arp_addr_list[IEEE80211_BSS_ARP_ADDR_LIST_LEN];
+ int arp_addr_cnt;
+ bool qos;
+ bool idle;
+ bool ps;
+ u8 ssid[IEEE80211_MAX_SSID_LEN];
+ size_t ssid_len;
+ bool hidden_ssid;
+ int txpower;
+ enum nl80211_tx_power_setting txpower_type;
+ struct ieee80211_p2p_noa_attr p2p_noa_attr;
+ bool allow_p2p_go_ps;
+ u16 max_idle_period;
+ bool protected_keep_alive;
+};
+
+/**
+ * enum mac80211_tx_info_flags - flags to describe transmission information/status
+ *
+ * These flags are used with the @flags member of &ieee80211_tx_info.
+ *
+ * @IEEE80211_TX_CTL_REQ_TX_STATUS: require TX status callback for this frame.
+ * @IEEE80211_TX_CTL_ASSIGN_SEQ: The driver has to assign a sequence
+ * number to this frame, taking care of not overwriting the fragment
+ * number and increasing the sequence number only when the
+ * IEEE80211_TX_CTL_FIRST_FRAGMENT flag is set. mac80211 will properly
+ * assign sequence numbers to QoS-data frames but cannot do so correctly
+ * for non-QoS-data and management frames because beacons need them from
+ * that counter as well and mac80211 cannot guarantee proper sequencing.
+ * If this flag is set, the driver should instruct the hardware to
+ * assign a sequence number to the frame or assign one itself. Cf. IEEE
+ * 802.11-2007 7.1.3.4.1 paragraph 3. This flag will always be set for
+ * beacons and always be clear for frames without a sequence number field.
+ * @IEEE80211_TX_CTL_NO_ACK: tell the low level not to wait for an ack
+ * @IEEE80211_TX_CTL_CLEAR_PS_FILT: clear powersave filter for destination
+ * station
+ * @IEEE80211_TX_CTL_FIRST_FRAGMENT: this is a first fragment of the frame
+ * @IEEE80211_TX_CTL_SEND_AFTER_DTIM: send this frame after DTIM beacon
+ * @IEEE80211_TX_CTL_AMPDU: this frame should be sent as part of an A-MPDU
+ * @IEEE80211_TX_CTL_INJECTED: Frame was injected, internal to mac80211.
+ * @IEEE80211_TX_STAT_TX_FILTERED: The frame was not transmitted
+ * because the destination STA was in powersave mode. Note that to
+ * avoid race conditions, the filter must be set by the hardware or
+ * firmware upon receiving a frame that indicates that the station
+ * went to sleep (must be done on device to filter frames already on
+ * the queue) and may only be unset after mac80211 gives the OK for
+ * that by setting the IEEE80211_TX_CTL_CLEAR_PS_FILT (see above),
+ * since only then is it guaranteed that no more frames are in the
+ * hardware queue.
+ * @IEEE80211_TX_STAT_ACK: Frame was acknowledged
+ * @IEEE80211_TX_STAT_AMPDU: The frame was aggregated, so status
+ * is for the whole aggregation.
+ * @IEEE80211_TX_STAT_AMPDU_NO_BACK: no block ack was returned,
+ * so consider using block ack request (BAR).
+ * @IEEE80211_TX_CTL_RATE_CTRL_PROBE: internal to mac80211, can be
+ * set by rate control algorithms to indicate probe rate, will
+ * be cleared for fragmented frames (except on the last fragment)
+ * @IEEE80211_TX_INTFL_OFFCHAN_TX_OK: Internal to mac80211. Used to indicate
+ * that a frame can be transmitted while the queues are stopped for
+ * off-channel operation.
+ * @IEEE80211_TX_INTFL_NEED_TXPROCESSING: completely internal to mac80211,
+ * used to indicate that a pending frame requires TX processing before
+ * it can be sent out.
+ * @IEEE80211_TX_INTFL_RETRIED: completely internal to mac80211,
+ * used to indicate that a frame was already retried due to PS
+ * @IEEE80211_TX_INTFL_DONT_ENCRYPT: completely internal to mac80211,
+ * used to indicate frame should not be encrypted
+ * @IEEE80211_TX_CTL_NO_PS_BUFFER: This frame is a response to a poll
+ * frame (PS-Poll or uAPSD) or a non-bufferable MMPDU and must
+ * be sent although the station is in powersave mode.
+ * @IEEE80211_TX_CTL_MORE_FRAMES: More frames will be passed to the
+ * transmit function after the current frame, this can be used
+ * by drivers to kick the DMA queue only if unset or when the
+ * queue gets full.
+ * @IEEE80211_TX_INTFL_RETRANSMISSION: This frame is being retransmitted
+ * after TX status because the destination was asleep, it must not
+ * be modified again (no seqno assignment, crypto, etc.)
+ * @IEEE80211_TX_INTFL_MLME_CONN_TX: This frame was transmitted by the MLME
+ * code for connection establishment, this indicates that its status
+ * should kick the MLME state machine.
+ * @IEEE80211_TX_INTFL_NL80211_FRAME_TX: Frame was requested through nl80211
+ * MLME command (internal to mac80211 to figure out whether to send TX
+ * status to user space)
+ * @IEEE80211_TX_CTL_LDPC: tells the driver to use LDPC for this frame
+ * @IEEE80211_TX_CTL_STBC: Enables Space-Time Block Coding (STBC) for this
+ * frame and selects the maximum number of streams that it can use.
+ * @IEEE80211_TX_CTL_TX_OFFCHAN: Marks this packet to be transmitted on
+ * the off-channel channel when a remain-on-channel offload is done
+ * in hardware -- normal packets still flow and are expected to be
+ * handled properly by the device.
+ * @IEEE80211_TX_INTFL_TKIP_MIC_FAILURE: Marks this packet to be used for TKIP
+ * testing. It will be sent out with incorrect Michael MIC key to allow
+ * TKIP countermeasures to be tested.
+ * @IEEE80211_TX_CTL_NO_CCK_RATE: This frame will be sent at non CCK rate.
+ * This flag is actually used for management frame especially for P2P
+ * frames not being sent at CCK rate in 2GHz band.
+ * @IEEE80211_TX_STATUS_EOSP: This packet marks the end of service period,
+ * when its status is reported the service period ends. For frames in
+ * an SP that mac80211 transmits, it is already set; for driver frames
+ * the driver may set this flag. It is also used to do the same for
+ * PS-Poll responses.
+ * @IEEE80211_TX_CTL_USE_MINRATE: This frame will be sent at lowest rate.
+ * This flag is used to send nullfunc frame at minimum rate when
+ * the nullfunc is used for connection monitoring purpose.
+ * @IEEE80211_TX_CTL_DONTFRAG: Don't fragment this packet even if it
+ * would be fragmented by size (this is optional, only used for
+ * monitor injection).
+ * @IEEE80211_TX_STAT_NOACK_TRANSMITTED: A frame that was marked with
+ * IEEE80211_TX_CTL_NO_ACK has been successfully transmitted without
+ * any errors (like issues specific to the driver/HW).
+ * This flag must not be set for frames that don't request no-ack
+ * behaviour with IEEE80211_TX_CTL_NO_ACK.
+ *
+ * Note: If you have to add new flags to the enumeration, then don't
+ * forget to update %IEEE80211_TX_TEMPORARY_FLAGS when necessary.
+ */
+enum mac80211_tx_info_flags {
+ IEEE80211_TX_CTL_REQ_TX_STATUS = BIT(0),
+ IEEE80211_TX_CTL_ASSIGN_SEQ = BIT(1),
+ IEEE80211_TX_CTL_NO_ACK = BIT(2),
+ IEEE80211_TX_CTL_CLEAR_PS_FILT = BIT(3),
+ IEEE80211_TX_CTL_FIRST_FRAGMENT = BIT(4),
+ IEEE80211_TX_CTL_SEND_AFTER_DTIM = BIT(5),
+ IEEE80211_TX_CTL_AMPDU = BIT(6),
+ IEEE80211_TX_CTL_INJECTED = BIT(7),
+ IEEE80211_TX_STAT_TX_FILTERED = BIT(8),
+ IEEE80211_TX_STAT_ACK = BIT(9),
+ IEEE80211_TX_STAT_AMPDU = BIT(10),
+ IEEE80211_TX_STAT_AMPDU_NO_BACK = BIT(11),
+ IEEE80211_TX_CTL_RATE_CTRL_PROBE = BIT(12),
+ IEEE80211_TX_INTFL_OFFCHAN_TX_OK = BIT(13),
+ IEEE80211_TX_INTFL_NEED_TXPROCESSING = BIT(14),
+ IEEE80211_TX_INTFL_RETRIED = BIT(15),
+ IEEE80211_TX_INTFL_DONT_ENCRYPT = BIT(16),
+ IEEE80211_TX_CTL_NO_PS_BUFFER = BIT(17),
+ IEEE80211_TX_CTL_MORE_FRAMES = BIT(18),
+ IEEE80211_TX_INTFL_RETRANSMISSION = BIT(19),
+ IEEE80211_TX_INTFL_MLME_CONN_TX = BIT(20),
+ IEEE80211_TX_INTFL_NL80211_FRAME_TX = BIT(21),
+ IEEE80211_TX_CTL_LDPC = BIT(22),
+ IEEE80211_TX_CTL_STBC = BIT(23) | BIT(24),
+ IEEE80211_TX_CTL_TX_OFFCHAN = BIT(25),
+ IEEE80211_TX_INTFL_TKIP_MIC_FAILURE = BIT(26),
+ IEEE80211_TX_CTL_NO_CCK_RATE = BIT(27),
+ IEEE80211_TX_STATUS_EOSP = BIT(28),
+ IEEE80211_TX_CTL_USE_MINRATE = BIT(29),
+ IEEE80211_TX_CTL_DONTFRAG = BIT(30),
+ IEEE80211_TX_STAT_NOACK_TRANSMITTED = BIT(31),
+};
+
+#define IEEE80211_TX_CTL_STBC_SHIFT 23
+
+/**
+ * enum mac80211_tx_control_flags - flags to describe transmit control
+ *
+ * @IEEE80211_TX_CTRL_PORT_CTRL_PROTO: this frame is a port control
+ * protocol frame (e.g. EAP)
+ * @IEEE80211_TX_CTRL_PS_RESPONSE: This frame is a response to a poll
+ * frame (PS-Poll or uAPSD).
+ * @IEEE80211_TX_CTRL_RATE_INJECT: This frame is injected with rate information
+ * @IEEE80211_TX_CTRL_AMSDU: This frame is an A-MSDU frame
+ * @IEEE80211_TX_CTRL_FAST_XMIT: This frame is going through the fast_xmit path
+ *
+ * These flags are used in tx_info->control.flags.
+ */
+enum mac80211_tx_control_flags {
+ IEEE80211_TX_CTRL_PORT_CTRL_PROTO = BIT(0),
+ IEEE80211_TX_CTRL_PS_RESPONSE = BIT(1),
+ IEEE80211_TX_CTRL_RATE_INJECT = BIT(2),
+ IEEE80211_TX_CTRL_AMSDU = BIT(3),
+ IEEE80211_TX_CTRL_FAST_XMIT = BIT(4),
+};
+
+/*
+ * This definition is used as a mask to clear all temporary flags, which are
+ * set by the tx handlers for each transmission attempt by the mac80211 stack.
+ */
+#define IEEE80211_TX_TEMPORARY_FLAGS (IEEE80211_TX_CTL_NO_ACK | \
+ IEEE80211_TX_CTL_CLEAR_PS_FILT | IEEE80211_TX_CTL_FIRST_FRAGMENT | \
+ IEEE80211_TX_CTL_SEND_AFTER_DTIM | IEEE80211_TX_CTL_AMPDU | \
+ IEEE80211_TX_STAT_TX_FILTERED | IEEE80211_TX_STAT_ACK | \
+ IEEE80211_TX_STAT_AMPDU | IEEE80211_TX_STAT_AMPDU_NO_BACK | \
+ IEEE80211_TX_CTL_RATE_CTRL_PROBE | IEEE80211_TX_CTL_NO_PS_BUFFER | \
+ IEEE80211_TX_CTL_MORE_FRAMES | IEEE80211_TX_CTL_LDPC | \
+ IEEE80211_TX_CTL_STBC | IEEE80211_TX_STATUS_EOSP)
+
+/**
+ * enum mac80211_rate_control_flags - per-rate flags set by the
+ * Rate Control algorithm.
+ *
+ * These flags are set by the Rate control algorithm for each rate during tx,
+ * in the @flags member of struct ieee80211_tx_rate.
+ *
+ * @IEEE80211_TX_RC_USE_RTS_CTS: Use RTS/CTS exchange for this rate.
+ * @IEEE80211_TX_RC_USE_CTS_PROTECT: CTS-to-self protection is required.
+ * This is set if the current BSS requires ERP protection.
+ * @IEEE80211_TX_RC_USE_SHORT_PREAMBLE: Use short preamble.
+ * @IEEE80211_TX_RC_MCS: HT rate.
+ * @IEEE80211_TX_RC_VHT_MCS: VHT MCS rate, in this case the idx field is split
+ * into a higher 4 bits (Nss) and lower 4 bits (MCS number)
+ * @IEEE80211_TX_RC_GREEN_FIELD: Indicates whether this rate should be used in
+ * Greenfield mode.
+ * @IEEE80211_TX_RC_40_MHZ_WIDTH: Indicates if the Channel Width should be 40 MHz.
+ * @IEEE80211_TX_RC_80_MHZ_WIDTH: Indicates 80 MHz transmission
+ * @IEEE80211_TX_RC_160_MHZ_WIDTH: Indicates 160 MHz transmission
+ * (80+80 isn't supported yet)
+ * @IEEE80211_TX_RC_DUP_DATA: The frame should be transmitted on both of the
+ * adjacent 20 MHz channels, if the current channel type is
+ * NL80211_CHAN_HT40MINUS or NL80211_CHAN_HT40PLUS.
+ * @IEEE80211_TX_RC_SHORT_GI: Short Guard interval should be used for this rate.
+ */
+enum mac80211_rate_control_flags {
+ IEEE80211_TX_RC_USE_RTS_CTS = BIT(0),
+ IEEE80211_TX_RC_USE_CTS_PROTECT = BIT(1),
+ IEEE80211_TX_RC_USE_SHORT_PREAMBLE = BIT(2),
+
+ /* rate index is an HT/VHT MCS instead of an index */
+ IEEE80211_TX_RC_MCS = BIT(3),
+ IEEE80211_TX_RC_GREEN_FIELD = BIT(4),
+ IEEE80211_TX_RC_40_MHZ_WIDTH = BIT(5),
+ IEEE80211_TX_RC_DUP_DATA = BIT(6),
+ IEEE80211_TX_RC_SHORT_GI = BIT(7),
+ IEEE80211_TX_RC_VHT_MCS = BIT(8),
+ IEEE80211_TX_RC_80_MHZ_WIDTH = BIT(9),
+ IEEE80211_TX_RC_160_MHZ_WIDTH = BIT(10),
+};
+
+
+/* there are 40 bytes if you don't need the rateset to be kept */
+#define IEEE80211_TX_INFO_DRIVER_DATA_SIZE 40
+
+/* if you do need the rateset, then you have less space */
+#define IEEE80211_TX_INFO_RATE_DRIVER_DATA_SIZE 24
+
+/* maximum number of rate stages */
+#define IEEE80211_TX_MAX_RATES 4
+
+/* maximum number of rate table entries */
+#define IEEE80211_TX_RATE_TABLE_SIZE 4
+
+/**
+ * struct ieee80211_tx_rate - rate selection/status
+ *
+ * @idx: rate index to attempt to send with
+ * @flags: rate control flags (&enum mac80211_rate_control_flags)
+ * @count: number of tries in this rate before going to the next rate
+ *
+ * A value of -1 for @idx indicates an invalid rate and, if used
+ * in an array of retry rates, that no more rates should be tried.
+ *
+ * When used for transmit status reporting, the driver should
+ * always report the rate along with the flags it used.
+ *
+ * &struct ieee80211_tx_info contains an array of these structs
+ * in the control information, and it will be filled by the rate
+ * control algorithm according to what should be sent. For example,
+ * if this array contains, in the format { <idx>, <count> } the
+ * information::
+ *
+ * { 3, 2 }, { 2, 2 }, { 1, 4 }, { -1, 0 }, { -1, 0 }
+ *
+ * then this means that the frame should be transmitted
+ * up to twice at rate 3, up to twice at rate 2, and up to four
+ * times at rate 1 if it doesn't get acknowledged. Say it gets
+ * acknowledged by the peer after the fifth attempt, the status
+ * information should then contain::
+ *
+ * { 3, 2 }, { 2, 2 }, { 1, 1 }, { -1, 0 } ...
+ *
+ * since it was transmitted twice at rate 3, twice at rate 2
+ * and once at rate 1 after which we received an acknowledgement.
+ */
+struct ieee80211_tx_rate {
+ s8 idx;
+ u16 count:5,
+ flags:11;
+} __packed;
+
+#define IEEE80211_MAX_TX_RETRY 31
+
+static inline void ieee80211_rate_set_vht(struct ieee80211_tx_rate *rate,
+ u8 mcs, u8 nss)
+{
+ WARN_ON(mcs & ~0xF);
+ WARN_ON((nss - 1) & ~0x7);
+ rate->idx = ((nss - 1) << 4) | mcs;
+}
+
+static inline u8
+ieee80211_rate_get_vht_mcs(const struct ieee80211_tx_rate *rate)
+{
+ return rate->idx & 0xF;
+}
+
+static inline u8
+ieee80211_rate_get_vht_nss(const struct ieee80211_tx_rate *rate)
+{
+ return (rate->idx >> 4) + 1;
+}
+
+/**
+ * struct ieee80211_tx_info - skb transmit information
+ *
+ * This structure is placed in skb->cb for three uses:
+ * (1) mac80211 TX control - mac80211 tells the driver what to do
+ * (2) driver internal use (if applicable)
+ * (3) TX status information - driver tells mac80211 what happened
+ *
+ * @flags: transmit info flags, defined above
+ * @band: the band to transmit on (use for checking for races)
+ * @hw_queue: HW queue to put the frame on, skb_get_queue_mapping() gives the AC
+ * @ack_frame_id: internal frame ID for TX status, used internally
+ * @control: union for control data
+ * @status: union for status data
+ * @driver_data: array of driver_data pointers
+ * @ampdu_ack_len: number of acked aggregated frames.
+ * relevant only if IEEE80211_TX_STAT_AMPDU was set.
+ * @ampdu_len: number of aggregated frames.
+ * relevant only if IEEE80211_TX_STAT_AMPDU was set.
+ * @ack_signal: signal strength of the ACK frame
+ */
+struct ieee80211_tx_info {
+ /* common information */
+ u32 flags;
+ u8 band;
+
+ u8 hw_queue;
+
+ u16 ack_frame_id;
+
+ union {
+ struct {
+ union {
+ /* rate control */
+ struct {
+ struct ieee80211_tx_rate rates[
+ IEEE80211_TX_MAX_RATES];
+ s8 rts_cts_rate_idx;
+ u8 use_rts:1;
+ u8 use_cts_prot:1;
+ u8 short_preamble:1;
+ u8 skip_table:1;
+ /* 2 bytes free */
+ };
+ /* only needed before rate control */
+ unsigned long jiffies;
+ };
+ /* NB: vif can be NULL for injected frames */
+ union {
+ /* NB: vif can be NULL for injected frames */
+ struct ieee80211_vif *vif;
+
+ /* When packets are enqueued on txq it's easy
+ * to re-construct the vif pointer. There's no
+ * more space in tx_info so it can be used to
+ * store the necessary enqueue time for packet
+ * sojourn time computation.
+ */
+ codel_time_t enqueue_time;
+ };
+ struct ieee80211_key_conf *hw_key;
+ u32 flags;
+ /* 4 bytes free */
+ } control;
+ struct {
+ u64 cookie;
+ } ack;
+ struct {
+ struct ieee80211_tx_rate rates[IEEE80211_TX_MAX_RATES];
+ s32 ack_signal;
+ u8 ampdu_ack_len;
+ u8 ampdu_len;
+ u8 antenna;
+ u16 tx_time;
+ void *status_driver_data[19 / sizeof(void *)];
+ } status;
+ struct {
+ struct ieee80211_tx_rate driver_rates[
+ IEEE80211_TX_MAX_RATES];
+ u8 pad[4];
+
+ void *rate_driver_data[
+ IEEE80211_TX_INFO_RATE_DRIVER_DATA_SIZE / sizeof(void *)];
+ };
+ void *driver_data[
+ IEEE80211_TX_INFO_DRIVER_DATA_SIZE / sizeof(void *)];
+ };
+};
+
+/**
+ * struct ieee80211_tx_status - extended tx staus info for rate control
+ *
+ * @sta: Station that the packet was transmitted for
+ * @info: Basic tx status information
+ * @skb: Packet skb (can be NULL if not provided by the driver)
+ */
+struct ieee80211_tx_status {
+ struct ieee80211_sta *sta;
+ struct ieee80211_tx_info *info;
+ struct sk_buff *skb;
+};
+
+/**
+ * struct ieee80211_scan_ies - descriptors for different blocks of IEs
+ *
+ * This structure is used to point to different blocks of IEs in HW scan
+ * and scheduled scan. These blocks contain the IEs passed by userspace
+ * and the ones generated by mac80211.
+ *
+ * @ies: pointers to band specific IEs.
+ * @len: lengths of band_specific IEs.
+ * @common_ies: IEs for all bands (especially vendor specific ones)
+ * @common_ie_len: length of the common_ies
+ */
+struct ieee80211_scan_ies {
+ const u8 *ies[NUM_NL80211_BANDS];
+ size_t len[NUM_NL80211_BANDS];
+ const u8 *common_ies;
+ size_t common_ie_len;
+};
+
+
+static inline struct ieee80211_tx_info *IEEE80211_SKB_CB(struct sk_buff *skb)
+{
+ return (struct ieee80211_tx_info *)skb->cb;
+}
+
+static inline struct ieee80211_rx_status *IEEE80211_SKB_RXCB(struct sk_buff *skb)
+{
+ return (struct ieee80211_rx_status *)skb->cb;
+}
+
+/**
+ * ieee80211_tx_info_clear_status - clear TX status
+ *
+ * @info: The &struct ieee80211_tx_info to be cleared.
+ *
+ * When the driver passes an skb back to mac80211, it must report
+ * a number of things in TX status. This function clears everything
+ * in the TX status but the rate control information (it does clear
+ * the count since you need to fill that in anyway).
+ *
+ * NOTE: You can only use this function if you do NOT use
+ * info->driver_data! Use info->rate_driver_data
+ * instead if you need only the less space that allows.
+ */
+static inline void
+ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info)
+{
+ int i;
+
+ BUILD_BUG_ON(offsetof(struct ieee80211_tx_info, status.rates) !=
+ offsetof(struct ieee80211_tx_info, control.rates));
+ BUILD_BUG_ON(offsetof(struct ieee80211_tx_info, status.rates) !=
+ offsetof(struct ieee80211_tx_info, driver_rates));
+ BUILD_BUG_ON(offsetof(struct ieee80211_tx_info, status.rates) != 8);
+ /* clear the rate counts */
+ for (i = 0; i < IEEE80211_TX_MAX_RATES; i++)
+ info->status.rates[i].count = 0;
+
+ BUILD_BUG_ON(
+ offsetof(struct ieee80211_tx_info, status.ack_signal) != 20);
+ memset(&info->status.ampdu_ack_len, 0,
+ sizeof(struct ieee80211_tx_info) -
+ offsetof(struct ieee80211_tx_info, status.ampdu_ack_len));
+}
+
+
+/**
+ * enum mac80211_rx_flags - receive flags
+ *
+ * These flags are used with the @flag member of &struct ieee80211_rx_status.
+ * @RX_FLAG_MMIC_ERROR: Michael MIC error was reported on this frame.
+ * Use together with %RX_FLAG_MMIC_STRIPPED.
+ * @RX_FLAG_DECRYPTED: This frame was decrypted in hardware.
+ * @RX_FLAG_MMIC_STRIPPED: the Michael MIC is stripped off this frame,
+ * verification has been done by the hardware.
+ * @RX_FLAG_IV_STRIPPED: The IV and ICV are stripped from this frame.
+ * If this flag is set, the stack cannot do any replay detection
+ * hence the driver or hardware will have to do that.
+ * @RX_FLAG_PN_VALIDATED: Currently only valid for CCMP/GCMP frames, this
+ * flag indicates that the PN was verified for replay protection.
+ * Note that this flag is also currently only supported when a frame
+ * is also decrypted (ie. @RX_FLAG_DECRYPTED must be set)
+ * @RX_FLAG_DUP_VALIDATED: The driver should set this flag if it did
+ * de-duplication by itself.
+ * @RX_FLAG_FAILED_FCS_CRC: Set this flag if the FCS check failed on
+ * the frame.
+ * @RX_FLAG_FAILED_PLCP_CRC: Set this flag if the PCLP check failed on
+ * the frame.
+ * @RX_FLAG_MACTIME_START: The timestamp passed in the RX status (@mactime
+ * field) is valid and contains the time the first symbol of the MPDU
+ * was received. This is useful in monitor mode and for proper IBSS
+ * merging.
+ * @RX_FLAG_MACTIME_END: The timestamp passed in the RX status (@mactime
+ * field) is valid and contains the time the last symbol of the MPDU
+ * (including FCS) was received.
+ * @RX_FLAG_MACTIME_PLCP_START: The timestamp passed in the RX status (@mactime
+ * field) is valid and contains the time the SYNC preamble was received.
+ * @RX_FLAG_NO_SIGNAL_VAL: The signal strength value is not present.
+ * Valid only for data frames (mainly A-MPDU)
+ * @RX_FLAG_AMPDU_DETAILS: A-MPDU details are known, in particular the reference
+ * number (@ampdu_reference) must be populated and be a distinct number for
+ * each A-MPDU
+ * @RX_FLAG_AMPDU_LAST_KNOWN: last subframe is known, should be set on all
+ * subframes of a single A-MPDU
+ * @RX_FLAG_AMPDU_IS_LAST: this subframe is the last subframe of the A-MPDU
+ * @RX_FLAG_AMPDU_DELIM_CRC_ERROR: A delimiter CRC error has been detected
+ * on this subframe
+ * @RX_FLAG_AMPDU_DELIM_CRC_KNOWN: The delimiter CRC field is known (the CRC
+ * is stored in the @ampdu_delimiter_crc field)
+ * @RX_FLAG_MIC_STRIPPED: The mic was stripped of this packet. Decryption was
+ * done by the hardware
+ * @RX_FLAG_ONLY_MONITOR: Report frame only to monitor interfaces without
+ * processing it in any regular way.
+ * This is useful if drivers offload some frames but still want to report
+ * them for sniffing purposes.
+ * @RX_FLAG_SKIP_MONITOR: Process and report frame to all interfaces except
+ * monitor interfaces.
+ * This is useful if drivers offload some frames but still want to report
+ * them for sniffing purposes.
+ * @RX_FLAG_AMSDU_MORE: Some drivers may prefer to report separate A-MSDU
+ * subframes instead of a one huge frame for performance reasons.
+ * All, but the last MSDU from an A-MSDU should have this flag set. E.g.
+ * if an A-MSDU has 3 frames, the first 2 must have the flag set, while
+ * the 3rd (last) one must not have this flag set. The flag is used to
+ * deal with retransmission/duplication recovery properly since A-MSDU
+ * subframes share the same sequence number. Reported subframes can be
+ * either regular MSDU or singly A-MSDUs. Subframes must not be
+ * interleaved with other frames.
+ * @RX_FLAG_RADIOTAP_VENDOR_DATA: This frame contains vendor-specific
+ * radiotap data in the skb->data (before the frame) as described by
+ * the &struct ieee80211_vendor_radiotap.
+ * @RX_FLAG_ALLOW_SAME_PN: Allow the same PN as same packet before.
+ * This is used for AMSDU subframes which can have the same PN as
+ * the first subframe.
+ * @RX_FLAG_ICV_STRIPPED: The ICV is stripped from this frame. CRC checking must
+ * be done in the hardware.
+ */
+enum mac80211_rx_flags {
+ RX_FLAG_MMIC_ERROR = BIT(0),
+ RX_FLAG_DECRYPTED = BIT(1),
+ RX_FLAG_MACTIME_PLCP_START = BIT(2),
+ RX_FLAG_MMIC_STRIPPED = BIT(3),
+ RX_FLAG_IV_STRIPPED = BIT(4),
+ RX_FLAG_FAILED_FCS_CRC = BIT(5),
+ RX_FLAG_FAILED_PLCP_CRC = BIT(6),
+ RX_FLAG_MACTIME_START = BIT(7),
+ RX_FLAG_NO_SIGNAL_VAL = BIT(8),
+ RX_FLAG_AMPDU_DETAILS = BIT(9),
+ RX_FLAG_PN_VALIDATED = BIT(10),
+ RX_FLAG_DUP_VALIDATED = BIT(11),
+ RX_FLAG_AMPDU_LAST_KNOWN = BIT(12),
+ RX_FLAG_AMPDU_IS_LAST = BIT(13),
+ RX_FLAG_AMPDU_DELIM_CRC_ERROR = BIT(14),
+ RX_FLAG_AMPDU_DELIM_CRC_KNOWN = BIT(15),
+ RX_FLAG_MACTIME_END = BIT(16),
+ RX_FLAG_ONLY_MONITOR = BIT(17),
+ RX_FLAG_SKIP_MONITOR = BIT(18),
+ RX_FLAG_AMSDU_MORE = BIT(19),
+ RX_FLAG_RADIOTAP_VENDOR_DATA = BIT(20),
+ RX_FLAG_MIC_STRIPPED = BIT(21),
+ RX_FLAG_ALLOW_SAME_PN = BIT(22),
+ RX_FLAG_ICV_STRIPPED = BIT(23),
+};
+
+/**
+ * enum mac80211_rx_encoding_flags - MCS & bandwidth flags
+ *
+ * @RX_ENC_FLAG_SHORTPRE: Short preamble was used for this frame
+ * @RX_ENC_FLAG_SHORT_GI: Short guard interval was used
+ * @RX_ENC_FLAG_HT_GF: This frame was received in a HT-greenfield transmission,
+ * if the driver fills this value it should add
+ * %IEEE80211_RADIOTAP_MCS_HAVE_FMT
+ * to hw.radiotap_mcs_details to advertise that fact
+ * @RX_ENC_FLAG_LDPC: LDPC was used
+ * @RX_ENC_FLAG_STBC_MASK: STBC 2 bit bitmask. 1 - Nss=1, 2 - Nss=2, 3 - Nss=3
+ * @RX_ENC_FLAG_BF: packet was beamformed
+ */
+enum mac80211_rx_encoding_flags {
+ RX_ENC_FLAG_SHORTPRE = BIT(0),
+ RX_ENC_FLAG_SHORT_GI = BIT(2),
+ RX_ENC_FLAG_HT_GF = BIT(3),
+ RX_ENC_FLAG_STBC_MASK = BIT(4) | BIT(5),
+ RX_ENC_FLAG_LDPC = BIT(6),
+ RX_ENC_FLAG_BF = BIT(7),
+};
+
+#define RX_ENC_FLAG_STBC_SHIFT 4
+
+enum mac80211_rx_encoding {
+ RX_ENC_LEGACY = 0,
+ RX_ENC_HT,
+ RX_ENC_VHT,
+};
+
+/**
+ * struct ieee80211_rx_status - receive status
+ *
+ * The low-level driver should provide this information (the subset
+ * supported by hardware) to the 802.11 code with each received
+ * frame, in the skb's control buffer (cb).
+ *
+ * @mactime: value in microseconds of the 64-bit Time Synchronization Function
+ * (TSF) timer when the first data symbol (MPDU) arrived at the hardware.
+ * @boottime_ns: CLOCK_BOOTTIME timestamp the frame was received at, this is
+ * needed only for beacons and probe responses that update the scan cache.
+ * @device_timestamp: arbitrary timestamp for the device, mac80211 doesn't use
+ * it but can store it and pass it back to the driver for synchronisation
+ * @band: the active band when this frame was received
+ * @freq: frequency the radio was tuned to when receiving this frame, in MHz
+ * This field must be set for management frames, but isn't strictly needed
+ * for data (other) frames - for those it only affects radiotap reporting.
+ * @signal: signal strength when receiving this frame, either in dBm, in dB or
+ * unspecified depending on the hardware capabilities flags
+ * @IEEE80211_HW_SIGNAL_*
+ * @chains: bitmask of receive chains for which separate signal strength
+ * values were filled.
+ * @chain_signal: per-chain signal strength, in dBm (unlike @signal, doesn't
+ * support dB or unspecified units)
+ * @antenna: antenna used
+ * @rate_idx: index of data rate into band's supported rates or MCS index if
+ * HT or VHT is used (%RX_FLAG_HT/%RX_FLAG_VHT)
+ * @nss: number of streams (VHT and HE only)
+ * @flag: %RX_FLAG_\*
+ * @encoding: &enum mac80211_rx_encoding
+ * @bw: &enum rate_info_bw
+ * @enc_flags: uses bits from &enum mac80211_rx_encoding_flags
+ * @rx_flags: internal RX flags for mac80211
+ * @ampdu_reference: A-MPDU reference number, must be a different value for
+ * each A-MPDU but the same for each subframe within one A-MPDU
+ * @ampdu_delimiter_crc: A-MPDU delimiter CRC
+ */
+struct ieee80211_rx_status {
+ u64 mactime;
+ u64 boottime_ns;
+ u32 device_timestamp;
+ u32 ampdu_reference;
+ u32 flag;
+ u16 freq;
+ u8 enc_flags;
+ u8 encoding:2, bw:3;
+ u8 rate_idx;
+ u8 nss;
+ u8 rx_flags;
+ u8 band;
+ u8 antenna;
+ s8 signal;
+ u8 chains;
+ s8 chain_signal[IEEE80211_MAX_CHAINS];
+ u8 ampdu_delimiter_crc;
+};
+
+/**
+ * struct ieee80211_vendor_radiotap - vendor radiotap data information
+ * @present: presence bitmap for this vendor namespace
+ * (this could be extended in the future if any vendor needs more
+ * bits, the radiotap spec does allow for that)
+ * @align: radiotap vendor namespace alignment. This defines the needed
+ * alignment for the @data field below, not for the vendor namespace
+ * description itself (which has a fixed 2-byte alignment)
+ * Must be a power of two, and be set to at least 1!
+ * @oui: radiotap vendor namespace OUI
+ * @subns: radiotap vendor sub namespace
+ * @len: radiotap vendor sub namespace skip length, if alignment is done
+ * then that's added to this, i.e. this is only the length of the
+ * @data field.
+ * @pad: number of bytes of padding after the @data, this exists so that
+ * the skb data alignment can be preserved even if the data has odd
+ * length
+ * @data: the actual vendor namespace data
+ *
+ * This struct, including the vendor data, goes into the skb->data before
+ * the 802.11 header. It's split up in mac80211 using the align/oui/subns
+ * data.
+ */
+struct ieee80211_vendor_radiotap {
+ u32 present;
+ u8 align;
+ u8 oui[3];
+ u8 subns;
+ u8 pad;
+ u16 len;
+ u8 data[];
+} __packed;
+
+/**
+ * enum ieee80211_conf_flags - configuration flags
+ *
+ * Flags to define PHY configuration options
+ *
+ * @IEEE80211_CONF_MONITOR: there's a monitor interface present -- use this
+ * to determine for example whether to calculate timestamps for packets
+ * or not, do not use instead of filter flags!
+ * @IEEE80211_CONF_PS: Enable 802.11 power save mode (managed mode only).
+ * This is the power save mode defined by IEEE 802.11-2007 section 11.2,
+ * meaning that the hardware still wakes up for beacons, is able to
+ * transmit frames and receive the possible acknowledgment frames.
+ * Not to be confused with hardware specific wakeup/sleep states,
+ * driver is responsible for that. See the section "Powersave support"
+ * for more.
+ * @IEEE80211_CONF_IDLE: The device is running, but idle; if the flag is set
+ * the driver should be prepared to handle configuration requests but
+ * may turn the device off as much as possible. Typically, this flag will
+ * be set when an interface is set UP but not associated or scanning, but
+ * it can also be unset in that case when monitor interfaces are active.
+ * @IEEE80211_CONF_OFFCHANNEL: The device is currently not on its main
+ * operating channel.
+ */
+enum ieee80211_conf_flags {
+ IEEE80211_CONF_MONITOR = (1<<0),
+ IEEE80211_CONF_PS = (1<<1),
+ IEEE80211_CONF_IDLE = (1<<2),
+ IEEE80211_CONF_OFFCHANNEL = (1<<3),
+};
+
+
+/**
+ * enum ieee80211_conf_changed - denotes which configuration changed
+ *
+ * @IEEE80211_CONF_CHANGE_LISTEN_INTERVAL: the listen interval changed
+ * @IEEE80211_CONF_CHANGE_MONITOR: the monitor flag changed
+ * @IEEE80211_CONF_CHANGE_PS: the PS flag or dynamic PS timeout changed
+ * @IEEE80211_CONF_CHANGE_POWER: the TX power changed
+ * @IEEE80211_CONF_CHANGE_CHANNEL: the channel/channel_type changed
+ * @IEEE80211_CONF_CHANGE_RETRY_LIMITS: retry limits changed
+ * @IEEE80211_CONF_CHANGE_IDLE: Idle flag changed
+ * @IEEE80211_CONF_CHANGE_SMPS: Spatial multiplexing powersave mode changed
+ * Note that this is only valid if channel contexts are not used,
+ * otherwise each channel context has the number of chains listed.
+ */
+enum ieee80211_conf_changed {
+ IEEE80211_CONF_CHANGE_SMPS = BIT(1),
+ IEEE80211_CONF_CHANGE_LISTEN_INTERVAL = BIT(2),
+ IEEE80211_CONF_CHANGE_MONITOR = BIT(3),
+ IEEE80211_CONF_CHANGE_PS = BIT(4),
+ IEEE80211_CONF_CHANGE_POWER = BIT(5),
+ IEEE80211_CONF_CHANGE_CHANNEL = BIT(6),
+ IEEE80211_CONF_CHANGE_RETRY_LIMITS = BIT(7),
+ IEEE80211_CONF_CHANGE_IDLE = BIT(8),
+};
+
+/**
+ * enum ieee80211_smps_mode - spatial multiplexing power save mode
+ *
+ * @IEEE80211_SMPS_AUTOMATIC: automatic
+ * @IEEE80211_SMPS_OFF: off
+ * @IEEE80211_SMPS_STATIC: static
+ * @IEEE80211_SMPS_DYNAMIC: dynamic
+ * @IEEE80211_SMPS_NUM_MODES: internal, don't use
+ */
+enum ieee80211_smps_mode {
+ IEEE80211_SMPS_AUTOMATIC,
+ IEEE80211_SMPS_OFF,
+ IEEE80211_SMPS_STATIC,
+ IEEE80211_SMPS_DYNAMIC,
+
+ /* keep last */
+ IEEE80211_SMPS_NUM_MODES,
+};
+
+/**
+ * struct ieee80211_conf - configuration of the device
+ *
+ * This struct indicates how the driver shall configure the hardware.
+ *
+ * @flags: configuration flags defined above
+ *
+ * @listen_interval: listen interval in units of beacon interval
+ * @ps_dtim_period: The DTIM period of the AP we're connected to, for use
+ * in power saving. Power saving will not be enabled until a beacon
+ * has been received and the DTIM period is known.
+ * @dynamic_ps_timeout: The dynamic powersave timeout (in ms), see the
+ * powersave documentation below. This variable is valid only when
+ * the CONF_PS flag is set.
+ *
+ * @power_level: requested transmit power (in dBm), backward compatibility
+ * value only that is set to the minimum of all interfaces
+ *
+ * @chandef: the channel definition to tune to
+ * @radar_enabled: whether radar detection is enabled
+ *
+ * @long_frame_max_tx_count: Maximum number of transmissions for a "long" frame
+ * (a frame not RTS protected), called "dot11LongRetryLimit" in 802.11,
+ * but actually means the number of transmissions not the number of retries
+ * @short_frame_max_tx_count: Maximum number of transmissions for a "short"
+ * frame, called "dot11ShortRetryLimit" in 802.11, but actually means the
+ * number of transmissions not the number of retries
+ *
+ * @smps_mode: spatial multiplexing powersave mode; note that
+ * %IEEE80211_SMPS_STATIC is used when the device is not
+ * configured for an HT channel.
+ * Note that this is only valid if channel contexts are not used,
+ * otherwise each channel context has the number of chains listed.
+ */
+struct ieee80211_conf {
+ u32 flags;
+ int power_level, dynamic_ps_timeout;
+
+ u16 listen_interval;
+ u8 ps_dtim_period;
+
+ u8 long_frame_max_tx_count, short_frame_max_tx_count;
+
+ struct cfg80211_chan_def chandef;
+ bool radar_enabled;
+ enum ieee80211_smps_mode smps_mode;
+};
+
+/**
+ * struct ieee80211_channel_switch - holds the channel switch data
+ *
+ * The information provided in this structure is required for channel switch
+ * operation.
+ *
+ * @timestamp: value in microseconds of the 64-bit Time Synchronization
+ * Function (TSF) timer when the frame containing the channel switch
+ * announcement was received. This is simply the rx.mactime parameter
+ * the driver passed into mac80211.
+ * @device_timestamp: arbitrary timestamp for the device, this is the
+ * rx.device_timestamp parameter the driver passed to mac80211.
+ * @block_tx: Indicates whether transmission must be blocked before the
+ * scheduled channel switch, as indicated by the AP.
+ * @chandef: the new channel to switch to
+ * @count: the number of TBTT's until the channel switch event
+ */
+struct ieee80211_channel_switch {
+ u64 timestamp;
+ u32 device_timestamp;
+ bool block_tx;
+ struct cfg80211_chan_def chandef;
+ u8 count;
+};
+
+/**
+ * enum ieee80211_vif_flags - virtual interface flags
+ *
+ * @IEEE80211_VIF_BEACON_FILTER: the device performs beacon filtering
+ * on this virtual interface to avoid unnecessary CPU wakeups
+ * @IEEE80211_VIF_SUPPORTS_CQM_RSSI: the device can do connection quality
+ * monitoring on this virtual interface -- i.e. it can monitor
+ * connection quality related parameters, such as the RSSI level and
+ * provide notifications if configured trigger levels are reached.
+ * @IEEE80211_VIF_SUPPORTS_UAPSD: The device can do U-APSD for this
+ * interface. This flag should be set during interface addition,
+ * but may be set/cleared as late as authentication to an AP. It is
+ * only valid for managed/station mode interfaces.
+ * @IEEE80211_VIF_GET_NOA_UPDATE: request to handle NOA attributes
+ * and send P2P_PS notification to the driver if NOA changed, even
+ * this is not pure P2P vif.
+ */
+enum ieee80211_vif_flags {
+ IEEE80211_VIF_BEACON_FILTER = BIT(0),
+ IEEE80211_VIF_SUPPORTS_CQM_RSSI = BIT(1),
+ IEEE80211_VIF_SUPPORTS_UAPSD = BIT(2),
+ IEEE80211_VIF_GET_NOA_UPDATE = BIT(3),
+};
+
+/**
+ * struct ieee80211_vif - per-interface data
+ *
+ * Data in this structure is continually present for driver
+ * use during the life of a virtual interface.
+ *
+ * @type: type of this virtual interface
+ * @bss_conf: BSS configuration for this interface, either our own
+ * or the BSS we're associated to
+ * @addr: address of this interface
+ * @p2p: indicates whether this AP or STA interface is a p2p
+ * interface, i.e. a GO or p2p-sta respectively
+ * @csa_active: marks whether a channel switch is going on. Internally it is
+ * write-protected by sdata_lock and local->mtx so holding either is fine
+ * for read access.
+ * @mu_mimo_owner: indicates interface owns MU-MIMO capability
+ * @driver_flags: flags/capabilities the driver has for this interface,
+ * these need to be set (or cleared) when the interface is added
+ * or, if supported by the driver, the interface type is changed
+ * at runtime, mac80211 will never touch this field
+ * @hw_queue: hardware queue for each AC
+ * @cab_queue: content-after-beacon (DTIM beacon really) queue, AP mode only
+ * @chanctx_conf: The channel context this interface is assigned to, or %NULL
+ * when it is not assigned. This pointer is RCU-protected due to the TX
+ * path needing to access it; even though the netdev carrier will always
+ * be off when it is %NULL there can still be races and packets could be
+ * processed after it switches back to %NULL.
+ * @debugfs_dir: debugfs dentry, can be used by drivers to create own per
+ * interface debug files. Note that it will be NULL for the virtual
+ * monitor interface (if that is requested.)
+ * @probe_req_reg: probe requests should be reported to mac80211 for this
+ * interface.
+ * @drv_priv: data area for driver use, will always be aligned to
+ * sizeof(void \*).
+ * @txq: the multicast data TX queue (if driver uses the TXQ abstraction)
+ */
+struct ieee80211_vif {
+ enum nl80211_iftype type;
+ struct ieee80211_bss_conf bss_conf;
+ u8 addr[ETH_ALEN] __aligned(2);
+ bool p2p;
+ bool csa_active;
+ bool mu_mimo_owner;
+
+ u8 cab_queue;
+ u8 hw_queue[IEEE80211_NUM_ACS];
+
+ struct ieee80211_txq *txq;
+
+ struct ieee80211_chanctx_conf __rcu *chanctx_conf;
+
+ u32 driver_flags;
+
+#ifdef CONFIG_MAC80211_DEBUGFS
+ struct dentry *debugfs_dir;
+#endif
+
+ unsigned int probe_req_reg;
+
+ /* must be last */
+ u8 drv_priv[0] __aligned(sizeof(void *));
+};
+
+static inline bool ieee80211_vif_is_mesh(struct ieee80211_vif *vif)
+{
+#ifdef CONFIG_MAC80211_MESH
+ return vif->type == NL80211_IFTYPE_MESH_POINT;
+#endif
+ return false;
+}
+
+/**
+ * wdev_to_ieee80211_vif - return a vif struct from a wdev
+ * @wdev: the wdev to get the vif for
+ *
+ * This can be used by mac80211 drivers with direct cfg80211 APIs
+ * (like the vendor commands) that get a wdev.
+ *
+ * Note that this function may return %NULL if the given wdev isn't
+ * associated with a vif that the driver knows about (e.g. monitor
+ * or AP_VLAN interfaces.)
+ */
+struct ieee80211_vif *wdev_to_ieee80211_vif(struct wireless_dev *wdev);
+
+/**
+ * ieee80211_vif_to_wdev - return a wdev struct from a vif
+ * @vif: the vif to get the wdev for
+ *
+ * This can be used by mac80211 drivers with direct cfg80211 APIs
+ * (like the vendor commands) that needs to get the wdev for a vif.
+ *
+ * Note that this function may return %NULL if the given wdev isn't
+ * associated with a vif that the driver knows about (e.g. monitor
+ * or AP_VLAN interfaces.)
+ */
+struct wireless_dev *ieee80211_vif_to_wdev(struct ieee80211_vif *vif);
+
+/**
+ * enum ieee80211_key_flags - key flags
+ *
+ * These flags are used for communication about keys between the driver
+ * and mac80211, with the @flags parameter of &struct ieee80211_key_conf.
+ *
+ * @IEEE80211_KEY_FLAG_GENERATE_IV: This flag should be set by the
+ * driver to indicate that it requires IV generation for this
+ * particular key. Setting this flag does not necessarily mean that SKBs
+ * will have sufficient tailroom for ICV or MIC.
+ * @IEEE80211_KEY_FLAG_GENERATE_MMIC: This flag should be set by
+ * the driver for a TKIP key if it requires Michael MIC
+ * generation in software.
+ * @IEEE80211_KEY_FLAG_PAIRWISE: Set by mac80211, this flag indicates
+ * that the key is pairwise rather then a shared key.
+ * @IEEE80211_KEY_FLAG_SW_MGMT_TX: This flag should be set by the driver for a
+ * CCMP/GCMP key if it requires CCMP/GCMP encryption of management frames
+ * (MFP) to be done in software.
+ * @IEEE80211_KEY_FLAG_PUT_IV_SPACE: This flag should be set by the driver
+ * if space should be prepared for the IV, but the IV
+ * itself should not be generated. Do not set together with
+ * @IEEE80211_KEY_FLAG_GENERATE_IV on the same key. Setting this flag does
+ * not necessarily mean that SKBs will have sufficient tailroom for ICV or
+ * MIC.
+ * @IEEE80211_KEY_FLAG_RX_MGMT: This key will be used to decrypt received
+ * management frames. The flag can help drivers that have a hardware
+ * crypto implementation that doesn't deal with management frames
+ * properly by allowing them to not upload the keys to hardware and
+ * fall back to software crypto. Note that this flag deals only with
+ * RX, if your crypto engine can't deal with TX you can also set the
+ * %IEEE80211_KEY_FLAG_SW_MGMT_TX flag to encrypt such frames in SW.
+ * @IEEE80211_KEY_FLAG_GENERATE_IV_MGMT: This flag should be set by the
+ * driver for a CCMP/GCMP key to indicate that is requires IV generation
+ * only for managment frames (MFP).
+ * @IEEE80211_KEY_FLAG_RESERVE_TAILROOM: This flag should be set by the
+ * driver for a key to indicate that sufficient tailroom must always
+ * be reserved for ICV or MIC, even when HW encryption is enabled.
+ */
+enum ieee80211_key_flags {
+ IEEE80211_KEY_FLAG_GENERATE_IV_MGMT = BIT(0),
+ IEEE80211_KEY_FLAG_GENERATE_IV = BIT(1),
+ IEEE80211_KEY_FLAG_GENERATE_MMIC = BIT(2),
+ IEEE80211_KEY_FLAG_PAIRWISE = BIT(3),
+ IEEE80211_KEY_FLAG_SW_MGMT_TX = BIT(4),
+ IEEE80211_KEY_FLAG_PUT_IV_SPACE = BIT(5),
+ IEEE80211_KEY_FLAG_RX_MGMT = BIT(6),
+ IEEE80211_KEY_FLAG_RESERVE_TAILROOM = BIT(7),
+};
+
+/**
+ * struct ieee80211_key_conf - key information
+ *
+ * This key information is given by mac80211 to the driver by
+ * the set_key() callback in &struct ieee80211_ops.
+ *
+ * @hw_key_idx: To be set by the driver, this is the key index the driver
+ * wants to be given when a frame is transmitted and needs to be
+ * encrypted in hardware.
+ * @cipher: The key's cipher suite selector.
+ * @tx_pn: PN used for TX keys, may be used by the driver as well if it
+ * needs to do software PN assignment by itself (e.g. due to TSO)
+ * @flags: key flags, see &enum ieee80211_key_flags.
+ * @keyidx: the key index (0-3)
+ * @keylen: key material length
+ * @key: key material. For ALG_TKIP the key is encoded as a 256-bit (32 byte)
+ * data block:
+ * - Temporal Encryption Key (128 bits)
+ * - Temporal Authenticator Tx MIC Key (64 bits)
+ * - Temporal Authenticator Rx MIC Key (64 bits)
+ * @icv_len: The ICV length for this key type
+ * @iv_len: The IV length for this key type
+ */
+struct ieee80211_key_conf {
+ atomic64_t tx_pn;
+ u32 cipher;
+ u8 icv_len;
+ u8 iv_len;
+ u8 hw_key_idx;
+ u8 flags;
+ s8 keyidx;
+ u8 keylen;
+ u8 key[0];
+};
+
+#define IEEE80211_MAX_PN_LEN 16
+
+#define TKIP_PN_TO_IV16(pn) ((u16)(pn & 0xffff))
+#define TKIP_PN_TO_IV32(pn) ((u32)((pn >> 16) & 0xffffffff))
+
+/**
+ * struct ieee80211_key_seq - key sequence counter
+ *
+ * @tkip: TKIP data, containing IV32 and IV16 in host byte order
+ * @ccmp: PN data, most significant byte first (big endian,
+ * reverse order than in packet)
+ * @aes_cmac: PN data, most significant byte first (big endian,
+ * reverse order than in packet)
+ * @aes_gmac: PN data, most significant byte first (big endian,
+ * reverse order than in packet)
+ * @gcmp: PN data, most significant byte first (big endian,
+ * reverse order than in packet)
+ * @hw: data for HW-only (e.g. cipher scheme) keys
+ */
+struct ieee80211_key_seq {
+ union {
+ struct {
+ u32 iv32;
+ u16 iv16;
+ } tkip;
+ struct {
+ u8 pn[6];
+ } ccmp;
+ struct {
+ u8 pn[6];
+ } aes_cmac;
+ struct {
+ u8 pn[6];
+ } aes_gmac;
+ struct {
+ u8 pn[6];
+ } gcmp;
+ struct {
+ u8 seq[IEEE80211_MAX_PN_LEN];
+ u8 seq_len;
+ } hw;
+ };
+};
+
+/**
+ * struct ieee80211_cipher_scheme - cipher scheme
+ *
+ * This structure contains a cipher scheme information defining
+ * the secure packet crypto handling.
+ *
+ * @cipher: a cipher suite selector
+ * @iftype: a cipher iftype bit mask indicating an allowed cipher usage
+ * @hdr_len: a length of a security header used the cipher
+ * @pn_len: a length of a packet number in the security header
+ * @pn_off: an offset of pn from the beginning of the security header
+ * @key_idx_off: an offset of key index byte in the security header
+ * @key_idx_mask: a bit mask of key_idx bits
+ * @key_idx_shift: a bit shift needed to get key_idx
+ * key_idx value calculation:
+ * (sec_header_base[key_idx_off] & key_idx_mask) >> key_idx_shift
+ * @mic_len: a mic length in bytes
+ */
+struct ieee80211_cipher_scheme {
+ u32 cipher;
+ u16 iftype;
+ u8 hdr_len;
+ u8 pn_len;
+ u8 pn_off;
+ u8 key_idx_off;
+ u8 key_idx_mask;
+ u8 key_idx_shift;
+ u8 mic_len;
+};
+
+/**
+ * enum set_key_cmd - key command
+ *
+ * Used with the set_key() callback in &struct ieee80211_ops, this
+ * indicates whether a key is being removed or added.
+ *
+ * @SET_KEY: a key is set
+ * @DISABLE_KEY: a key must be disabled
+ */
+enum set_key_cmd {
+ SET_KEY, DISABLE_KEY,
+};
+
+/**
+ * enum ieee80211_sta_state - station state
+ *
+ * @IEEE80211_STA_NOTEXIST: station doesn't exist at all,
+ * this is a special state for add/remove transitions
+ * @IEEE80211_STA_NONE: station exists without special state
+ * @IEEE80211_STA_AUTH: station is authenticated
+ * @IEEE80211_STA_ASSOC: station is associated
+ * @IEEE80211_STA_AUTHORIZED: station is authorized (802.1X)
+ */
+enum ieee80211_sta_state {
+ /* NOTE: These need to be ordered correctly! */
+ IEEE80211_STA_NOTEXIST,
+ IEEE80211_STA_NONE,
+ IEEE80211_STA_AUTH,
+ IEEE80211_STA_ASSOC,
+ IEEE80211_STA_AUTHORIZED,
+};
+
+/**
+ * enum ieee80211_sta_rx_bandwidth - station RX bandwidth
+ * @IEEE80211_STA_RX_BW_20: station can only receive 20 MHz
+ * @IEEE80211_STA_RX_BW_40: station can receive up to 40 MHz
+ * @IEEE80211_STA_RX_BW_80: station can receive up to 80 MHz
+ * @IEEE80211_STA_RX_BW_160: station can receive up to 160 MHz
+ * (including 80+80 MHz)
+ *
+ * Implementation note: 20 must be zero to be initialized
+ * correctly, the values must be sorted.
+ */
+enum ieee80211_sta_rx_bandwidth {
+ IEEE80211_STA_RX_BW_20 = 0,
+ IEEE80211_STA_RX_BW_40,
+ IEEE80211_STA_RX_BW_80,
+ IEEE80211_STA_RX_BW_160,
+};
+
+/**
+ * struct ieee80211_sta_rates - station rate selection table
+ *
+ * @rcu_head: RCU head used for freeing the table on update
+ * @rate: transmit rates/flags to be used by default.
+ * Overriding entries per-packet is possible by using cb tx control.
+ */
+struct ieee80211_sta_rates {
+ struct rcu_head rcu_head;
+ struct {
+ s8 idx;
+ u8 count;
+ u8 count_cts;
+ u8 count_rts;
+ u16 flags;
+ } rate[IEEE80211_TX_RATE_TABLE_SIZE];
+};
+
+/**
+ * struct ieee80211_sta - station table entry
+ *
+ * A station table entry represents a station we are possibly
+ * communicating with. Since stations are RCU-managed in
+ * mac80211, any ieee80211_sta pointer you get access to must
+ * either be protected by rcu_read_lock() explicitly or implicitly,
+ * or you must take good care to not use such a pointer after a
+ * call to your sta_remove callback that removed it.
+ *
+ * @addr: MAC address
+ * @aid: AID we assigned to the station if we're an AP
+ * @supp_rates: Bitmap of supported rates (per band)
+ * @ht_cap: HT capabilities of this STA; restricted to our own capabilities
+ * @vht_cap: VHT capabilities of this STA; restricted to our own capabilities
+ * @max_rx_aggregation_subframes: maximal amount of frames in a single AMPDU
+ * that this station is allowed to transmit to us.
+ * Can be modified by driver.
+ * @wme: indicates whether the STA supports QoS/WME (if local devices does,
+ * otherwise always false)
+ * @drv_priv: data area for driver use, will always be aligned to
+ * sizeof(void \*), size is determined in hw information.
+ * @uapsd_queues: bitmap of queues configured for uapsd. Only valid
+ * if wme is supported. The bits order is like in
+ * IEEE80211_WMM_IE_STA_QOSINFO_AC_*.
+ * @max_sp: max Service Period. Only valid if wme is supported.
+ * @bandwidth: current bandwidth the station can receive with
+ * @rx_nss: in HT/VHT, the maximum number of spatial streams the
+ * station can receive at the moment, changed by operating mode
+ * notifications and capabilities. The value is only valid after
+ * the station moves to associated state.
+ * @smps_mode: current SMPS mode (off, static or dynamic)
+ * @rates: rate control selection table
+ * @tdls: indicates whether the STA is a TDLS peer
+ * @tdls_initiator: indicates the STA is an initiator of the TDLS link. Only
+ * valid if the STA is a TDLS peer in the first place.
+ * @mfp: indicates whether the STA uses management frame protection or not.
+ * @max_amsdu_subframes: indicates the maximal number of MSDUs in a single
+ * A-MSDU. Taken from the Extended Capabilities element. 0 means
+ * unlimited.
+ * @support_p2p_ps: indicates whether the STA supports P2P PS mechanism or not.
+ * @max_rc_amsdu_len: Maximum A-MSDU size in bytes recommended by rate control.
+ * @txq: per-TID data TX queues (if driver uses the TXQ abstraction)
+ */
+struct ieee80211_sta {
+ u32 supp_rates[NUM_NL80211_BANDS];
+ u8 addr[ETH_ALEN];
+ u16 aid;
+ struct ieee80211_sta_ht_cap ht_cap;
+ struct ieee80211_sta_vht_cap vht_cap;
+ u8 max_rx_aggregation_subframes;
+ bool wme;
+ u8 uapsd_queues;
+ u8 max_sp;
+ u8 rx_nss;
+ enum ieee80211_sta_rx_bandwidth bandwidth;
+ enum ieee80211_smps_mode smps_mode;
+ struct ieee80211_sta_rates __rcu *rates;
+ bool tdls;
+ bool tdls_initiator;
+ bool mfp;
+ u8 max_amsdu_subframes;
+
+ /**
+ * @max_amsdu_len:
+ * indicates the maximal length of an A-MSDU in bytes.
+ * This field is always valid for packets with a VHT preamble.
+ * For packets with a HT preamble, additional limits apply:
+ *
+ * * If the skb is transmitted as part of a BA agreement, the
+ * A-MSDU maximal size is min(max_amsdu_len, 4065) bytes.
+ * * If the skb is not part of a BA aggreement, the A-MSDU maximal
+ * size is min(max_amsdu_len, 7935) bytes.
+ *
+ * Both additional HT limits must be enforced by the low level
+ * driver. This is defined by the spec (IEEE 802.11-2012 section
+ * 8.3.2.2 NOTE 2).
+ */
+ u16 max_amsdu_len;
+ bool support_p2p_ps;
+ u16 max_rc_amsdu_len;
+
+ struct ieee80211_txq *txq[IEEE80211_NUM_TIDS];
+
+ /* must be last */
+ u8 drv_priv[0] __aligned(sizeof(void *));
+};
+
+/**
+ * enum sta_notify_cmd - sta notify command
+ *
+ * Used with the sta_notify() callback in &struct ieee80211_ops, this
+ * indicates if an associated station made a power state transition.
+ *
+ * @STA_NOTIFY_SLEEP: a station is now sleeping
+ * @STA_NOTIFY_AWAKE: a sleeping station woke up
+ */
+enum sta_notify_cmd {
+ STA_NOTIFY_SLEEP, STA_NOTIFY_AWAKE,
+};
+
+/**
+ * struct ieee80211_tx_control - TX control data
+ *
+ * @sta: station table entry, this sta pointer may be NULL and
+ * it is not allowed to copy the pointer, due to RCU.
+ */
+struct ieee80211_tx_control {
+ struct ieee80211_sta *sta;
+};
+
+/**
+ * struct ieee80211_txq - Software intermediate tx queue
+ *
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ * @sta: station table entry, %NULL for per-vif queue
+ * @tid: the TID for this queue (unused for per-vif queue)
+ * @ac: the AC for this queue
+ * @drv_priv: driver private area, sized by hw->txq_data_size
+ *
+ * The driver can obtain packets from this queue by calling
+ * ieee80211_tx_dequeue().
+ */
+struct ieee80211_txq {
+ struct ieee80211_vif *vif;
+ struct ieee80211_sta *sta;
+ u8 tid;
+ u8 ac;
+
+ /* must be last */
+ u8 drv_priv[0] __aligned(sizeof(void *));
+};
+
+/**
+ * enum ieee80211_hw_flags - hardware flags
+ *
+ * These flags are used to indicate hardware capabilities to
+ * the stack. Generally, flags here should have their meaning
+ * done in a way that the simplest hardware doesn't need setting
+ * any particular flags. There are some exceptions to this rule,
+ * however, so you are advised to review these flags carefully.
+ *
+ * @IEEE80211_HW_HAS_RATE_CONTROL:
+ * The hardware or firmware includes rate control, and cannot be
+ * controlled by the stack. As such, no rate control algorithm
+ * should be instantiated, and the TX rate reported to userspace
+ * will be taken from the TX status instead of the rate control
+ * algorithm.
+ * Note that this requires that the driver implement a number of
+ * callbacks so it has the correct information, it needs to have
+ * the @set_rts_threshold callback and must look at the BSS config
+ * @use_cts_prot for G/N protection, @use_short_slot for slot
+ * timing in 2.4 GHz and @use_short_preamble for preambles for
+ * CCK frames.
+ *
+ * @IEEE80211_HW_RX_INCLUDES_FCS:
+ * Indicates that received frames passed to the stack include
+ * the FCS at the end.
+ *
+ * @IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING:
+ * Some wireless LAN chipsets buffer broadcast/multicast frames
+ * for power saving stations in the hardware/firmware and others
+ * rely on the host system for such buffering. This option is used
+ * to configure the IEEE 802.11 upper layer to buffer broadcast and
+ * multicast frames when there are power saving stations so that
+ * the driver can fetch them with ieee80211_get_buffered_bc().
+ *
+ * @IEEE80211_HW_SIGNAL_UNSPEC:
+ * Hardware can provide signal values but we don't know its units. We
+ * expect values between 0 and @max_signal.
+ * If possible please provide dB or dBm instead.
+ *
+ * @IEEE80211_HW_SIGNAL_DBM:
+ * Hardware gives signal values in dBm, decibel difference from
+ * one milliwatt. This is the preferred method since it is standardized
+ * between different devices. @max_signal does not need to be set.
+ *
+ * @IEEE80211_HW_SPECTRUM_MGMT:
+ * Hardware supports spectrum management defined in 802.11h
+ * Measurement, Channel Switch, Quieting, TPC
+ *
+ * @IEEE80211_HW_AMPDU_AGGREGATION:
+ * Hardware supports 11n A-MPDU aggregation.
+ *
+ * @IEEE80211_HW_SUPPORTS_PS:
+ * Hardware has power save support (i.e. can go to sleep).
+ *
+ * @IEEE80211_HW_PS_NULLFUNC_STACK:
+ * Hardware requires nullfunc frame handling in stack, implies
+ * stack support for dynamic PS.
+ *
+ * @IEEE80211_HW_SUPPORTS_DYNAMIC_PS:
+ * Hardware has support for dynamic PS.
+ *
+ * @IEEE80211_HW_MFP_CAPABLE:
+ * Hardware supports management frame protection (MFP, IEEE 802.11w).
+ *
+ * @IEEE80211_HW_REPORTS_TX_ACK_STATUS:
+ * Hardware can provide ack status reports of Tx frames to
+ * the stack.
+ *
+ * @IEEE80211_HW_CONNECTION_MONITOR:
+ * The hardware performs its own connection monitoring, including
+ * periodic keep-alives to the AP and probing the AP on beacon loss.
+ *
+ * @IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC:
+ * This device needs to get data from beacon before association (i.e.
+ * dtim_period).
+ *
+ * @IEEE80211_HW_SUPPORTS_PER_STA_GTK: The device's crypto engine supports
+ * per-station GTKs as used by IBSS RSN or during fast transition. If
+ * the device doesn't support per-station GTKs, but can be asked not
+ * to decrypt group addressed frames, then IBSS RSN support is still
+ * possible but software crypto will be used. Advertise the wiphy flag
+ * only in that case.
+ *
+ * @IEEE80211_HW_AP_LINK_PS: When operating in AP mode the device
+ * autonomously manages the PS status of connected stations. When
+ * this flag is set mac80211 will not trigger PS mode for connected
+ * stations based on the PM bit of incoming frames.
+ * Use ieee80211_start_ps()/ieee8021_end_ps() to manually configure
+ * the PS mode of connected stations.
+ *
+ * @IEEE80211_HW_TX_AMPDU_SETUP_IN_HW: The device handles TX A-MPDU session
+ * setup strictly in HW. mac80211 should not attempt to do this in
+ * software.
+ *
+ * @IEEE80211_HW_WANT_MONITOR_VIF: The driver would like to be informed of
+ * a virtual monitor interface when monitor interfaces are the only
+ * active interfaces.
+ *
+ * @IEEE80211_HW_NO_AUTO_VIF: The driver would like for no wlanX to
+ * be created. It is expected user-space will create vifs as
+ * desired (and thus have them named as desired).
+ *
+ * @IEEE80211_HW_SW_CRYPTO_CONTROL: The driver wants to control which of the
+ * crypto algorithms can be done in software - so don't automatically
+ * try to fall back to it if hardware crypto fails, but do so only if
+ * the driver returns 1. This also forces the driver to advertise its
+ * supported cipher suites.
+ *
+ * @IEEE80211_HW_SUPPORT_FAST_XMIT: The driver/hardware supports fast-xmit,
+ * this currently requires only the ability to calculate the duration
+ * for frames.
+ *
+ * @IEEE80211_HW_QUEUE_CONTROL: The driver wants to control per-interface
+ * queue mapping in order to use different queues (not just one per AC)
+ * for different virtual interfaces. See the doc section on HW queue
+ * control for more details.
+ *
+ * @IEEE80211_HW_SUPPORTS_RC_TABLE: The driver supports using a rate
+ * selection table provided by the rate control algorithm.
+ *
+ * @IEEE80211_HW_P2P_DEV_ADDR_FOR_INTF: Use the P2P Device address for any
+ * P2P Interface. This will be honoured even if more than one interface
+ * is supported.
+ *
+ * @IEEE80211_HW_TIMING_BEACON_ONLY: Use sync timing from beacon frames
+ * only, to allow getting TBTT of a DTIM beacon.
+ *
+ * @IEEE80211_HW_SUPPORTS_HT_CCK_RATES: Hardware supports mixing HT/CCK rates
+ * and can cope with CCK rates in an aggregation session (e.g. by not
+ * using aggregation for such frames.)
+ *
+ * @IEEE80211_HW_CHANCTX_STA_CSA: Support 802.11h based channel-switch (CSA)
+ * for a single active channel while using channel contexts. When support
+ * is not enabled the default action is to disconnect when getting the
+ * CSA frame.
+ *
+ * @IEEE80211_HW_SUPPORTS_CLONED_SKBS: The driver will never modify the payload
+ * or tailroom of TX skbs without copying them first.
+ *
+ * @IEEE80211_HW_SINGLE_SCAN_ON_ALL_BANDS: The HW supports scanning on all bands
+ * in one command, mac80211 doesn't have to run separate scans per band.
+ *
+ * @IEEE80211_HW_TDLS_WIDER_BW: The device/driver supports wider bandwidth
+ * than then BSS bandwidth for a TDLS link on the base channel.
+ *
+ * @IEEE80211_HW_SUPPORTS_AMSDU_IN_AMPDU: The driver supports receiving A-MSDUs
+ * within A-MPDU.
+ *
+ * @IEEE80211_HW_BEACON_TX_STATUS: The device/driver provides TX status
+ * for sent beacons.
+ *
+ * @IEEE80211_HW_NEEDS_UNIQUE_STA_ADDR: Hardware (or driver) requires that each
+ * station has a unique address, i.e. each station entry can be identified
+ * by just its MAC address; this prevents, for example, the same station
+ * from connecting to two virtual AP interfaces at the same time.
+ *
+ * @IEEE80211_HW_SUPPORTS_REORDERING_BUFFER: Hardware (or driver) manages the
+ * reordering buffer internally, guaranteeing mac80211 receives frames in
+ * order and does not need to manage its own reorder buffer or BA session
+ * timeout.
+ *
+ * @IEEE80211_HW_USES_RSS: The device uses RSS and thus requires parallel RX,
+ * which implies using per-CPU station statistics.
+ *
+ * @IEEE80211_HW_TX_AMSDU: Hardware (or driver) supports software aggregated
+ * A-MSDU frames. Requires software tx queueing and fast-xmit support.
+ * When not using minstrel/minstrel_ht rate control, the driver must
+ * limit the maximum A-MSDU size based on the current tx rate by setting
+ * max_rc_amsdu_len in struct ieee80211_sta.
+ *
+ * @IEEE80211_HW_TX_FRAG_LIST: Hardware (or driver) supports sending frag_list
+ * skbs, needed for zero-copy software A-MSDU.
+ *
+ * @IEEE80211_HW_REPORTS_LOW_ACK: The driver (or firmware) reports low ack event
+ * by ieee80211_report_low_ack() based on its own algorithm. For such
+ * drivers, mac80211 packet loss mechanism will not be triggered and driver
+ * is completely depending on firmware event for station kickout.
+ *
+ * @IEEE80211_HW_SUPPORTS_TX_FRAG: Hardware does fragmentation by itself.
+ * The stack will not do fragmentation.
+ * The callback for @set_frag_threshold should be set as well.
+ *
+ * @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays
+ */
+enum ieee80211_hw_flags {
+ IEEE80211_HW_HAS_RATE_CONTROL,
+ IEEE80211_HW_RX_INCLUDES_FCS,
+ IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING,
+ IEEE80211_HW_SIGNAL_UNSPEC,
+ IEEE80211_HW_SIGNAL_DBM,
+ IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC,
+ IEEE80211_HW_SPECTRUM_MGMT,
+ IEEE80211_HW_AMPDU_AGGREGATION,
+ IEEE80211_HW_SUPPORTS_PS,
+ IEEE80211_HW_PS_NULLFUNC_STACK,
+ IEEE80211_HW_SUPPORTS_DYNAMIC_PS,
+ IEEE80211_HW_MFP_CAPABLE,
+ IEEE80211_HW_WANT_MONITOR_VIF,
+ IEEE80211_HW_NO_AUTO_VIF,
+ IEEE80211_HW_SW_CRYPTO_CONTROL,
+ IEEE80211_HW_SUPPORT_FAST_XMIT,
+ IEEE80211_HW_REPORTS_TX_ACK_STATUS,
+ IEEE80211_HW_CONNECTION_MONITOR,
+ IEEE80211_HW_QUEUE_CONTROL,
+ IEEE80211_HW_SUPPORTS_PER_STA_GTK,
+ IEEE80211_HW_AP_LINK_PS,
+ IEEE80211_HW_TX_AMPDU_SETUP_IN_HW,
+ IEEE80211_HW_SUPPORTS_RC_TABLE,
+ IEEE80211_HW_P2P_DEV_ADDR_FOR_INTF,
+ IEEE80211_HW_TIMING_BEACON_ONLY,
+ IEEE80211_HW_SUPPORTS_HT_CCK_RATES,
+ IEEE80211_HW_CHANCTX_STA_CSA,
+ IEEE80211_HW_SUPPORTS_CLONED_SKBS,
+ IEEE80211_HW_SINGLE_SCAN_ON_ALL_BANDS,
+ IEEE80211_HW_TDLS_WIDER_BW,
+ IEEE80211_HW_SUPPORTS_AMSDU_IN_AMPDU,
+ IEEE80211_HW_BEACON_TX_STATUS,
+ IEEE80211_HW_NEEDS_UNIQUE_STA_ADDR,
+ IEEE80211_HW_SUPPORTS_REORDERING_BUFFER,
+ IEEE80211_HW_USES_RSS,
+ IEEE80211_HW_TX_AMSDU,
+ IEEE80211_HW_TX_FRAG_LIST,
+ IEEE80211_HW_REPORTS_LOW_ACK,
+ IEEE80211_HW_SUPPORTS_TX_FRAG,
+
+ /* keep last, obviously */
+ NUM_IEEE80211_HW_FLAGS
+};
+
+/**
+ * struct ieee80211_hw - hardware information and state
+ *
+ * This structure contains the configuration and hardware
+ * information for an 802.11 PHY.
+ *
+ * @wiphy: This points to the &struct wiphy allocated for this
+ * 802.11 PHY. You must fill in the @perm_addr and @dev
+ * members of this structure using SET_IEEE80211_DEV()
+ * and SET_IEEE80211_PERM_ADDR(). Additionally, all supported
+ * bands (with channels, bitrates) are registered here.
+ *
+ * @conf: &struct ieee80211_conf, device configuration, don't use.
+ *
+ * @priv: pointer to private area that was allocated for driver use
+ * along with this structure.
+ *
+ * @flags: hardware flags, see &enum ieee80211_hw_flags.
+ *
+ * @extra_tx_headroom: headroom to reserve in each transmit skb
+ * for use by the driver (e.g. for transmit headers.)
+ *
+ * @extra_beacon_tailroom: tailroom to reserve in each beacon tx skb.
+ * Can be used by drivers to add extra IEs.
+ *
+ * @max_signal: Maximum value for signal (rssi) in RX information, used
+ * only when @IEEE80211_HW_SIGNAL_UNSPEC or @IEEE80211_HW_SIGNAL_DB
+ *
+ * @max_listen_interval: max listen interval in units of beacon interval
+ * that HW supports
+ *
+ * @queues: number of available hardware transmit queues for
+ * data packets. WMM/QoS requires at least four, these
+ * queues need to have configurable access parameters.
+ *
+ * @rate_control_algorithm: rate control algorithm for this hardware.
+ * If unset (NULL), the default algorithm will be used. Must be
+ * set before calling ieee80211_register_hw().
+ *
+ * @vif_data_size: size (in bytes) of the drv_priv data area
+ * within &struct ieee80211_vif.
+ * @sta_data_size: size (in bytes) of the drv_priv data area
+ * within &struct ieee80211_sta.
+ * @chanctx_data_size: size (in bytes) of the drv_priv data area
+ * within &struct ieee80211_chanctx_conf.
+ * @txq_data_size: size (in bytes) of the drv_priv data area
+ * within @struct ieee80211_txq.
+ *
+ * @max_rates: maximum number of alternate rate retry stages the hw
+ * can handle.
+ * @max_report_rates: maximum number of alternate rate retry stages
+ * the hw can report back.
+ * @max_rate_tries: maximum number of tries for each stage
+ *
+ * @max_rx_aggregation_subframes: maximum buffer size (number of
+ * sub-frames) to be used for A-MPDU block ack receiver
+ * aggregation.
+ * This is only relevant if the device has restrictions on the
+ * number of subframes, if it relies on mac80211 to do reordering
+ * it shouldn't be set.
+ *
+ * @max_tx_aggregation_subframes: maximum number of subframes in an
+ * aggregate an HT driver will transmit. Though ADDBA will advertise
+ * a constant value of 64 as some older APs can crash if the window
+ * size is smaller (an example is LinkSys WRT120N with FW v1.0.07
+ * build 002 Jun 18 2012).
+ *
+ * @max_tx_fragments: maximum number of tx buffers per (A)-MSDU, sum
+ * of 1 + skb_shinfo(skb)->nr_frags for each skb in the frag_list.
+ *
+ * @offchannel_tx_hw_queue: HW queue ID to use for offchannel TX
+ * (if %IEEE80211_HW_QUEUE_CONTROL is set)
+ *
+ * @radiotap_mcs_details: lists which MCS information can the HW
+ * reports, by default it is set to _MCS, _GI and _BW but doesn't
+ * include _FMT. Use %IEEE80211_RADIOTAP_MCS_HAVE_\* values, only
+ * adding _BW is supported today.
+ *
+ * @radiotap_vht_details: lists which VHT MCS information the HW reports,
+ * the default is _GI | _BANDWIDTH.
+ * Use the %IEEE80211_RADIOTAP_VHT_KNOWN_\* values.
+ *
+ * @radiotap_timestamp: Information for the radiotap timestamp field; if the
+ * 'units_pos' member is set to a non-negative value it must be set to
+ * a combination of a IEEE80211_RADIOTAP_TIMESTAMP_UNIT_* and a
+ * IEEE80211_RADIOTAP_TIMESTAMP_SPOS_* value, and then the timestamp
+ * field will be added and populated from the &struct ieee80211_rx_status
+ * device_timestamp. If the 'accuracy' member is non-negative, it's put
+ * into the accuracy radiotap field and the accuracy known flag is set.
+ *
+ * @netdev_features: netdev features to be set in each netdev created
+ * from this HW. Note that not all features are usable with mac80211,
+ * other features will be rejected during HW registration.
+ *
+ * @uapsd_queues: This bitmap is included in (re)association frame to indicate
+ * for each access category if it is uAPSD trigger-enabled and delivery-
+ * enabled. Use IEEE80211_WMM_IE_STA_QOSINFO_AC_* to set this bitmap.
+ * Each bit corresponds to different AC. Value '1' in specific bit means
+ * that corresponding AC is both trigger- and delivery-enabled. '0' means
+ * neither enabled.
+ *
+ * @uapsd_max_sp_len: maximum number of total buffered frames the WMM AP may
+ * deliver to a WMM STA during any Service Period triggered by the WMM STA.
+ * Use IEEE80211_WMM_IE_STA_QOSINFO_SP_* for correct values.
+ *
+ * @n_cipher_schemes: a size of an array of cipher schemes definitions.
+ * @cipher_schemes: a pointer to an array of cipher scheme definitions
+ * supported by HW.
+ * @max_nan_de_entries: maximum number of NAN DE functions supported by the
+ * device.
+ */
+struct ieee80211_hw {
+ struct ieee80211_conf conf;
+ struct wiphy *wiphy;
+ const char *rate_control_algorithm;
+ void *priv;
+ unsigned long flags[BITS_TO_LONGS(NUM_IEEE80211_HW_FLAGS)];
+ unsigned int extra_tx_headroom;
+ unsigned int extra_beacon_tailroom;
+ int vif_data_size;
+ int sta_data_size;
+ int chanctx_data_size;
+ int txq_data_size;
+ u16 queues;
+ u16 max_listen_interval;
+ s8 max_signal;
+ u8 max_rates;
+ u8 max_report_rates;
+ u8 max_rate_tries;
+ u8 max_rx_aggregation_subframes;
+ u8 max_tx_aggregation_subframes;
+ u8 max_tx_fragments;
+ u8 offchannel_tx_hw_queue;
+ u8 radiotap_mcs_details;
+ u16 radiotap_vht_details;
+ struct {
+ int units_pos;
+ s16 accuracy;
+ } radiotap_timestamp;
+ netdev_features_t netdev_features;
+ u8 uapsd_queues;
+ u8 uapsd_max_sp_len;
+ u8 n_cipher_schemes;
+ const struct ieee80211_cipher_scheme *cipher_schemes;
+ u8 max_nan_de_entries;
+};
+
+static inline bool _ieee80211_hw_check(struct ieee80211_hw *hw,
+ enum ieee80211_hw_flags flg)
+{
+ return test_bit(flg, hw->flags);
+}
+#define ieee80211_hw_check(hw, flg) _ieee80211_hw_check(hw, IEEE80211_HW_##flg)
+
+static inline void _ieee80211_hw_set(struct ieee80211_hw *hw,
+ enum ieee80211_hw_flags flg)
+{
+ return __set_bit(flg, hw->flags);
+}
+#define ieee80211_hw_set(hw, flg) _ieee80211_hw_set(hw, IEEE80211_HW_##flg)
+
+/**
+ * struct ieee80211_scan_request - hw scan request
+ *
+ * @ies: pointers different parts of IEs (in req.ie)
+ * @req: cfg80211 request.
+ */
+struct ieee80211_scan_request {
+ struct ieee80211_scan_ies ies;
+
+ /* Keep last */
+ struct cfg80211_scan_request req;
+};
+
+/**
+ * struct ieee80211_tdls_ch_sw_params - TDLS channel switch parameters
+ *
+ * @sta: peer this TDLS channel-switch request/response came from
+ * @chandef: channel referenced in a TDLS channel-switch request
+ * @action_code: see &enum ieee80211_tdls_actioncode
+ * @status: channel-switch response status
+ * @timestamp: time at which the frame was received
+ * @switch_time: switch-timing parameter received in the frame
+ * @switch_timeout: switch-timing parameter received in the frame
+ * @tmpl_skb: TDLS switch-channel response template
+ * @ch_sw_tm_ie: offset of the channel-switch timing IE inside @tmpl_skb
+ */
+struct ieee80211_tdls_ch_sw_params {
+ struct ieee80211_sta *sta;
+ struct cfg80211_chan_def *chandef;
+ u8 action_code;
+ u32 status;
+ u32 timestamp;
+ u16 switch_time;
+ u16 switch_timeout;
+ struct sk_buff *tmpl_skb;
+ u32 ch_sw_tm_ie;
+};
+
+/**
+ * wiphy_to_ieee80211_hw - return a mac80211 driver hw struct from a wiphy
+ *
+ * @wiphy: the &struct wiphy which we want to query
+ *
+ * mac80211 drivers can use this to get to their respective
+ * &struct ieee80211_hw. Drivers wishing to get to their own private
+ * structure can then access it via hw->priv. Note that mac802111 drivers should
+ * not use wiphy_priv() to try to get their private driver structure as this
+ * is already used internally by mac80211.
+ *
+ * Return: The mac80211 driver hw struct of @wiphy.
+ */
+struct ieee80211_hw *wiphy_to_ieee80211_hw(struct wiphy *wiphy);
+
+/**
+ * SET_IEEE80211_DEV - set device for 802.11 hardware
+ *
+ * @hw: the &struct ieee80211_hw to set the device for
+ * @dev: the &struct device of this 802.11 device
+ */
+static inline void SET_IEEE80211_DEV(struct ieee80211_hw *hw, struct device *dev)
+{
+ set_wiphy_dev(hw->wiphy, dev);
+}
+
+/**
+ * SET_IEEE80211_PERM_ADDR - set the permanent MAC address for 802.11 hardware
+ *
+ * @hw: the &struct ieee80211_hw to set the MAC address for
+ * @addr: the address to set
+ */
+static inline void SET_IEEE80211_PERM_ADDR(struct ieee80211_hw *hw, const u8 *addr)
+{
+ memcpy(hw->wiphy->perm_addr, addr, ETH_ALEN);
+}
+
+static inline struct ieee80211_rate *
+ieee80211_get_tx_rate(const struct ieee80211_hw *hw,
+ const struct ieee80211_tx_info *c)
+{
+ if (WARN_ON_ONCE(c->control.rates[0].idx < 0))
+ return NULL;
+ return &hw->wiphy->bands[c->band]->bitrates[c->control.rates[0].idx];
+}
+
+static inline struct ieee80211_rate *
+ieee80211_get_rts_cts_rate(const struct ieee80211_hw *hw,
+ const struct ieee80211_tx_info *c)
+{
+ if (c->control.rts_cts_rate_idx < 0)
+ return NULL;
+ return &hw->wiphy->bands[c->band]->bitrates[c->control.rts_cts_rate_idx];
+}
+
+static inline struct ieee80211_rate *
+ieee80211_get_alt_retry_rate(const struct ieee80211_hw *hw,
+ const struct ieee80211_tx_info *c, int idx)
+{
+ if (c->control.rates[idx + 1].idx < 0)
+ return NULL;
+ return &hw->wiphy->bands[c->band]->bitrates[c->control.rates[idx + 1].idx];
+}
+
+/**
+ * ieee80211_free_txskb - free TX skb
+ * @hw: the hardware
+ * @skb: the skb
+ *
+ * Free a transmit skb. Use this funtion when some failure
+ * to transmit happened and thus status cannot be reported.
+ */
+void ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb);
+
+/**
+ * DOC: Hardware crypto acceleration
+ *
+ * mac80211 is capable of taking advantage of many hardware
+ * acceleration designs for encryption and decryption operations.
+ *
+ * The set_key() callback in the &struct ieee80211_ops for a given
+ * device is called to enable hardware acceleration of encryption and
+ * decryption. The callback takes a @sta parameter that will be NULL
+ * for default keys or keys used for transmission only, or point to
+ * the station information for the peer for individual keys.
+ * Multiple transmission keys with the same key index may be used when
+ * VLANs are configured for an access point.
+ *
+ * When transmitting, the TX control data will use the @hw_key_idx
+ * selected by the driver by modifying the &struct ieee80211_key_conf
+ * pointed to by the @key parameter to the set_key() function.
+ *
+ * The set_key() call for the %SET_KEY command should return 0 if
+ * the key is now in use, -%EOPNOTSUPP or -%ENOSPC if it couldn't be
+ * added; if you return 0 then hw_key_idx must be assigned to the
+ * hardware key index, you are free to use the full u8 range.
+ *
+ * Note that in the case that the @IEEE80211_HW_SW_CRYPTO_CONTROL flag is
+ * set, mac80211 will not automatically fall back to software crypto if
+ * enabling hardware crypto failed. The set_key() call may also return the
+ * value 1 to permit this specific key/algorithm to be done in software.
+ *
+ * When the cmd is %DISABLE_KEY then it must succeed.
+ *
+ * Note that it is permissible to not decrypt a frame even if a key
+ * for it has been uploaded to hardware, the stack will not make any
+ * decision based on whether a key has been uploaded or not but rather
+ * based on the receive flags.
+ *
+ * The &struct ieee80211_key_conf structure pointed to by the @key
+ * parameter is guaranteed to be valid until another call to set_key()
+ * removes it, but it can only be used as a cookie to differentiate
+ * keys.
+ *
+ * In TKIP some HW need to be provided a phase 1 key, for RX decryption
+ * acceleration (i.e. iwlwifi). Those drivers should provide update_tkip_key
+ * handler.
+ * The update_tkip_key() call updates the driver with the new phase 1 key.
+ * This happens every time the iv16 wraps around (every 65536 packets). The
+ * set_key() call will happen only once for each key (unless the AP did
+ * rekeying), it will not include a valid phase 1 key. The valid phase 1 key is
+ * provided by update_tkip_key only. The trigger that makes mac80211 call this
+ * handler is software decryption with wrap around of iv16.
+ *
+ * The set_default_unicast_key() call updates the default WEP key index
+ * configured to the hardware for WEP encryption type. This is required
+ * for devices that support offload of data packets (e.g. ARP responses).
+ */
+
+/**
+ * DOC: Powersave support
+ *
+ * mac80211 has support for various powersave implementations.
+ *
+ * First, it can support hardware that handles all powersaving by itself,
+ * such hardware should simply set the %IEEE80211_HW_SUPPORTS_PS hardware
+ * flag. In that case, it will be told about the desired powersave mode
+ * with the %IEEE80211_CONF_PS flag depending on the association status.
+ * The hardware must take care of sending nullfunc frames when necessary,
+ * i.e. when entering and leaving powersave mode. The hardware is required
+ * to look at the AID in beacons and signal to the AP that it woke up when
+ * it finds traffic directed to it.
+ *
+ * %IEEE80211_CONF_PS flag enabled means that the powersave mode defined in
+ * IEEE 802.11-2007 section 11.2 is enabled. This is not to be confused
+ * with hardware wakeup and sleep states. Driver is responsible for waking
+ * up the hardware before issuing commands to the hardware and putting it
+ * back to sleep at appropriate times.
+ *
+ * When PS is enabled, hardware needs to wakeup for beacons and receive the
+ * buffered multicast/broadcast frames after the beacon. Also it must be
+ * possible to send frames and receive the acknowledment frame.
+ *
+ * Other hardware designs cannot send nullfunc frames by themselves and also
+ * need software support for parsing the TIM bitmap. This is also supported
+ * by mac80211 by combining the %IEEE80211_HW_SUPPORTS_PS and
+ * %IEEE80211_HW_PS_NULLFUNC_STACK flags. The hardware is of course still
+ * required to pass up beacons. The hardware is still required to handle
+ * waking up for multicast traffic; if it cannot the driver must handle that
+ * as best as it can, mac80211 is too slow to do that.
+ *
+ * Dynamic powersave is an extension to normal powersave in which the
+ * hardware stays awake for a user-specified period of time after sending a
+ * frame so that reply frames need not be buffered and therefore delayed to
+ * the next wakeup. It's compromise of getting good enough latency when
+ * there's data traffic and still saving significantly power in idle
+ * periods.
+ *
+ * Dynamic powersave is simply supported by mac80211 enabling and disabling
+ * PS based on traffic. Driver needs to only set %IEEE80211_HW_SUPPORTS_PS
+ * flag and mac80211 will handle everything automatically. Additionally,
+ * hardware having support for the dynamic PS feature may set the
+ * %IEEE80211_HW_SUPPORTS_DYNAMIC_PS flag to indicate that it can support
+ * dynamic PS mode itself. The driver needs to look at the
+ * @dynamic_ps_timeout hardware configuration value and use it that value
+ * whenever %IEEE80211_CONF_PS is set. In this case mac80211 will disable
+ * dynamic PS feature in stack and will just keep %IEEE80211_CONF_PS
+ * enabled whenever user has enabled powersave.
+ *
+ * Driver informs U-APSD client support by enabling
+ * %IEEE80211_VIF_SUPPORTS_UAPSD flag. The mode is configured through the
+ * uapsd parameter in conf_tx() operation. Hardware needs to send the QoS
+ * Nullfunc frames and stay awake until the service period has ended. To
+ * utilize U-APSD, dynamic powersave is disabled for voip AC and all frames
+ * from that AC are transmitted with powersave enabled.
+ *
+ * Note: U-APSD client mode is not yet supported with
+ * %IEEE80211_HW_PS_NULLFUNC_STACK.
+ */
+
+/**
+ * DOC: Beacon filter support
+ *
+ * Some hardware have beacon filter support to reduce host cpu wakeups
+ * which will reduce system power consumption. It usually works so that
+ * the firmware creates a checksum of the beacon but omits all constantly
+ * changing elements (TSF, TIM etc). Whenever the checksum changes the
+ * beacon is forwarded to the host, otherwise it will be just dropped. That
+ * way the host will only receive beacons where some relevant information
+ * (for example ERP protection or WMM settings) have changed.
+ *
+ * Beacon filter support is advertised with the %IEEE80211_VIF_BEACON_FILTER
+ * interface capability. The driver needs to enable beacon filter support
+ * whenever power save is enabled, that is %IEEE80211_CONF_PS is set. When
+ * power save is enabled, the stack will not check for beacon loss and the
+ * driver needs to notify about loss of beacons with ieee80211_beacon_loss().
+ *
+ * The time (or number of beacons missed) until the firmware notifies the
+ * driver of a beacon loss event (which in turn causes the driver to call
+ * ieee80211_beacon_loss()) should be configurable and will be controlled
+ * by mac80211 and the roaming algorithm in the future.
+ *
+ * Since there may be constantly changing information elements that nothing
+ * in the software stack cares about, we will, in the future, have mac80211
+ * tell the driver which information elements are interesting in the sense
+ * that we want to see changes in them. This will include
+ *
+ * - a list of information element IDs
+ * - a list of OUIs for the vendor information element
+ *
+ * Ideally, the hardware would filter out any beacons without changes in the
+ * requested elements, but if it cannot support that it may, at the expense
+ * of some efficiency, filter out only a subset. For example, if the device
+ * doesn't support checking for OUIs it should pass up all changes in all
+ * vendor information elements.
+ *
+ * Note that change, for the sake of simplification, also includes information
+ * elements appearing or disappearing from the beacon.
+ *
+ * Some hardware supports an "ignore list" instead, just make sure nothing
+ * that was requested is on the ignore list, and include commonly changing
+ * information element IDs in the ignore list, for example 11 (BSS load) and
+ * the various vendor-assigned IEs with unknown contents (128, 129, 133-136,
+ * 149, 150, 155, 156, 173, 176, 178, 179, 219); for forward compatibility
+ * it could also include some currently unused IDs.
+ *
+ *
+ * In addition to these capabilities, hardware should support notifying the
+ * host of changes in the beacon RSSI. This is relevant to implement roaming
+ * when no traffic is flowing (when traffic is flowing we see the RSSI of
+ * the received data packets). This can consist in notifying the host when
+ * the RSSI changes significantly or when it drops below or rises above
+ * configurable thresholds. In the future these thresholds will also be
+ * configured by mac80211 (which gets them from userspace) to implement
+ * them as the roaming algorithm requires.
+ *
+ * If the hardware cannot implement this, the driver should ask it to
+ * periodically pass beacon frames to the host so that software can do the
+ * signal strength threshold checking.
+ */
+
+/**
+ * DOC: Spatial multiplexing power save
+ *
+ * SMPS (Spatial multiplexing power save) is a mechanism to conserve
+ * power in an 802.11n implementation. For details on the mechanism
+ * and rationale, please refer to 802.11 (as amended by 802.11n-2009)
+ * "11.2.3 SM power save".
+ *
+ * The mac80211 implementation is capable of sending action frames
+ * to update the AP about the station's SMPS mode, and will instruct
+ * the driver to enter the specific mode. It will also announce the
+ * requested SMPS mode during the association handshake. Hardware
+ * support for this feature is required, and can be indicated by
+ * hardware flags.
+ *
+ * The default mode will be "automatic", which nl80211/cfg80211
+ * defines to be dynamic SMPS in (regular) powersave, and SMPS
+ * turned off otherwise.
+ *
+ * To support this feature, the driver must set the appropriate
+ * hardware support flags, and handle the SMPS flag to the config()
+ * operation. It will then with this mechanism be instructed to
+ * enter the requested SMPS mode while associated to an HT AP.
+ */
+
+/**
+ * DOC: Frame filtering
+ *
+ * mac80211 requires to see many management frames for proper
+ * operation, and users may want to see many more frames when
+ * in monitor mode. However, for best CPU usage and power consumption,
+ * having as few frames as possible percolate through the stack is
+ * desirable. Hence, the hardware should filter as much as possible.
+ *
+ * To achieve this, mac80211 uses filter flags (see below) to tell
+ * the driver's configure_filter() function which frames should be
+ * passed to mac80211 and which should be filtered out.
+ *
+ * Before configure_filter() is invoked, the prepare_multicast()
+ * callback is invoked with the parameters @mc_count and @mc_list
+ * for the combined multicast address list of all virtual interfaces.
+ * It's use is optional, and it returns a u64 that is passed to
+ * configure_filter(). Additionally, configure_filter() has the
+ * arguments @changed_flags telling which flags were changed and
+ * @total_flags with the new flag states.
+ *
+ * If your device has no multicast address filters your driver will
+ * need to check both the %FIF_ALLMULTI flag and the @mc_count
+ * parameter to see whether multicast frames should be accepted
+ * or dropped.
+ *
+ * All unsupported flags in @total_flags must be cleared.
+ * Hardware does not support a flag if it is incapable of _passing_
+ * the frame to the stack. Otherwise the driver must ignore
+ * the flag, but not clear it.
+ * You must _only_ clear the flag (announce no support for the
+ * flag to mac80211) if you are not able to pass the packet type
+ * to the stack (so the hardware always filters it).
+ * So for example, you should clear @FIF_CONTROL, if your hardware
+ * always filters control frames. If your hardware always passes
+ * control frames to the kernel and is incapable of filtering them,
+ * you do _not_ clear the @FIF_CONTROL flag.
+ * This rule applies to all other FIF flags as well.
+ */
+
+/**
+ * DOC: AP support for powersaving clients
+ *
+ * In order to implement AP and P2P GO modes, mac80211 has support for
+ * client powersaving, both "legacy" PS (PS-Poll/null data) and uAPSD.
+ * There currently is no support for sAPSD.
+ *
+ * There is one assumption that mac80211 makes, namely that a client
+ * will not poll with PS-Poll and trigger with uAPSD at the same time.
+ * Both are supported, and both can be used by the same client, but
+ * they can't be used concurrently by the same client. This simplifies
+ * the driver code.
+ *
+ * The first thing to keep in mind is that there is a flag for complete
+ * driver implementation: %IEEE80211_HW_AP_LINK_PS. If this flag is set,
+ * mac80211 expects the driver to handle most of the state machine for
+ * powersaving clients and will ignore the PM bit in incoming frames.
+ * Drivers then use ieee80211_sta_ps_transition() to inform mac80211 of
+ * stations' powersave transitions. In this mode, mac80211 also doesn't
+ * handle PS-Poll/uAPSD.
+ *
+ * In the mode without %IEEE80211_HW_AP_LINK_PS, mac80211 will check the
+ * PM bit in incoming frames for client powersave transitions. When a
+ * station goes to sleep, we will stop transmitting to it. There is,
+ * however, a race condition: a station might go to sleep while there is
+ * data buffered on hardware queues. If the device has support for this
+ * it will reject frames, and the driver should give the frames back to
+ * mac80211 with the %IEEE80211_TX_STAT_TX_FILTERED flag set which will
+ * cause mac80211 to retry the frame when the station wakes up. The
+ * driver is also notified of powersave transitions by calling its
+ * @sta_notify callback.
+ *
+ * When the station is asleep, it has three choices: it can wake up,
+ * it can PS-Poll, or it can possibly start a uAPSD service period.
+ * Waking up is implemented by simply transmitting all buffered (and
+ * filtered) frames to the station. This is the easiest case. When
+ * the station sends a PS-Poll or a uAPSD trigger frame, mac80211
+ * will inform the driver of this with the @allow_buffered_frames
+ * callback; this callback is optional. mac80211 will then transmit
+ * the frames as usual and set the %IEEE80211_TX_CTL_NO_PS_BUFFER
+ * on each frame. The last frame in the service period (or the only
+ * response to a PS-Poll) also has %IEEE80211_TX_STATUS_EOSP set to
+ * indicate that it ends the service period; as this frame must have
+ * TX status report it also sets %IEEE80211_TX_CTL_REQ_TX_STATUS.
+ * When TX status is reported for this frame, the service period is
+ * marked has having ended and a new one can be started by the peer.
+ *
+ * Additionally, non-bufferable MMPDUs can also be transmitted by
+ * mac80211 with the %IEEE80211_TX_CTL_NO_PS_BUFFER set in them.
+ *
+ * Another race condition can happen on some devices like iwlwifi
+ * when there are frames queued for the station and it wakes up
+ * or polls; the frames that are already queued could end up being
+ * transmitted first instead, causing reordering and/or wrong
+ * processing of the EOSP. The cause is that allowing frames to be
+ * transmitted to a certain station is out-of-band communication to
+ * the device. To allow this problem to be solved, the driver can
+ * call ieee80211_sta_block_awake() if frames are buffered when it
+ * is notified that the station went to sleep. When all these frames
+ * have been filtered (see above), it must call the function again
+ * to indicate that the station is no longer blocked.
+ *
+ * If the driver buffers frames in the driver for aggregation in any
+ * way, it must use the ieee80211_sta_set_buffered() call when it is
+ * notified of the station going to sleep to inform mac80211 of any
+ * TIDs that have frames buffered. Note that when a station wakes up
+ * this information is reset (hence the requirement to call it when
+ * informed of the station going to sleep). Then, when a service
+ * period starts for any reason, @release_buffered_frames is called
+ * with the number of frames to be released and which TIDs they are
+ * to come from. In this case, the driver is responsible for setting
+ * the EOSP (for uAPSD) and MORE_DATA bits in the released frames,
+ * to help the @more_data parameter is passed to tell the driver if
+ * there is more data on other TIDs -- the TIDs to release frames
+ * from are ignored since mac80211 doesn't know how many frames the
+ * buffers for those TIDs contain.
+ *
+ * If the driver also implement GO mode, where absence periods may
+ * shorten service periods (or abort PS-Poll responses), it must
+ * filter those response frames except in the case of frames that
+ * are buffered in the driver -- those must remain buffered to avoid
+ * reordering. Because it is possible that no frames are released
+ * in this case, the driver must call ieee80211_sta_eosp()
+ * to indicate to mac80211 that the service period ended anyway.
+ *
+ * Finally, if frames from multiple TIDs are released from mac80211
+ * but the driver might reorder them, it must clear & set the flags
+ * appropriately (only the last frame may have %IEEE80211_TX_STATUS_EOSP)
+ * and also take care of the EOSP and MORE_DATA bits in the frame.
+ * The driver may also use ieee80211_sta_eosp() in this case.
+ *
+ * Note that if the driver ever buffers frames other than QoS-data
+ * frames, it must take care to never send a non-QoS-data frame as
+ * the last frame in a service period, adding a QoS-nulldata frame
+ * after a non-QoS-data frame if needed.
+ */
+
+/**
+ * DOC: HW queue control
+ *
+ * Before HW queue control was introduced, mac80211 only had a single static
+ * assignment of per-interface AC software queues to hardware queues. This
+ * was problematic for a few reasons:
+ * 1) off-channel transmissions might get stuck behind other frames
+ * 2) multiple virtual interfaces couldn't be handled correctly
+ * 3) after-DTIM frames could get stuck behind other frames
+ *
+ * To solve this, hardware typically uses multiple different queues for all
+ * the different usages, and this needs to be propagated into mac80211 so it
+ * won't have the same problem with the software queues.
+ *
+ * Therefore, mac80211 now offers the %IEEE80211_HW_QUEUE_CONTROL capability
+ * flag that tells it that the driver implements its own queue control. To do
+ * so, the driver will set up the various queues in each &struct ieee80211_vif
+ * and the offchannel queue in &struct ieee80211_hw. In response, mac80211 will
+ * use those queue IDs in the hw_queue field of &struct ieee80211_tx_info and
+ * if necessary will queue the frame on the right software queue that mirrors
+ * the hardware queue.
+ * Additionally, the driver has to then use these HW queue IDs for the queue
+ * management functions (ieee80211_stop_queue() et al.)
+ *
+ * The driver is free to set up the queue mappings as needed, multiple virtual
+ * interfaces may map to the same hardware queues if needed. The setup has to
+ * happen during add_interface or change_interface callbacks. For example, a
+ * driver supporting station+station and station+AP modes might decide to have
+ * 10 hardware queues to handle different scenarios:
+ *
+ * 4 AC HW queues for 1st vif: 0, 1, 2, 3
+ * 4 AC HW queues for 2nd vif: 4, 5, 6, 7
+ * after-DTIM queue for AP: 8
+ * off-channel queue: 9
+ *
+ * It would then set up the hardware like this:
+ * hw.offchannel_tx_hw_queue = 9
+ *
+ * and the first virtual interface that is added as follows:
+ * vif.hw_queue[IEEE80211_AC_VO] = 0
+ * vif.hw_queue[IEEE80211_AC_VI] = 1
+ * vif.hw_queue[IEEE80211_AC_BE] = 2
+ * vif.hw_queue[IEEE80211_AC_BK] = 3
+ * vif.cab_queue = 8 // if AP mode, otherwise %IEEE80211_INVAL_HW_QUEUE
+ * and the second virtual interface with 4-7.
+ *
+ * If queue 6 gets full, for example, mac80211 would only stop the second
+ * virtual interface's BE queue since virtual interface queues are per AC.
+ *
+ * Note that the vif.cab_queue value should be set to %IEEE80211_INVAL_HW_QUEUE
+ * whenever the queue is not used (i.e. the interface is not in AP mode) if the
+ * queue could potentially be shared since mac80211 will look at cab_queue when
+ * a queue is stopped/woken even if the interface is not in AP mode.
+ */
+
+/**
+ * enum ieee80211_filter_flags - hardware filter flags
+ *
+ * These flags determine what the filter in hardware should be
+ * programmed to let through and what should not be passed to the
+ * stack. It is always safe to pass more frames than requested,
+ * but this has negative impact on power consumption.
+ *
+ * @FIF_ALLMULTI: pass all multicast frames, this is used if requested
+ * by the user or if the hardware is not capable of filtering by
+ * multicast address.
+ *
+ * @FIF_FCSFAIL: pass frames with failed FCS (but you need to set the
+ * %RX_FLAG_FAILED_FCS_CRC for them)
+ *
+ * @FIF_PLCPFAIL: pass frames with failed PLCP CRC (but you need to set
+ * the %RX_FLAG_FAILED_PLCP_CRC for them
+ *
+ * @FIF_BCN_PRBRESP_PROMISC: This flag is set during scanning to indicate
+ * to the hardware that it should not filter beacons or probe responses
+ * by BSSID. Filtering them can greatly reduce the amount of processing
+ * mac80211 needs to do and the amount of CPU wakeups, so you should
+ * honour this flag if possible.
+ *
+ * @FIF_CONTROL: pass control frames (except for PS Poll) addressed to this
+ * station
+ *
+ * @FIF_OTHER_BSS: pass frames destined to other BSSes
+ *
+ * @FIF_PSPOLL: pass PS Poll frames
+ *
+ * @FIF_PROBE_REQ: pass probe request frames
+ */
+enum ieee80211_filter_flags {
+ FIF_ALLMULTI = 1<<1,
+ FIF_FCSFAIL = 1<<2,
+ FIF_PLCPFAIL = 1<<3,
+ FIF_BCN_PRBRESP_PROMISC = 1<<4,
+ FIF_CONTROL = 1<<5,
+ FIF_OTHER_BSS = 1<<6,
+ FIF_PSPOLL = 1<<7,
+ FIF_PROBE_REQ = 1<<8,
+};
+
+/**
+ * enum ieee80211_ampdu_mlme_action - A-MPDU actions
+ *
+ * These flags are used with the ampdu_action() callback in
+ * &struct ieee80211_ops to indicate which action is needed.
+ *
+ * Note that drivers MUST be able to deal with a TX aggregation
+ * session being stopped even before they OK'ed starting it by
+ * calling ieee80211_start_tx_ba_cb_irqsafe, because the peer
+ * might receive the addBA frame and send a delBA right away!
+ *
+ * @IEEE80211_AMPDU_RX_START: start RX aggregation
+ * @IEEE80211_AMPDU_RX_STOP: stop RX aggregation
+ * @IEEE80211_AMPDU_TX_START: start TX aggregation
+ * @IEEE80211_AMPDU_TX_OPERATIONAL: TX aggregation has become operational
+ * @IEEE80211_AMPDU_TX_STOP_CONT: stop TX aggregation but continue transmitting
+ * queued packets, now unaggregated. After all packets are transmitted the
+ * driver has to call ieee80211_stop_tx_ba_cb_irqsafe().
+ * @IEEE80211_AMPDU_TX_STOP_FLUSH: stop TX aggregation and flush all packets,
+ * called when the station is removed. There's no need or reason to call
+ * ieee80211_stop_tx_ba_cb_irqsafe() in this case as mac80211 assumes the
+ * session is gone and removes the station.
+ * @IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: called when TX aggregation is stopped
+ * but the driver hasn't called ieee80211_stop_tx_ba_cb_irqsafe() yet and
+ * now the connection is dropped and the station will be removed. Drivers
+ * should clean up and drop remaining packets when this is called.
+ */
+enum ieee80211_ampdu_mlme_action {
+ IEEE80211_AMPDU_RX_START,
+ IEEE80211_AMPDU_RX_STOP,
+ IEEE80211_AMPDU_TX_START,
+ IEEE80211_AMPDU_TX_STOP_CONT,
+ IEEE80211_AMPDU_TX_STOP_FLUSH,
+ IEEE80211_AMPDU_TX_STOP_FLUSH_CONT,
+ IEEE80211_AMPDU_TX_OPERATIONAL,
+};
+
+/**
+ * struct ieee80211_ampdu_params - AMPDU action parameters
+ *
+ * @action: the ampdu action, value from %ieee80211_ampdu_mlme_action.
+ * @sta: peer of this AMPDU session
+ * @tid: tid of the BA session
+ * @ssn: start sequence number of the session. TX/RX_STOP can pass 0. When
+ * action is set to %IEEE80211_AMPDU_RX_START the driver passes back the
+ * actual ssn value used to start the session and writes the value here.
+ * @buf_size: reorder buffer size (number of subframes). Valid only when the
+ * action is set to %IEEE80211_AMPDU_RX_START or
+ * %IEEE80211_AMPDU_TX_OPERATIONAL
+ * @amsdu: indicates the peer's ability to receive A-MSDU within A-MPDU.
+ * valid when the action is set to %IEEE80211_AMPDU_TX_OPERATIONAL
+ * @timeout: BA session timeout. Valid only when the action is set to
+ * %IEEE80211_AMPDU_RX_START
+ */
+struct ieee80211_ampdu_params {
+ enum ieee80211_ampdu_mlme_action action;
+ struct ieee80211_sta *sta;
+ u16 tid;
+ u16 ssn;
+ u8 buf_size;
+ bool amsdu;
+ u16 timeout;
+};
+
+/**
+ * enum ieee80211_frame_release_type - frame release reason
+ * @IEEE80211_FRAME_RELEASE_PSPOLL: frame released for PS-Poll
+ * @IEEE80211_FRAME_RELEASE_UAPSD: frame(s) released due to
+ * frame received on trigger-enabled AC
+ */
+enum ieee80211_frame_release_type {
+ IEEE80211_FRAME_RELEASE_PSPOLL,
+ IEEE80211_FRAME_RELEASE_UAPSD,
+};
+
+/**
+ * enum ieee80211_rate_control_changed - flags to indicate what changed
+ *
+ * @IEEE80211_RC_BW_CHANGED: The bandwidth that can be used to transmit
+ * to this station changed. The actual bandwidth is in the station
+ * information -- for HT20/40 the IEEE80211_HT_CAP_SUP_WIDTH_20_40
+ * flag changes, for HT and VHT the bandwidth field changes.
+ * @IEEE80211_RC_SMPS_CHANGED: The SMPS state of the station changed.
+ * @IEEE80211_RC_SUPP_RATES_CHANGED: The supported rate set of this peer
+ * changed (in IBSS mode) due to discovering more information about
+ * the peer.
+ * @IEEE80211_RC_NSS_CHANGED: N_SS (number of spatial streams) was changed
+ * by the peer
+ */
+enum ieee80211_rate_control_changed {
+ IEEE80211_RC_BW_CHANGED = BIT(0),
+ IEEE80211_RC_SMPS_CHANGED = BIT(1),
+ IEEE80211_RC_SUPP_RATES_CHANGED = BIT(2),
+ IEEE80211_RC_NSS_CHANGED = BIT(3),
+};
+
+/**
+ * enum ieee80211_roc_type - remain on channel type
+ *
+ * With the support for multi channel contexts and multi channel operations,
+ * remain on channel operations might be limited/deferred/aborted by other
+ * flows/operations which have higher priority (and vise versa).
+ * Specifying the ROC type can be used by devices to prioritize the ROC
+ * operations compared to other operations/flows.
+ *
+ * @IEEE80211_ROC_TYPE_NORMAL: There are no special requirements for this ROC.
+ * @IEEE80211_ROC_TYPE_MGMT_TX: The remain on channel request is required
+ * for sending managment frames offchannel.
+ */
+enum ieee80211_roc_type {
+ IEEE80211_ROC_TYPE_NORMAL = 0,
+ IEEE80211_ROC_TYPE_MGMT_TX,
+};
+
+/**
+ * enum ieee80211_reconfig_complete_type - reconfig type
+ *
+ * This enum is used by the reconfig_complete() callback to indicate what
+ * reconfiguration type was completed.
+ *
+ * @IEEE80211_RECONFIG_TYPE_RESTART: hw restart type
+ * (also due to resume() callback returning 1)
+ * @IEEE80211_RECONFIG_TYPE_SUSPEND: suspend type (regardless
+ * of wowlan configuration)
+ */
+enum ieee80211_reconfig_type {
+ IEEE80211_RECONFIG_TYPE_RESTART,
+ IEEE80211_RECONFIG_TYPE_SUSPEND,
+};
+
+/**
+ * struct ieee80211_ops - callbacks from mac80211 to the driver
+ *
+ * This structure contains various callbacks that the driver may
+ * handle or, in some cases, must handle, for example to configure
+ * the hardware to a new channel or to transmit a frame.
+ *
+ * @tx: Handler that 802.11 module calls for each transmitted frame.
+ * skb contains the buffer starting from the IEEE 802.11 header.
+ * The low-level driver should send the frame out based on
+ * configuration in the TX control data. This handler should,
+ * preferably, never fail and stop queues appropriately.
+ * Must be atomic.
+ *
+ * @start: Called before the first netdevice attached to the hardware
+ * is enabled. This should turn on the hardware and must turn on
+ * frame reception (for possibly enabled monitor interfaces.)
+ * Returns negative error codes, these may be seen in userspace,
+ * or zero.
+ * When the device is started it should not have a MAC address
+ * to avoid acknowledging frames before a non-monitor device
+ * is added.
+ * Must be implemented and can sleep.
+ *
+ * @stop: Called after last netdevice attached to the hardware
+ * is disabled. This should turn off the hardware (at least
+ * it must turn off frame reception.)
+ * May be called right after add_interface if that rejects
+ * an interface. If you added any work onto the mac80211 workqueue
+ * you should ensure to cancel it on this callback.
+ * Must be implemented and can sleep.
+ *
+ * @suspend: Suspend the device; mac80211 itself will quiesce before and
+ * stop transmitting and doing any other configuration, and then
+ * ask the device to suspend. This is only invoked when WoWLAN is
+ * configured, otherwise the device is deconfigured completely and
+ * reconfigured at resume time.
+ * The driver may also impose special conditions under which it
+ * wants to use the "normal" suspend (deconfigure), say if it only
+ * supports WoWLAN when the device is associated. In this case, it
+ * must return 1 from this function.
+ *
+ * @resume: If WoWLAN was configured, this indicates that mac80211 is
+ * now resuming its operation, after this the device must be fully
+ * functional again. If this returns an error, the only way out is
+ * to also unregister the device. If it returns 1, then mac80211
+ * will also go through the regular complete restart on resume.
+ *
+ * @set_wakeup: Enable or disable wakeup when WoWLAN configuration is
+ * modified. The reason is that device_set_wakeup_enable() is
+ * supposed to be called when the configuration changes, not only
+ * in suspend().
+ *
+ * @add_interface: Called when a netdevice attached to the hardware is
+ * enabled. Because it is not called for monitor mode devices, @start
+ * and @stop must be implemented.
+ * The driver should perform any initialization it needs before
+ * the device can be enabled. The initial configuration for the
+ * interface is given in the conf parameter.
+ * The callback may refuse to add an interface by returning a
+ * negative error code (which will be seen in userspace.)
+ * Must be implemented and can sleep.
+ *
+ * @change_interface: Called when a netdevice changes type. This callback
+ * is optional, but only if it is supported can interface types be
+ * switched while the interface is UP. The callback may sleep.
+ * Note that while an interface is being switched, it will not be
+ * found by the interface iteration callbacks.
+ *
+ * @remove_interface: Notifies a driver that an interface is going down.
+ * The @stop callback is called after this if it is the last interface
+ * and no monitor interfaces are present.
+ * When all interfaces are removed, the MAC address in the hardware
+ * must be cleared so the device no longer acknowledges packets,
+ * the mac_addr member of the conf structure is, however, set to the
+ * MAC address of the device going away.
+ * Hence, this callback must be implemented. It can sleep.
+ *
+ * @config: Handler for configuration requests. IEEE 802.11 code calls this
+ * function to change hardware configuration, e.g., channel.
+ * This function should never fail but returns a negative error code
+ * if it does. The callback can sleep.
+ *
+ * @bss_info_changed: Handler for configuration requests related to BSS
+ * parameters that may vary during BSS's lifespan, and may affect low
+ * level driver (e.g. assoc/disassoc status, erp parameters).
+ * This function should not be used if no BSS has been set, unless
+ * for association indication. The @changed parameter indicates which
+ * of the bss parameters has changed when a call is made. The callback
+ * can sleep.
+ *
+ * @prepare_multicast: Prepare for multicast filter configuration.
+ * This callback is optional, and its return value is passed
+ * to configure_filter(). This callback must be atomic.
+ *
+ * @configure_filter: Configure the device's RX filter.
+ * See the section "Frame filtering" for more information.
+ * This callback must be implemented and can sleep.
+ *
+ * @config_iface_filter: Configure the interface's RX filter.
+ * This callback is optional and is used to configure which frames
+ * should be passed to mac80211. The filter_flags is the combination
+ * of FIF_* flags. The changed_flags is a bit mask that indicates
+ * which flags are changed.
+ * This callback can sleep.
+ *
+ * @set_tim: Set TIM bit. mac80211 calls this function when a TIM bit
+ * must be set or cleared for a given STA. Must be atomic.
+ *
+ * @set_key: See the section "Hardware crypto acceleration"
+ * This callback is only called between add_interface and
+ * remove_interface calls, i.e. while the given virtual interface
+ * is enabled.
+ * Returns a negative error code if the key can't be added.
+ * The callback can sleep.
+ *
+ * @update_tkip_key: See the section "Hardware crypto acceleration"
+ * This callback will be called in the context of Rx. Called for drivers
+ * which set IEEE80211_KEY_FLAG_TKIP_REQ_RX_P1_KEY.
+ * The callback must be atomic.
+ *
+ * @set_rekey_data: If the device supports GTK rekeying, for example while the
+ * host is suspended, it can assign this callback to retrieve the data
+ * necessary to do GTK rekeying, this is the KEK, KCK and replay counter.
+ * After rekeying was done it should (for example during resume) notify
+ * userspace of the new replay counter using ieee80211_gtk_rekey_notify().
+ *
+ * @set_default_unicast_key: Set the default (unicast) key index, useful for
+ * WEP when the device sends data packets autonomously, e.g. for ARP
+ * offloading. The index can be 0-3, or -1 for unsetting it.
+ *
+ * @hw_scan: Ask the hardware to service the scan request, no need to start
+ * the scan state machine in stack. The scan must honour the channel
+ * configuration done by the regulatory agent in the wiphy's
+ * registered bands. The hardware (or the driver) needs to make sure
+ * that power save is disabled.
+ * The @req ie/ie_len members are rewritten by mac80211 to contain the
+ * entire IEs after the SSID, so that drivers need not look at these
+ * at all but just send them after the SSID -- mac80211 includes the
+ * (extended) supported rates and HT information (where applicable).
+ * When the scan finishes, ieee80211_scan_completed() must be called;
+ * note that it also must be called when the scan cannot finish due to
+ * any error unless this callback returned a negative error code.
+ * The callback can sleep.
+ *
+ * @cancel_hw_scan: Ask the low-level tp cancel the active hw scan.
+ * The driver should ask the hardware to cancel the scan (if possible),
+ * but the scan will be completed only after the driver will call
+ * ieee80211_scan_completed().
+ * This callback is needed for wowlan, to prevent enqueueing a new
+ * scan_work after the low-level driver was already suspended.
+ * The callback can sleep.
+ *
+ * @sched_scan_start: Ask the hardware to start scanning repeatedly at
+ * specific intervals. The driver must call the
+ * ieee80211_sched_scan_results() function whenever it finds results.
+ * This process will continue until sched_scan_stop is called.
+ *
+ * @sched_scan_stop: Tell the hardware to stop an ongoing scheduled scan.
+ * In this case, ieee80211_sched_scan_stopped() must not be called.
+ *
+ * @sw_scan_start: Notifier function that is called just before a software scan
+ * is started. Can be NULL, if the driver doesn't need this notification.
+ * The mac_addr parameter allows supporting NL80211_SCAN_FLAG_RANDOM_ADDR,
+ * the driver may set the NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR flag if it
+ * can use this parameter. The callback can sleep.
+ *
+ * @sw_scan_complete: Notifier function that is called just after a
+ * software scan finished. Can be NULL, if the driver doesn't need
+ * this notification.
+ * The callback can sleep.
+ *
+ * @get_stats: Return low-level statistics.
+ * Returns zero if statistics are available.
+ * The callback can sleep.
+ *
+ * @get_key_seq: If your device implements encryption in hardware and does
+ * IV/PN assignment then this callback should be provided to read the
+ * IV/PN for the given key from hardware.
+ * The callback must be atomic.
+ *
+ * @set_frag_threshold: Configuration of fragmentation threshold. Assign this
+ * if the device does fragmentation by itself. Note that to prevent the
+ * stack from doing fragmentation IEEE80211_HW_SUPPORTS_TX_FRAG
+ * should be set as well.
+ * The callback can sleep.
+ *
+ * @set_rts_threshold: Configuration of RTS threshold (if device needs it)
+ * The callback can sleep.
+ *
+ * @sta_add: Notifies low level driver about addition of an associated station,
+ * AP, IBSS/WDS/mesh peer etc. This callback can sleep.
+ *
+ * @sta_remove: Notifies low level driver about removal of an associated
+ * station, AP, IBSS/WDS/mesh peer etc. Note that after the callback
+ * returns it isn't safe to use the pointer, not even RCU protected;
+ * no RCU grace period is guaranteed between returning here and freeing
+ * the station. See @sta_pre_rcu_remove if needed.
+ * This callback can sleep.
+ *
+ * @sta_add_debugfs: Drivers can use this callback to add debugfs files
+ * when a station is added to mac80211's station list. This callback
+ * should be within a CONFIG_MAC80211_DEBUGFS conditional. This
+ * callback can sleep.
+ *
+ * @sta_notify: Notifies low level driver about power state transition of an
+ * associated station, AP, IBSS/WDS/mesh peer etc. For a VIF operating
+ * in AP mode, this callback will not be called when the flag
+ * %IEEE80211_HW_AP_LINK_PS is set. Must be atomic.
+ *
+ * @sta_state: Notifies low level driver about state transition of a
+ * station (which can be the AP, a client, IBSS/WDS/mesh peer etc.)
+ * This callback is mutually exclusive with @sta_add/@sta_remove.
+ * It must not fail for down transitions but may fail for transitions
+ * up the list of states. Also note that after the callback returns it
+ * isn't safe to use the pointer, not even RCU protected - no RCU grace
+ * period is guaranteed between returning here and freeing the station.
+ * See @sta_pre_rcu_remove if needed.
+ * The callback can sleep.
+ *
+ * @sta_pre_rcu_remove: Notify driver about station removal before RCU
+ * synchronisation. This is useful if a driver needs to have station
+ * pointers protected using RCU, it can then use this call to clear
+ * the pointers instead of waiting for an RCU grace period to elapse
+ * in @sta_state.
+ * The callback can sleep.
+ *
+ * @sta_rc_update: Notifies the driver of changes to the bitrates that can be
+ * used to transmit to the station. The changes are advertised with bits
+ * from &enum ieee80211_rate_control_changed and the values are reflected
+ * in the station data. This callback should only be used when the driver
+ * uses hardware rate control (%IEEE80211_HW_HAS_RATE_CONTROL) since
+ * otherwise the rate control algorithm is notified directly.
+ * Must be atomic.
+ * @sta_rate_tbl_update: Notifies the driver that the rate table changed. This
+ * is only used if the configured rate control algorithm actually uses
+ * the new rate table API, and is therefore optional. Must be atomic.
+ *
+ * @sta_statistics: Get statistics for this station. For example with beacon
+ * filtering, the statistics kept by mac80211 might not be accurate, so
+ * let the driver pre-fill the statistics. The driver can fill most of
+ * the values (indicating which by setting the filled bitmap), but not
+ * all of them make sense - see the source for which ones are possible.
+ * Statistics that the driver doesn't fill will be filled by mac80211.
+ * The callback can sleep.
+ *
+ * @conf_tx: Configure TX queue parameters (EDCF (aifs, cw_min, cw_max),
+ * bursting) for a hardware TX queue.
+ * Returns a negative error code on failure.
+ * The callback can sleep.
+ *
+ * @get_tsf: Get the current TSF timer value from firmware/hardware. Currently,
+ * this is only used for IBSS mode BSSID merging and debugging. Is not a
+ * required function.
+ * The callback can sleep.
+ *
+ * @set_tsf: Set the TSF timer to the specified value in the firmware/hardware.
+ * Currently, this is only used for IBSS mode debugging. Is not a
+ * required function.
+ * The callback can sleep.
+ *
+ * @offset_tsf: Offset the TSF timer by the specified value in the
+ * firmware/hardware. Preferred to set_tsf as it avoids delay between
+ * calling set_tsf() and hardware getting programmed, which will show up
+ * as TSF delay. Is not a required function.
+ * The callback can sleep.
+ *
+ * @reset_tsf: Reset the TSF timer and allow firmware/hardware to synchronize
+ * with other STAs in the IBSS. This is only used in IBSS mode. This
+ * function is optional if the firmware/hardware takes full care of
+ * TSF synchronization.
+ * The callback can sleep.
+ *
+ * @tx_last_beacon: Determine whether the last IBSS beacon was sent by us.
+ * This is needed only for IBSS mode and the result of this function is
+ * used to determine whether to reply to Probe Requests.
+ * Returns non-zero if this device sent the last beacon.
+ * The callback can sleep.
+ *
+ * @get_survey: Return per-channel survey information
+ *
+ * @rfkill_poll: Poll rfkill hardware state. If you need this, you also
+ * need to set wiphy->rfkill_poll to %true before registration,
+ * and need to call wiphy_rfkill_set_hw_state() in the callback.
+ * The callback can sleep.
+ *
+ * @set_coverage_class: Set slot time for given coverage class as specified
+ * in IEEE 802.11-2007 section 17.3.8.6 and modify ACK timeout
+ * accordingly; coverage class equals to -1 to enable ACK timeout
+ * estimation algorithm (dynack). To disable dynack set valid value for
+ * coverage class. This callback is not required and may sleep.
+ *
+ * @testmode_cmd: Implement a cfg80211 test mode command. The passed @vif may
+ * be %NULL. The callback can sleep.
+ * @testmode_dump: Implement a cfg80211 test mode dump. The callback can sleep.
+ *
+ * @flush: Flush all pending frames from the hardware queue, making sure
+ * that the hardware queues are empty. The @queues parameter is a bitmap
+ * of queues to flush, which is useful if different virtual interfaces
+ * use different hardware queues; it may also indicate all queues.
+ * If the parameter @drop is set to %true, pending frames may be dropped.
+ * Note that vif can be NULL.
+ * The callback can sleep.
+ *
+ * @channel_switch: Drivers that need (or want) to offload the channel
+ * switch operation for CSAs received from the AP may implement this
+ * callback. They must then call ieee80211_chswitch_done() to indicate
+ * completion of the channel switch.
+ *
+ * @set_antenna: Set antenna configuration (tx_ant, rx_ant) on the device.
+ * Parameters are bitmaps of allowed antennas to use for TX/RX. Drivers may
+ * reject TX/RX mask combinations they cannot support by returning -EINVAL
+ * (also see nl80211.h @NL80211_ATTR_WIPHY_ANTENNA_TX).
+ *
+ * @get_antenna: Get current antenna configuration from device (tx_ant, rx_ant).
+ *
+ * @remain_on_channel: Starts an off-channel period on the given channel, must
+ * call back to ieee80211_ready_on_channel() when on that channel. Note
+ * that normal channel traffic is not stopped as this is intended for hw
+ * offload. Frames to transmit on the off-channel channel are transmitted
+ * normally except for the %IEEE80211_TX_CTL_TX_OFFCHAN flag. When the
+ * duration (which will always be non-zero) expires, the driver must call
+ * ieee80211_remain_on_channel_expired().
+ * Note that this callback may be called while the device is in IDLE and
+ * must be accepted in this case.
+ * This callback may sleep.
+ * @cancel_remain_on_channel: Requests that an ongoing off-channel period is
+ * aborted before it expires. This callback may sleep.
+ *
+ * @set_ringparam: Set tx and rx ring sizes.
+ *
+ * @get_ringparam: Get tx and rx ring current and maximum sizes.
+ *
+ * @tx_frames_pending: Check if there is any pending frame in the hardware
+ * queues before entering power save.
+ *
+ * @set_bitrate_mask: Set a mask of rates to be used for rate control selection
+ * when transmitting a frame. Currently only legacy rates are handled.
+ * The callback can sleep.
+ * @event_callback: Notify driver about any event in mac80211. See
+ * &enum ieee80211_event_type for the different types.
+ * The callback must be atomic.
+ *
+ * @release_buffered_frames: Release buffered frames according to the given
+ * parameters. In the case where the driver buffers some frames for
+ * sleeping stations mac80211 will use this callback to tell the driver
+ * to release some frames, either for PS-poll or uAPSD.
+ * Note that if the @more_data parameter is %false the driver must check
+ * if there are more frames on the given TIDs, and if there are more than
+ * the frames being released then it must still set the more-data bit in
+ * the frame. If the @more_data parameter is %true, then of course the
+ * more-data bit must always be set.
+ * The @tids parameter tells the driver which TIDs to release frames
+ * from, for PS-poll it will always have only a single bit set.
+ * In the case this is used for a PS-poll initiated release, the
+ * @num_frames parameter will always be 1 so code can be shared. In
+ * this case the driver must also set %IEEE80211_TX_STATUS_EOSP flag
+ * on the TX status (and must report TX status) so that the PS-poll
+ * period is properly ended. This is used to avoid sending multiple
+ * responses for a retried PS-poll frame.
+ * In the case this is used for uAPSD, the @num_frames parameter may be
+ * bigger than one, but the driver may send fewer frames (it must send
+ * at least one, however). In this case it is also responsible for
+ * setting the EOSP flag in the QoS header of the frames. Also, when the
+ * service period ends, the driver must set %IEEE80211_TX_STATUS_EOSP
+ * on the last frame in the SP. Alternatively, it may call the function
+ * ieee80211_sta_eosp() to inform mac80211 of the end of the SP.
+ * This callback must be atomic.
+ * @allow_buffered_frames: Prepare device to allow the given number of frames
+ * to go out to the given station. The frames will be sent by mac80211
+ * via the usual TX path after this call. The TX information for frames
+ * released will also have the %IEEE80211_TX_CTL_NO_PS_BUFFER flag set
+ * and the last one will also have %IEEE80211_TX_STATUS_EOSP set. In case
+ * frames from multiple TIDs are released and the driver might reorder
+ * them between the TIDs, it must set the %IEEE80211_TX_STATUS_EOSP flag
+ * on the last frame and clear it on all others and also handle the EOSP
+ * bit in the QoS header correctly. Alternatively, it can also call the
+ * ieee80211_sta_eosp() function.
+ * The @tids parameter is a bitmap and tells the driver which TIDs the
+ * frames will be on; it will at most have two bits set.
+ * This callback must be atomic.
+ *
+ * @get_et_sset_count: Ethtool API to get string-set count.
+ *
+ * @get_et_stats: Ethtool API to get a set of u64 stats.
+ *
+ * @get_et_strings: Ethtool API to get a set of strings to describe stats
+ * and perhaps other supported types of ethtool data-sets.
+ *
+ * @mgd_prepare_tx: Prepare for transmitting a management frame for association
+ * before associated. In multi-channel scenarios, a virtual interface is
+ * bound to a channel before it is associated, but as it isn't associated
+ * yet it need not necessarily be given airtime, in particular since any
+ * transmission to a P2P GO needs to be synchronized against the GO's
+ * powersave state. mac80211 will call this function before transmitting a
+ * management frame prior to having successfully associated to allow the
+ * driver to give it channel time for the transmission, to get a response
+ * and to be able to synchronize with the GO.
+ * The callback will be called before each transmission and upon return
+ * mac80211 will transmit the frame right away.
+ * The callback is optional and can (should!) sleep.
+ *
+ * @mgd_protect_tdls_discover: Protect a TDLS discovery session. After sending
+ * a TDLS discovery-request, we expect a reply to arrive on the AP's
+ * channel. We must stay on the channel (no PSM, scan, etc.), since a TDLS
+ * setup-response is a direct packet not buffered by the AP.
+ * mac80211 will call this function just before the transmission of a TDLS
+ * discovery-request. The recommended period of protection is at least
+ * 2 * (DTIM period).
+ * The callback is optional and can sleep.
+ *
+ * @add_chanctx: Notifies device driver about new channel context creation.
+ * This callback may sleep.
+ * @remove_chanctx: Notifies device driver about channel context destruction.
+ * This callback may sleep.
+ * @change_chanctx: Notifies device driver about channel context changes that
+ * may happen when combining different virtual interfaces on the same
+ * channel context with different settings
+ * This callback may sleep.
+ * @assign_vif_chanctx: Notifies device driver about channel context being bound
+ * to vif. Possible use is for hw queue remapping.
+ * This callback may sleep.
+ * @unassign_vif_chanctx: Notifies device driver about channel context being
+ * unbound from vif.
+ * This callback may sleep.
+ * @switch_vif_chanctx: switch a number of vifs from one chanctx to
+ * another, as specified in the list of
+ * @ieee80211_vif_chanctx_switch passed to the driver, according
+ * to the mode defined in &ieee80211_chanctx_switch_mode.
+ * This callback may sleep.
+ *
+ * @start_ap: Start operation on the AP interface, this is called after all the
+ * information in bss_conf is set and beacon can be retrieved. A channel
+ * context is bound before this is called. Note that if the driver uses
+ * software scan or ROC, this (and @stop_ap) isn't called when the AP is
+ * just "paused" for scanning/ROC, which is indicated by the beacon being
+ * disabled/enabled via @bss_info_changed.
+ * @stop_ap: Stop operation on the AP interface.
+ *
+ * @reconfig_complete: Called after a call to ieee80211_restart_hw() and
+ * during resume, when the reconfiguration has completed.
+ * This can help the driver implement the reconfiguration step (and
+ * indicate mac80211 is ready to receive frames).
+ * This callback may sleep.
+ *
+ * @ipv6_addr_change: IPv6 address assignment on the given interface changed.
+ * Currently, this is only called for managed or P2P client interfaces.
+ * This callback is optional; it must not sleep.
+ *
+ * @channel_switch_beacon: Starts a channel switch to a new channel.
+ * Beacons are modified to include CSA or ECSA IEs before calling this
+ * function. The corresponding count fields in these IEs must be
+ * decremented, and when they reach 1 the driver must call
+ * ieee80211_csa_finish(). Drivers which use ieee80211_beacon_get()
+ * get the csa counter decremented by mac80211, but must check if it is
+ * 1 using ieee80211_csa_is_complete() after the beacon has been
+ * transmitted and then call ieee80211_csa_finish().
+ * If the CSA count starts as zero or 1, this function will not be called,
+ * since there won't be any time to beacon before the switch anyway.
+ * @pre_channel_switch: This is an optional callback that is called
+ * before a channel switch procedure is started (ie. when a STA
+ * gets a CSA or a userspace initiated channel-switch), allowing
+ * the driver to prepare for the channel switch.
+ * @post_channel_switch: This is an optional callback that is called
+ * after a channel switch procedure is completed, allowing the
+ * driver to go back to a normal configuration.
+ *
+ * @join_ibss: Join an IBSS (on an IBSS interface); this is called after all
+ * information in bss_conf is set up and the beacon can be retrieved. A
+ * channel context is bound before this is called.
+ * @leave_ibss: Leave the IBSS again.
+ *
+ * @get_expected_throughput: extract the expected throughput towards the
+ * specified station. The returned value is expressed in Kbps. It returns 0
+ * if the RC algorithm does not have proper data to provide.
+ *
+ * @get_txpower: get current maximum tx power (in dBm) based on configuration
+ * and hardware limits.
+ *
+ * @tdls_channel_switch: Start channel-switching with a TDLS peer. The driver
+ * is responsible for continually initiating channel-switching operations
+ * and returning to the base channel for communication with the AP. The
+ * driver receives a channel-switch request template and the location of
+ * the switch-timing IE within the template as part of the invocation.
+ * The template is valid only within the call, and the driver can
+ * optionally copy the skb for further re-use.
+ * @tdls_cancel_channel_switch: Stop channel-switching with a TDLS peer. Both
+ * peers must be on the base channel when the call completes.
+ * @tdls_recv_channel_switch: a TDLS channel-switch related frame (request or
+ * response) has been received from a remote peer. The driver gets
+ * parameters parsed from the incoming frame and may use them to continue
+ * an ongoing channel-switch operation. In addition, a channel-switch
+ * response template is provided, together with the location of the
+ * switch-timing IE within the template. The skb can only be used within
+ * the function call.
+ *
+ * @wake_tx_queue: Called when new packets have been added to the queue.
+ * @sync_rx_queues: Process all pending frames in RSS queues. This is a
+ * synchronization which is needed in case driver has in its RSS queues
+ * pending frames that were received prior to the control path action
+ * currently taken (e.g. disassociation) but are not processed yet.
+ *
+ * @start_nan: join an existing NAN cluster, or create a new one.
+ * @stop_nan: leave the NAN cluster.
+ * @nan_change_conf: change NAN configuration. The data in cfg80211_nan_conf
+ * contains full new configuration and changes specify which parameters
+ * are changed with respect to the last NAN config.
+ * The driver gets both full configuration and the changed parameters since
+ * some devices may need the full configuration while others need only the
+ * changed parameters.
+ * @add_nan_func: Add a NAN function. Returns 0 on success. The data in
+ * cfg80211_nan_func must not be referenced outside the scope of
+ * this call.
+ * @del_nan_func: Remove a NAN function. The driver must call
+ * ieee80211_nan_func_terminated() with
+ * NL80211_NAN_FUNC_TERM_REASON_USER_REQUEST reason code upon removal.
+ */
+struct ieee80211_ops {
+ void (*tx)(struct ieee80211_hw *hw,
+ struct ieee80211_tx_control *control,
+ struct sk_buff *skb);
+ int (*start)(struct ieee80211_hw *hw);
+ void (*stop)(struct ieee80211_hw *hw);
+#ifdef CONFIG_PM
+ int (*suspend)(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan);
+ int (*resume)(struct ieee80211_hw *hw);
+ void (*set_wakeup)(struct ieee80211_hw *hw, bool enabled);
+#endif
+ int (*add_interface)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif);
+ int (*change_interface)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ enum nl80211_iftype new_type, bool p2p);
+ void (*remove_interface)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif);
+ int (*config)(struct ieee80211_hw *hw, u32 changed);
+ void (*bss_info_changed)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *info,
+ u32 changed);
+
+ int (*start_ap)(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
+ void (*stop_ap)(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
+
+ u64 (*prepare_multicast)(struct ieee80211_hw *hw,
+ struct netdev_hw_addr_list *mc_list);
+ void (*configure_filter)(struct ieee80211_hw *hw,
+ unsigned int changed_flags,
+ unsigned int *total_flags,
+ u64 multicast);
+ void (*config_iface_filter)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ unsigned int filter_flags,
+ unsigned int changed_flags);
+ int (*set_tim)(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
+ bool set);
+ int (*set_key)(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+ struct ieee80211_vif *vif, struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key);
+ void (*update_tkip_key)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_key_conf *conf,
+ struct ieee80211_sta *sta,
+ u32 iv32, u16 *phase1key);
+ void (*set_rekey_data)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct cfg80211_gtk_rekey_data *data);
+ void (*set_default_unicast_key)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, int idx);
+ int (*hw_scan)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_scan_request *req);
+ void (*cancel_hw_scan)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif);
+ int (*sched_scan_start)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct cfg80211_sched_scan_request *req,
+ struct ieee80211_scan_ies *ies);
+ int (*sched_scan_stop)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif);
+ void (*sw_scan_start)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ const u8 *mac_addr);
+ void (*sw_scan_complete)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif);
+ int (*get_stats)(struct ieee80211_hw *hw,
+ struct ieee80211_low_level_stats *stats);
+ void (*get_key_seq)(struct ieee80211_hw *hw,
+ struct ieee80211_key_conf *key,
+ struct ieee80211_key_seq *seq);
+ int (*set_frag_threshold)(struct ieee80211_hw *hw, u32 value);
+ int (*set_rts_threshold)(struct ieee80211_hw *hw, u32 value);
+ int (*sta_add)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta);
+ int (*sta_remove)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta);
+#ifdef CONFIG_MAC80211_DEBUGFS
+ void (*sta_add_debugfs)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct dentry *dir);
+#endif
+ void (*sta_notify)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ enum sta_notify_cmd, struct ieee80211_sta *sta);
+ int (*sta_state)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ enum ieee80211_sta_state old_state,
+ enum ieee80211_sta_state new_state);
+ void (*sta_pre_rcu_remove)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta);
+ void (*sta_rc_update)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ u32 changed);
+ void (*sta_rate_tbl_update)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta);
+ void (*sta_statistics)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct station_info *sinfo);
+ int (*conf_tx)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, u16 ac,
+ const struct ieee80211_tx_queue_params *params);
+ u64 (*get_tsf)(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
+ void (*set_tsf)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ u64 tsf);
+ void (*offset_tsf)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ s64 offset);
+ void (*reset_tsf)(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
+ int (*tx_last_beacon)(struct ieee80211_hw *hw);
+
+ /**
+ * @ampdu_action:
+ * Perform a certain A-MPDU action.
+ * The RA/TID combination determines the destination and TID we want
+ * the ampdu action to be performed for. The action is defined through
+ * ieee80211_ampdu_mlme_action.
+ * When the action is set to %IEEE80211_AMPDU_TX_OPERATIONAL the driver
+ * may neither send aggregates containing more subframes than @buf_size
+ * nor send aggregates in a way that lost frames would exceed the
+ * buffer size. If just limiting the aggregate size, this would be
+ * possible with a buf_size of 8:
+ *
+ * - ``TX: 1.....7``
+ * - ``RX: 2....7`` (lost frame #1)
+ * - ``TX: 8..1...``
+ *
+ * which is invalid since #1 was now re-transmitted well past the
+ * buffer size of 8. Correct ways to retransmit #1 would be:
+ *
+ * - ``TX: 1 or``
+ * - ``TX: 18 or``
+ * - ``TX: 81``
+ *
+ * Even ``189`` would be wrong since 1 could be lost again.
+ *
+ * Returns a negative error code on failure.
+ * The callback can sleep.
+ */
+ int (*ampdu_action)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_ampdu_params *params);
+ int (*get_survey)(struct ieee80211_hw *hw, int idx,
+ struct survey_info *survey);
+ void (*rfkill_poll)(struct ieee80211_hw *hw);
+ void (*set_coverage_class)(struct ieee80211_hw *hw, s16 coverage_class);
+#ifdef CPTCFG_NL80211_TESTMODE
+ int (*testmode_cmd)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ void *data, int len);
+ int (*testmode_dump)(struct ieee80211_hw *hw, struct sk_buff *skb,
+ struct netlink_callback *cb,
+ void *data, int len);
+#endif
+ void (*flush)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ u32 queues, bool drop);
+ void (*channel_switch)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_channel_switch *ch_switch);
+ int (*set_antenna)(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant);
+ int (*get_antenna)(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant);
+
+ int (*remain_on_channel)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_channel *chan,
+ int duration,
+ enum ieee80211_roc_type type);
+ int (*cancel_remain_on_channel)(struct ieee80211_hw *hw);
+ int (*set_ringparam)(struct ieee80211_hw *hw, u32 tx, u32 rx);
+ void (*get_ringparam)(struct ieee80211_hw *hw,
+ u32 *tx, u32 *tx_max, u32 *rx, u32 *rx_max);
+ bool (*tx_frames_pending)(struct ieee80211_hw *hw);
+ int (*set_bitrate_mask)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ const struct cfg80211_bitrate_mask *mask);
+ void (*event_callback)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ const struct ieee80211_event *event);
+
+ void (*allow_buffered_frames)(struct ieee80211_hw *hw,
+ struct ieee80211_sta *sta,
+ u16 tids, int num_frames,
+ enum ieee80211_frame_release_type reason,
+ bool more_data);
+ void (*release_buffered_frames)(struct ieee80211_hw *hw,
+ struct ieee80211_sta *sta,
+ u16 tids, int num_frames,
+ enum ieee80211_frame_release_type reason,
+ bool more_data);
+
+ int (*get_et_sset_count)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, int sset);
+ void (*get_et_stats)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ethtool_stats *stats, u64 *data);
+ void (*get_et_strings)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ u32 sset, u8 *data);
+
+ void (*mgd_prepare_tx)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif);
+
+ void (*mgd_protect_tdls_discover)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif);
+
+ int (*add_chanctx)(struct ieee80211_hw *hw,
+ struct ieee80211_chanctx_conf *ctx);
+ void (*remove_chanctx)(struct ieee80211_hw *hw,
+ struct ieee80211_chanctx_conf *ctx);
+ void (*change_chanctx)(struct ieee80211_hw *hw,
+ struct ieee80211_chanctx_conf *ctx,
+ u32 changed);
+ int (*assign_vif_chanctx)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_chanctx_conf *ctx);
+ void (*unassign_vif_chanctx)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_chanctx_conf *ctx);
+ int (*switch_vif_chanctx)(struct ieee80211_hw *hw,
+ struct ieee80211_vif_chanctx_switch *vifs,
+ int n_vifs,
+ enum ieee80211_chanctx_switch_mode mode);
+
+ void (*reconfig_complete)(struct ieee80211_hw *hw,
+ enum ieee80211_reconfig_type reconfig_type);
+
+#if IS_ENABLED(CONFIG_IPV6)
+ void (*ipv6_addr_change)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct inet6_dev *idev);
+#endif
+ void (*channel_switch_beacon)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct cfg80211_chan_def *chandef);
+ int (*pre_channel_switch)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_channel_switch *ch_switch);
+
+ int (*post_channel_switch)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif);
+
+ int (*join_ibss)(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
+ void (*leave_ibss)(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
+ u32 (*get_expected_throughput)(struct ieee80211_hw *hw,
+ struct ieee80211_sta *sta);
+ int (*get_txpower)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ int *dbm);
+
+ int (*tdls_channel_switch)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, u8 oper_class,
+ struct cfg80211_chan_def *chandef,
+ struct sk_buff *tmpl_skb, u32 ch_sw_tm_ie);
+ void (*tdls_cancel_channel_switch)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta);
+ void (*tdls_recv_channel_switch)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_tdls_ch_sw_params *params);
+
+ void (*wake_tx_queue)(struct ieee80211_hw *hw,
+ struct ieee80211_txq *txq);
+ void (*sync_rx_queues)(struct ieee80211_hw *hw);
+
+ int (*start_nan)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct cfg80211_nan_conf *conf);
+ int (*stop_nan)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif);
+ int (*nan_change_conf)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct cfg80211_nan_conf *conf, u32 changes);
+ int (*add_nan_func)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ const struct cfg80211_nan_func *nan_func);
+ void (*del_nan_func)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ u8 instance_id);
+};
+
+/**
+ * ieee80211_alloc_hw_nm - Allocate a new hardware device
+ *
+ * This must be called once for each hardware device. The returned pointer
+ * must be used to refer to this device when calling other functions.
+ * mac80211 allocates a private data area for the driver pointed to by
+ * @priv in &struct ieee80211_hw, the size of this area is given as
+ * @priv_data_len.
+ *
+ * @priv_data_len: length of private data
+ * @ops: callbacks for this device
+ * @requested_name: Requested name for this device.
+ * NULL is valid value, and means use the default naming (phy%d)
+ *
+ * Return: A pointer to the new hardware device, or %NULL on error.
+ */
+struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len,
+ const struct ieee80211_ops *ops,
+ const char *requested_name);
+
+/**
+ * ieee80211_alloc_hw - Allocate a new hardware device
+ *
+ * This must be called once for each hardware device. The returned pointer
+ * must be used to refer to this device when calling other functions.
+ * mac80211 allocates a private data area for the driver pointed to by
+ * @priv in &struct ieee80211_hw, the size of this area is given as
+ * @priv_data_len.
+ *
+ * @priv_data_len: length of private data
+ * @ops: callbacks for this device
+ *
+ * Return: A pointer to the new hardware device, or %NULL on error.
+ */
+static inline
+struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
+ const struct ieee80211_ops *ops)
+{
+ return ieee80211_alloc_hw_nm(priv_data_len, ops, NULL);
+}
+
+/**
+ * ieee80211_register_hw - Register hardware device
+ *
+ * You must call this function before any other functions in
+ * mac80211. Note that before a hardware can be registered, you
+ * need to fill the contained wiphy's information.
+ *
+ * @hw: the device to register as returned by ieee80211_alloc_hw()
+ *
+ * Return: 0 on success. An error code otherwise.
+ */
+int ieee80211_register_hw(struct ieee80211_hw *hw);
+
+/**
+ * struct ieee80211_tpt_blink - throughput blink description
+ * @throughput: throughput in Kbit/sec
+ * @blink_time: blink time in milliseconds
+ * (full cycle, ie. one off + one on period)
+ */
+struct ieee80211_tpt_blink {
+ int throughput;
+ int blink_time;
+};
+
+/**
+ * enum ieee80211_tpt_led_trigger_flags - throughput trigger flags
+ * @IEEE80211_TPT_LEDTRIG_FL_RADIO: enable blinking with radio
+ * @IEEE80211_TPT_LEDTRIG_FL_WORK: enable blinking when working
+ * @IEEE80211_TPT_LEDTRIG_FL_CONNECTED: enable blinking when at least one
+ * interface is connected in some way, including being an AP
+ */
+enum ieee80211_tpt_led_trigger_flags {
+ IEEE80211_TPT_LEDTRIG_FL_RADIO = BIT(0),
+ IEEE80211_TPT_LEDTRIG_FL_WORK = BIT(1),
+ IEEE80211_TPT_LEDTRIG_FL_CONNECTED = BIT(2),
+};
+
+#ifdef CONFIG_MAC80211_LEDS
+const char *__ieee80211_get_tx_led_name(struct ieee80211_hw *hw);
+const char *__ieee80211_get_rx_led_name(struct ieee80211_hw *hw);
+const char *__ieee80211_get_assoc_led_name(struct ieee80211_hw *hw);
+const char *__ieee80211_get_radio_led_name(struct ieee80211_hw *hw);
+const char *
+__ieee80211_create_tpt_led_trigger(struct ieee80211_hw *hw,
+ unsigned int flags,
+ const struct ieee80211_tpt_blink *blink_table,
+ unsigned int blink_table_len);
+#endif
+/**
+ * ieee80211_get_tx_led_name - get name of TX LED
+ *
+ * mac80211 creates a transmit LED trigger for each wireless hardware
+ * that can be used to drive LEDs if your driver registers a LED device.
+ * This function returns the name (or %NULL if not configured for LEDs)
+ * of the trigger so you can automatically link the LED device.
+ *
+ * @hw: the hardware to get the LED trigger name for
+ *
+ * Return: The name of the LED trigger. %NULL if not configured for LEDs.
+ */
+static inline const char *ieee80211_get_tx_led_name(struct ieee80211_hw *hw)
+{
+#ifdef CONFIG_MAC80211_LEDS
+ return __ieee80211_get_tx_led_name(hw);
+#else
+ return NULL;
+#endif
+}
+
+/**
+ * ieee80211_get_rx_led_name - get name of RX LED
+ *
+ * mac80211 creates a receive LED trigger for each wireless hardware
+ * that can be used to drive LEDs if your driver registers a LED device.
+ * This function returns the name (or %NULL if not configured for LEDs)
+ * of the trigger so you can automatically link the LED device.
+ *
+ * @hw: the hardware to get the LED trigger name for
+ *
+ * Return: The name of the LED trigger. %NULL if not configured for LEDs.
+ */
+static inline const char *ieee80211_get_rx_led_name(struct ieee80211_hw *hw)
+{
+#ifdef CONFIG_MAC80211_LEDS
+ return __ieee80211_get_rx_led_name(hw);
+#else
+ return NULL;
+#endif
+}
+
+/**
+ * ieee80211_get_assoc_led_name - get name of association LED
+ *
+ * mac80211 creates a association LED trigger for each wireless hardware
+ * that can be used to drive LEDs if your driver registers a LED device.
+ * This function returns the name (or %NULL if not configured for LEDs)
+ * of the trigger so you can automatically link the LED device.
+ *
+ * @hw: the hardware to get the LED trigger name for
+ *
+ * Return: The name of the LED trigger. %NULL if not configured for LEDs.
+ */
+static inline const char *ieee80211_get_assoc_led_name(struct ieee80211_hw *hw)
+{
+#ifdef CONFIG_MAC80211_LEDS
+ return __ieee80211_get_assoc_led_name(hw);
+#else
+ return NULL;
+#endif
+}
+
+/**
+ * ieee80211_get_radio_led_name - get name of radio LED
+ *
+ * mac80211 creates a radio change LED trigger for each wireless hardware
+ * that can be used to drive LEDs if your driver registers a LED device.
+ * This function returns the name (or %NULL if not configured for LEDs)
+ * of the trigger so you can automatically link the LED device.
+ *
+ * @hw: the hardware to get the LED trigger name for
+ *
+ * Return: The name of the LED trigger. %NULL if not configured for LEDs.
+ */
+static inline const char *ieee80211_get_radio_led_name(struct ieee80211_hw *hw)
+{
+#ifdef CONFIG_MAC80211_LEDS
+ return __ieee80211_get_radio_led_name(hw);
+#else
+ return NULL;
+#endif
+}
+
+/**
+ * ieee80211_create_tpt_led_trigger - create throughput LED trigger
+ * @hw: the hardware to create the trigger for
+ * @flags: trigger flags, see &enum ieee80211_tpt_led_trigger_flags
+ * @blink_table: the blink table -- needs to be ordered by throughput
+ * @blink_table_len: size of the blink table
+ *
+ * Return: %NULL (in case of error, or if no LED triggers are
+ * configured) or the name of the new trigger.
+ *
+ * Note: This function must be called before ieee80211_register_hw().
+ */
+static inline const char *
+ieee80211_create_tpt_led_trigger(struct ieee80211_hw *hw, unsigned int flags,
+ const struct ieee80211_tpt_blink *blink_table,
+ unsigned int blink_table_len)
+{
+#ifdef CONFIG_MAC80211_LEDS
+ return __ieee80211_create_tpt_led_trigger(hw, flags, blink_table,
+ blink_table_len);
+#else
+ return NULL;
+#endif
+}
+
+/**
+ * ieee80211_unregister_hw - Unregister a hardware device
+ *
+ * This function instructs mac80211 to free allocated resources
+ * and unregister netdevices from the networking subsystem.
+ *
+ * @hw: the hardware to unregister
+ */
+void ieee80211_unregister_hw(struct ieee80211_hw *hw);
+
+/**
+ * ieee80211_free_hw - free hardware descriptor
+ *
+ * This function frees everything that was allocated, including the
+ * private data for the driver. You must call ieee80211_unregister_hw()
+ * before calling this function.
+ *
+ * @hw: the hardware to free
+ */
+void ieee80211_free_hw(struct ieee80211_hw *hw);
+
+/**
+ * ieee80211_restart_hw - restart hardware completely
+ *
+ * Call this function when the hardware was restarted for some reason
+ * (hardware error, ...) and the driver is unable to restore its state
+ * by itself. mac80211 assumes that at this point the driver/hardware
+ * is completely uninitialised and stopped, it starts the process by
+ * calling the ->start() operation. The driver will need to reset all
+ * internal state that it has prior to calling this function.
+ *
+ * @hw: the hardware to restart
+ */
+void ieee80211_restart_hw(struct ieee80211_hw *hw);
+
+/**
+ * ieee80211_rx_napi - receive frame from NAPI context
+ *
+ * Use this function to hand received frames to mac80211. The receive
+ * buffer in @skb must start with an IEEE 802.11 header. In case of a
+ * paged @skb is used, the driver is recommended to put the ieee80211
+ * header of the frame on the linear part of the @skb to avoid memory
+ * allocation and/or memcpy by the stack.
+ *
+ * This function may not be called in IRQ context. Calls to this function
+ * for a single hardware must be synchronized against each other. Calls to
+ * this function, ieee80211_rx_ni() and ieee80211_rx_irqsafe() may not be
+ * mixed for a single hardware. Must not run concurrently with
+ * ieee80211_tx_status() or ieee80211_tx_status_ni().
+ *
+ * This function must be called with BHs disabled.
+ *
+ * @hw: the hardware this frame came in on
+ * @sta: the station the frame was received from, or %NULL
+ * @skb: the buffer to receive, owned by mac80211 after this call
+ * @napi: the NAPI context
+ */
+void ieee80211_rx_napi(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
+ struct sk_buff *skb, struct napi_struct *napi);
+
+/**
+ * ieee80211_rx - receive frame
+ *
+ * Use this function to hand received frames to mac80211. The receive
+ * buffer in @skb must start with an IEEE 802.11 header. In case of a
+ * paged @skb is used, the driver is recommended to put the ieee80211
+ * header of the frame on the linear part of the @skb to avoid memory
+ * allocation and/or memcpy by the stack.
+ *
+ * This function may not be called in IRQ context. Calls to this function
+ * for a single hardware must be synchronized against each other. Calls to
+ * this function, ieee80211_rx_ni() and ieee80211_rx_irqsafe() may not be
+ * mixed for a single hardware. Must not run concurrently with
+ * ieee80211_tx_status() or ieee80211_tx_status_ni().
+ *
+ * In process context use instead ieee80211_rx_ni().
+ *
+ * @hw: the hardware this frame came in on
+ * @skb: the buffer to receive, owned by mac80211 after this call
+ */
+static inline void ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb)
+{
+ ieee80211_rx_napi(hw, NULL, skb, NULL);
+}
+
+/**
+ * ieee80211_rx_irqsafe - receive frame
+ *
+ * Like ieee80211_rx() but can be called in IRQ context
+ * (internally defers to a tasklet.)
+ *
+ * Calls to this function, ieee80211_rx() or ieee80211_rx_ni() may not
+ * be mixed for a single hardware.Must not run concurrently with
+ * ieee80211_tx_status() or ieee80211_tx_status_ni().
+ *
+ * @hw: the hardware this frame came in on
+ * @skb: the buffer to receive, owned by mac80211 after this call
+ */
+void ieee80211_rx_irqsafe(struct ieee80211_hw *hw, struct sk_buff *skb);
+
+/**
+ * ieee80211_rx_ni - receive frame (in process context)
+ *
+ * Like ieee80211_rx() but can be called in process context
+ * (internally disables bottom halves).
+ *
+ * Calls to this function, ieee80211_rx() and ieee80211_rx_irqsafe() may
+ * not be mixed for a single hardware. Must not run concurrently with
+ * ieee80211_tx_status() or ieee80211_tx_status_ni().
+ *
+ * @hw: the hardware this frame came in on
+ * @skb: the buffer to receive, owned by mac80211 after this call
+ */
+static inline void ieee80211_rx_ni(struct ieee80211_hw *hw,
+ struct sk_buff *skb)
+{
+ local_bh_disable();
+ ieee80211_rx(hw, skb);
+ local_bh_enable();
+}
+
+/**
+ * ieee80211_sta_ps_transition - PS transition for connected sta
+ *
+ * When operating in AP mode with the %IEEE80211_HW_AP_LINK_PS
+ * flag set, use this function to inform mac80211 about a connected station
+ * entering/leaving PS mode.
+ *
+ * This function may not be called in IRQ context or with softirqs enabled.
+ *
+ * Calls to this function for a single hardware must be synchronized against
+ * each other.
+ *
+ * @sta: currently connected sta
+ * @start: start or stop PS
+ *
+ * Return: 0 on success. -EINVAL when the requested PS mode is already set.
+ */
+int ieee80211_sta_ps_transition(struct ieee80211_sta *sta, bool start);
+
+/**
+ * ieee80211_sta_ps_transition_ni - PS transition for connected sta
+ * (in process context)
+ *
+ * Like ieee80211_sta_ps_transition() but can be called in process context
+ * (internally disables bottom halves). Concurrent call restriction still
+ * applies.
+ *
+ * @sta: currently connected sta
+ * @start: start or stop PS
+ *
+ * Return: Like ieee80211_sta_ps_transition().
+ */
+static inline int ieee80211_sta_ps_transition_ni(struct ieee80211_sta *sta,
+ bool start)
+{
+ int ret;
+
+ local_bh_disable();
+ ret = ieee80211_sta_ps_transition(sta, start);
+ local_bh_enable();
+
+ return ret;
+}
+
+/**
+ * ieee80211_sta_pspoll - PS-Poll frame received
+ * @sta: currently connected station
+ *
+ * When operating in AP mode with the %IEEE80211_HW_AP_LINK_PS flag set,
+ * use this function to inform mac80211 that a PS-Poll frame from a
+ * connected station was received.
+ * This must be used in conjunction with ieee80211_sta_ps_transition()
+ * and possibly ieee80211_sta_uapsd_trigger(); calls to all three must
+ * be serialized.
+ */
+void ieee80211_sta_pspoll(struct ieee80211_sta *sta);
+
+/**
+ * ieee80211_sta_uapsd_trigger - (potential) U-APSD trigger frame received
+ * @sta: currently connected station
+ * @tid: TID of the received (potential) trigger frame
+ *
+ * When operating in AP mode with the %IEEE80211_HW_AP_LINK_PS flag set,
+ * use this function to inform mac80211 that a (potential) trigger frame
+ * from a connected station was received.
+ * This must be used in conjunction with ieee80211_sta_ps_transition()
+ * and possibly ieee80211_sta_pspoll(); calls to all three must be
+ * serialized.
+ * %IEEE80211_NUM_TIDS can be passed as the tid if the tid is unknown.
+ * In this case, mac80211 will not check that this tid maps to an AC
+ * that is trigger enabled and assume that the caller did the proper
+ * checks.
+ */
+void ieee80211_sta_uapsd_trigger(struct ieee80211_sta *sta, u8 tid);
+
+/*
+ * The TX headroom reserved by mac80211 for its own tx_status functions.
+ * This is enough for the radiotap header.
+ */
+#define IEEE80211_TX_STATUS_HEADROOM 14
+
+/**
+ * ieee80211_sta_set_buffered - inform mac80211 about driver-buffered frames
+ * @sta: &struct ieee80211_sta pointer for the sleeping station
+ * @tid: the TID that has buffered frames
+ * @buffered: indicates whether or not frames are buffered for this TID
+ *
+ * If a driver buffers frames for a powersave station instead of passing
+ * them back to mac80211 for retransmission, the station may still need
+ * to be told that there are buffered frames via the TIM bit.
+ *
+ * This function informs mac80211 whether or not there are frames that are
+ * buffered in the driver for a given TID; mac80211 can then use this data
+ * to set the TIM bit (NOTE: This may call back into the driver's set_tim
+ * call! Beware of the locking!)
+ *
+ * If all frames are released to the station (due to PS-poll or uAPSD)
+ * then the driver needs to inform mac80211 that there no longer are
+ * frames buffered. However, when the station wakes up mac80211 assumes
+ * that all buffered frames will be transmitted and clears this data,
+ * drivers need to make sure they inform mac80211 about all buffered
+ * frames on the sleep transition (sta_notify() with %STA_NOTIFY_SLEEP).
+ *
+ * Note that technically mac80211 only needs to know this per AC, not per
+ * TID, but since driver buffering will inevitably happen per TID (since
+ * it is related to aggregation) it is easier to make mac80211 map the
+ * TID to the AC as required instead of keeping track in all drivers that
+ * use this API.
+ */
+void ieee80211_sta_set_buffered(struct ieee80211_sta *sta,
+ u8 tid, bool buffered);
+
+/**
+ * ieee80211_get_tx_rates - get the selected transmit rates for a packet
+ *
+ * Call this function in a driver with per-packet rate selection support
+ * to combine the rate info in the packet tx info with the most recent
+ * rate selection table for the station entry.
+ *
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ * @sta: the receiver station to which this packet is sent.
+ * @skb: the frame to be transmitted.
+ * @dest: buffer for extracted rate/retry information
+ * @max_rates: maximum number of rates to fetch
+ */
+void ieee80211_get_tx_rates(struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct sk_buff *skb,
+ struct ieee80211_tx_rate *dest,
+ int max_rates);
+
+/**
+ * ieee80211_tx_status - transmit status callback
+ *
+ * Call this function for all transmitted frames after they have been
+ * transmitted. It is permissible to not call this function for
+ * multicast frames but this can affect statistics.
+ *
+ * This function may not be called in IRQ context. Calls to this function
+ * for a single hardware must be synchronized against each other. Calls
+ * to this function, ieee80211_tx_status_ni() and ieee80211_tx_status_irqsafe()
+ * may not be mixed for a single hardware. Must not run concurrently with
+ * ieee80211_rx() or ieee80211_rx_ni().
+ *
+ * @hw: the hardware the frame was transmitted by
+ * @skb: the frame that was transmitted, owned by mac80211 after this call
+ */
+void ieee80211_tx_status(struct ieee80211_hw *hw,
+ struct sk_buff *skb);
+
+/**
+ * ieee80211_tx_status_ext - extended transmit status callback
+ *
+ * This function can be used as a replacement for ieee80211_tx_status
+ * in drivers that may want to provide extra information that does not
+ * fit into &struct ieee80211_tx_info.
+ *
+ * Calls to this function for a single hardware must be synchronized
+ * against each other. Calls to this function, ieee80211_tx_status_ni()
+ * and ieee80211_tx_status_irqsafe() may not be mixed for a single hardware.
+ *
+ * @hw: the hardware the frame was transmitted by
+ * @status: tx status information
+ */
+void ieee80211_tx_status_ext(struct ieee80211_hw *hw,
+ struct ieee80211_tx_status *status);
+
+/**
+ * ieee80211_tx_status_noskb - transmit status callback without skb
+ *
+ * This function can be used as a replacement for ieee80211_tx_status
+ * in drivers that cannot reliably map tx status information back to
+ * specific skbs.
+ *
+ * Calls to this function for a single hardware must be synchronized
+ * against each other. Calls to this function, ieee80211_tx_status_ni()
+ * and ieee80211_tx_status_irqsafe() may not be mixed for a single hardware.
+ *
+ * @hw: the hardware the frame was transmitted by
+ * @sta: the receiver station to which this packet is sent
+ * (NULL for multicast packets)
+ * @info: tx status information
+ */
+static inline void ieee80211_tx_status_noskb(struct ieee80211_hw *hw,
+ struct ieee80211_sta *sta,
+ struct ieee80211_tx_info *info)
+{
+ struct ieee80211_tx_status status = {
+ .sta = sta,
+ .info = info,
+ };
+
+ ieee80211_tx_status_ext(hw, &status);
+}
+
+/**
+ * ieee80211_tx_status_ni - transmit status callback (in process context)
+ *
+ * Like ieee80211_tx_status() but can be called in process context.
+ *
+ * Calls to this function, ieee80211_tx_status() and
+ * ieee80211_tx_status_irqsafe() may not be mixed
+ * for a single hardware.
+ *
+ * @hw: the hardware the frame was transmitted by
+ * @skb: the frame that was transmitted, owned by mac80211 after this call
+ */
+static inline void ieee80211_tx_status_ni(struct ieee80211_hw *hw,
+ struct sk_buff *skb)
+{
+ local_bh_disable();
+ ieee80211_tx_status(hw, skb);
+ local_bh_enable();
+}
+
+/**
+ * ieee80211_tx_status_irqsafe - IRQ-safe transmit status callback
+ *
+ * Like ieee80211_tx_status() but can be called in IRQ context
+ * (internally defers to a tasklet.)
+ *
+ * Calls to this function, ieee80211_tx_status() and
+ * ieee80211_tx_status_ni() may not be mixed for a single hardware.
+ *
+ * @hw: the hardware the frame was transmitted by
+ * @skb: the frame that was transmitted, owned by mac80211 after this call
+ */
+void ieee80211_tx_status_irqsafe(struct ieee80211_hw *hw,
+ struct sk_buff *skb);
+
+/**
+ * ieee80211_report_low_ack - report non-responding station
+ *
+ * When operating in AP-mode, call this function to report a non-responding
+ * connected STA.
+ *
+ * @sta: the non-responding connected sta
+ * @num_packets: number of packets sent to @sta without a response
+ */
+void ieee80211_report_low_ack(struct ieee80211_sta *sta, u32 num_packets);
+
+#define IEEE80211_MAX_CSA_COUNTERS_NUM 2
+
+/**
+ * struct ieee80211_mutable_offsets - mutable beacon offsets
+ * @tim_offset: position of TIM element
+ * @tim_length: size of TIM element
+ * @csa_counter_offs: array of IEEE80211_MAX_CSA_COUNTERS_NUM offsets
+ * to CSA counters. This array can contain zero values which
+ * should be ignored.
+ */
+struct ieee80211_mutable_offsets {
+ u16 tim_offset;
+ u16 tim_length;
+
+ u16 csa_counter_offs[IEEE80211_MAX_CSA_COUNTERS_NUM];
+};
+
+/**
+ * ieee80211_beacon_get_template - beacon template generation function
+ * @hw: pointer obtained from ieee80211_alloc_hw().
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ * @offs: &struct ieee80211_mutable_offsets pointer to struct that will
+ * receive the offsets that may be updated by the driver.
+ *
+ * If the driver implements beaconing modes, it must use this function to
+ * obtain the beacon template.
+ *
+ * This function should be used if the beacon frames are generated by the
+ * device, and then the driver must use the returned beacon as the template
+ * The driver or the device are responsible to update the DTIM and, when
+ * applicable, the CSA count.
+ *
+ * The driver is responsible for freeing the returned skb.
+ *
+ * Return: The beacon template. %NULL on error.
+ */
+struct sk_buff *
+ieee80211_beacon_get_template(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_mutable_offsets *offs);
+
+/**
+ * ieee80211_beacon_get_tim - beacon generation function
+ * @hw: pointer obtained from ieee80211_alloc_hw().
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ * @tim_offset: pointer to variable that will receive the TIM IE offset.
+ * Set to 0 if invalid (in non-AP modes).
+ * @tim_length: pointer to variable that will receive the TIM IE length,
+ * (including the ID and length bytes!).
+ * Set to 0 if invalid (in non-AP modes).
+ *
+ * If the driver implements beaconing modes, it must use this function to
+ * obtain the beacon frame.
+ *
+ * If the beacon frames are generated by the host system (i.e., not in
+ * hardware/firmware), the driver uses this function to get each beacon
+ * frame from mac80211 -- it is responsible for calling this function exactly
+ * once before the beacon is needed (e.g. based on hardware interrupt).
+ *
+ * The driver is responsible for freeing the returned skb.
+ *
+ * Return: The beacon template. %NULL on error.
+ */
+struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ u16 *tim_offset, u16 *tim_length);
+
+/**
+ * ieee80211_beacon_get - beacon generation function
+ * @hw: pointer obtained from ieee80211_alloc_hw().
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ *
+ * See ieee80211_beacon_get_tim().
+ *
+ * Return: See ieee80211_beacon_get_tim().
+ */
+static inline struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ return ieee80211_beacon_get_tim(hw, vif, NULL, NULL);
+}
+
+/**
+ * ieee80211_csa_update_counter - request mac80211 to decrement the csa counter
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ *
+ * The csa counter should be updated after each beacon transmission.
+ * This function is called implicitly when
+ * ieee80211_beacon_get/ieee80211_beacon_get_tim are called, however if the
+ * beacon frames are generated by the device, the driver should call this
+ * function after each beacon transmission to sync mac80211's csa counters.
+ *
+ * Return: new csa counter value
+ */
+u8 ieee80211_csa_update_counter(struct ieee80211_vif *vif);
+
+/**
+ * ieee80211_csa_finish - notify mac80211 about channel switch
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ *
+ * After a channel switch announcement was scheduled and the counter in this
+ * announcement hits 1, this function must be called by the driver to
+ * notify mac80211 that the channel can be changed.
+ */
+void ieee80211_csa_finish(struct ieee80211_vif *vif);
+
+/**
+ * ieee80211_csa_is_complete - find out if counters reached 1
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ *
+ * This function returns whether the channel switch counters reached zero.
+ */
+bool ieee80211_csa_is_complete(struct ieee80211_vif *vif);
+
+
+/**
+ * ieee80211_proberesp_get - retrieve a Probe Response template
+ * @hw: pointer obtained from ieee80211_alloc_hw().
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ *
+ * Creates a Probe Response template which can, for example, be uploaded to
+ * hardware. The destination address should be set by the caller.
+ *
+ * Can only be called in AP mode.
+ *
+ * Return: The Probe Response template. %NULL on error.
+ */
+struct sk_buff *ieee80211_proberesp_get(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif);
+
+/**
+ * ieee80211_pspoll_get - retrieve a PS Poll template
+ * @hw: pointer obtained from ieee80211_alloc_hw().
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ *
+ * Creates a PS Poll a template which can, for example, uploaded to
+ * hardware. The template must be updated after association so that correct
+ * AID, BSSID and MAC address is used.
+ *
+ * Note: Caller (or hardware) is responsible for setting the
+ * &IEEE80211_FCTL_PM bit.
+ *
+ * Return: The PS Poll template. %NULL on error.
+ */
+struct sk_buff *ieee80211_pspoll_get(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif);
+
+/**
+ * ieee80211_nullfunc_get - retrieve a nullfunc template
+ * @hw: pointer obtained from ieee80211_alloc_hw().
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ *
+ * Creates a Nullfunc template which can, for example, uploaded to
+ * hardware. The template must be updated after association so that correct
+ * BSSID and address is used.
+ *
+ * Note: Caller (or hardware) is responsible for setting the
+ * &IEEE80211_FCTL_PM bit as well as Duration and Sequence Control fields.
+ *
+ * Return: The nullfunc template. %NULL on error.
+ */
+struct sk_buff *ieee80211_nullfunc_get(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif);
+
+/**
+ * ieee80211_probereq_get - retrieve a Probe Request template
+ * @hw: pointer obtained from ieee80211_alloc_hw().
+ * @src_addr: source MAC address
+ * @ssid: SSID buffer
+ * @ssid_len: length of SSID
+ * @tailroom: tailroom to reserve at end of SKB for IEs
+ *
+ * Creates a Probe Request template which can, for example, be uploaded to
+ * hardware.
+ *
+ * Return: The Probe Request template. %NULL on error.
+ */
+struct sk_buff *ieee80211_probereq_get(struct ieee80211_hw *hw,
+ const u8 *src_addr,
+ const u8 *ssid, size_t ssid_len,
+ size_t tailroom);
+
+/**
+ * ieee80211_rts_get - RTS frame generation function
+ * @hw: pointer obtained from ieee80211_alloc_hw().
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ * @frame: pointer to the frame that is going to be protected by the RTS.
+ * @frame_len: the frame length (in octets).
+ * @frame_txctl: &struct ieee80211_tx_info of the frame.
+ * @rts: The buffer where to store the RTS frame.
+ *
+ * If the RTS frames are generated by the host system (i.e., not in
+ * hardware/firmware), the low-level driver uses this function to receive
+ * the next RTS frame from the 802.11 code. The low-level is responsible
+ * for calling this function before and RTS frame is needed.
+ */
+void ieee80211_rts_get(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ const void *frame, size_t frame_len,
+ const struct ieee80211_tx_info *frame_txctl,
+ struct ieee80211_rts *rts);
+
+/**
+ * ieee80211_rts_duration - Get the duration field for an RTS frame
+ * @hw: pointer obtained from ieee80211_alloc_hw().
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ * @frame_len: the length of the frame that is going to be protected by the RTS.
+ * @frame_txctl: &struct ieee80211_tx_info of the frame.
+ *
+ * If the RTS is generated in firmware, but the host system must provide
+ * the duration field, the low-level driver uses this function to receive
+ * the duration field value in little-endian byteorder.
+ *
+ * Return: The duration.
+ */
+__le16 ieee80211_rts_duration(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, size_t frame_len,
+ const struct ieee80211_tx_info *frame_txctl);
+
+/**
+ * ieee80211_ctstoself_get - CTS-to-self frame generation function
+ * @hw: pointer obtained from ieee80211_alloc_hw().
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ * @frame: pointer to the frame that is going to be protected by the CTS-to-self.
+ * @frame_len: the frame length (in octets).
+ * @frame_txctl: &struct ieee80211_tx_info of the frame.
+ * @cts: The buffer where to store the CTS-to-self frame.
+ *
+ * If the CTS-to-self frames are generated by the host system (i.e., not in
+ * hardware/firmware), the low-level driver uses this function to receive
+ * the next CTS-to-self frame from the 802.11 code. The low-level is responsible
+ * for calling this function before and CTS-to-self frame is needed.
+ */
+void ieee80211_ctstoself_get(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ const void *frame, size_t frame_len,
+ const struct ieee80211_tx_info *frame_txctl,
+ struct ieee80211_cts *cts);
+
+/**
+ * ieee80211_ctstoself_duration - Get the duration field for a CTS-to-self frame
+ * @hw: pointer obtained from ieee80211_alloc_hw().
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ * @frame_len: the length of the frame that is going to be protected by the CTS-to-self.
+ * @frame_txctl: &struct ieee80211_tx_info of the frame.
+ *
+ * If the CTS-to-self is generated in firmware, but the host system must provide
+ * the duration field, the low-level driver uses this function to receive
+ * the duration field value in little-endian byteorder.
+ *
+ * Return: The duration.
+ */
+__le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ size_t frame_len,
+ const struct ieee80211_tx_info *frame_txctl);
+
+/**
+ * ieee80211_generic_frame_duration - Calculate the duration field for a frame
+ * @hw: pointer obtained from ieee80211_alloc_hw().
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ * @band: the band to calculate the frame duration on
+ * @frame_len: the length of the frame.
+ * @rate: the rate at which the frame is going to be transmitted.
+ *
+ * Calculate the duration field of some generic frame, given its
+ * length and transmission rate (in 100kbps).
+ *
+ * Return: The duration.
+ */
+__le16 ieee80211_generic_frame_duration(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ enum nl80211_band band,
+ size_t frame_len,
+ struct ieee80211_rate *rate);
+
+/**
+ * ieee80211_get_buffered_bc - accessing buffered broadcast and multicast frames
+ * @hw: pointer as obtained from ieee80211_alloc_hw().
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ *
+ * Function for accessing buffered broadcast and multicast frames. If
+ * hardware/firmware does not implement buffering of broadcast/multicast
+ * frames when power saving is used, 802.11 code buffers them in the host
+ * memory. The low-level driver uses this function to fetch next buffered
+ * frame. In most cases, this is used when generating beacon frame.
+ *
+ * Return: A pointer to the next buffered skb or NULL if no more buffered
+ * frames are available.
+ *
+ * Note: buffered frames are returned only after DTIM beacon frame was
+ * generated with ieee80211_beacon_get() and the low-level driver must thus
+ * call ieee80211_beacon_get() first. ieee80211_get_buffered_bc() returns
+ * NULL if the previous generated beacon was not DTIM, so the low-level driver
+ * does not need to check for DTIM beacons separately and should be able to
+ * use common code for all beacons.
+ */
+struct sk_buff *
+ieee80211_get_buffered_bc(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
+
+/**
+ * ieee80211_get_tkip_p1k_iv - get a TKIP phase 1 key for IV32
+ *
+ * This function returns the TKIP phase 1 key for the given IV32.
+ *
+ * @keyconf: the parameter passed with the set key
+ * @iv32: IV32 to get the P1K for
+ * @p1k: a buffer to which the key will be written, as 5 u16 values
+ */
+void ieee80211_get_tkip_p1k_iv(struct ieee80211_key_conf *keyconf,
+ u32 iv32, u16 *p1k);
+
+/**
+ * ieee80211_get_tkip_p1k - get a TKIP phase 1 key
+ *
+ * This function returns the TKIP phase 1 key for the IV32 taken
+ * from the given packet.
+ *
+ * @keyconf: the parameter passed with the set key
+ * @skb: the packet to take the IV32 value from that will be encrypted
+ * with this P1K
+ * @p1k: a buffer to which the key will be written, as 5 u16 values
+ */
+static inline void ieee80211_get_tkip_p1k(struct ieee80211_key_conf *keyconf,
+ struct sk_buff *skb, u16 *p1k)
+{
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+ const u8 *data = (u8 *)hdr + ieee80211_hdrlen(hdr->frame_control);
+ u32 iv32 = get_unaligned_le32(&data[4]);
+
+ ieee80211_get_tkip_p1k_iv(keyconf, iv32, p1k);
+}
+
+/**
+ * ieee80211_get_tkip_rx_p1k - get a TKIP phase 1 key for RX
+ *
+ * This function returns the TKIP phase 1 key for the given IV32
+ * and transmitter address.
+ *
+ * @keyconf: the parameter passed with the set key
+ * @ta: TA that will be used with the key
+ * @iv32: IV32 to get the P1K for
+ * @p1k: a buffer to which the key will be written, as 5 u16 values
+ */
+void ieee80211_get_tkip_rx_p1k(struct ieee80211_key_conf *keyconf,
+ const u8 *ta, u32 iv32, u16 *p1k);
+
+/**
+ * ieee80211_get_tkip_p2k - get a TKIP phase 2 key
+ *
+ * This function computes the TKIP RC4 key for the IV values
+ * in the packet.
+ *
+ * @keyconf: the parameter passed with the set key
+ * @skb: the packet to take the IV32/IV16 values from that will be
+ * encrypted with this key
+ * @p2k: a buffer to which the key will be written, 16 bytes
+ */
+void ieee80211_get_tkip_p2k(struct ieee80211_key_conf *keyconf,
+ struct sk_buff *skb, u8 *p2k);
+
+/**
+ * ieee80211_tkip_add_iv - write TKIP IV and Ext. IV to pos
+ *
+ * @pos: start of crypto header
+ * @keyconf: the parameter passed with the set key
+ * @pn: PN to add
+ *
+ * Returns: pointer to the octet following IVs (i.e. beginning of
+ * the packet payload)
+ *
+ * This function writes the tkip IV value to pos (which should
+ * point to the crypto header)
+ */
+u8 *ieee80211_tkip_add_iv(u8 *pos, struct ieee80211_key_conf *keyconf, u64 pn);
+
+/**
+ * ieee80211_get_key_rx_seq - get key RX sequence counter
+ *
+ * @keyconf: the parameter passed with the set key
+ * @tid: The TID, or -1 for the management frame value (CCMP/GCMP only);
+ * the value on TID 0 is also used for non-QoS frames. For
+ * CMAC, only TID 0 is valid.
+ * @seq: buffer to receive the sequence data
+ *
+ * This function allows a driver to retrieve the current RX IV/PNs
+ * for the given key. It must not be called if IV checking is done
+ * by the device and not by mac80211.
+ *
+ * Note that this function may only be called when no RX processing
+ * can be done concurrently.
+ */
+void ieee80211_get_key_rx_seq(struct ieee80211_key_conf *keyconf,
+ int tid, struct ieee80211_key_seq *seq);
+
+/**
+ * ieee80211_set_key_rx_seq - set key RX sequence counter
+ *
+ * @keyconf: the parameter passed with the set key
+ * @tid: The TID, or -1 for the management frame value (CCMP/GCMP only);
+ * the value on TID 0 is also used for non-QoS frames. For
+ * CMAC, only TID 0 is valid.
+ * @seq: new sequence data
+ *
+ * This function allows a driver to set the current RX IV/PNs for the
+ * given key. This is useful when resuming from WoWLAN sleep and GTK
+ * rekey may have been done while suspended. It should not be called
+ * if IV checking is done by the device and not by mac80211.
+ *
+ * Note that this function may only be called when no RX processing
+ * can be done concurrently.
+ */
+void ieee80211_set_key_rx_seq(struct ieee80211_key_conf *keyconf,
+ int tid, struct ieee80211_key_seq *seq);
+
+/**
+ * ieee80211_remove_key - remove the given key
+ * @keyconf: the parameter passed with the set key
+ *
+ * Remove the given key. If the key was uploaded to the hardware at the
+ * time this function is called, it is not deleted in the hardware but
+ * instead assumed to have been removed already.
+ *
+ * Note that due to locking considerations this function can (currently)
+ * only be called during key iteration (ieee80211_iter_keys().)
+ */
+void ieee80211_remove_key(struct ieee80211_key_conf *keyconf);
+
+/**
+ * ieee80211_gtk_rekey_add - add a GTK key from rekeying during WoWLAN
+ * @vif: the virtual interface to add the key on
+ * @keyconf: new key data
+ *
+ * When GTK rekeying was done while the system was suspended, (a) new
+ * key(s) will be available. These will be needed by mac80211 for proper
+ * RX processing, so this function allows setting them.
+ *
+ * The function returns the newly allocated key structure, which will
+ * have similar contents to the passed key configuration but point to
+ * mac80211-owned memory. In case of errors, the function returns an
+ * ERR_PTR(), use IS_ERR() etc.
+ *
+ * Note that this function assumes the key isn't added to hardware
+ * acceleration, so no TX will be done with the key. Since it's a GTK
+ * on managed (station) networks, this is true anyway. If the driver
+ * calls this function from the resume callback and subsequently uses
+ * the return code 1 to reconfigure the device, this key will be part
+ * of the reconfiguration.
+ *
+ * Note that the driver should also call ieee80211_set_key_rx_seq()
+ * for the new key for each TID to set up sequence counters properly.
+ *
+ * IMPORTANT: If this replaces a key that is present in the hardware,
+ * then it will attempt to remove it during this call. In many cases
+ * this isn't what you want, so call ieee80211_remove_key() first for
+ * the key that's being replaced.
+ */
+struct ieee80211_key_conf *
+ieee80211_gtk_rekey_add(struct ieee80211_vif *vif,
+ struct ieee80211_key_conf *keyconf);
+
+/**
+ * ieee80211_gtk_rekey_notify - notify userspace supplicant of rekeying
+ * @vif: virtual interface the rekeying was done on
+ * @bssid: The BSSID of the AP, for checking association
+ * @replay_ctr: the new replay counter after GTK rekeying
+ * @gfp: allocation flags
+ */
+void ieee80211_gtk_rekey_notify(struct ieee80211_vif *vif, const u8 *bssid,
+ const u8 *replay_ctr, gfp_t gfp);
+
+/**
+ * ieee80211_wake_queue - wake specific queue
+ * @hw: pointer as obtained from ieee80211_alloc_hw().
+ * @queue: queue number (counted from zero).
+ *
+ * Drivers should use this function instead of netif_wake_queue.
+ */
+void ieee80211_wake_queue(struct ieee80211_hw *hw, int queue);
+
+/**
+ * ieee80211_stop_queue - stop specific queue
+ * @hw: pointer as obtained from ieee80211_alloc_hw().
+ * @queue: queue number (counted from zero).
+ *
+ * Drivers should use this function instead of netif_stop_queue.
+ */
+void ieee80211_stop_queue(struct ieee80211_hw *hw, int queue);
+
+/**
+ * ieee80211_queue_stopped - test status of the queue
+ * @hw: pointer as obtained from ieee80211_alloc_hw().
+ * @queue: queue number (counted from zero).
+ *
+ * Drivers should use this function instead of netif_stop_queue.
+ *
+ * Return: %true if the queue is stopped. %false otherwise.
+ */
+
+int ieee80211_queue_stopped(struct ieee80211_hw *hw, int queue);
+
+/**
+ * ieee80211_stop_queues - stop all queues
+ * @hw: pointer as obtained from ieee80211_alloc_hw().
+ *
+ * Drivers should use this function instead of netif_stop_queue.
+ */
+void ieee80211_stop_queues(struct ieee80211_hw *hw);
+
+/**
+ * ieee80211_wake_queues - wake all queues
+ * @hw: pointer as obtained from ieee80211_alloc_hw().
+ *
+ * Drivers should use this function instead of netif_wake_queue.
+ */
+void ieee80211_wake_queues(struct ieee80211_hw *hw);
+
+/**
+ * ieee80211_scan_completed - completed hardware scan
+ *
+ * When hardware scan offload is used (i.e. the hw_scan() callback is
+ * assigned) this function needs to be called by the driver to notify
+ * mac80211 that the scan finished. This function can be called from
+ * any context, including hardirq context.
+ *
+ * @hw: the hardware that finished the scan
+ * @info: information about the completed scan
+ */
+void ieee80211_scan_completed(struct ieee80211_hw *hw,
+ struct cfg80211_scan_info *info);
+
+/**
+ * ieee80211_sched_scan_results - got results from scheduled scan
+ *
+ * When a scheduled scan is running, this function needs to be called by the
+ * driver whenever there are new scan results available.
+ *
+ * @hw: the hardware that is performing scheduled scans
+ */
+void ieee80211_sched_scan_results(struct ieee80211_hw *hw);
+
+/**
+ * ieee80211_sched_scan_stopped - inform that the scheduled scan has stopped
+ *
+ * When a scheduled scan is running, this function can be called by
+ * the driver if it needs to stop the scan to perform another task.
+ * Usual scenarios are drivers that cannot continue the scheduled scan
+ * while associating, for instance.
+ *
+ * @hw: the hardware that is performing scheduled scans
+ */
+void ieee80211_sched_scan_stopped(struct ieee80211_hw *hw);
+
+/**
+ * enum ieee80211_interface_iteration_flags - interface iteration flags
+ * @IEEE80211_IFACE_ITER_NORMAL: Iterate over all interfaces that have
+ * been added to the driver; However, note that during hardware
+ * reconfiguration (after restart_hw) it will iterate over a new
+ * interface and over all the existing interfaces even if they
+ * haven't been re-added to the driver yet.
+ * @IEEE80211_IFACE_ITER_RESUME_ALL: During resume, iterate over all
+ * interfaces, even if they haven't been re-added to the driver yet.
+ * @IEEE80211_IFACE_ITER_ACTIVE: Iterate only active interfaces (netdev is up).
+ */
+enum ieee80211_interface_iteration_flags {
+ IEEE80211_IFACE_ITER_NORMAL = 0,
+ IEEE80211_IFACE_ITER_RESUME_ALL = BIT(0),
+ IEEE80211_IFACE_ITER_ACTIVE = BIT(1),
+};
+
+/**
+ * ieee80211_iterate_interfaces - iterate interfaces
+ *
+ * This function iterates over the interfaces associated with a given
+ * hardware and calls the callback for them. This includes active as well as
+ * inactive interfaces. This function allows the iterator function to sleep.
+ * Will iterate over a new interface during add_interface().
+ *
+ * @hw: the hardware struct of which the interfaces should be iterated over
+ * @iter_flags: iteration flags, see &enum ieee80211_interface_iteration_flags
+ * @iterator: the iterator function to call
+ * @data: first argument of the iterator function
+ */
+void ieee80211_iterate_interfaces(struct ieee80211_hw *hw, u32 iter_flags,
+ void (*iterator)(void *data, u8 *mac,
+ struct ieee80211_vif *vif),
+ void *data);
+
+/**
+ * ieee80211_iterate_active_interfaces - iterate active interfaces
+ *
+ * This function iterates over the interfaces associated with a given
+ * hardware that are currently active and calls the callback for them.
+ * This function allows the iterator function to sleep, when the iterator
+ * function is atomic @ieee80211_iterate_active_interfaces_atomic can
+ * be used.
+ * Does not iterate over a new interface during add_interface().
+ *
+ * @hw: the hardware struct of which the interfaces should be iterated over
+ * @iter_flags: iteration flags, see &enum ieee80211_interface_iteration_flags
+ * @iterator: the iterator function to call
+ * @data: first argument of the iterator function
+ */
+static inline void
+ieee80211_iterate_active_interfaces(struct ieee80211_hw *hw, u32 iter_flags,
+ void (*iterator)(void *data, u8 *mac,
+ struct ieee80211_vif *vif),
+ void *data)
+{
+ ieee80211_iterate_interfaces(hw,
+ iter_flags | IEEE80211_IFACE_ITER_ACTIVE,
+ iterator, data);
+}
+
+/**
+ * ieee80211_iterate_active_interfaces_atomic - iterate active interfaces
+ *
+ * This function iterates over the interfaces associated with a given
+ * hardware that are currently active and calls the callback for them.
+ * This function requires the iterator callback function to be atomic,
+ * if that is not desired, use @ieee80211_iterate_active_interfaces instead.
+ * Does not iterate over a new interface during add_interface().
+ *
+ * @hw: the hardware struct of which the interfaces should be iterated over
+ * @iter_flags: iteration flags, see &enum ieee80211_interface_iteration_flags
+ * @iterator: the iterator function to call, cannot sleep
+ * @data: first argument of the iterator function
+ */
+void ieee80211_iterate_active_interfaces_atomic(struct ieee80211_hw *hw,
+ u32 iter_flags,
+ void (*iterator)(void *data,
+ u8 *mac,
+ struct ieee80211_vif *vif),
+ void *data);
+
+/**
+ * ieee80211_iterate_active_interfaces_rtnl - iterate active interfaces
+ *
+ * This function iterates over the interfaces associated with a given
+ * hardware that are currently active and calls the callback for them.
+ * This version can only be used while holding the RTNL.
+ *
+ * @hw: the hardware struct of which the interfaces should be iterated over
+ * @iter_flags: iteration flags, see &enum ieee80211_interface_iteration_flags
+ * @iterator: the iterator function to call, cannot sleep
+ * @data: first argument of the iterator function
+ */
+void ieee80211_iterate_active_interfaces_rtnl(struct ieee80211_hw *hw,
+ u32 iter_flags,
+ void (*iterator)(void *data,
+ u8 *mac,
+ struct ieee80211_vif *vif),
+ void *data);
+
+/**
+ * ieee80211_iterate_stations_atomic - iterate stations
+ *
+ * This function iterates over all stations associated with a given
+ * hardware that are currently uploaded to the driver and calls the callback
+ * function for them.
+ * This function requires the iterator callback function to be atomic,
+ *
+ * @hw: the hardware struct of which the interfaces should be iterated over
+ * @iterator: the iterator function to call, cannot sleep
+ * @data: first argument of the iterator function
+ */
+void ieee80211_iterate_stations_atomic(struct ieee80211_hw *hw,
+ void (*iterator)(void *data,
+ struct ieee80211_sta *sta),
+ void *data);
+/**
+ * ieee80211_queue_work - add work onto the mac80211 workqueue
+ *
+ * Drivers and mac80211 use this to add work onto the mac80211 workqueue.
+ * This helper ensures drivers are not queueing work when they should not be.
+ *
+ * @hw: the hardware struct for the interface we are adding work for
+ * @work: the work we want to add onto the mac80211 workqueue
+ */
+void ieee80211_queue_work(struct ieee80211_hw *hw, struct work_struct *work);
+
+/**
+ * ieee80211_queue_delayed_work - add work onto the mac80211 workqueue
+ *
+ * Drivers and mac80211 use this to queue delayed work onto the mac80211
+ * workqueue.
+ *
+ * @hw: the hardware struct for the interface we are adding work for
+ * @dwork: delayable work to queue onto the mac80211 workqueue
+ * @delay: number of jiffies to wait before queueing
+ */
+void ieee80211_queue_delayed_work(struct ieee80211_hw *hw,
+ struct delayed_work *dwork,
+ unsigned long delay);
+
+/**
+ * ieee80211_start_tx_ba_session - Start a tx Block Ack session.
+ * @sta: the station for which to start a BA session
+ * @tid: the TID to BA on.
+ * @timeout: session timeout value (in TUs)
+ *
+ * Return: success if addBA request was sent, failure otherwise
+ *
+ * Although mac80211/low level driver/user space application can estimate
+ * the need to start aggregation on a certain RA/TID, the session level
+ * will be managed by the mac80211.
+ */
+int ieee80211_start_tx_ba_session(struct ieee80211_sta *sta, u16 tid,
+ u16 timeout);
+
+/**
+ * ieee80211_start_tx_ba_cb_irqsafe - low level driver ready to aggregate.
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback
+ * @ra: receiver address of the BA session recipient.
+ * @tid: the TID to BA on.
+ *
+ * This function must be called by low level driver once it has
+ * finished with preparations for the BA session. It can be called
+ * from any context.
+ */
+void ieee80211_start_tx_ba_cb_irqsafe(struct ieee80211_vif *vif, const u8 *ra,
+ u16 tid);
+
+/**
+ * ieee80211_stop_tx_ba_session - Stop a Block Ack session.
+ * @sta: the station whose BA session to stop
+ * @tid: the TID to stop BA.
+ *
+ * Return: negative error if the TID is invalid, or no aggregation active
+ *
+ * Although mac80211/low level driver/user space application can estimate
+ * the need to stop aggregation on a certain RA/TID, the session level
+ * will be managed by the mac80211.
+ */
+int ieee80211_stop_tx_ba_session(struct ieee80211_sta *sta, u16 tid);
+
+/**
+ * ieee80211_stop_tx_ba_cb_irqsafe - low level driver ready to stop aggregate.
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback
+ * @ra: receiver address of the BA session recipient.
+ * @tid: the desired TID to BA on.
+ *
+ * This function must be called by low level driver once it has
+ * finished with preparations for the BA session tear down. It
+ * can be called from any context.
+ */
+void ieee80211_stop_tx_ba_cb_irqsafe(struct ieee80211_vif *vif, const u8 *ra,
+ u16 tid);
+
+/**
+ * ieee80211_find_sta - find a station
+ *
+ * @vif: virtual interface to look for station on
+ * @addr: station's address
+ *
+ * Return: The station, if found. %NULL otherwise.
+ *
+ * Note: This function must be called under RCU lock and the
+ * resulting pointer is only valid under RCU lock as well.
+ */
+struct ieee80211_sta *ieee80211_find_sta(struct ieee80211_vif *vif,
+ const u8 *addr);
+
+/**
+ * ieee80211_find_sta_by_ifaddr - find a station on hardware
+ *
+ * @hw: pointer as obtained from ieee80211_alloc_hw()
+ * @addr: remote station's address
+ * @localaddr: local address (vif->sdata->vif.addr). Use NULL for 'any'.
+ *
+ * Return: The station, if found. %NULL otherwise.
+ *
+ * Note: This function must be called under RCU lock and the
+ * resulting pointer is only valid under RCU lock as well.
+ *
+ * NOTE: You may pass NULL for localaddr, but then you will just get
+ * the first STA that matches the remote address 'addr'.
+ * We can have multiple STA associated with multiple
+ * logical stations (e.g. consider a station connecting to another
+ * BSSID on the same AP hardware without disconnecting first).
+ * In this case, the result of this method with localaddr NULL
+ * is not reliable.
+ *
+ * DO NOT USE THIS FUNCTION with localaddr NULL if at all possible.
+ */
+struct ieee80211_sta *ieee80211_find_sta_by_ifaddr(struct ieee80211_hw *hw,
+ const u8 *addr,
+ const u8 *localaddr);
+
+/**
+ * ieee80211_sta_block_awake - block station from waking up
+ * @hw: the hardware
+ * @pubsta: the station
+ * @block: whether to block or unblock
+ *
+ * Some devices require that all frames that are on the queues
+ * for a specific station that went to sleep are flushed before
+ * a poll response or frames after the station woke up can be
+ * delivered to that it. Note that such frames must be rejected
+ * by the driver as filtered, with the appropriate status flag.
+ *
+ * This function allows implementing this mode in a race-free
+ * manner.
+ *
+ * To do this, a driver must keep track of the number of frames
+ * still enqueued for a specific station. If this number is not
+ * zero when the station goes to sleep, the driver must call
+ * this function to force mac80211 to consider the station to
+ * be asleep regardless of the station's actual state. Once the
+ * number of outstanding frames reaches zero, the driver must
+ * call this function again to unblock the station. That will
+ * cause mac80211 to be able to send ps-poll responses, and if
+ * the station queried in the meantime then frames will also
+ * be sent out as a result of this. Additionally, the driver
+ * will be notified that the station woke up some time after
+ * it is unblocked, regardless of whether the station actually
+ * woke up while blocked or not.
+ */
+void ieee80211_sta_block_awake(struct ieee80211_hw *hw,
+ struct ieee80211_sta *pubsta, bool block);
+
+/**
+ * ieee80211_sta_eosp - notify mac80211 about end of SP
+ * @pubsta: the station
+ *
+ * When a device transmits frames in a way that it can't tell
+ * mac80211 in the TX status about the EOSP, it must clear the
+ * %IEEE80211_TX_STATUS_EOSP bit and call this function instead.
+ * This applies for PS-Poll as well as uAPSD.
+ *
+ * Note that just like with _tx_status() and _rx() drivers must
+ * not mix calls to irqsafe/non-irqsafe versions, this function
+ * must not be mixed with those either. Use the all irqsafe, or
+ * all non-irqsafe, don't mix!
+ *
+ * NB: the _irqsafe version of this function doesn't exist, no
+ * driver needs it right now. Don't call this function if
+ * you'd need the _irqsafe version, look at the git history
+ * and restore the _irqsafe version!
+ */
+void ieee80211_sta_eosp(struct ieee80211_sta *pubsta);
+
+/**
+ * ieee80211_send_eosp_nullfunc - ask mac80211 to send NDP with EOSP
+ * @pubsta: the station
+ * @tid: the tid of the NDP
+ *
+ * Sometimes the device understands that it needs to close
+ * the Service Period unexpectedly. This can happen when
+ * sending frames that are filling holes in the BA window.
+ * In this case, the device can ask mac80211 to send a
+ * Nullfunc frame with EOSP set. When that happens, the
+ * driver must have called ieee80211_sta_set_buffered() to
+ * let mac80211 know that there are no buffered frames any
+ * more, otherwise mac80211 will get the more_data bit wrong.
+ * The low level driver must have made sure that the frame
+ * will be sent despite the station being in power-save.
+ * Mac80211 won't call allow_buffered_frames().
+ * Note that calling this function, doesn't exempt the driver
+ * from closing the EOSP properly, it will still have to call
+ * ieee80211_sta_eosp when the NDP is sent.
+ */
+void ieee80211_send_eosp_nullfunc(struct ieee80211_sta *pubsta, int tid);
+
+/**
+ * ieee80211_iter_keys - iterate keys programmed into the device
+ * @hw: pointer obtained from ieee80211_alloc_hw()
+ * @vif: virtual interface to iterate, may be %NULL for all
+ * @iter: iterator function that will be called for each key
+ * @iter_data: custom data to pass to the iterator function
+ *
+ * This function can be used to iterate all the keys known to
+ * mac80211, even those that weren't previously programmed into
+ * the device. This is intended for use in WoWLAN if the device
+ * needs reprogramming of the keys during suspend. Note that due
+ * to locking reasons, it is also only safe to call this at few
+ * spots since it must hold the RTNL and be able to sleep.
+ *
+ * The order in which the keys are iterated matches the order
+ * in which they were originally installed and handed to the
+ * set_key callback.
+ */
+void ieee80211_iter_keys(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ void (*iter)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key,
+ void *data),
+ void *iter_data);
+
+/**
+ * ieee80211_iter_keys_rcu - iterate keys programmed into the device
+ * @hw: pointer obtained from ieee80211_alloc_hw()
+ * @vif: virtual interface to iterate, may be %NULL for all
+ * @iter: iterator function that will be called for each key
+ * @iter_data: custom data to pass to the iterator function
+ *
+ * This function can be used to iterate all the keys known to
+ * mac80211, even those that weren't previously programmed into
+ * the device. Note that due to locking reasons, keys of station
+ * in removal process will be skipped.
+ *
+ * This function requires being called in an RCU critical section,
+ * and thus iter must be atomic.
+ */
+void ieee80211_iter_keys_rcu(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ void (*iter)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key,
+ void *data),
+ void *iter_data);
+
+/**
+ * ieee80211_iter_chan_contexts_atomic - iterate channel contexts
+ * @hw: pointre obtained from ieee80211_alloc_hw().
+ * @iter: iterator function
+ * @iter_data: data passed to iterator function
+ *
+ * Iterate all active channel contexts. This function is atomic and
+ * doesn't acquire any locks internally that might be held in other
+ * places while calling into the driver.
+ *
+ * The iterator will not find a context that's being added (during
+ * the driver callback to add it) but will find it while it's being
+ * removed.
+ *
+ * Note that during hardware restart, all contexts that existed
+ * before the restart are considered already present so will be
+ * found while iterating, whether they've been re-added already
+ * or not.
+ */
+void ieee80211_iter_chan_contexts_atomic(
+ struct ieee80211_hw *hw,
+ void (*iter)(struct ieee80211_hw *hw,
+ struct ieee80211_chanctx_conf *chanctx_conf,
+ void *data),
+ void *iter_data);
+
+/**
+ * ieee80211_ap_probereq_get - retrieve a Probe Request template
+ * @hw: pointer obtained from ieee80211_alloc_hw().
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ *
+ * Creates a Probe Request template which can, for example, be uploaded to
+ * hardware. The template is filled with bssid, ssid and supported rate
+ * information. This function must only be called from within the
+ * .bss_info_changed callback function and only in managed mode. The function
+ * is only useful when the interface is associated, otherwise it will return
+ * %NULL.
+ *
+ * Return: The Probe Request template. %NULL on error.
+ */
+struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif);
+
+/**
+ * ieee80211_beacon_loss - inform hardware does not receive beacons
+ *
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ *
+ * When beacon filtering is enabled with %IEEE80211_VIF_BEACON_FILTER and
+ * %IEEE80211_CONF_PS is set, the driver needs to inform whenever the
+ * hardware is not receiving beacons with this function.
+ */
+void ieee80211_beacon_loss(struct ieee80211_vif *vif);
+
+/**
+ * ieee80211_connection_loss - inform hardware has lost connection to the AP
+ *
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ *
+ * When beacon filtering is enabled with %IEEE80211_VIF_BEACON_FILTER, and
+ * %IEEE80211_CONF_PS and %IEEE80211_HW_CONNECTION_MONITOR are set, the driver
+ * needs to inform if the connection to the AP has been lost.
+ * The function may also be called if the connection needs to be terminated
+ * for some other reason, even if %IEEE80211_HW_CONNECTION_MONITOR isn't set.
+ *
+ * This function will cause immediate change to disassociated state,
+ * without connection recovery attempts.
+ */
+void ieee80211_connection_loss(struct ieee80211_vif *vif);
+
+/**
+ * ieee80211_resume_disconnect - disconnect from AP after resume
+ *
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ *
+ * Instructs mac80211 to disconnect from the AP after resume.
+ * Drivers can use this after WoWLAN if they know that the
+ * connection cannot be kept up, for example because keys were
+ * used while the device was asleep but the replay counters or
+ * similar cannot be retrieved from the device during resume.
+ *
+ * Note that due to implementation issues, if the driver uses
+ * the reconfiguration functionality during resume the interface
+ * will still be added as associated first during resume and then
+ * disconnect normally later.
+ *
+ * This function can only be called from the resume callback and
+ * the driver must not be holding any of its own locks while it
+ * calls this function, or at least not any locks it needs in the
+ * key configuration paths (if it supports HW crypto).
+ */
+void ieee80211_resume_disconnect(struct ieee80211_vif *vif);
+
+/**
+ * ieee80211_cqm_rssi_notify - inform a configured connection quality monitoring
+ * rssi threshold triggered
+ *
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ * @rssi_event: the RSSI trigger event type
+ * @rssi_level: new RSSI level value or 0 if not available
+ * @gfp: context flags
+ *
+ * When the %IEEE80211_VIF_SUPPORTS_CQM_RSSI is set, and a connection quality
+ * monitoring is configured with an rssi threshold, the driver will inform
+ * whenever the rssi level reaches the threshold.
+ */
+void ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif,
+ enum nl80211_cqm_rssi_threshold_event rssi_event,
+ s32 rssi_level,
+ gfp_t gfp);
+
+/**
+ * ieee80211_cqm_beacon_loss_notify - inform CQM of beacon loss
+ *
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ * @gfp: context flags
+ */
+void ieee80211_cqm_beacon_loss_notify(struct ieee80211_vif *vif, gfp_t gfp);
+
+/**
+ * ieee80211_radar_detected - inform that a radar was detected
+ *
+ * @hw: pointer as obtained from ieee80211_alloc_hw()
+ */
+void ieee80211_radar_detected(struct ieee80211_hw *hw);
+
+/**
+ * ieee80211_chswitch_done - Complete channel switch process
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ * @success: make the channel switch successful or not
+ *
+ * Complete the channel switch post-process: set the new operational channel
+ * and wake up the suspended queues.
+ */
+void ieee80211_chswitch_done(struct ieee80211_vif *vif, bool success);
+
+/**
+ * ieee80211_request_smps - request SM PS transition
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ * @smps_mode: new SM PS mode
+ *
+ * This allows the driver to request an SM PS transition in managed
+ * mode. This is useful when the driver has more information than
+ * the stack about possible interference, for example by bluetooth.
+ */
+void ieee80211_request_smps(struct ieee80211_vif *vif,
+ enum ieee80211_smps_mode smps_mode);
+
+/**
+ * ieee80211_ready_on_channel - notification of remain-on-channel start
+ * @hw: pointer as obtained from ieee80211_alloc_hw()
+ */
+void ieee80211_ready_on_channel(struct ieee80211_hw *hw);
+
+/**
+ * ieee80211_remain_on_channel_expired - remain_on_channel duration expired
+ * @hw: pointer as obtained from ieee80211_alloc_hw()
+ */
+void ieee80211_remain_on_channel_expired(struct ieee80211_hw *hw);
+
+/**
+ * ieee80211_stop_rx_ba_session - callback to stop existing BA sessions
+ *
+ * in order not to harm the system performance and user experience, the device
+ * may request not to allow any rx ba session and tear down existing rx ba
+ * sessions based on system constraints such as periodic BT activity that needs
+ * to limit wlan activity (eg.sco or a2dp)."
+ * in such cases, the intention is to limit the duration of the rx ppdu and
+ * therefore prevent the peer device to use a-mpdu aggregation.
+ *
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ * @ba_rx_bitmap: Bit map of open rx ba per tid
+ * @addr: & to bssid mac address
+ */
+void ieee80211_stop_rx_ba_session(struct ieee80211_vif *vif, u16 ba_rx_bitmap,
+ const u8 *addr);
+
+/**
+ * ieee80211_mark_rx_ba_filtered_frames - move RX BA window and mark filtered
+ * @pubsta: station struct
+ * @tid: the session's TID
+ * @ssn: starting sequence number of the bitmap, all frames before this are
+ * assumed to be out of the window after the call
+ * @filtered: bitmap of filtered frames, BIT(0) is the @ssn entry etc.
+ * @received_mpdus: number of received mpdus in firmware
+ *
+ * This function moves the BA window and releases all frames before @ssn, and
+ * marks frames marked in the bitmap as having been filtered. Afterwards, it
+ * checks if any frames in the window starting from @ssn can now be released
+ * (in case they were only waiting for frames that were filtered.)
+ */
+void ieee80211_mark_rx_ba_filtered_frames(struct ieee80211_sta *pubsta, u8 tid,
+ u16 ssn, u64 filtered,
+ u16 received_mpdus);
+
+/**
+ * ieee80211_send_bar - send a BlockAckReq frame
+ *
+ * can be used to flush pending frames from the peer's aggregation reorder
+ * buffer.
+ *
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ * @ra: the peer's destination address
+ * @tid: the TID of the aggregation session
+ * @ssn: the new starting sequence number for the receiver
+ */
+void ieee80211_send_bar(struct ieee80211_vif *vif, u8 *ra, u16 tid, u16 ssn);
+
+/**
+ * ieee80211_start_rx_ba_session_offl - start a Rx BA session
+ *
+ * Some device drivers may offload part of the Rx aggregation flow including
+ * AddBa/DelBa negotiation but may otherwise be incapable of full Rx
+ * reordering.
+ *
+ * Create structures responsible for reordering so device drivers may call here
+ * when they complete AddBa negotiation.
+ *
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback
+ * @addr: station mac address
+ * @tid: the rx tid
+ */
+void ieee80211_start_rx_ba_session_offl(struct ieee80211_vif *vif,
+ const u8 *addr, u16 tid);
+
+/**
+ * ieee80211_stop_rx_ba_session_offl - stop a Rx BA session
+ *
+ * Some device drivers may offload part of the Rx aggregation flow including
+ * AddBa/DelBa negotiation but may otherwise be incapable of full Rx
+ * reordering.
+ *
+ * Destroy structures responsible for reordering so device drivers may call here
+ * when they complete DelBa negotiation.
+ *
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback
+ * @addr: station mac address
+ * @tid: the rx tid
+ */
+void ieee80211_stop_rx_ba_session_offl(struct ieee80211_vif *vif,
+ const u8 *addr, u16 tid);
+
+/* Rate control API */
+
+/**
+ * struct ieee80211_tx_rate_control - rate control information for/from RC algo
+ *
+ * @hw: The hardware the algorithm is invoked for.
+ * @sband: The band this frame is being transmitted on.
+ * @bss_conf: the current BSS configuration
+ * @skb: the skb that will be transmitted, the control information in it needs
+ * to be filled in
+ * @reported_rate: The rate control algorithm can fill this in to indicate
+ * which rate should be reported to userspace as the current rate and
+ * used for rate calculations in the mesh network.
+ * @rts: whether RTS will be used for this frame because it is longer than the
+ * RTS threshold
+ * @short_preamble: whether mac80211 will request short-preamble transmission
+ * if the selected rate supports it
+ * @rate_idx_mask: user-requested (legacy) rate mask
+ * @rate_idx_mcs_mask: user-requested MCS rate mask (NULL if not in use)
+ * @bss: whether this frame is sent out in AP or IBSS mode
+ */
+struct ieee80211_tx_rate_control {
+ struct ieee80211_hw *hw;
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_bss_conf *bss_conf;
+ struct sk_buff *skb;
+ struct ieee80211_tx_rate reported_rate;
+ bool rts, short_preamble;
+ u32 rate_idx_mask;
+ u8 *rate_idx_mcs_mask;
+ bool bss;
+};
+
+struct rate_control_ops {
+ const char *name;
+ void *(*alloc)(struct ieee80211_hw *hw, struct dentry *debugfsdir);
+ void (*free)(void *priv);
+
+ void *(*alloc_sta)(void *priv, struct ieee80211_sta *sta, gfp_t gfp);
+ void (*rate_init)(void *priv, struct ieee80211_supported_band *sband,
+ struct cfg80211_chan_def *chandef,
+ struct ieee80211_sta *sta, void *priv_sta);
+ void (*rate_update)(void *priv, struct ieee80211_supported_band *sband,
+ struct cfg80211_chan_def *chandef,
+ struct ieee80211_sta *sta, void *priv_sta,
+ u32 changed);
+ void (*free_sta)(void *priv, struct ieee80211_sta *sta,
+ void *priv_sta);
+
+ void (*tx_status_ext)(void *priv,
+ struct ieee80211_supported_band *sband,
+ void *priv_sta, struct ieee80211_tx_status *st);
+ void (*tx_status)(void *priv, struct ieee80211_supported_band *sband,
+ struct ieee80211_sta *sta, void *priv_sta,
+ struct sk_buff *skb);
+ void (*get_rate)(void *priv, struct ieee80211_sta *sta, void *priv_sta,
+ struct ieee80211_tx_rate_control *txrc);
+
+ void (*add_sta_debugfs)(void *priv, void *priv_sta,
+ struct dentry *dir);
+ void (*remove_sta_debugfs)(void *priv, void *priv_sta);
+
+ u32 (*get_expected_throughput)(void *priv_sta);
+};
+
+static inline int rate_supported(struct ieee80211_sta *sta,
+ enum nl80211_band band,
+ int index)
+{
+ return (sta == NULL || sta->supp_rates[band] & BIT(index));
+}
+
+/**
+ * rate_control_send_low - helper for drivers for management/no-ack frames
+ *
+ * Rate control algorithms that agree to use the lowest rate to
+ * send management frames and NO_ACK data with the respective hw
+ * retries should use this in the beginning of their mac80211 get_rate
+ * callback. If true is returned the rate control can simply return.
+ * If false is returned we guarantee that sta and sta and priv_sta is
+ * not null.
+ *
+ * Rate control algorithms wishing to do more intelligent selection of
+ * rate for multicast/broadcast frames may choose to not use this.
+ *
+ * @sta: &struct ieee80211_sta pointer to the target destination. Note
+ * that this may be null.
+ * @priv_sta: private rate control structure. This may be null.
+ * @txrc: rate control information we sholud populate for mac80211.
+ */
+bool rate_control_send_low(struct ieee80211_sta *sta,
+ void *priv_sta,
+ struct ieee80211_tx_rate_control *txrc);
+
+
+static inline s8
+rate_lowest_index(struct ieee80211_supported_band *sband,
+ struct ieee80211_sta *sta)
+{
+ int i;
+
+ for (i = 0; i < sband->n_bitrates; i++)
+ if (rate_supported(sta, sband->band, i))
+ return i;
+
+ /* warn when we cannot find a rate. */
+ WARN_ON_ONCE(1);
+
+ /* and return 0 (the lowest index) */
+ return 0;
+}
+
+static inline
+bool rate_usable_index_exists(struct ieee80211_supported_band *sband,
+ struct ieee80211_sta *sta)
+{
+ unsigned int i;
+
+ for (i = 0; i < sband->n_bitrates; i++)
+ if (rate_supported(sta, sband->band, i))
+ return true;
+ return false;
+}
+
+/**
+ * rate_control_set_rates - pass the sta rate selection to mac80211/driver
+ *
+ * When not doing a rate control probe to test rates, rate control should pass
+ * its rate selection to mac80211. If the driver supports receiving a station
+ * rate table, it will use it to ensure that frames are always sent based on
+ * the most recent rate control module decision.
+ *
+ * @hw: pointer as obtained from ieee80211_alloc_hw()
+ * @pubsta: &struct ieee80211_sta pointer to the target destination.
+ * @rates: new tx rate set to be used for this station.
+ */
+int rate_control_set_rates(struct ieee80211_hw *hw,
+ struct ieee80211_sta *pubsta,
+ struct ieee80211_sta_rates *rates);
+
+int ieee80211_rate_control_register(const struct rate_control_ops *ops);
+void ieee80211_rate_control_unregister(const struct rate_control_ops *ops);
+
+static inline bool
+conf_is_ht20(struct ieee80211_conf *conf)
+{
+ return conf->chandef.width == NL80211_CHAN_WIDTH_20;
+}
+
+static inline bool
+conf_is_ht40_minus(struct ieee80211_conf *conf)
+{
+ return conf->chandef.width == NL80211_CHAN_WIDTH_40 &&
+ conf->chandef.center_freq1 < conf->chandef.chan->center_freq;
+}
+
+static inline bool
+conf_is_ht40_plus(struct ieee80211_conf *conf)
+{
+ return conf->chandef.width == NL80211_CHAN_WIDTH_40 &&
+ conf->chandef.center_freq1 > conf->chandef.chan->center_freq;
+}
+
+static inline bool
+conf_is_ht40(struct ieee80211_conf *conf)
+{
+ return conf->chandef.width == NL80211_CHAN_WIDTH_40;
+}
+
+static inline bool
+conf_is_ht(struct ieee80211_conf *conf)
+{
+ return (conf->chandef.width != NL80211_CHAN_WIDTH_5) &&
+ (conf->chandef.width != NL80211_CHAN_WIDTH_10) &&
+ (conf->chandef.width != NL80211_CHAN_WIDTH_20_NOHT);
+}
+
+static inline enum nl80211_iftype
+ieee80211_iftype_p2p(enum nl80211_iftype type, bool p2p)
+{
+ if (p2p) {
+ switch (type) {
+ case NL80211_IFTYPE_STATION:
+ return NL80211_IFTYPE_P2P_CLIENT;
+ case NL80211_IFTYPE_AP:
+ return NL80211_IFTYPE_P2P_GO;
+ default:
+ break;
+ }
+ }
+ return type;
+}
+
+static inline enum nl80211_iftype
+ieee80211_vif_type_p2p(struct ieee80211_vif *vif)
+{
+ return ieee80211_iftype_p2p(vif->type, vif->p2p);
+}
+
+/**
+ * ieee80211_update_mu_groups - set the VHT MU-MIMO groud data
+ *
+ * @vif: the specified virtual interface
+ * @membership: 64 bits array - a bit is set if station is member of the group
+ * @position: 2 bits per group id indicating the position in the group
+ *
+ * Note: This function assumes that the given vif is valid and the position and
+ * membership data is of the correct size and are in the same byte order as the
+ * matching GroupId management frame.
+ * Calls to this function need to be serialized with RX path.
+ */
+void ieee80211_update_mu_groups(struct ieee80211_vif *vif,
+ const u8 *membership, const u8 *position);
+
+void ieee80211_enable_rssi_reports(struct ieee80211_vif *vif,
+ int rssi_min_thold,
+ int rssi_max_thold);
+
+void ieee80211_disable_rssi_reports(struct ieee80211_vif *vif);
+
+/**
+ * ieee80211_ave_rssi - report the average RSSI for the specified interface
+ *
+ * @vif: the specified virtual interface
+ *
+ * Note: This function assumes that the given vif is valid.
+ *
+ * Return: The average RSSI value for the requested interface, or 0 if not
+ * applicable.
+ */
+int ieee80211_ave_rssi(struct ieee80211_vif *vif);
+
+/**
+ * ieee80211_report_wowlan_wakeup - report WoWLAN wakeup
+ * @vif: virtual interface
+ * @wakeup: wakeup reason(s)
+ * @gfp: allocation flags
+ *
+ * See cfg80211_report_wowlan_wakeup().
+ */
+void ieee80211_report_wowlan_wakeup(struct ieee80211_vif *vif,
+ struct cfg80211_wowlan_wakeup *wakeup,
+ gfp_t gfp);
+
+/**
+ * ieee80211_tx_prepare_skb - prepare an 802.11 skb for transmission
+ * @hw: pointer as obtained from ieee80211_alloc_hw()
+ * @vif: virtual interface
+ * @skb: frame to be sent from within the driver
+ * @band: the band to transmit on
+ * @sta: optional pointer to get the station to send the frame to
+ *
+ * Note: must be called under RCU lock
+ */
+bool ieee80211_tx_prepare_skb(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, struct sk_buff *skb,
+ int band, struct ieee80211_sta **sta);
+
+/**
+ * struct ieee80211_noa_data - holds temporary data for tracking P2P NoA state
+ *
+ * @next_tsf: TSF timestamp of the next absent state change
+ * @has_next_tsf: next absent state change event pending
+ *
+ * @absent: descriptor bitmask, set if GO is currently absent
+ *
+ * private:
+ *
+ * @count: count fields from the NoA descriptors
+ * @desc: adjusted data from the NoA
+ */
+struct ieee80211_noa_data {
+ u32 next_tsf;
+ bool has_next_tsf;
+
+ u8 absent;
+
+ u8 count[IEEE80211_P2P_NOA_DESC_MAX];
+ struct {
+ u32 start;
+ u32 duration;
+ u32 interval;
+ } desc[IEEE80211_P2P_NOA_DESC_MAX];
+};
+
+/**
+ * ieee80211_parse_p2p_noa - initialize NoA tracking data from P2P IE
+ *
+ * @attr: P2P NoA IE
+ * @data: NoA tracking data
+ * @tsf: current TSF timestamp
+ *
+ * Return: number of successfully parsed descriptors
+ */
+int ieee80211_parse_p2p_noa(const struct ieee80211_p2p_noa_attr *attr,
+ struct ieee80211_noa_data *data, u32 tsf);
+
+/**
+ * ieee80211_update_p2p_noa - get next pending P2P GO absent state change
+ *
+ * @data: NoA tracking data
+ * @tsf: current TSF timestamp
+ */
+void ieee80211_update_p2p_noa(struct ieee80211_noa_data *data, u32 tsf);
+
+/**
+ * ieee80211_tdls_oper - request userspace to perform a TDLS operation
+ * @vif: virtual interface
+ * @peer: the peer's destination address
+ * @oper: the requested TDLS operation
+ * @reason_code: reason code for the operation, valid for TDLS teardown
+ * @gfp: allocation flags
+ *
+ * See cfg80211_tdls_oper_request().
+ */
+void ieee80211_tdls_oper_request(struct ieee80211_vif *vif, const u8 *peer,
+ enum nl80211_tdls_operation oper,
+ u16 reason_code, gfp_t gfp);
+
+/**
+ * ieee80211_reserve_tid - request to reserve a specific TID
+ *
+ * There is sometimes a need (such as in TDLS) for blocking the driver from
+ * using a specific TID so that the FW can use it for certain operations such
+ * as sending PTI requests. To make sure that the driver doesn't use that TID,
+ * this function must be called as it flushes out packets on this TID and marks
+ * it as blocked, so that any transmit for the station on this TID will be
+ * redirected to the alternative TID in the same AC.
+ *
+ * Note that this function blocks and may call back into the driver, so it
+ * should be called without driver locks held. Also note this function should
+ * only be called from the driver's @sta_state callback.
+ *
+ * @sta: the station to reserve the TID for
+ * @tid: the TID to reserve
+ *
+ * Returns: 0 on success, else on failure
+ */
+int ieee80211_reserve_tid(struct ieee80211_sta *sta, u8 tid);
+
+/**
+ * ieee80211_unreserve_tid - request to unreserve a specific TID
+ *
+ * Once there is no longer any need for reserving a certain TID, this function
+ * should be called, and no longer will packets have their TID modified for
+ * preventing use of this TID in the driver.
+ *
+ * Note that this function blocks and acquires a lock, so it should be called
+ * without driver locks held. Also note this function should only be called
+ * from the driver's @sta_state callback.
+ *
+ * @sta: the station
+ * @tid: the TID to unreserve
+ */
+void ieee80211_unreserve_tid(struct ieee80211_sta *sta, u8 tid);
+
+/**
+ * ieee80211_tx_dequeue - dequeue a packet from a software tx queue
+ *
+ * @hw: pointer as obtained from ieee80211_alloc_hw()
+ * @txq: pointer obtained from station or virtual interface
+ *
+ * Returns the skb if successful, %NULL if no frame was available.
+ */
+struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw,
+ struct ieee80211_txq *txq);
+
+/**
+ * ieee80211_txq_get_depth - get pending frame/byte count of given txq
+ *
+ * The values are not guaranteed to be coherent with regard to each other, i.e.
+ * txq state can change half-way of this function and the caller may end up
+ * with "new" frame_cnt and "old" byte_cnt or vice-versa.
+ *
+ * @txq: pointer obtained from station or virtual interface
+ * @frame_cnt: pointer to store frame count
+ * @byte_cnt: pointer to store byte count
+ */
+void ieee80211_txq_get_depth(struct ieee80211_txq *txq,
+ unsigned long *frame_cnt,
+ unsigned long *byte_cnt);
+
+/**
+ * ieee80211_nan_func_terminated - notify about NAN function termination.
+ *
+ * This function is used to notify mac80211 about NAN function termination.
+ * Note that this function can't be called from hard irq.
+ *
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ * @inst_id: the local instance id
+ * @reason: termination reason (one of the NL80211_NAN_FUNC_TERM_REASON_*)
+ * @gfp: allocation flags
+ */
+void ieee80211_nan_func_terminated(struct ieee80211_vif *vif,
+ u8 inst_id,
+ enum nl80211_nan_func_term_reason reason,
+ gfp_t gfp);
+
+/**
+ * ieee80211_nan_func_match - notify about NAN function match event.
+ *
+ * This function is used to notify mac80211 about NAN function match. The
+ * cookie inside the match struct will be assigned by mac80211.
+ * Note that this function can't be called from hard irq.
+ *
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ * @match: match event information
+ * @gfp: allocation flags
+ */
+void ieee80211_nan_func_match(struct ieee80211_vif *vif,
+ struct cfg80211_nan_match_params *match,
+ gfp_t gfp);
+
+#endif /* MAC80211_H */
diff --git a/include/net/regulatory.h b/include/net/regulatory.h
new file mode 100644
index 0000000..ebc5a2e
--- /dev/null
+++ b/include/net/regulatory.h
@@ -0,0 +1,227 @@
+#ifndef __NET_REGULATORY_H
+#define __NET_REGULATORY_H
+/*
+ * regulatory support structures
+ *
+ * Copyright 2008-2009 Luis R. Rodriguez <mcgrof@qca.qualcomm.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/rcupdate.h>
+
+/**
+ * enum environment_cap - Environment parsed from country IE
+ * @ENVIRON_ANY: indicates country IE applies to both indoor and
+ * outdoor operation.
+ * @ENVIRON_INDOOR: indicates country IE applies only to indoor operation
+ * @ENVIRON_OUTDOOR: indicates country IE applies only to outdoor operation
+ */
+enum environment_cap {
+ ENVIRON_ANY,
+ ENVIRON_INDOOR,
+ ENVIRON_OUTDOOR,
+};
+
+/**
+ * struct regulatory_request - used to keep track of regulatory requests
+ *
+ * @rcu_head: RCU head struct used to free the request
+ * @wiphy_idx: this is set if this request's initiator is
+ * %REGDOM_SET_BY_COUNTRY_IE or %REGDOM_SET_BY_DRIVER. This
+ * can be used by the wireless core to deal with conflicts
+ * and potentially inform users of which devices specifically
+ * cased the conflicts.
+ * @initiator: indicates who sent this request, could be any of
+ * of those set in nl80211_reg_initiator (%NL80211_REGDOM_SET_BY_*)
+ * @alpha2: the ISO / IEC 3166 alpha2 country code of the requested
+ * regulatory domain. We have a few special codes:
+ * 00 - World regulatory domain
+ * 99 - built by driver but a specific alpha2 cannot be determined
+ * 98 - result of an intersection between two regulatory domains
+ * 97 - regulatory domain has not yet been configured
+ * @dfs_region: If CRDA responded with a regulatory domain that requires
+ * DFS master operation on a known DFS region (NL80211_DFS_*),
+ * dfs_region represents that region. Drivers can use this and the
+ * @alpha2 to adjust their device's DFS parameters as required.
+ * @user_reg_hint_type: if the @initiator was of type
+ * %NL80211_REGDOM_SET_BY_USER, this classifies the type
+ * of hint passed. This could be any of the %NL80211_USER_REG_HINT_*
+ * types.
+ * @intersect: indicates whether the wireless core should intersect
+ * the requested regulatory domain with the presently set regulatory
+ * domain.
+ * @processed: indicates whether or not this requests has already been
+ * processed. When the last request is processed it means that the
+ * currently regulatory domain set on cfg80211 is updated from
+ * CRDA and can be used by other regulatory requests. When a
+ * the last request is not yet processed we must yield until it
+ * is processed before processing any new requests.
+ * @country_ie_checksum: checksum of the last processed and accepted
+ * country IE
+ * @country_ie_env: lets us know if the AP is telling us we are outdoor,
+ * indoor, or if it doesn't matter
+ * @list: used to insert into the reg_requests_list linked list
+ */
+struct regulatory_request {
+ struct rcu_head rcu_head;
+ int wiphy_idx;
+ enum nl80211_reg_initiator initiator;
+ enum nl80211_user_reg_hint_type user_reg_hint_type;
+ char alpha2[2];
+ enum nl80211_dfs_regions dfs_region;
+ bool intersect;
+ bool processed;
+ enum environment_cap country_ie_env;
+ struct list_head list;
+};
+
+/**
+ * enum ieee80211_regulatory_flags - device regulatory flags
+ *
+ * @REGULATORY_CUSTOM_REG: tells us the driver for this device
+ * has its own custom regulatory domain and cannot identify the
+ * ISO / IEC 3166 alpha2 it belongs to. When this is enabled
+ * we will disregard the first regulatory hint (when the
+ * initiator is %REGDOM_SET_BY_CORE). Drivers that use
+ * wiphy_apply_custom_regulatory() should have this flag set
+ * or the regulatory core will set it for the wiphy.
+ * If you use regulatory_hint() *after* using
+ * wiphy_apply_custom_regulatory() the wireless core will
+ * clear the REGULATORY_CUSTOM_REG for your wiphy as it would be
+ * implied that the device somehow gained knowledge of its region.
+ * @REGULATORY_STRICT_REG: tells us that the wiphy for this device
+ * has regulatory domain that it wishes to be considered as the
+ * superset for regulatory rules. After this device gets its regulatory
+ * domain programmed further regulatory hints shall only be considered
+ * for this device to enhance regulatory compliance, forcing the
+ * device to only possibly use subsets of the original regulatory
+ * rules. For example if channel 13 and 14 are disabled by this
+ * device's regulatory domain no user specified regulatory hint which
+ * has these channels enabled would enable them for this wiphy,
+ * the device's original regulatory domain will be trusted as the
+ * base. You can program the superset of regulatory rules for this
+ * wiphy with regulatory_hint() for cards programmed with an
+ * ISO3166-alpha2 country code. wiphys that use regulatory_hint()
+ * will have their wiphy->regd programmed once the regulatory
+ * domain is set, and all other regulatory hints will be ignored
+ * until their own regulatory domain gets programmed.
+ * @REGULATORY_DISABLE_BEACON_HINTS: enable this if your driver needs to
+ * ensure that passive scan flags and beaconing flags may not be lifted by
+ * cfg80211 due to regulatory beacon hints. For more information on beacon
+ * hints read the documenation for regulatory_hint_found_beacon()
+ * @REGULATORY_COUNTRY_IE_FOLLOW_POWER: for devices that have a preference
+ * that even though they may have programmed their own custom power
+ * setting prior to wiphy registration, they want to ensure their channel
+ * power settings are updated for this connection with the power settings
+ * derived from the regulatory domain. The regulatory domain used will be
+ * based on the ISO3166-alpha2 from country IE provided through
+ * regulatory_hint_country_ie()
+ * @REGULATORY_COUNTRY_IE_IGNORE: for devices that have a preference to ignore
+ * all country IE information processed by the regulatory core. This will
+ * override %REGULATORY_COUNTRY_IE_FOLLOW_POWER as all country IEs will
+ * be ignored.
+ * @REGULATORY_ENABLE_RELAX_NO_IR: for devices that wish to allow the
+ * NO_IR relaxation, which enables transmissions on channels on which
+ * otherwise initiating radiation is not allowed. This will enable the
+ * relaxations enabled under the CFG80211_REG_RELAX_NO_IR configuration
+ * option
+ * @REGULATORY_IGNORE_STALE_KICKOFF: the regulatory core will _not_ make sure
+ * all interfaces on this wiphy reside on allowed channels. If this flag
+ * is not set, upon a regdomain change, the interfaces are given a grace
+ * period (currently 60 seconds) to disconnect or move to an allowed
+ * channel. Interfaces on forbidden channels are forcibly disconnected.
+ * Currently these types of interfaces are supported for enforcement:
+ * NL80211_IFTYPE_ADHOC, NL80211_IFTYPE_STATION, NL80211_IFTYPE_AP,
+ * NL80211_IFTYPE_AP_VLAN, NL80211_IFTYPE_MONITOR,
+ * NL80211_IFTYPE_P2P_CLIENT, NL80211_IFTYPE_P2P_GO,
+ * NL80211_IFTYPE_P2P_DEVICE. The flag will be set by default if a device
+ * includes any modes unsupported for enforcement checking.
+ * @REGULATORY_WIPHY_SELF_MANAGED: for devices that employ wiphy-specific
+ * regdom management. These devices will ignore all regdom changes not
+ * originating from their own wiphy.
+ * A self-managed wiphys only employs regulatory information obtained from
+ * the FW and driver and does not use other cfg80211 sources like
+ * beacon-hints, country-code IEs and hints from other devices on the same
+ * system. Conversely, a self-managed wiphy does not share its regulatory
+ * hints with other devices in the system. If a system contains several
+ * devices, one or more of which are self-managed, there might be
+ * contradictory regulatory settings between them. Usage of flag is
+ * generally discouraged. Only use it if the FW/driver is incompatible
+ * with non-locally originated hints.
+ * This flag is incompatible with the flags: %REGULATORY_CUSTOM_REG,
+ * %REGULATORY_STRICT_REG, %REGULATORY_COUNTRY_IE_FOLLOW_POWER,
+ * %REGULATORY_COUNTRY_IE_IGNORE and %REGULATORY_DISABLE_BEACON_HINTS.
+ * Mixing any of the above flags with this flag will result in a failure
+ * to register the wiphy. This flag implies
+ * %REGULATORY_DISABLE_BEACON_HINTS and %REGULATORY_COUNTRY_IE_IGNORE.
+ */
+enum ieee80211_regulatory_flags {
+ REGULATORY_CUSTOM_REG = BIT(0),
+ REGULATORY_STRICT_REG = BIT(1),
+ REGULATORY_DISABLE_BEACON_HINTS = BIT(2),
+ REGULATORY_COUNTRY_IE_FOLLOW_POWER = BIT(3),
+ REGULATORY_COUNTRY_IE_IGNORE = BIT(4),
+ REGULATORY_ENABLE_RELAX_NO_IR = BIT(5),
+ REGULATORY_IGNORE_STALE_KICKOFF = BIT(6),
+ REGULATORY_WIPHY_SELF_MANAGED = BIT(7),
+};
+
+struct ieee80211_freq_range {
+ u32 start_freq_khz;
+ u32 end_freq_khz;
+ u32 max_bandwidth_khz;
+};
+
+struct ieee80211_power_rule {
+ u32 max_antenna_gain;
+ u32 max_eirp;
+};
+
+struct ieee80211_reg_rule {
+ struct ieee80211_freq_range freq_range;
+ struct ieee80211_power_rule power_rule;
+ u32 flags;
+ u32 dfs_cac_ms;
+};
+
+struct ieee80211_regdomain {
+ struct rcu_head rcu_head;
+ u32 n_reg_rules;
+ char alpha2[3];
+ enum nl80211_dfs_regions dfs_region;
+ struct ieee80211_reg_rule reg_rules[];
+};
+
+#define MHZ_TO_KHZ(freq) ((freq) * 1000)
+#define KHZ_TO_MHZ(freq) ((freq) / 1000)
+#define DBI_TO_MBI(gain) ((gain) * 100)
+#define MBI_TO_DBI(gain) ((gain) / 100)
+#define DBM_TO_MBM(gain) ((gain) * 100)
+#define MBM_TO_DBM(gain) ((gain) / 100)
+
+#define REG_RULE_EXT(start, end, bw, gain, eirp, dfs_cac, reg_flags) \
+{ \
+ .freq_range.start_freq_khz = MHZ_TO_KHZ(start), \
+ .freq_range.end_freq_khz = MHZ_TO_KHZ(end), \
+ .freq_range.max_bandwidth_khz = MHZ_TO_KHZ(bw), \
+ .power_rule.max_antenna_gain = DBI_TO_MBI(gain), \
+ .power_rule.max_eirp = DBM_TO_MBM(eirp), \
+ .flags = reg_flags, \
+ .dfs_cac_ms = dfs_cac, \
+}
+
+#define REG_RULE(start, end, bw, gain, eirp, reg_flags) \
+ REG_RULE_EXT(start, end, bw, gain, eirp, 0, reg_flags)
+
+#endif
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
new file mode 100644
index 0000000..2e38363
--- /dev/null
+++ b/include/uapi/linux/nl80211.h
@@ -0,0 +1,5450 @@
+#ifndef __LINUX_NL80211_H
+#define __LINUX_NL80211_H
+/*
+ * 802.11 netlink interface public header
+ *
+ * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2008 Michael Wu <flamingice@sourmilk.net>
+ * Copyright 2008 Luis Carlos Cobo <luisca@cozybit.com>
+ * Copyright 2008 Michael Buesch <m@bues.ch>
+ * Copyright 2008, 2009 Luis R. Rodriguez <lrodriguez@atheros.com>
+ * Copyright 2008 Jouni Malinen <jouni.malinen@atheros.com>
+ * Copyright 2008 Colin McCabe <colin@cozybit.com>
+ * Copyright 2015-2017 Intel Deutschland GmbH
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+/*
+ * This header file defines the userspace API to the wireless stack. Please
+ * be careful not to break things - i.e. don't move anything around or so
+ * unless you can demonstrate that it breaks neither API nor ABI.
+ *
+ * Additions to the API should be accompanied by actual implementations in
+ * an upstream driver, so that example implementations exist in case there
+ * are ever concerns about the precise semantics of the API or changes are
+ * needed, and to ensure that code for dead (no longer implemented) API
+ * can actually be identified and removed.
+ * Nonetheless, semantics should also be documented carefully in this file.
+ */
+
+#include <linux/types.h>
+
+#define NL80211_GENL_NAME "nl80211"
+
+#define NL80211_MULTICAST_GROUP_CONFIG "config"
+#define NL80211_MULTICAST_GROUP_SCAN "scan"
+#define NL80211_MULTICAST_GROUP_REG "regulatory"
+#define NL80211_MULTICAST_GROUP_MLME "mlme"
+#define NL80211_MULTICAST_GROUP_VENDOR "vendor"
+#define NL80211_MULTICAST_GROUP_NAN "nan"
+#define NL80211_MULTICAST_GROUP_TESTMODE "testmode"
+
+/**
+ * DOC: Station handling
+ *
+ * Stations are added per interface, but a special case exists with VLAN
+ * interfaces. When a station is bound to an AP interface, it may be moved
+ * into a VLAN identified by a VLAN interface index (%NL80211_ATTR_STA_VLAN).
+ * The station is still assumed to belong to the AP interface it was added
+ * to.
+ *
+ * Station handling varies per interface type and depending on the driver's
+ * capabilities.
+ *
+ * For drivers supporting TDLS with external setup (WIPHY_FLAG_SUPPORTS_TDLS
+ * and WIPHY_FLAG_TDLS_EXTERNAL_SETUP), the station lifetime is as follows:
+ * - a setup station entry is added, not yet authorized, without any rate
+ * or capability information, this just exists to avoid race conditions
+ * - when the TDLS setup is done, a single NL80211_CMD_SET_STATION is valid
+ * to add rate and capability information to the station and at the same
+ * time mark it authorized.
+ * - %NL80211_TDLS_ENABLE_LINK is then used
+ * - after this, the only valid operation is to remove it by tearing down
+ * the TDLS link (%NL80211_TDLS_DISABLE_LINK)
+ *
+ * TODO: need more info for other interface types
+ */
+
+/**
+ * DOC: Frame transmission/registration support
+ *
+ * Frame transmission and registration support exists to allow userspace
+ * management entities such as wpa_supplicant react to management frames
+ * that are not being handled by the kernel. This includes, for example,
+ * certain classes of action frames that cannot be handled in the kernel
+ * for various reasons.
+ *
+ * Frame registration is done on a per-interface basis and registrations
+ * cannot be removed other than by closing the socket. It is possible to
+ * specify a registration filter to register, for example, only for a
+ * certain type of action frame. In particular with action frames, those
+ * that userspace registers for will not be returned as unhandled by the
+ * driver, so that the registered application has to take responsibility
+ * for doing that.
+ *
+ * The type of frame that can be registered for is also dependent on the
+ * driver and interface type. The frame types are advertised in wiphy
+ * attributes so applications know what to expect.
+ *
+ * NOTE: When an interface changes type while registrations are active,
+ * these registrations are ignored until the interface type is
+ * changed again. This means that changing the interface type can
+ * lead to a situation that couldn't otherwise be produced, but
+ * any such registrations will be dormant in the sense that they
+ * will not be serviced, i.e. they will not receive any frames.
+ *
+ * Frame transmission allows userspace to send for example the required
+ * responses to action frames. It is subject to some sanity checking,
+ * but many frames can be transmitted. When a frame was transmitted, its
+ * status is indicated to the sending socket.
+ *
+ * For more technical details, see the corresponding command descriptions
+ * below.
+ */
+
+/**
+ * DOC: Virtual interface / concurrency capabilities
+ *
+ * Some devices are able to operate with virtual MACs, they can have
+ * more than one virtual interface. The capability handling for this
+ * is a bit complex though, as there may be a number of restrictions
+ * on the types of concurrency that are supported.
+ *
+ * To start with, each device supports the interface types listed in
+ * the %NL80211_ATTR_SUPPORTED_IFTYPES attribute, but by listing the
+ * types there no concurrency is implied.
+ *
+ * Once concurrency is desired, more attributes must be observed:
+ * To start with, since some interface types are purely managed in
+ * software, like the AP-VLAN type in mac80211 for example, there's
+ * an additional list of these, they can be added at any time and
+ * are only restricted by some semantic restrictions (e.g. AP-VLAN
+ * cannot be added without a corresponding AP interface). This list
+ * is exported in the %NL80211_ATTR_SOFTWARE_IFTYPES attribute.
+ *
+ * Further, the list of supported combinations is exported. This is
+ * in the %NL80211_ATTR_INTERFACE_COMBINATIONS attribute. Basically,
+ * it exports a list of "groups", and at any point in time the
+ * interfaces that are currently active must fall into any one of
+ * the advertised groups. Within each group, there are restrictions
+ * on the number of interfaces of different types that are supported
+ * and also the number of different channels, along with potentially
+ * some other restrictions. See &enum nl80211_if_combination_attrs.
+ *
+ * All together, these attributes define the concurrency of virtual
+ * interfaces that a given device supports.
+ */
+
+/**
+ * DOC: packet coalesce support
+ *
+ * In most cases, host that receives IPv4 and IPv6 multicast/broadcast
+ * packets does not do anything with these packets. Therefore the
+ * reception of these unwanted packets causes unnecessary processing
+ * and power consumption.
+ *
+ * Packet coalesce feature helps to reduce number of received interrupts
+ * to host by buffering these packets in firmware/hardware for some
+ * predefined time. Received interrupt will be generated when one of the
+ * following events occur.
+ * a) Expiration of hardware timer whose expiration time is set to maximum
+ * coalescing delay of matching coalesce rule.
+ * b) Coalescing buffer in hardware reaches it's limit.
+ * c) Packet doesn't match any of the configured coalesce rules.
+ *
+ * User needs to configure following parameters for creating a coalesce
+ * rule.
+ * a) Maximum coalescing delay
+ * b) List of packet patterns which needs to be matched
+ * c) Condition for coalescence. pattern 'match' or 'no match'
+ * Multiple such rules can be created.
+ */
+
+/**
+ * DOC: WPA/WPA2 EAPOL handshake offload
+ *
+ * By setting @NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK flag drivers
+ * can indicate they support offloading EAPOL handshakes for WPA/WPA2
+ * preshared key authentication. In %NL80211_CMD_CONNECT the preshared
+ * key should be specified using %NL80211_ATTR_PMK. Drivers supporting
+ * this offload may reject the %NL80211_CMD_CONNECT when no preshared
+ * key material is provided, for example when that driver does not
+ * support setting the temporal keys through %CMD_NEW_KEY.
+ *
+ * Similarly @NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X flag can be
+ * set by drivers indicating offload support of the PTK/GTK EAPOL
+ * handshakes during 802.1X authentication. In order to use the offload
+ * the %NL80211_CMD_CONNECT should have %NL80211_ATTR_WANT_1X_4WAY_HS
+ * attribute flag. Drivers supporting this offload may reject the
+ * %NL80211_CMD_CONNECT when the attribute flag is not present.
+ *
+ * For 802.1X the PMK or PMK-R0 are set by providing %NL80211_ATTR_PMK
+ * using %NL80211_CMD_SET_PMK. For offloaded FT support also
+ * %NL80211_ATTR_PMKR0_NAME must be provided.
+ */
+
+/**
+ * DOC: FILS shared key authentication offload
+ *
+ * FILS shared key authentication offload can be advertized by drivers by
+ * setting @NL80211_EXT_FEATURE_FILS_SK_OFFLOAD flag. The drivers that support
+ * FILS shared key authentication offload should be able to construct the
+ * authentication and association frames for FILS shared key authentication and
+ * eventually do a key derivation as per IEEE 802.11ai. The below additional
+ * parameters should be given to driver in %NL80211_CMD_CONNECT.
+ * %NL80211_ATTR_FILS_ERP_USERNAME - used to construct keyname_nai
+ * %NL80211_ATTR_FILS_ERP_REALM - used to construct keyname_nai
+ * %NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM - used to construct erp message
+ * %NL80211_ATTR_FILS_ERP_RRK - used to generate the rIK and rMSK
+ * rIK should be used to generate an authentication tag on the ERP message and
+ * rMSK should be used to derive a PMKSA.
+ * rIK, rMSK should be generated and keyname_nai, sequence number should be used
+ * as specified in IETF RFC 6696.
+ *
+ * When FILS shared key authentication is completed, driver needs to provide the
+ * below additional parameters to userspace.
+ * %NL80211_ATTR_FILS_KEK - used for key renewal
+ * %NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM - used in further EAP-RP exchanges
+ * %NL80211_ATTR_PMKID - used to identify the PMKSA used/generated
+ * %Nl80211_ATTR_PMK - used to update PMKSA cache in userspace
+ * The PMKSA can be maintained in userspace persistently so that it can be used
+ * later after reboots or wifi turn off/on also.
+ *
+ * %NL80211_ATTR_FILS_CACHE_ID is the cache identifier advertized by a FILS
+ * capable AP supporting PMK caching. It specifies the scope within which the
+ * PMKSAs are cached in an ESS. %NL80211_CMD_SET_PMKSA and
+ * %NL80211_CMD_DEL_PMKSA are enhanced to allow support for PMKSA caching based
+ * on FILS cache identifier. Additionally %NL80211_ATTR_PMK is used with
+ * %NL80211_SET_PMKSA to specify the PMK corresponding to a PMKSA for driver to
+ * use in a FILS shared key connection with PMKSA caching.
+ */
+
+/**
+ * enum nl80211_commands - supported nl80211 commands
+ *
+ * @NL80211_CMD_UNSPEC: unspecified command to catch errors
+ *
+ * @NL80211_CMD_GET_WIPHY: request information about a wiphy or dump request
+ * to get a list of all present wiphys.
+ * @NL80211_CMD_SET_WIPHY: set wiphy parameters, needs %NL80211_ATTR_WIPHY or
+ * %NL80211_ATTR_IFINDEX; can be used to set %NL80211_ATTR_WIPHY_NAME,
+ * %NL80211_ATTR_WIPHY_TXQ_PARAMS, %NL80211_ATTR_WIPHY_FREQ (and the
+ * attributes determining the channel width; this is used for setting
+ * monitor mode channel), %NL80211_ATTR_WIPHY_RETRY_SHORT,
+ * %NL80211_ATTR_WIPHY_RETRY_LONG, %NL80211_ATTR_WIPHY_FRAG_THRESHOLD,
+ * and/or %NL80211_ATTR_WIPHY_RTS_THRESHOLD.
+ * However, for setting the channel, see %NL80211_CMD_SET_CHANNEL
+ * instead, the support here is for backward compatibility only.
+ * @NL80211_CMD_NEW_WIPHY: Newly created wiphy, response to get request
+ * or rename notification. Has attributes %NL80211_ATTR_WIPHY and
+ * %NL80211_ATTR_WIPHY_NAME.
+ * @NL80211_CMD_DEL_WIPHY: Wiphy deleted. Has attributes
+ * %NL80211_ATTR_WIPHY and %NL80211_ATTR_WIPHY_NAME.
+ *
+ * @NL80211_CMD_GET_INTERFACE: Request an interface's configuration;
+ * either a dump request for all interfaces or a specific get with a
+ * single %NL80211_ATTR_IFINDEX is supported.
+ * @NL80211_CMD_SET_INTERFACE: Set type of a virtual interface, requires
+ * %NL80211_ATTR_IFINDEX and %NL80211_ATTR_IFTYPE.
+ * @NL80211_CMD_NEW_INTERFACE: Newly created virtual interface or response
+ * to %NL80211_CMD_GET_INTERFACE. Has %NL80211_ATTR_IFINDEX,
+ * %NL80211_ATTR_WIPHY and %NL80211_ATTR_IFTYPE attributes. Can also
+ * be sent from userspace to request creation of a new virtual interface,
+ * then requires attributes %NL80211_ATTR_WIPHY, %NL80211_ATTR_IFTYPE and
+ * %NL80211_ATTR_IFNAME.
+ * @NL80211_CMD_DEL_INTERFACE: Virtual interface was deleted, has attributes
+ * %NL80211_ATTR_IFINDEX and %NL80211_ATTR_WIPHY. Can also be sent from
+ * userspace to request deletion of a virtual interface, then requires
+ * attribute %NL80211_ATTR_IFINDEX.
+ *
+ * @NL80211_CMD_GET_KEY: Get sequence counter information for a key specified
+ * by %NL80211_ATTR_KEY_IDX and/or %NL80211_ATTR_MAC.
+ * @NL80211_CMD_SET_KEY: Set key attributes %NL80211_ATTR_KEY_DEFAULT,
+ * %NL80211_ATTR_KEY_DEFAULT_MGMT, or %NL80211_ATTR_KEY_THRESHOLD.
+ * @NL80211_CMD_NEW_KEY: add a key with given %NL80211_ATTR_KEY_DATA,
+ * %NL80211_ATTR_KEY_IDX, %NL80211_ATTR_MAC, %NL80211_ATTR_KEY_CIPHER,
+ * and %NL80211_ATTR_KEY_SEQ attributes.
+ * @NL80211_CMD_DEL_KEY: delete a key identified by %NL80211_ATTR_KEY_IDX
+ * or %NL80211_ATTR_MAC.
+ *
+ * @NL80211_CMD_GET_BEACON: (not used)
+ * @NL80211_CMD_SET_BEACON: change the beacon on an access point interface
+ * using the %NL80211_ATTR_BEACON_HEAD and %NL80211_ATTR_BEACON_TAIL
+ * attributes. For drivers that generate the beacon and probe responses
+ * internally, the following attributes must be provided: %NL80211_ATTR_IE,
+ * %NL80211_ATTR_IE_PROBE_RESP and %NL80211_ATTR_IE_ASSOC_RESP.
+ * @NL80211_CMD_START_AP: Start AP operation on an AP interface, parameters
+ * are like for %NL80211_CMD_SET_BEACON, and additionally parameters that
+ * do not change are used, these include %NL80211_ATTR_BEACON_INTERVAL,
+ * %NL80211_ATTR_DTIM_PERIOD, %NL80211_ATTR_SSID,
+ * %NL80211_ATTR_HIDDEN_SSID, %NL80211_ATTR_CIPHERS_PAIRWISE,
+ * %NL80211_ATTR_CIPHER_GROUP, %NL80211_ATTR_WPA_VERSIONS,
+ * %NL80211_ATTR_AKM_SUITES, %NL80211_ATTR_PRIVACY,
+ * %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_INACTIVITY_TIMEOUT,
+ * %NL80211_ATTR_ACL_POLICY and %NL80211_ATTR_MAC_ADDRS.
+ * The channel to use can be set on the interface or be given using the
+ * %NL80211_ATTR_WIPHY_FREQ and the attributes determining channel width.
+ * @NL80211_CMD_NEW_BEACON: old alias for %NL80211_CMD_START_AP
+ * @NL80211_CMD_STOP_AP: Stop AP operation on the given interface
+ * @NL80211_CMD_DEL_BEACON: old alias for %NL80211_CMD_STOP_AP
+ *
+ * @NL80211_CMD_GET_STATION: Get station attributes for station identified by
+ * %NL80211_ATTR_MAC on the interface identified by %NL80211_ATTR_IFINDEX.
+ * @NL80211_CMD_SET_STATION: Set station attributes for station identified by
+ * %NL80211_ATTR_MAC on the interface identified by %NL80211_ATTR_IFINDEX.
+ * @NL80211_CMD_NEW_STATION: Add a station with given attributes to the
+ * the interface identified by %NL80211_ATTR_IFINDEX.
+ * @NL80211_CMD_DEL_STATION: Remove a station identified by %NL80211_ATTR_MAC
+ * or, if no MAC address given, all stations, on the interface identified
+ * by %NL80211_ATTR_IFINDEX. %NL80211_ATTR_MGMT_SUBTYPE and
+ * %NL80211_ATTR_REASON_CODE can optionally be used to specify which type
+ * of disconnection indication should be sent to the station
+ * (Deauthentication or Disassociation frame and reason code for that
+ * frame).
+ *
+ * @NL80211_CMD_GET_MPATH: Get mesh path attributes for mesh path to
+ * destination %NL80211_ATTR_MAC on the interface identified by
+ * %NL80211_ATTR_IFINDEX.
+ * @NL80211_CMD_SET_MPATH: Set mesh path attributes for mesh path to
+ * destination %NL80211_ATTR_MAC on the interface identified by
+ * %NL80211_ATTR_IFINDEX.
+ * @NL80211_CMD_NEW_MPATH: Create a new mesh path for the destination given by
+ * %NL80211_ATTR_MAC via %NL80211_ATTR_MPATH_NEXT_HOP.
+ * @NL80211_CMD_DEL_MPATH: Delete a mesh path to the destination given by
+ * %NL80211_ATTR_MAC.
+ * @NL80211_CMD_NEW_PATH: Add a mesh path with given attributes to the
+ * the interface identified by %NL80211_ATTR_IFINDEX.
+ * @NL80211_CMD_DEL_PATH: Remove a mesh path identified by %NL80211_ATTR_MAC
+ * or, if no MAC address given, all mesh paths, on the interface identified
+ * by %NL80211_ATTR_IFINDEX.
+ * @NL80211_CMD_SET_BSS: Set BSS attributes for BSS identified by
+ * %NL80211_ATTR_IFINDEX.
+ *
+ * @NL80211_CMD_GET_REG: ask the wireless core to send us its currently set
+ * regulatory domain. If %NL80211_ATTR_WIPHY is specified and the device
+ * has a private regulatory domain, it will be returned. Otherwise, the
+ * global regdomain will be returned.
+ * A device will have a private regulatory domain if it uses the
+ * regulatory_hint() API. Even when a private regdomain is used the channel
+ * information will still be mended according to further hints from
+ * the regulatory core to help with compliance. A dump version of this API
+ * is now available which will returns the global regdomain as well as
+ * all private regdomains of present wiphys (for those that have it).
+ * If a wiphy is self-managed (%NL80211_ATTR_WIPHY_SELF_MANAGED_REG), then
+ * its private regdomain is the only valid one for it. The regulatory
+ * core is not used to help with compliance in this case.
+ * @NL80211_CMD_SET_REG: Set current regulatory domain. CRDA sends this command
+ * after being queried by the kernel. CRDA replies by sending a regulatory
+ * domain structure which consists of %NL80211_ATTR_REG_ALPHA set to our
+ * current alpha2 if it found a match. It also provides
+ * NL80211_ATTR_REG_RULE_FLAGS, and a set of regulatory rules. Each
+ * regulatory rule is a nested set of attributes given by
+ * %NL80211_ATTR_REG_RULE_FREQ_[START|END] and
+ * %NL80211_ATTR_FREQ_RANGE_MAX_BW with an attached power rule given by
+ * %NL80211_ATTR_REG_RULE_POWER_MAX_ANT_GAIN and
+ * %NL80211_ATTR_REG_RULE_POWER_MAX_EIRP.
+ * @NL80211_CMD_REQ_SET_REG: ask the wireless core to set the regulatory domain
+ * to the specified ISO/IEC 3166-1 alpha2 country code. The core will
+ * store this as a valid request and then query userspace for it.
+ *
+ * @NL80211_CMD_GET_MESH_CONFIG: Get mesh networking properties for the
+ * interface identified by %NL80211_ATTR_IFINDEX
+ *
+ * @NL80211_CMD_SET_MESH_CONFIG: Set mesh networking properties for the
+ * interface identified by %NL80211_ATTR_IFINDEX
+ *
+ * @NL80211_CMD_SET_MGMT_EXTRA_IE: Set extra IEs for management frames. The
+ * interface is identified with %NL80211_ATTR_IFINDEX and the management
+ * frame subtype with %NL80211_ATTR_MGMT_SUBTYPE. The extra IE data to be
+ * added to the end of the specified management frame is specified with
+ * %NL80211_ATTR_IE. If the command succeeds, the requested data will be
+ * added to all specified management frames generated by
+ * kernel/firmware/driver.
+ * Note: This command has been removed and it is only reserved at this
+ * point to avoid re-using existing command number. The functionality this
+ * command was planned for has been provided with cleaner design with the
+ * option to specify additional IEs in NL80211_CMD_TRIGGER_SCAN,
+ * NL80211_CMD_AUTHENTICATE, NL80211_CMD_ASSOCIATE,
+ * NL80211_CMD_DEAUTHENTICATE, and NL80211_CMD_DISASSOCIATE.
+ *
+ * @NL80211_CMD_GET_SCAN: get scan results
+ * @NL80211_CMD_TRIGGER_SCAN: trigger a new scan with the given parameters
+ * %NL80211_ATTR_TX_NO_CCK_RATE is used to decide whether to send the
+ * probe requests at CCK rate or not. %NL80211_ATTR_BSSID can be used to
+ * specify a BSSID to scan for; if not included, the wildcard BSSID will
+ * be used.
+ * @NL80211_CMD_NEW_SCAN_RESULTS: scan notification (as a reply to
+ * NL80211_CMD_GET_SCAN and on the "scan" multicast group)
+ * @NL80211_CMD_SCAN_ABORTED: scan was aborted, for unspecified reasons,
+ * partial scan results may be available
+ *
+ * @NL80211_CMD_START_SCHED_SCAN: start a scheduled scan at certain
+ * intervals and certain number of cycles, as specified by
+ * %NL80211_ATTR_SCHED_SCAN_PLANS. If %NL80211_ATTR_SCHED_SCAN_PLANS is
+ * not specified and only %NL80211_ATTR_SCHED_SCAN_INTERVAL is specified,
+ * scheduled scan will run in an infinite loop with the specified interval.
+ * These attributes are mutually exculsive,
+ * i.e. NL80211_ATTR_SCHED_SCAN_INTERVAL must not be passed if
+ * NL80211_ATTR_SCHED_SCAN_PLANS is defined.
+ * If for some reason scheduled scan is aborted by the driver, all scan
+ * plans are canceled (including scan plans that did not start yet).
+ * Like with normal scans, if SSIDs (%NL80211_ATTR_SCAN_SSIDS)
+ * are passed, they are used in the probe requests. For
+ * broadcast, a broadcast SSID must be passed (ie. an empty
+ * string). If no SSID is passed, no probe requests are sent and
+ * a passive scan is performed. %NL80211_ATTR_SCAN_FREQUENCIES,
+ * if passed, define which channels should be scanned; if not
+ * passed, all channels allowed for the current regulatory domain
+ * are used. Extra IEs can also be passed from the userspace by
+ * using the %NL80211_ATTR_IE attribute. The first cycle of the
+ * scheduled scan can be delayed by %NL80211_ATTR_SCHED_SCAN_DELAY
+ * is supplied. If the device supports multiple concurrent scheduled
+ * scans, it will allow such when the caller provides the flag attribute
+ * %NL80211_ATTR_SCHED_SCAN_MULTI to indicate user-space support for it.
+ * @NL80211_CMD_STOP_SCHED_SCAN: stop a scheduled scan. Returns -ENOENT if
+ * scheduled scan is not running. The caller may assume that as soon
+ * as the call returns, it is safe to start a new scheduled scan again.
+ * @NL80211_CMD_SCHED_SCAN_RESULTS: indicates that there are scheduled scan
+ * results available.
+ * @NL80211_CMD_SCHED_SCAN_STOPPED: indicates that the scheduled scan has
+ * stopped. The driver may issue this event at any time during a
+ * scheduled scan. One reason for stopping the scan is if the hardware
+ * does not support starting an association or a normal scan while running
+ * a scheduled scan. This event is also sent when the
+ * %NL80211_CMD_STOP_SCHED_SCAN command is received or when the interface
+ * is brought down while a scheduled scan was running.
+ *
+ * @NL80211_CMD_GET_SURVEY: get survey resuls, e.g. channel occupation
+ * or noise level
+ * @NL80211_CMD_NEW_SURVEY_RESULTS: survey data notification (as a reply to
+ * NL80211_CMD_GET_SURVEY and on the "scan" multicast group)
+ *
+ * @NL80211_CMD_SET_PMKSA: Add a PMKSA cache entry using %NL80211_ATTR_MAC
+ * (for the BSSID), %NL80211_ATTR_PMKID, and optionally %NL80211_ATTR_PMK
+ * (PMK is used for PTKSA derivation in case of FILS shared key offload) or
+ * using %NL80211_ATTR_SSID, %NL80211_ATTR_FILS_CACHE_ID,
+ * %NL80211_ATTR_PMKID, and %NL80211_ATTR_PMK in case of FILS
+ * authentication where %NL80211_ATTR_FILS_CACHE_ID is the identifier
+ * advertized by a FILS capable AP identifying the scope of PMKSA in an
+ * ESS.
+ * @NL80211_CMD_DEL_PMKSA: Delete a PMKSA cache entry, using %NL80211_ATTR_MAC
+ * (for the BSSID) and %NL80211_ATTR_PMKID or using %NL80211_ATTR_SSID,
+ * %NL80211_ATTR_FILS_CACHE_ID, and %NL80211_ATTR_PMKID in case of FILS
+ * authentication.
+ * @NL80211_CMD_FLUSH_PMKSA: Flush all PMKSA cache entries.
+ *
+ * @NL80211_CMD_REG_CHANGE: indicates to userspace the regulatory domain
+ * has been changed and provides details of the request information
+ * that caused the change such as who initiated the regulatory request
+ * (%NL80211_ATTR_REG_INITIATOR), the wiphy_idx
+ * (%NL80211_ATTR_REG_ALPHA2) on which the request was made from if
+ * the initiator was %NL80211_REGDOM_SET_BY_COUNTRY_IE or
+ * %NL80211_REGDOM_SET_BY_DRIVER, the type of regulatory domain
+ * set (%NL80211_ATTR_REG_TYPE), if the type of regulatory domain is
+ * %NL80211_REG_TYPE_COUNTRY the alpha2 to which we have moved on
+ * to (%NL80211_ATTR_REG_ALPHA2).
+ * @NL80211_CMD_REG_BEACON_HINT: indicates to userspace that an AP beacon
+ * has been found while world roaming thus enabling active scan or
+ * any mode of operation that initiates TX (beacons) on a channel
+ * where we would not have been able to do either before. As an example
+ * if you are world roaming (regulatory domain set to world or if your
+ * driver is using a custom world roaming regulatory domain) and while
+ * doing a passive scan on the 5 GHz band you find an AP there (if not
+ * on a DFS channel) you will now be able to actively scan for that AP
+ * or use AP mode on your card on that same channel. Note that this will
+ * never be used for channels 1-11 on the 2 GHz band as they are always
+ * enabled world wide. This beacon hint is only sent if your device had
+ * either disabled active scanning or beaconing on a channel. We send to
+ * userspace the wiphy on which we removed a restriction from
+ * (%NL80211_ATTR_WIPHY) and the channel on which this occurred
+ * before (%NL80211_ATTR_FREQ_BEFORE) and after (%NL80211_ATTR_FREQ_AFTER)
+ * the beacon hint was processed.
+ *
+ * @NL80211_CMD_AUTHENTICATE: authentication request and notification.
+ * This command is used both as a command (request to authenticate) and
+ * as an event on the "mlme" multicast group indicating completion of the
+ * authentication process.
+ * When used as a command, %NL80211_ATTR_IFINDEX is used to identify the
+ * interface. %NL80211_ATTR_MAC is used to specify PeerSTAAddress (and
+ * BSSID in case of station mode). %NL80211_ATTR_SSID is used to specify
+ * the SSID (mainly for association, but is included in authentication
+ * request, too, to help BSS selection. %NL80211_ATTR_WIPHY_FREQ is used
+ * to specify the frequence of the channel in MHz. %NL80211_ATTR_AUTH_TYPE
+ * is used to specify the authentication type. %NL80211_ATTR_IE is used to
+ * define IEs (VendorSpecificInfo, but also including RSN IE and FT IEs)
+ * to be added to the frame.
+ * When used as an event, this reports reception of an Authentication
+ * frame in station and IBSS modes when the local MLME processed the
+ * frame, i.e., it was for the local STA and was received in correct
+ * state. This is similar to MLME-AUTHENTICATE.confirm primitive in the
+ * MLME SAP interface (kernel providing MLME, userspace SME). The
+ * included %NL80211_ATTR_FRAME attribute contains the management frame
+ * (including both the header and frame body, but not FCS). This event is
+ * also used to indicate if the authentication attempt timed out. In that
+ * case the %NL80211_ATTR_FRAME attribute is replaced with a
+ * %NL80211_ATTR_TIMED_OUT flag (and %NL80211_ATTR_MAC to indicate which
+ * pending authentication timed out).
+ * @NL80211_CMD_ASSOCIATE: association request and notification; like
+ * NL80211_CMD_AUTHENTICATE but for Association and Reassociation
+ * (similar to MLME-ASSOCIATE.request, MLME-REASSOCIATE.request,
+ * MLME-ASSOCIATE.confirm or MLME-REASSOCIATE.confirm primitives). The
+ * %NL80211_ATTR_PREV_BSSID attribute is used to specify whether the
+ * request is for the initial association to an ESS (that attribute not
+ * included) or for reassociation within the ESS (that attribute is
+ * included).
+ * @NL80211_CMD_DEAUTHENTICATE: deauthentication request and notification; like
+ * NL80211_CMD_AUTHENTICATE but for Deauthentication frames (similar to
+ * MLME-DEAUTHENTICATION.request and MLME-DEAUTHENTICATE.indication
+ * primitives).
+ * @NL80211_CMD_DISASSOCIATE: disassociation request and notification; like
+ * NL80211_CMD_AUTHENTICATE but for Disassociation frames (similar to
+ * MLME-DISASSOCIATE.request and MLME-DISASSOCIATE.indication primitives).
+ *
+ * @NL80211_CMD_MICHAEL_MIC_FAILURE: notification of a locally detected Michael
+ * MIC (part of TKIP) failure; sent on the "mlme" multicast group; the
+ * event includes %NL80211_ATTR_MAC to describe the source MAC address of
+ * the frame with invalid MIC, %NL80211_ATTR_KEY_TYPE to show the key
+ * type, %NL80211_ATTR_KEY_IDX to indicate the key identifier, and
+ * %NL80211_ATTR_KEY_SEQ to indicate the TSC value of the frame; this
+ * event matches with MLME-MICHAELMICFAILURE.indication() primitive
+ *
+ * @NL80211_CMD_JOIN_IBSS: Join a new IBSS -- given at least an SSID and a
+ * FREQ attribute (for the initial frequency if no peer can be found)
+ * and optionally a MAC (as BSSID) and FREQ_FIXED attribute if those
+ * should be fixed rather than automatically determined. Can only be
+ * executed on a network interface that is UP, and fixed BSSID/FREQ
+ * may be rejected. Another optional parameter is the beacon interval,
+ * given in the %NL80211_ATTR_BEACON_INTERVAL attribute, which if not
+ * given defaults to 100 TU (102.4ms).
+ * @NL80211_CMD_LEAVE_IBSS: Leave the IBSS -- no special arguments, the IBSS is
+ * determined by the network interface.
+ *
+ * @NL80211_CMD_TESTMODE: testmode command, takes a wiphy (or ifindex) attribute
+ * to identify the device, and the TESTDATA blob attribute to pass through
+ * to the driver.
+ *
+ * @NL80211_CMD_CONNECT: connection request and notification; this command
+ * requests to connect to a specified network but without separating
+ * auth and assoc steps. For this, you need to specify the SSID in a
+ * %NL80211_ATTR_SSID attribute, and can optionally specify the association
+ * IEs in %NL80211_ATTR_IE, %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_USE_MFP,
+ * %NL80211_ATTR_MAC, %NL80211_ATTR_WIPHY_FREQ, %NL80211_ATTR_CONTROL_PORT,
+ * %NL80211_ATTR_CONTROL_PORT_ETHERTYPE,
+ * %NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT, %NL80211_ATTR_MAC_HINT, and
+ * %NL80211_ATTR_WIPHY_FREQ_HINT.
+ * If included, %NL80211_ATTR_MAC and %NL80211_ATTR_WIPHY_FREQ are
+ * restrictions on BSS selection, i.e., they effectively prevent roaming
+ * within the ESS. %NL80211_ATTR_MAC_HINT and %NL80211_ATTR_WIPHY_FREQ_HINT
+ * can be included to provide a recommendation of the initial BSS while
+ * allowing the driver to roam to other BSSes within the ESS and also to
+ * ignore this recommendation if the indicated BSS is not ideal. Only one
+ * set of BSSID,frequency parameters is used (i.e., either the enforcing
+ * %NL80211_ATTR_MAC,%NL80211_ATTR_WIPHY_FREQ or the less strict
+ * %NL80211_ATTR_MAC_HINT and %NL80211_ATTR_WIPHY_FREQ_HINT).
+ * %NL80211_ATTR_PREV_BSSID can be used to request a reassociation within
+ * the ESS in case the device is already associated and an association with
+ * a different BSS is desired.
+ * Background scan period can optionally be
+ * specified in %NL80211_ATTR_BG_SCAN_PERIOD,
+ * if not specified default background scan configuration
+ * in driver is used and if period value is 0, bg scan will be disabled.
+ * This attribute is ignored if driver does not support roam scan.
+ * It is also sent as an event, with the BSSID and response IEs when the
+ * connection is established or failed to be established. This can be
+ * determined by the %NL80211_ATTR_STATUS_CODE attribute (0 = success,
+ * non-zero = failure). If %NL80211_ATTR_TIMED_OUT is included in the
+ * event, the connection attempt failed due to not being able to initiate
+ * authentication/association or not receiving a response from the AP.
+ * Non-zero %NL80211_ATTR_STATUS_CODE value is indicated in that case as
+ * well to remain backwards compatible.
+ * @NL80211_CMD_ROAM: request that the card roam (currently not implemented),
+ * sent as an event when the card/driver roamed by itself.
+ * @NL80211_CMD_DISCONNECT: drop a given connection; also used to notify
+ * userspace that a connection was dropped by the AP or due to other
+ * reasons, for this the %NL80211_ATTR_DISCONNECTED_BY_AP and
+ * %NL80211_ATTR_REASON_CODE attributes are used.
+ *
+ * @NL80211_CMD_SET_WIPHY_NETNS: Set a wiphy's netns. Note that all devices
+ * associated with this wiphy must be down and will follow.
+ *
+ * @NL80211_CMD_REMAIN_ON_CHANNEL: Request to remain awake on the specified
+ * channel for the specified amount of time. This can be used to do
+ * off-channel operations like transmit a Public Action frame and wait for
+ * a response while being associated to an AP on another channel.
+ * %NL80211_ATTR_IFINDEX is used to specify which interface (and thus
+ * radio) is used. %NL80211_ATTR_WIPHY_FREQ is used to specify the
+ * frequency for the operation.
+ * %NL80211_ATTR_DURATION is used to specify the duration in milliseconds
+ * to remain on the channel. This command is also used as an event to
+ * notify when the requested duration starts (it may take a while for the
+ * driver to schedule this time due to other concurrent needs for the
+ * radio).
+ * When called, this operation returns a cookie (%NL80211_ATTR_COOKIE)
+ * that will be included with any events pertaining to this request;
+ * the cookie is also used to cancel the request.
+ * @NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL: This command can be used to cancel a
+ * pending remain-on-channel duration if the desired operation has been
+ * completed prior to expiration of the originally requested duration.
+ * %NL80211_ATTR_WIPHY or %NL80211_ATTR_IFINDEX is used to specify the
+ * radio. The %NL80211_ATTR_COOKIE attribute must be given as well to
+ * uniquely identify the request.
+ * This command is also used as an event to notify when a requested
+ * remain-on-channel duration has expired.
+ *
+ * @NL80211_CMD_SET_TX_BITRATE_MASK: Set the mask of rates to be used in TX
+ * rate selection. %NL80211_ATTR_IFINDEX is used to specify the interface
+ * and @NL80211_ATTR_TX_RATES the set of allowed rates.
+ *
+ * @NL80211_CMD_REGISTER_FRAME: Register for receiving certain mgmt frames
+ * (via @NL80211_CMD_FRAME) for processing in userspace. This command
+ * requires an interface index, a frame type attribute (optional for
+ * backward compatibility reasons, if not given assumes action frames)
+ * and a match attribute containing the first few bytes of the frame
+ * that should match, e.g. a single byte for only a category match or
+ * four bytes for vendor frames including the OUI. The registration
+ * cannot be dropped, but is removed automatically when the netlink
+ * socket is closed. Multiple registrations can be made.
+ * @NL80211_CMD_REGISTER_ACTION: Alias for @NL80211_CMD_REGISTER_FRAME for
+ * backward compatibility
+ * @NL80211_CMD_FRAME: Management frame TX request and RX notification. This
+ * command is used both as a request to transmit a management frame and
+ * as an event indicating reception of a frame that was not processed in
+ * kernel code, but is for us (i.e., which may need to be processed in a
+ * user space application). %NL80211_ATTR_FRAME is used to specify the
+ * frame contents (including header). %NL80211_ATTR_WIPHY_FREQ is used
+ * to indicate on which channel the frame is to be transmitted or was
+ * received. If this channel is not the current channel (remain-on-channel
+ * or the operational channel) the device will switch to the given channel
+ * and transmit the frame, optionally waiting for a response for the time
+ * specified using %NL80211_ATTR_DURATION. When called, this operation
+ * returns a cookie (%NL80211_ATTR_COOKIE) that will be included with the
+ * TX status event pertaining to the TX request.
+ * %NL80211_ATTR_TX_NO_CCK_RATE is used to decide whether to send the
+ * management frames at CCK rate or not in 2GHz band.
+ * %NL80211_ATTR_CSA_C_OFFSETS_TX is an array of offsets to CSA
+ * counters which will be updated to the current value. This attribute
+ * is used during CSA period.
+ * @NL80211_CMD_FRAME_WAIT_CANCEL: When an off-channel TX was requested, this
+ * command may be used with the corresponding cookie to cancel the wait
+ * time if it is known that it is no longer necessary.
+ * @NL80211_CMD_ACTION: Alias for @NL80211_CMD_FRAME for backward compatibility.
+ * @NL80211_CMD_FRAME_TX_STATUS: Report TX status of a management frame
+ * transmitted with %NL80211_CMD_FRAME. %NL80211_ATTR_COOKIE identifies
+ * the TX command and %NL80211_ATTR_FRAME includes the contents of the
+ * frame. %NL80211_ATTR_ACK flag is included if the recipient acknowledged
+ * the frame.
+ * @NL80211_CMD_ACTION_TX_STATUS: Alias for @NL80211_CMD_FRAME_TX_STATUS for
+ * backward compatibility.
+ *
+ * @NL80211_CMD_SET_POWER_SAVE: Set powersave, using %NL80211_ATTR_PS_STATE
+ * @NL80211_CMD_GET_POWER_SAVE: Get powersave status in %NL80211_ATTR_PS_STATE
+ *
+ * @NL80211_CMD_SET_CQM: Connection quality monitor configuration. This command
+ * is used to configure connection quality monitoring notification trigger
+ * levels.
+ * @NL80211_CMD_NOTIFY_CQM: Connection quality monitor notification. This
+ * command is used as an event to indicate the that a trigger level was
+ * reached.
+ * @NL80211_CMD_SET_CHANNEL: Set the channel (using %NL80211_ATTR_WIPHY_FREQ
+ * and the attributes determining channel width) the given interface
+ * (identifed by %NL80211_ATTR_IFINDEX) shall operate on.
+ * In case multiple channels are supported by the device, the mechanism
+ * with which it switches channels is implementation-defined.
+ * When a monitor interface is given, it can only switch channel while
+ * no other interfaces are operating to avoid disturbing the operation
+ * of any other interfaces, and other interfaces will again take
+ * precedence when they are used.
+ *
+ * @NL80211_CMD_SET_WDS_PEER: Set the MAC address of the peer on a WDS interface.
+ *
+ * @NL80211_CMD_SET_MULTICAST_TO_UNICAST: Configure if this AP should perform
+ * multicast to unicast conversion. When enabled, all multicast packets
+ * with ethertype ARP, IPv4 or IPv6 (possibly within an 802.1Q header)
+ * will be sent out to each station once with the destination (multicast)
+ * MAC address replaced by the station's MAC address. Note that this may
+ * break certain expectations of the receiver, e.g. the ability to drop
+ * unicast IP packets encapsulated in multicast L2 frames, or the ability
+ * to not send destination unreachable messages in such cases.
+ * This can only be toggled per BSS. Configure this on an interface of
+ * type %NL80211_IFTYPE_AP. It applies to all its VLAN interfaces
+ * (%NL80211_IFTYPE_AP_VLAN), except for those in 4addr (WDS) mode.
+ * If %NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED is not present with this
+ * command, the feature is disabled.
+ *
+ * @NL80211_CMD_JOIN_MESH: Join a mesh. The mesh ID must be given, and initial
+ * mesh config parameters may be given.
+ * @NL80211_CMD_LEAVE_MESH: Leave the mesh network -- no special arguments, the
+ * network is determined by the network interface.
+ *
+ * @NL80211_CMD_UNPROT_DEAUTHENTICATE: Unprotected deauthentication frame
+ * notification. This event is used to indicate that an unprotected
+ * deauthentication frame was dropped when MFP is in use.
+ * @NL80211_CMD_UNPROT_DISASSOCIATE: Unprotected disassociation frame
+ * notification. This event is used to indicate that an unprotected
+ * disassociation frame was dropped when MFP is in use.
+ *
+ * @NL80211_CMD_NEW_PEER_CANDIDATE: Notification on the reception of a
+ * beacon or probe response from a compatible mesh peer. This is only
+ * sent while no station information (sta_info) exists for the new peer
+ * candidate and when @NL80211_MESH_SETUP_USERSPACE_AUTH,
+ * @NL80211_MESH_SETUP_USERSPACE_AMPE, or
+ * @NL80211_MESH_SETUP_USERSPACE_MPM is set. On reception of this
+ * notification, userspace may decide to create a new station
+ * (@NL80211_CMD_NEW_STATION). To stop this notification from
+ * reoccurring, the userspace authentication daemon may want to create the
+ * new station with the AUTHENTICATED flag unset and maybe change it later
+ * depending on the authentication result.
+ *
+ * @NL80211_CMD_GET_WOWLAN: get Wake-on-Wireless-LAN (WoWLAN) settings.
+ * @NL80211_CMD_SET_WOWLAN: set Wake-on-Wireless-LAN (WoWLAN) settings.
+ * Since wireless is more complex than wired ethernet, it supports
+ * various triggers. These triggers can be configured through this
+ * command with the %NL80211_ATTR_WOWLAN_TRIGGERS attribute. For
+ * more background information, see
+ * http://wireless.kernel.org/en/users/Documentation/WoWLAN.
+ * The @NL80211_CMD_SET_WOWLAN command can also be used as a notification
+ * from the driver reporting the wakeup reason. In this case, the
+ * @NL80211_ATTR_WOWLAN_TRIGGERS attribute will contain the reason
+ * for the wakeup, if it was caused by wireless. If it is not present
+ * in the wakeup notification, the wireless device didn't cause the
+ * wakeup but reports that it was woken up.
+ *
+ * @NL80211_CMD_SET_REKEY_OFFLOAD: This command is used give the driver
+ * the necessary information for supporting GTK rekey offload. This
+ * feature is typically used during WoWLAN. The configuration data
+ * is contained in %NL80211_ATTR_REKEY_DATA (which is nested and
+ * contains the data in sub-attributes). After rekeying happened,
+ * this command may also be sent by the driver as an MLME event to
+ * inform userspace of the new replay counter.
+ *
+ * @NL80211_CMD_PMKSA_CANDIDATE: This is used as an event to inform userspace
+ * of PMKSA caching dandidates.
+ *
+ * @NL80211_CMD_TDLS_OPER: Perform a high-level TDLS command (e.g. link setup).
+ * In addition, this can be used as an event to request userspace to take
+ * actions on TDLS links (set up a new link or tear down an existing one).
+ * In such events, %NL80211_ATTR_TDLS_OPERATION indicates the requested
+ * operation, %NL80211_ATTR_MAC contains the peer MAC address, and
+ * %NL80211_ATTR_REASON_CODE the reason code to be used (only with
+ * %NL80211_TDLS_TEARDOWN).
+ * @NL80211_CMD_TDLS_MGMT: Send a TDLS management frame. The
+ * %NL80211_ATTR_TDLS_ACTION attribute determines the type of frame to be
+ * sent. Public Action codes (802.11-2012 8.1.5.1) will be sent as
+ * 802.11 management frames, while TDLS action codes (802.11-2012
+ * 8.5.13.1) will be encapsulated and sent as data frames. The currently
+ * supported Public Action code is %WLAN_PUB_ACTION_TDLS_DISCOVER_RES
+ * and the currently supported TDLS actions codes are given in
+ * &enum ieee80211_tdls_actioncode.
+ *
+ * @NL80211_CMD_UNEXPECTED_FRAME: Used by an application controlling an AP
+ * (or GO) interface (i.e. hostapd) to ask for unexpected frames to
+ * implement sending deauth to stations that send unexpected class 3
+ * frames. Also used as the event sent by the kernel when such a frame
+ * is received.
+ * For the event, the %NL80211_ATTR_MAC attribute carries the TA and
+ * other attributes like the interface index are present.
+ * If used as the command it must have an interface index and you can
+ * only unsubscribe from the event by closing the socket. Subscription
+ * is also for %NL80211_CMD_UNEXPECTED_4ADDR_FRAME events.
+ *
+ * @NL80211_CMD_UNEXPECTED_4ADDR_FRAME: Sent as an event indicating that the
+ * associated station identified by %NL80211_ATTR_MAC sent a 4addr frame
+ * and wasn't already in a 4-addr VLAN. The event will be sent similarly
+ * to the %NL80211_CMD_UNEXPECTED_FRAME event, to the same listener.
+ *
+ * @NL80211_CMD_PROBE_CLIENT: Probe an associated station on an AP interface
+ * by sending a null data frame to it and reporting when the frame is
+ * acknowleged. This is used to allow timing out inactive clients. Uses
+ * %NL80211_ATTR_IFINDEX and %NL80211_ATTR_MAC. The command returns a
+ * direct reply with an %NL80211_ATTR_COOKIE that is later used to match
+ * up the event with the request. The event includes the same data and
+ * has %NL80211_ATTR_ACK set if the frame was ACKed.
+ *
+ * @NL80211_CMD_REGISTER_BEACONS: Register this socket to receive beacons from
+ * other BSSes when any interfaces are in AP mode. This helps implement
+ * OLBC handling in hostapd. Beacons are reported in %NL80211_CMD_FRAME
+ * messages. Note that per PHY only one application may register.
+ *
+ * @NL80211_CMD_SET_NOACK_MAP: sets a bitmap for the individual TIDs whether
+ * No Acknowledgement Policy should be applied.
+ *
+ * @NL80211_CMD_CH_SWITCH_NOTIFY: An AP or GO may decide to switch channels
+ * independently of the userspace SME, send this event indicating
+ * %NL80211_ATTR_IFINDEX is now on %NL80211_ATTR_WIPHY_FREQ and the
+ * attributes determining channel width. This indication may also be
+ * sent when a remotely-initiated switch (e.g., when a STA receives a CSA
+ * from the remote AP) is completed;
+ *
+ * @NL80211_CMD_CH_SWITCH_STARTED_NOTIFY: Notify that a channel switch
+ * has been started on an interface, regardless of the initiator
+ * (ie. whether it was requested from a remote device or
+ * initiated on our own). It indicates that
+ * %NL80211_ATTR_IFINDEX will be on %NL80211_ATTR_WIPHY_FREQ
+ * after %NL80211_ATTR_CH_SWITCH_COUNT TBTT's. The userspace may
+ * decide to react to this indication by requesting other
+ * interfaces to change channel as well.
+ *
+ * @NL80211_CMD_START_P2P_DEVICE: Start the given P2P Device, identified by
+ * its %NL80211_ATTR_WDEV identifier. It must have been created with
+ * %NL80211_CMD_NEW_INTERFACE previously. After it has been started, the
+ * P2P Device can be used for P2P operations, e.g. remain-on-channel and
+ * public action frame TX.
+ * @NL80211_CMD_STOP_P2P_DEVICE: Stop the given P2P Device, identified by
+ * its %NL80211_ATTR_WDEV identifier.
+ *
+ * @NL80211_CMD_CONN_FAILED: connection request to an AP failed; used to
+ * notify userspace that AP has rejected the connection request from a
+ * station, due to particular reason. %NL80211_ATTR_CONN_FAILED_REASON
+ * is used for this.
+ *
+ * @NL80211_CMD_SET_MCAST_RATE: Change the rate used to send multicast frames
+ * for IBSS or MESH vif.
+ *
+ * @NL80211_CMD_SET_MAC_ACL: sets ACL for MAC address based access control.
+ * This is to be used with the drivers advertising the support of MAC
+ * address based access control. List of MAC addresses is passed in
+ * %NL80211_ATTR_MAC_ADDRS and ACL policy is passed in
+ * %NL80211_ATTR_ACL_POLICY. Driver will enable ACL with this list, if it
+ * is not already done. The new list will replace any existing list. Driver
+ * will clear its ACL when the list of MAC addresses passed is empty. This
+ * command is used in AP/P2P GO mode. Driver has to make sure to clear its
+ * ACL list during %NL80211_CMD_STOP_AP.
+ *
+ * @NL80211_CMD_RADAR_DETECT: Start a Channel availability check (CAC). Once
+ * a radar is detected or the channel availability scan (CAC) has finished
+ * or was aborted, or a radar was detected, usermode will be notified with
+ * this event. This command is also used to notify userspace about radars
+ * while operating on this channel.
+ * %NL80211_ATTR_RADAR_EVENT is used to inform about the type of the
+ * event.
+ *
+ * @NL80211_CMD_GET_PROTOCOL_FEATURES: Get global nl80211 protocol features,
+ * i.e. features for the nl80211 protocol rather than device features.
+ * Returns the features in the %NL80211_ATTR_PROTOCOL_FEATURES bitmap.
+ *
+ * @NL80211_CMD_UPDATE_FT_IES: Pass down the most up-to-date Fast Transition
+ * Information Element to the WLAN driver
+ *
+ * @NL80211_CMD_FT_EVENT: Send a Fast transition event from the WLAN driver
+ * to the supplicant. This will carry the target AP's MAC address along
+ * with the relevant Information Elements. This event is used to report
+ * received FT IEs (MDIE, FTIE, RSN IE, TIE, RICIE).
+ *
+ * @NL80211_CMD_CRIT_PROTOCOL_START: Indicates user-space will start running
+ * a critical protocol that needs more reliability in the connection to
+ * complete.
+ *
+ * @NL80211_CMD_CRIT_PROTOCOL_STOP: Indicates the connection reliability can
+ * return back to normal.
+ *
+ * @NL80211_CMD_GET_COALESCE: Get currently supported coalesce rules.
+ * @NL80211_CMD_SET_COALESCE: Configure coalesce rules or clear existing rules.
+ *
+ * @NL80211_CMD_CHANNEL_SWITCH: Perform a channel switch by announcing the
+ * the new channel information (Channel Switch Announcement - CSA)
+ * in the beacon for some time (as defined in the
+ * %NL80211_ATTR_CH_SWITCH_COUNT parameter) and then change to the
+ * new channel. Userspace provides the new channel information (using
+ * %NL80211_ATTR_WIPHY_FREQ and the attributes determining channel
+ * width). %NL80211_ATTR_CH_SWITCH_BLOCK_TX may be supplied to inform
+ * other station that transmission must be blocked until the channel
+ * switch is complete.
+ *
+ * @NL80211_CMD_VENDOR: Vendor-specified command/event. The command is specified
+ * by the %NL80211_ATTR_VENDOR_ID attribute and a sub-command in
+ * %NL80211_ATTR_VENDOR_SUBCMD. Parameter(s) can be transported in
+ * %NL80211_ATTR_VENDOR_DATA.
+ * For feature advertisement, the %NL80211_ATTR_VENDOR_DATA attribute is
+ * used in the wiphy data as a nested attribute containing descriptions
+ * (&struct nl80211_vendor_cmd_info) of the supported vendor commands.
+ * This may also be sent as an event with the same attributes.
+ *
+ * @NL80211_CMD_SET_QOS_MAP: Set Interworking QoS mapping for IP DSCP values.
+ * The QoS mapping information is included in %NL80211_ATTR_QOS_MAP. If
+ * that attribute is not included, QoS mapping is disabled. Since this
+ * QoS mapping is relevant for IP packets, it is only valid during an
+ * association. This is cleared on disassociation and AP restart.
+ *
+ * @NL80211_CMD_ADD_TX_TS: Ask the kernel to add a traffic stream for the given
+ * %NL80211_ATTR_TSID and %NL80211_ATTR_MAC with %NL80211_ATTR_USER_PRIO
+ * and %NL80211_ATTR_ADMITTED_TIME parameters.
+ * Note that the action frame handshake with the AP shall be handled by
+ * userspace via the normal management RX/TX framework, this only sets
+ * up the TX TS in the driver/device.
+ * If the admitted time attribute is not added then the request just checks
+ * if a subsequent setup could be successful, the intent is to use this to
+ * avoid setting up a session with the AP when local restrictions would
+ * make that impossible. However, the subsequent "real" setup may still
+ * fail even if the check was successful.
+ * @NL80211_CMD_DEL_TX_TS: Remove an existing TS with the %NL80211_ATTR_TSID
+ * and %NL80211_ATTR_MAC parameters. It isn't necessary to call this
+ * before removing a station entry entirely, or before disassociating
+ * or similar, cleanup will happen in the driver/device in this case.
+ *
+ * @NL80211_CMD_GET_MPP: Get mesh path attributes for mesh proxy path to
+ * destination %NL80211_ATTR_MAC on the interface identified by
+ * %NL80211_ATTR_IFINDEX.
+ *
+ * @NL80211_CMD_JOIN_OCB: Join the OCB network. The center frequency and
+ * bandwidth of a channel must be given.
+ * @NL80211_CMD_LEAVE_OCB: Leave the OCB network -- no special arguments, the
+ * network is determined by the network interface.
+ *
+ * @NL80211_CMD_TDLS_CHANNEL_SWITCH: Start channel-switching with a TDLS peer,
+ * identified by the %NL80211_ATTR_MAC parameter. A target channel is
+ * provided via %NL80211_ATTR_WIPHY_FREQ and other attributes determining
+ * channel width/type. The target operating class is given via
+ * %NL80211_ATTR_OPER_CLASS.
+ * The driver is responsible for continually initiating channel-switching
+ * operations and returning to the base channel for communication with the
+ * AP.
+ * @NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH: Stop channel-switching with a TDLS
+ * peer given by %NL80211_ATTR_MAC. Both peers must be on the base channel
+ * when this command completes.
+ *
+ * @NL80211_CMD_WIPHY_REG_CHANGE: Similar to %NL80211_CMD_REG_CHANGE, but used
+ * as an event to indicate changes for devices with wiphy-specific regdom
+ * management.
+ *
+ * @NL80211_CMD_ABORT_SCAN: Stop an ongoing scan. Returns -ENOENT if a scan is
+ * not running. The driver indicates the status of the scan through
+ * cfg80211_scan_done().
+ *
+ * @NL80211_CMD_START_NAN: Start NAN operation, identified by its
+ * %NL80211_ATTR_WDEV interface. This interface must have been
+ * previously created with %NL80211_CMD_NEW_INTERFACE. After it
+ * has been started, the NAN interface will create or join a
+ * cluster. This command must have a valid
+ * %NL80211_ATTR_NAN_MASTER_PREF attribute and optional
+ * %NL80211_ATTR_BANDS attributes. If %NL80211_ATTR_BANDS is
+ * omitted or set to 0, it means don't-care and the device will
+ * decide what to use. After this command NAN functions can be
+ * added.
+ * @NL80211_CMD_STOP_NAN: Stop the NAN operation, identified by
+ * its %NL80211_ATTR_WDEV interface.
+ * @NL80211_CMD_ADD_NAN_FUNCTION: Add a NAN function. The function is defined
+ * with %NL80211_ATTR_NAN_FUNC nested attribute. When called, this
+ * operation returns the strictly positive and unique instance id
+ * (%NL80211_ATTR_NAN_FUNC_INST_ID) and a cookie (%NL80211_ATTR_COOKIE)
+ * of the function upon success.
+ * Since instance ID's can be re-used, this cookie is the right
+ * way to identify the function. This will avoid races when a termination
+ * event is handled by the user space after it has already added a new
+ * function that got the same instance id from the kernel as the one
+ * which just terminated.
+ * This cookie may be used in NAN events even before the command
+ * returns, so userspace shouldn't process NAN events until it processes
+ * the response to this command.
+ * Look at %NL80211_ATTR_SOCKET_OWNER as well.
+ * @NL80211_CMD_DEL_NAN_FUNCTION: Delete a NAN function by cookie.
+ * This command is also used as a notification sent when a NAN function is
+ * terminated. This will contain a %NL80211_ATTR_NAN_FUNC_INST_ID
+ * and %NL80211_ATTR_COOKIE attributes.
+ * @NL80211_CMD_CHANGE_NAN_CONFIG: Change current NAN
+ * configuration. NAN must be operational (%NL80211_CMD_START_NAN
+ * was executed). It must contain at least one of the following
+ * attributes: %NL80211_ATTR_NAN_MASTER_PREF,
+ * %NL80211_ATTR_BANDS. If %NL80211_ATTR_BANDS is omitted, the
+ * current configuration is not changed. If it is present but
+ * set to zero, the configuration is changed to don't-care
+ * (i.e. the device can decide what to do).
+ * @NL80211_CMD_NAN_FUNC_MATCH: Notification sent when a match is reported.
+ * This will contain a %NL80211_ATTR_NAN_MATCH nested attribute and
+ * %NL80211_ATTR_COOKIE.
+ *
+ * @NL80211_CMD_UPDATE_CONNECT_PARAMS: Update one or more connect parameters
+ * for subsequent roaming cases if the driver or firmware uses internal
+ * BSS selection. This command can be issued only while connected and it
+ * does not result in a change for the current association. Currently,
+ * only the %NL80211_ATTR_IE data is used and updated with this command.
+ *
+ * @NL80211_CMD_SET_PMK: For offloaded 4-Way handshake, set the PMK or PMK-R0
+ * for the given authenticator address (specified with &NL80211_ATTR_MAC).
+ * When &NL80211_ATTR_PMKR0_NAME is set, &NL80211_ATTR_PMK specifies the
+ * PMK-R0, otherwise it specifies the PMK.
+ * @NL80211_CMD_DEL_PMK: For offloaded 4-Way handshake, delete the previously
+ * configured PMK for the authenticator address identified by
+ * &NL80211_ATTR_MAC.
+ *
+ * @NL80211_CMD_MAX: highest used command number
+ * @__NL80211_CMD_AFTER_LAST: internal use
+ */
+enum nl80211_commands {
+/* don't change the order or add anything between, this is ABI! */
+ NL80211_CMD_UNSPEC,
+
+ NL80211_CMD_GET_WIPHY, /* can dump */
+ NL80211_CMD_SET_WIPHY,
+ NL80211_CMD_NEW_WIPHY,
+ NL80211_CMD_DEL_WIPHY,
+
+ NL80211_CMD_GET_INTERFACE, /* can dump */
+ NL80211_CMD_SET_INTERFACE,
+ NL80211_CMD_NEW_INTERFACE,
+ NL80211_CMD_DEL_INTERFACE,
+
+ NL80211_CMD_GET_KEY,
+ NL80211_CMD_SET_KEY,
+ NL80211_CMD_NEW_KEY,
+ NL80211_CMD_DEL_KEY,
+
+ NL80211_CMD_GET_BEACON,
+ NL80211_CMD_SET_BEACON,
+ NL80211_CMD_START_AP,
+ NL80211_CMD_NEW_BEACON = NL80211_CMD_START_AP,
+ NL80211_CMD_STOP_AP,
+ NL80211_CMD_DEL_BEACON = NL80211_CMD_STOP_AP,
+
+ NL80211_CMD_GET_STATION,
+ NL80211_CMD_SET_STATION,
+ NL80211_CMD_NEW_STATION,
+ NL80211_CMD_DEL_STATION,
+
+ NL80211_CMD_GET_MPATH,
+ NL80211_CMD_SET_MPATH,
+ NL80211_CMD_NEW_MPATH,
+ NL80211_CMD_DEL_MPATH,
+
+ NL80211_CMD_SET_BSS,
+
+ NL80211_CMD_SET_REG,
+ NL80211_CMD_REQ_SET_REG,
+
+ NL80211_CMD_GET_MESH_CONFIG,
+ NL80211_CMD_SET_MESH_CONFIG,
+
+ NL80211_CMD_SET_MGMT_EXTRA_IE /* reserved; not used */,
+
+ NL80211_CMD_GET_REG,
+
+ NL80211_CMD_GET_SCAN,
+ NL80211_CMD_TRIGGER_SCAN,
+ NL80211_CMD_NEW_SCAN_RESULTS,
+ NL80211_CMD_SCAN_ABORTED,
+
+ NL80211_CMD_REG_CHANGE,
+
+ NL80211_CMD_AUTHENTICATE,
+ NL80211_CMD_ASSOCIATE,
+ NL80211_CMD_DEAUTHENTICATE,
+ NL80211_CMD_DISASSOCIATE,
+
+ NL80211_CMD_MICHAEL_MIC_FAILURE,
+
+ NL80211_CMD_REG_BEACON_HINT,
+
+ NL80211_CMD_JOIN_IBSS,
+ NL80211_CMD_LEAVE_IBSS,
+
+ NL80211_CMD_TESTMODE,
+
+ NL80211_CMD_CONNECT,
+ NL80211_CMD_ROAM,
+ NL80211_CMD_DISCONNECT,
+
+ NL80211_CMD_SET_WIPHY_NETNS,
+
+ NL80211_CMD_GET_SURVEY,
+ NL80211_CMD_NEW_SURVEY_RESULTS,
+
+ NL80211_CMD_SET_PMKSA,
+ NL80211_CMD_DEL_PMKSA,
+ NL80211_CMD_FLUSH_PMKSA,
+
+ NL80211_CMD_REMAIN_ON_CHANNEL,
+ NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL,
+
+ NL80211_CMD_SET_TX_BITRATE_MASK,
+
+ NL80211_CMD_REGISTER_FRAME,
+ NL80211_CMD_REGISTER_ACTION = NL80211_CMD_REGISTER_FRAME,
+ NL80211_CMD_FRAME,
+ NL80211_CMD_ACTION = NL80211_CMD_FRAME,
+ NL80211_CMD_FRAME_TX_STATUS,
+ NL80211_CMD_ACTION_TX_STATUS = NL80211_CMD_FRAME_TX_STATUS,
+
+ NL80211_CMD_SET_POWER_SAVE,
+ NL80211_CMD_GET_POWER_SAVE,
+
+ NL80211_CMD_SET_CQM,
+ NL80211_CMD_NOTIFY_CQM,
+
+ NL80211_CMD_SET_CHANNEL,
+ NL80211_CMD_SET_WDS_PEER,
+
+ NL80211_CMD_FRAME_WAIT_CANCEL,
+
+ NL80211_CMD_JOIN_MESH,
+ NL80211_CMD_LEAVE_MESH,
+
+ NL80211_CMD_UNPROT_DEAUTHENTICATE,
+ NL80211_CMD_UNPROT_DISASSOCIATE,
+
+ NL80211_CMD_NEW_PEER_CANDIDATE,
+
+ NL80211_CMD_GET_WOWLAN,
+ NL80211_CMD_SET_WOWLAN,
+
+ NL80211_CMD_START_SCHED_SCAN,
+ NL80211_CMD_STOP_SCHED_SCAN,
+ NL80211_CMD_SCHED_SCAN_RESULTS,
+ NL80211_CMD_SCHED_SCAN_STOPPED,
+
+ NL80211_CMD_SET_REKEY_OFFLOAD,
+
+ NL80211_CMD_PMKSA_CANDIDATE,
+
+ NL80211_CMD_TDLS_OPER,
+ NL80211_CMD_TDLS_MGMT,
+
+ NL80211_CMD_UNEXPECTED_FRAME,
+
+ NL80211_CMD_PROBE_CLIENT,
+
+ NL80211_CMD_REGISTER_BEACONS,
+
+ NL80211_CMD_UNEXPECTED_4ADDR_FRAME,
+
+ NL80211_CMD_SET_NOACK_MAP,
+
+ NL80211_CMD_CH_SWITCH_NOTIFY,
+
+ NL80211_CMD_START_P2P_DEVICE,
+ NL80211_CMD_STOP_P2P_DEVICE,
+
+ NL80211_CMD_CONN_FAILED,
+
+ NL80211_CMD_SET_MCAST_RATE,
+
+ NL80211_CMD_SET_MAC_ACL,
+
+ NL80211_CMD_RADAR_DETECT,
+
+ NL80211_CMD_GET_PROTOCOL_FEATURES,
+
+ NL80211_CMD_UPDATE_FT_IES,
+ NL80211_CMD_FT_EVENT,
+
+ NL80211_CMD_CRIT_PROTOCOL_START,
+ NL80211_CMD_CRIT_PROTOCOL_STOP,
+
+ NL80211_CMD_GET_COALESCE,
+ NL80211_CMD_SET_COALESCE,
+
+ NL80211_CMD_CHANNEL_SWITCH,
+
+ NL80211_CMD_VENDOR,
+
+ NL80211_CMD_SET_QOS_MAP,
+
+ NL80211_CMD_ADD_TX_TS,
+ NL80211_CMD_DEL_TX_TS,
+
+ NL80211_CMD_GET_MPP,
+
+ NL80211_CMD_JOIN_OCB,
+ NL80211_CMD_LEAVE_OCB,
+
+ NL80211_CMD_CH_SWITCH_STARTED_NOTIFY,
+
+ NL80211_CMD_TDLS_CHANNEL_SWITCH,
+ NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH,
+
+ NL80211_CMD_WIPHY_REG_CHANGE,
+
+ NL80211_CMD_ABORT_SCAN,
+
+ NL80211_CMD_START_NAN,
+ NL80211_CMD_STOP_NAN,
+ NL80211_CMD_ADD_NAN_FUNCTION,
+ NL80211_CMD_DEL_NAN_FUNCTION,
+ NL80211_CMD_CHANGE_NAN_CONFIG,
+ NL80211_CMD_NAN_MATCH,
+
+ NL80211_CMD_SET_MULTICAST_TO_UNICAST,
+
+ NL80211_CMD_UPDATE_CONNECT_PARAMS,
+
+ NL80211_CMD_SET_PMK,
+ NL80211_CMD_DEL_PMK,
+
+ /* add new commands above here */
+
+ /* Nest private commands start here */
+ NL80211_CMD_GET_KEEPALIVE = 200,
+ NL80211_CMD_SET_KEEPALIVE,
+
+ /* used to define NL80211_CMD_MAX below */
+ __NL80211_CMD_AFTER_LAST,
+ NL80211_CMD_MAX = __NL80211_CMD_AFTER_LAST - 1
+};
+
+/*
+ * Allow user space programs to use #ifdef on new commands by defining them
+ * here
+ */
+#define NL80211_CMD_SET_BSS NL80211_CMD_SET_BSS
+#define NL80211_CMD_SET_MGMT_EXTRA_IE NL80211_CMD_SET_MGMT_EXTRA_IE
+#define NL80211_CMD_REG_CHANGE NL80211_CMD_REG_CHANGE
+#define NL80211_CMD_AUTHENTICATE NL80211_CMD_AUTHENTICATE
+#define NL80211_CMD_ASSOCIATE NL80211_CMD_ASSOCIATE
+#define NL80211_CMD_DEAUTHENTICATE NL80211_CMD_DEAUTHENTICATE
+#define NL80211_CMD_DISASSOCIATE NL80211_CMD_DISASSOCIATE
+#define NL80211_CMD_REG_BEACON_HINT NL80211_CMD_REG_BEACON_HINT
+
+#define NL80211_ATTR_FEATURE_FLAGS NL80211_ATTR_FEATURE_FLAGS
+
+/* source-level API compatibility */
+#define NL80211_CMD_GET_MESH_PARAMS NL80211_CMD_GET_MESH_CONFIG
+#define NL80211_CMD_SET_MESH_PARAMS NL80211_CMD_SET_MESH_CONFIG
+#define NL80211_MESH_SETUP_VENDOR_PATH_SEL_IE NL80211_MESH_SETUP_IE
+
+/**
+ * enum nl80211_attrs - nl80211 netlink attributes
+ *
+ * @NL80211_ATTR_UNSPEC: unspecified attribute to catch errors
+ *
+ * @NL80211_ATTR_WIPHY: index of wiphy to operate on, cf.
+ * /sys/class/ieee80211/<phyname>/index
+ * @NL80211_ATTR_WIPHY_NAME: wiphy name (used for renaming)
+ * @NL80211_ATTR_WIPHY_TXQ_PARAMS: a nested array of TX queue parameters
+ * @NL80211_ATTR_WIPHY_FREQ: frequency of the selected channel in MHz,
+ * defines the channel together with the (deprecated)
+ * %NL80211_ATTR_WIPHY_CHANNEL_TYPE attribute or the attributes
+ * %NL80211_ATTR_CHANNEL_WIDTH and if needed %NL80211_ATTR_CENTER_FREQ1
+ * and %NL80211_ATTR_CENTER_FREQ2
+ * @NL80211_ATTR_CHANNEL_WIDTH: u32 attribute containing one of the values
+ * of &enum nl80211_chan_width, describing the channel width. See the
+ * documentation of the enum for more information.
+ * @NL80211_ATTR_CENTER_FREQ1: Center frequency of the first part of the
+ * channel, used for anything but 20 MHz bandwidth
+ * @NL80211_ATTR_CENTER_FREQ2: Center frequency of the second part of the
+ * channel, used only for 80+80 MHz bandwidth
+ * @NL80211_ATTR_WIPHY_CHANNEL_TYPE: included with NL80211_ATTR_WIPHY_FREQ
+ * if HT20 or HT40 are to be used (i.e., HT disabled if not included):
+ * NL80211_CHAN_NO_HT = HT not allowed (i.e., same as not including
+ * this attribute)
+ * NL80211_CHAN_HT20 = HT20 only
+ * NL80211_CHAN_HT40MINUS = secondary channel is below the primary channel
+ * NL80211_CHAN_HT40PLUS = secondary channel is above the primary channel
+ * This attribute is now deprecated.
+ * @NL80211_ATTR_WIPHY_RETRY_SHORT: TX retry limit for frames whose length is
+ * less than or equal to the RTS threshold; allowed range: 1..255;
+ * dot11ShortRetryLimit; u8
+ * @NL80211_ATTR_WIPHY_RETRY_LONG: TX retry limit for frames whose length is
+ * greater than the RTS threshold; allowed range: 1..255;
+ * dot11ShortLongLimit; u8
+ * @NL80211_ATTR_WIPHY_FRAG_THRESHOLD: fragmentation threshold, i.e., maximum
+ * length in octets for frames; allowed range: 256..8000, disable
+ * fragmentation with (u32)-1; dot11FragmentationThreshold; u32
+ * @NL80211_ATTR_WIPHY_RTS_THRESHOLD: RTS threshold (TX frames with length
+ * larger than or equal to this use RTS/CTS handshake); allowed range:
+ * 0..65536, disable with (u32)-1; dot11RTSThreshold; u32
+ * @NL80211_ATTR_WIPHY_COVERAGE_CLASS: Coverage Class as defined by IEEE 802.11
+ * section 7.3.2.9; dot11CoverageClass; u8
+ *
+ * @NL80211_ATTR_IFINDEX: network interface index of the device to operate on
+ * @NL80211_ATTR_IFNAME: network interface name
+ * @NL80211_ATTR_IFTYPE: type of virtual interface, see &enum nl80211_iftype
+ *
+ * @NL80211_ATTR_WDEV: wireless device identifier, used for pseudo-devices
+ * that don't have a netdev (u64)
+ *
+ * @NL80211_ATTR_MAC: MAC address (various uses)
+ *
+ * @NL80211_ATTR_KEY_DATA: (temporal) key data; for TKIP this consists of
+ * 16 bytes encryption key followed by 8 bytes each for TX and RX MIC
+ * keys
+ * @NL80211_ATTR_KEY_IDX: key ID (u8, 0-3)
+ * @NL80211_ATTR_KEY_CIPHER: key cipher suite (u32, as defined by IEEE 802.11
+ * section 7.3.2.25.1, e.g. 0x000FAC04)
+ * @NL80211_ATTR_KEY_SEQ: transmit key sequence number (IV/PN) for TKIP and
+ * CCMP keys, each six bytes in little endian
+ * @NL80211_ATTR_KEY_DEFAULT: Flag attribute indicating the key is default key
+ * @NL80211_ATTR_KEY_DEFAULT_MGMT: Flag attribute indicating the key is the
+ * default management key
+ * @NL80211_ATTR_CIPHER_SUITES_PAIRWISE: For crypto settings for connect or
+ * other commands, indicates which pairwise cipher suites are used
+ * @NL80211_ATTR_CIPHER_SUITE_GROUP: For crypto settings for connect or
+ * other commands, indicates which group cipher suite is used
+ *
+ * @NL80211_ATTR_BEACON_INTERVAL: beacon interval in TU
+ * @NL80211_ATTR_DTIM_PERIOD: DTIM period for beaconing
+ * @NL80211_ATTR_BEACON_HEAD: portion of the beacon before the TIM IE
+ * @NL80211_ATTR_BEACON_TAIL: portion of the beacon after the TIM IE
+ *
+ * @NL80211_ATTR_STA_AID: Association ID for the station (u16)
+ * @NL80211_ATTR_STA_FLAGS: flags, nested element with NLA_FLAG attributes of
+ * &enum nl80211_sta_flags (deprecated, use %NL80211_ATTR_STA_FLAGS2)
+ * @NL80211_ATTR_STA_LISTEN_INTERVAL: listen interval as defined by
+ * IEEE 802.11 7.3.1.6 (u16).
+ * @NL80211_ATTR_STA_SUPPORTED_RATES: supported rates, array of supported
+ * rates as defined by IEEE 802.11 7.3.2.2 but without the length
+ * restriction (at most %NL80211_MAX_SUPP_RATES).
+ * @NL80211_ATTR_STA_VLAN: interface index of VLAN interface to move station
+ * to, or the AP interface the station was originally added to to.
+ * @NL80211_ATTR_STA_INFO: information about a station, part of station info
+ * given for %NL80211_CMD_GET_STATION, nested attribute containing
+ * info as possible, see &enum nl80211_sta_info.
+ *
+ * @NL80211_ATTR_WIPHY_BANDS: Information about an operating bands,
+ * consisting of a nested array.
+ *
+ * @NL80211_ATTR_MESH_ID: mesh id (1-32 bytes).
+ * @NL80211_ATTR_STA_PLINK_ACTION: action to perform on the mesh peer link
+ * (see &enum nl80211_plink_action).
+ * @NL80211_ATTR_MPATH_NEXT_HOP: MAC address of the next hop for a mesh path.
+ * @NL80211_ATTR_MPATH_INFO: information about a mesh_path, part of mesh path
+ * info given for %NL80211_CMD_GET_MPATH, nested attribute described at
+ * &enum nl80211_mpath_info.
+ *
+ * @NL80211_ATTR_MNTR_FLAGS: flags, nested element with NLA_FLAG attributes of
+ * &enum nl80211_mntr_flags.
+ *
+ * @NL80211_ATTR_REG_ALPHA2: an ISO-3166-alpha2 country code for which the
+ * current regulatory domain should be set to or is already set to.
+ * For example, 'CR', for Costa Rica. This attribute is used by the kernel
+ * to query the CRDA to retrieve one regulatory domain. This attribute can
+ * also be used by userspace to query the kernel for the currently set
+ * regulatory domain. We chose an alpha2 as that is also used by the
+ * IEEE-802.11 country information element to identify a country.
+ * Users can also simply ask the wireless core to set regulatory domain
+ * to a specific alpha2.
+ * @NL80211_ATTR_REG_RULES: a nested array of regulatory domain regulatory
+ * rules.
+ *
+ * @NL80211_ATTR_BSS_CTS_PROT: whether CTS protection is enabled (u8, 0 or 1)
+ * @NL80211_ATTR_BSS_SHORT_PREAMBLE: whether short preamble is enabled
+ * (u8, 0 or 1)
+ * @NL80211_ATTR_BSS_SHORT_SLOT_TIME: whether short slot time enabled
+ * (u8, 0 or 1)
+ * @NL80211_ATTR_BSS_BASIC_RATES: basic rates, array of basic
+ * rates in format defined by IEEE 802.11 7.3.2.2 but without the length
+ * restriction (at most %NL80211_MAX_SUPP_RATES).
+ *
+ * @NL80211_ATTR_HT_CAPABILITY: HT Capability information element (from
+ * association request when used with NL80211_CMD_NEW_STATION)
+ *
+ * @NL80211_ATTR_SUPPORTED_IFTYPES: nested attribute containing all
+ * supported interface types, each a flag attribute with the number
+ * of the interface mode.
+ *
+ * @NL80211_ATTR_MGMT_SUBTYPE: Management frame subtype for
+ * %NL80211_CMD_SET_MGMT_EXTRA_IE.
+ *
+ * @NL80211_ATTR_IE: Information element(s) data (used, e.g., with
+ * %NL80211_CMD_SET_MGMT_EXTRA_IE).
+ *
+ * @NL80211_ATTR_MAX_NUM_SCAN_SSIDS: number of SSIDs you can scan with
+ * a single scan request, a wiphy attribute.
+ * @NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS: number of SSIDs you can
+ * scan with a single scheduled scan request, a wiphy attribute.
+ * @NL80211_ATTR_MAX_SCAN_IE_LEN: maximum length of information elements
+ * that can be added to a scan request
+ * @NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN: maximum length of information
+ * elements that can be added to a scheduled scan request
+ * @NL80211_ATTR_MAX_MATCH_SETS: maximum number of sets that can be
+ * used with @NL80211_ATTR_SCHED_SCAN_MATCH, a wiphy attribute.
+ *
+ * @NL80211_ATTR_SCAN_FREQUENCIES: nested attribute with frequencies (in MHz)
+ * @NL80211_ATTR_SCAN_SSIDS: nested attribute with SSIDs, leave out for passive
+ * scanning and include a zero-length SSID (wildcard) for wildcard scan
+ * @NL80211_ATTR_BSS: scan result BSS
+ *
+ * @NL80211_ATTR_REG_INITIATOR: indicates who requested the regulatory domain
+ * currently in effect. This could be any of the %NL80211_REGDOM_SET_BY_*
+ * @NL80211_ATTR_REG_TYPE: indicates the type of the regulatory domain currently
+ * set. This can be one of the nl80211_reg_type (%NL80211_REGDOM_TYPE_*)
+ *
+ * @NL80211_ATTR_SUPPORTED_COMMANDS: wiphy attribute that specifies
+ * an array of command numbers (i.e. a mapping index to command number)
+ * that the driver for the given wiphy supports.
+ *
+ * @NL80211_ATTR_FRAME: frame data (binary attribute), including frame header
+ * and body, but not FCS; used, e.g., with NL80211_CMD_AUTHENTICATE and
+ * NL80211_CMD_ASSOCIATE events
+ * @NL80211_ATTR_SSID: SSID (binary attribute, 0..32 octets)
+ * @NL80211_ATTR_AUTH_TYPE: AuthenticationType, see &enum nl80211_auth_type,
+ * represented as a u32
+ * @NL80211_ATTR_REASON_CODE: ReasonCode for %NL80211_CMD_DEAUTHENTICATE and
+ * %NL80211_CMD_DISASSOCIATE, u16
+ *
+ * @NL80211_ATTR_KEY_TYPE: Key Type, see &enum nl80211_key_type, represented as
+ * a u32
+ *
+ * @NL80211_ATTR_FREQ_BEFORE: A channel which has suffered a regulatory change
+ * due to considerations from a beacon hint. This attribute reflects
+ * the state of the channel _before_ the beacon hint processing. This
+ * attributes consists of a nested attribute containing
+ * NL80211_FREQUENCY_ATTR_*
+ * @NL80211_ATTR_FREQ_AFTER: A channel which has suffered a regulatory change
+ * due to considerations from a beacon hint. This attribute reflects
+ * the state of the channel _after_ the beacon hint processing. This
+ * attributes consists of a nested attribute containing
+ * NL80211_FREQUENCY_ATTR_*
+ *
+ * @NL80211_ATTR_CIPHER_SUITES: a set of u32 values indicating the supported
+ * cipher suites
+ *
+ * @NL80211_ATTR_FREQ_FIXED: a flag indicating the IBSS should not try to look
+ * for other networks on different channels
+ *
+ * @NL80211_ATTR_TIMED_OUT: a flag indicating than an operation timed out; this
+ * is used, e.g., with %NL80211_CMD_AUTHENTICATE event
+ *
+ * @NL80211_ATTR_USE_MFP: Whether management frame protection (IEEE 802.11w) is
+ * used for the association (&enum nl80211_mfp, represented as a u32);
+ * this attribute can be used
+ * with %NL80211_CMD_ASSOCIATE and %NL80211_CMD_CONNECT requests
+ *
+ * @NL80211_ATTR_STA_FLAGS2: Attribute containing a
+ * &struct nl80211_sta_flag_update.
+ *
+ * @NL80211_ATTR_CONTROL_PORT: A flag indicating whether user space controls
+ * IEEE 802.1X port, i.e., sets/clears %NL80211_STA_FLAG_AUTHORIZED, in
+ * station mode. If the flag is included in %NL80211_CMD_ASSOCIATE
+ * request, the driver will assume that the port is unauthorized until
+ * authorized by user space. Otherwise, port is marked authorized by
+ * default in station mode.
+ * @NL80211_ATTR_CONTROL_PORT_ETHERTYPE: A 16-bit value indicating the
+ * ethertype that will be used for key negotiation. It can be
+ * specified with the associate and connect commands. If it is not
+ * specified, the value defaults to 0x888E (PAE, 802.1X). This
+ * attribute is also used as a flag in the wiphy information to
+ * indicate that protocols other than PAE are supported.
+ * @NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT: When included along with
+ * %NL80211_ATTR_CONTROL_PORT_ETHERTYPE, indicates that the custom
+ * ethertype frames used for key negotiation must not be encrypted.
+ *
+ * @NL80211_ATTR_TESTDATA: Testmode data blob, passed through to the driver.
+ * We recommend using nested, driver-specific attributes within this.
+ *
+ * @NL80211_ATTR_DISCONNECTED_BY_AP: A flag indicating that the DISCONNECT
+ * event was due to the AP disconnecting the station, and not due to
+ * a local disconnect request.
+ * @NL80211_ATTR_STATUS_CODE: StatusCode for the %NL80211_CMD_CONNECT
+ * event (u16)
+ * @NL80211_ATTR_PRIVACY: Flag attribute, used with connect(), indicating
+ * that protected APs should be used. This is also used with NEW_BEACON to
+ * indicate that the BSS is to use protection.
+ *
+ * @NL80211_ATTR_CIPHERS_PAIRWISE: Used with CONNECT, ASSOCIATE, and NEW_BEACON
+ * to indicate which unicast key ciphers will be used with the connection
+ * (an array of u32).
+ * @NL80211_ATTR_CIPHER_GROUP: Used with CONNECT, ASSOCIATE, and NEW_BEACON to
+ * indicate which group key cipher will be used with the connection (a
+ * u32).
+ * @NL80211_ATTR_WPA_VERSIONS: Used with CONNECT, ASSOCIATE, and NEW_BEACON to
+ * indicate which WPA version(s) the AP we want to associate with is using
+ * (a u32 with flags from &enum nl80211_wpa_versions).
+ * @NL80211_ATTR_AKM_SUITES: Used with CONNECT, ASSOCIATE, and NEW_BEACON to
+ * indicate which key management algorithm(s) to use (an array of u32).
+ *
+ * @NL80211_ATTR_REQ_IE: (Re)association request information elements as
+ * sent out by the card, for ROAM and successful CONNECT events.
+ * @NL80211_ATTR_RESP_IE: (Re)association response information elements as
+ * sent by peer, for ROAM and successful CONNECT events.
+ *
+ * @NL80211_ATTR_PREV_BSSID: previous BSSID, to be used in ASSOCIATE and CONNECT
+ * commands to specify a request to reassociate within an ESS, i.e., to use
+ * Reassociate Request frame (with the value of this attribute in the
+ * Current AP address field) instead of Association Request frame which is
+ * used for the initial association to an ESS.
+ *
+ * @NL80211_ATTR_KEY: key information in a nested attribute with
+ * %NL80211_KEY_* sub-attributes
+ * @NL80211_ATTR_KEYS: array of keys for static WEP keys for connect()
+ * and join_ibss(), key information is in a nested attribute each
+ * with %NL80211_KEY_* sub-attributes
+ *
+ * @NL80211_ATTR_PID: Process ID of a network namespace.
+ *
+ * @NL80211_ATTR_GENERATION: Used to indicate consistent snapshots for
+ * dumps. This number increases whenever the object list being
+ * dumped changes, and as such userspace can verify that it has
+ * obtained a complete and consistent snapshot by verifying that
+ * all dump messages contain the same generation number. If it
+ * changed then the list changed and the dump should be repeated
+ * completely from scratch.
+ *
+ * @NL80211_ATTR_4ADDR: Use 4-address frames on a virtual interface
+ *
+ * @NL80211_ATTR_SURVEY_INFO: survey information about a channel, part of
+ * the survey response for %NL80211_CMD_GET_SURVEY, nested attribute
+ * containing info as possible, see &enum survey_info.
+ *
+ * @NL80211_ATTR_PMKID: PMK material for PMKSA caching.
+ * @NL80211_ATTR_MAX_NUM_PMKIDS: maximum number of PMKIDs a firmware can
+ * cache, a wiphy attribute.
+ *
+ * @NL80211_ATTR_DURATION: Duration of an operation in milliseconds, u32.
+ * @NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION: Device attribute that
+ * specifies the maximum duration that can be requested with the
+ * remain-on-channel operation, in milliseconds, u32.
+ *
+ * @NL80211_ATTR_COOKIE: Generic 64-bit cookie to identify objects.
+ *
+ * @NL80211_ATTR_TX_RATES: Nested set of attributes
+ * (enum nl80211_tx_rate_attributes) describing TX rates per band. The
+ * enum nl80211_band value is used as the index (nla_type() of the nested
+ * data. If a band is not included, it will be configured to allow all
+ * rates based on negotiated supported rates information. This attribute
+ * is used with %NL80211_CMD_SET_TX_BITRATE_MASK and with starting AP,
+ * and joining mesh networks (not IBSS yet). In the later case, it must
+ * specify just a single bitrate, which is to be used for the beacon.
+ * The driver must also specify support for this with the extended
+ * features NL80211_EXT_FEATURE_BEACON_RATE_LEGACY,
+ * NL80211_EXT_FEATURE_BEACON_RATE_HT and
+ * NL80211_EXT_FEATURE_BEACON_RATE_VHT.
+ *
+ * @NL80211_ATTR_FRAME_MATCH: A binary attribute which typically must contain
+ * at least one byte, currently used with @NL80211_CMD_REGISTER_FRAME.
+ * @NL80211_ATTR_FRAME_TYPE: A u16 indicating the frame type/subtype for the
+ * @NL80211_CMD_REGISTER_FRAME command.
+ * @NL80211_ATTR_TX_FRAME_TYPES: wiphy capability attribute, which is a
+ * nested attribute of %NL80211_ATTR_FRAME_TYPE attributes, containing
+ * information about which frame types can be transmitted with
+ * %NL80211_CMD_FRAME.
+ * @NL80211_ATTR_RX_FRAME_TYPES: wiphy capability attribute, which is a
+ * nested attribute of %NL80211_ATTR_FRAME_TYPE attributes, containing
+ * information about which frame types can be registered for RX.
+ *
+ * @NL80211_ATTR_ACK: Flag attribute indicating that the frame was
+ * acknowledged by the recipient.
+ *
+ * @NL80211_ATTR_PS_STATE: powersave state, using &enum nl80211_ps_state values.
+ *
+ * @NL80211_ATTR_CQM: connection quality monitor configuration in a
+ * nested attribute with %NL80211_ATTR_CQM_* sub-attributes.
+ *
+ * @NL80211_ATTR_LOCAL_STATE_CHANGE: Flag attribute to indicate that a command
+ * is requesting a local authentication/association state change without
+ * invoking actual management frame exchange. This can be used with
+ * NL80211_CMD_AUTHENTICATE, NL80211_CMD_DEAUTHENTICATE,
+ * NL80211_CMD_DISASSOCIATE.
+ *
+ * @NL80211_ATTR_AP_ISOLATE: (AP mode) Do not forward traffic between stations
+ * connected to this BSS.
+ *
+ * @NL80211_ATTR_WIPHY_TX_POWER_SETTING: Transmit power setting type. See
+ * &enum nl80211_tx_power_setting for possible values.
+ * @NL80211_ATTR_WIPHY_TX_POWER_LEVEL: Transmit power level in signed mBm units.
+ * This is used in association with @NL80211_ATTR_WIPHY_TX_POWER_SETTING
+ * for non-automatic settings.
+ *
+ * @NL80211_ATTR_SUPPORT_IBSS_RSN: The device supports IBSS RSN, which mostly
+ * means support for per-station GTKs.
+ *
+ * @NL80211_ATTR_WIPHY_ANTENNA_TX: Bitmap of allowed antennas for transmitting.
+ * This can be used to mask out antennas which are not attached or should
+ * not be used for transmitting. If an antenna is not selected in this
+ * bitmap the hardware is not allowed to transmit on this antenna.
+ *
+ * Each bit represents one antenna, starting with antenna 1 at the first
+ * bit. Depending on which antennas are selected in the bitmap, 802.11n
+ * drivers can derive which chainmasks to use (if all antennas belonging to
+ * a particular chain are disabled this chain should be disabled) and if
+ * a chain has diversity antennas wether diversity should be used or not.
+ * HT capabilities (STBC, TX Beamforming, Antenna selection) can be
+ * derived from the available chains after applying the antenna mask.
+ * Non-802.11n drivers can derive wether to use diversity or not.
+ * Drivers may reject configurations or RX/TX mask combinations they cannot
+ * support by returning -EINVAL.
+ *
+ * @NL80211_ATTR_WIPHY_ANTENNA_RX: Bitmap of allowed antennas for receiving.
+ * This can be used to mask out antennas which are not attached or should
+ * not be used for receiving. If an antenna is not selected in this bitmap
+ * the hardware should not be configured to receive on this antenna.
+ * For a more detailed description see @NL80211_ATTR_WIPHY_ANTENNA_TX.
+ *
+ * @NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX: Bitmap of antennas which are available
+ * for configuration as TX antennas via the above parameters.
+ *
+ * @NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX: Bitmap of antennas which are available
+ * for configuration as RX antennas via the above parameters.
+ *
+ * @NL80211_ATTR_MCAST_RATE: Multicast tx rate (in 100 kbps) for IBSS
+ *
+ * @NL80211_ATTR_OFFCHANNEL_TX_OK: For management frame TX, the frame may be
+ * transmitted on another channel when the channel given doesn't match
+ * the current channel. If the current channel doesn't match and this
+ * flag isn't set, the frame will be rejected. This is also used as an
+ * nl80211 capability flag.
+ *
+ * @NL80211_ATTR_BSS_HT_OPMODE: HT operation mode (u16)
+ *
+ * @NL80211_ATTR_KEY_DEFAULT_TYPES: A nested attribute containing flags
+ * attributes, specifying what a key should be set as default as.
+ * See &enum nl80211_key_default_types.
+ *
+ * @NL80211_ATTR_MESH_SETUP: Optional mesh setup parameters. These cannot be
+ * changed once the mesh is active.
+ * @NL80211_ATTR_MESH_CONFIG: Mesh configuration parameters, a nested attribute
+ * containing attributes from &enum nl80211_meshconf_params.
+ * @NL80211_ATTR_SUPPORT_MESH_AUTH: Currently, this means the underlying driver
+ * allows auth frames in a mesh to be passed to userspace for processing via
+ * the @NL80211_MESH_SETUP_USERSPACE_AUTH flag.
+ * @NL80211_ATTR_STA_PLINK_STATE: The state of a mesh peer link as defined in
+ * &enum nl80211_plink_state. Used when userspace is driving the peer link
+ * management state machine. @NL80211_MESH_SETUP_USERSPACE_AMPE or
+ * @NL80211_MESH_SETUP_USERSPACE_MPM must be enabled.
+ *
+ * @NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED: indicates, as part of the wiphy
+ * capabilities, the supported WoWLAN triggers
+ * @NL80211_ATTR_WOWLAN_TRIGGERS: used by %NL80211_CMD_SET_WOWLAN to
+ * indicate which WoW triggers should be enabled. This is also
+ * used by %NL80211_CMD_GET_WOWLAN to get the currently enabled WoWLAN
+ * triggers.
+ *
+ * @NL80211_ATTR_SCHED_SCAN_INTERVAL: Interval between scheduled scan
+ * cycles, in msecs.
+ *
+ * @NL80211_ATTR_SCHED_SCAN_MATCH: Nested attribute with one or more
+ * sets of attributes to match during scheduled scans. Only BSSs
+ * that match any of the sets will be reported. These are
+ * pass-thru filter rules.
+ * For a match to succeed, the BSS must match all attributes of a
+ * set. Since not every hardware supports matching all types of
+ * attributes, there is no guarantee that the reported BSSs are
+ * fully complying with the match sets and userspace needs to be
+ * able to ignore them by itself.
+ * Thus, the implementation is somewhat hardware-dependent, but
+ * this is only an optimization and the userspace application
+ * needs to handle all the non-filtered results anyway.
+ * If the match attributes don't make sense when combined with
+ * the values passed in @NL80211_ATTR_SCAN_SSIDS (eg. if an SSID
+ * is included in the probe request, but the match attributes
+ * will never let it go through), -EINVAL may be returned.
+ * If ommited, no filtering is done.
+ *
+ * @NL80211_ATTR_INTERFACE_COMBINATIONS: Nested attribute listing the supported
+ * interface combinations. In each nested item, it contains attributes
+ * defined in &enum nl80211_if_combination_attrs.
+ * @NL80211_ATTR_SOFTWARE_IFTYPES: Nested attribute (just like
+ * %NL80211_ATTR_SUPPORTED_IFTYPES) containing the interface types that
+ * are managed in software: interfaces of these types aren't subject to
+ * any restrictions in their number or combinations.
+ *
+ * @NL80211_ATTR_REKEY_DATA: nested attribute containing the information
+ * necessary for GTK rekeying in the device, see &enum nl80211_rekey_data.
+ *
+ * @NL80211_ATTR_SCAN_SUPP_RATES: rates per to be advertised as supported in scan,
+ * nested array attribute containing an entry for each band, with the entry
+ * being a list of supported rates as defined by IEEE 802.11 7.3.2.2 but
+ * without the length restriction (at most %NL80211_MAX_SUPP_RATES).
+ *
+ * @NL80211_ATTR_HIDDEN_SSID: indicates whether SSID is to be hidden from Beacon
+ * and Probe Response (when response to wildcard Probe Request); see
+ * &enum nl80211_hidden_ssid, represented as a u32
+ *
+ * @NL80211_ATTR_IE_PROBE_RESP: Information element(s) for Probe Response frame.
+ * This is used with %NL80211_CMD_NEW_BEACON and %NL80211_CMD_SET_BEACON to
+ * provide extra IEs (e.g., WPS/P2P IE) into Probe Response frames when the
+ * driver (or firmware) replies to Probe Request frames.
+ * @NL80211_ATTR_IE_ASSOC_RESP: Information element(s) for (Re)Association
+ * Response frames. This is used with %NL80211_CMD_NEW_BEACON and
+ * %NL80211_CMD_SET_BEACON to provide extra IEs (e.g., WPS/P2P IE) into
+ * (Re)Association Response frames when the driver (or firmware) replies to
+ * (Re)Association Request frames.
+ *
+ * @NL80211_ATTR_STA_WME: Nested attribute containing the wme configuration
+ * of the station, see &enum nl80211_sta_wme_attr.
+ * @NL80211_ATTR_SUPPORT_AP_UAPSD: the device supports uapsd when working
+ * as AP.
+ *
+ * @NL80211_ATTR_ROAM_SUPPORT: Indicates whether the firmware is capable of
+ * roaming to another AP in the same ESS if the signal lever is low.
+ *
+ * @NL80211_ATTR_PMKSA_CANDIDATE: Nested attribute containing the PMKSA caching
+ * candidate information, see &enum nl80211_pmksa_candidate_attr.
+ *
+ * @NL80211_ATTR_TX_NO_CCK_RATE: Indicates whether to use CCK rate or not
+ * for management frames transmission. In order to avoid p2p probe/action
+ * frames are being transmitted at CCK rate in 2GHz band, the user space
+ * applications use this attribute.
+ * This attribute is used with %NL80211_CMD_TRIGGER_SCAN and
+ * %NL80211_CMD_FRAME commands.
+ *
+ * @NL80211_ATTR_TDLS_ACTION: Low level TDLS action code (e.g. link setup
+ * request, link setup confirm, link teardown, etc.). Values are
+ * described in the TDLS (802.11z) specification.
+ * @NL80211_ATTR_TDLS_DIALOG_TOKEN: Non-zero token for uniquely identifying a
+ * TDLS conversation between two devices.
+ * @NL80211_ATTR_TDLS_OPERATION: High level TDLS operation; see
+ * &enum nl80211_tdls_operation, represented as a u8.
+ * @NL80211_ATTR_TDLS_SUPPORT: A flag indicating the device can operate
+ * as a TDLS peer sta.
+ * @NL80211_ATTR_TDLS_EXTERNAL_SETUP: The TDLS discovery/setup and teardown
+ * procedures should be performed by sending TDLS packets via
+ * %NL80211_CMD_TDLS_MGMT. Otherwise %NL80211_CMD_TDLS_OPER should be
+ * used for asking the driver to perform a TDLS operation.
+ *
+ * @NL80211_ATTR_DEVICE_AP_SME: This u32 attribute may be listed for devices
+ * that have AP support to indicate that they have the AP SME integrated
+ * with support for the features listed in this attribute, see
+ * &enum nl80211_ap_sme_features.
+ *
+ * @NL80211_ATTR_DONT_WAIT_FOR_ACK: Used with %NL80211_CMD_FRAME, this tells
+ * the driver to not wait for an acknowledgement. Note that due to this,
+ * it will also not give a status callback nor return a cookie. This is
+ * mostly useful for probe responses to save airtime.
+ *
+ * @NL80211_ATTR_FEATURE_FLAGS: This u32 attribute contains flags from
+ * &enum nl80211_feature_flags and is advertised in wiphy information.
+ * @NL80211_ATTR_PROBE_RESP_OFFLOAD: Indicates that the HW responds to probe
+ * requests while operating in AP-mode.
+ * This attribute holds a bitmap of the supported protocols for
+ * offloading (see &enum nl80211_probe_resp_offload_support_attr).
+ *
+ * @NL80211_ATTR_PROBE_RESP: Probe Response template data. Contains the entire
+ * probe-response frame. The DA field in the 802.11 header is zero-ed out,
+ * to be filled by the FW.
+ * @NL80211_ATTR_DISABLE_HT: Force HT capable interfaces to disable
+ * this feature. Currently, only supported in mac80211 drivers.
+ * @NL80211_ATTR_HT_CAPABILITY_MASK: Specify which bits of the
+ * ATTR_HT_CAPABILITY to which attention should be paid.
+ * Currently, only mac80211 NICs support this feature.
+ * The values that may be configured are:
+ * MCS rates, MAX-AMSDU, HT-20-40 and HT_CAP_SGI_40
+ * AMPDU density and AMPDU factor.
+ * All values are treated as suggestions and may be ignored
+ * by the driver as required. The actual values may be seen in
+ * the station debugfs ht_caps file.
+ *
+ * @NL80211_ATTR_DFS_REGION: region for regulatory rules which this country
+ * abides to when initiating radiation on DFS channels. A country maps
+ * to one DFS region.
+ *
+ * @NL80211_ATTR_NOACK_MAP: This u16 bitmap contains the No Ack Policy of
+ * up to 16 TIDs.
+ *
+ * @NL80211_ATTR_INACTIVITY_TIMEOUT: timeout value in seconds, this can be
+ * used by the drivers which has MLME in firmware and does not have support
+ * to report per station tx/rx activity to free up the staion entry from
+ * the list. This needs to be used when the driver advertises the
+ * capability to timeout the stations.
+ *
+ * @NL80211_ATTR_RX_SIGNAL_DBM: signal strength in dBm (as a 32-bit int);
+ * this attribute is (depending on the driver capabilities) added to
+ * received frames indicated with %NL80211_CMD_FRAME.
+ *
+ * @NL80211_ATTR_BG_SCAN_PERIOD: Background scan period in seconds
+ * or 0 to disable background scan.
+ *
+ * @NL80211_ATTR_USER_REG_HINT_TYPE: type of regulatory hint passed from
+ * userspace. If unset it is assumed the hint comes directly from
+ * a user. If set code could specify exactly what type of source
+ * was used to provide the hint. For the different types of
+ * allowed user regulatory hints see nl80211_user_reg_hint_type.
+ *
+ * @NL80211_ATTR_CONN_FAILED_REASON: The reason for which AP has rejected
+ * the connection request from a station. nl80211_connect_failed_reason
+ * enum has different reasons of connection failure.
+ *
+ * @NL80211_ATTR_AUTH_DATA: Fields and elements in Authentication frames.
+ * This contains the authentication frame body (non-IE and IE data),
+ * excluding the Authentication algorithm number, i.e., starting at the
+ * Authentication transaction sequence number field. It is used with
+ * authentication algorithms that need special fields to be added into
+ * the frames (SAE and FILS). Currently, only the SAE cases use the
+ * initial two fields (Authentication transaction sequence number and
+ * Status code). However, those fields are included in the attribute data
+ * for all authentication algorithms to keep the attribute definition
+ * consistent.
+ *
+ * @NL80211_ATTR_VHT_CAPABILITY: VHT Capability information element (from
+ * association request when used with NL80211_CMD_NEW_STATION)
+ *
+ * @NL80211_ATTR_SCAN_FLAGS: scan request control flags (u32)
+ *
+ * @NL80211_ATTR_P2P_CTWINDOW: P2P GO Client Traffic Window (u8), used with
+ * the START_AP and SET_BSS commands
+ * @NL80211_ATTR_P2P_OPPPS: P2P GO opportunistic PS (u8), used with the
+ * START_AP and SET_BSS commands. This can have the values 0 or 1;
+ * if not given in START_AP 0 is assumed, if not given in SET_BSS
+ * no change is made.
+ *
+ * @NL80211_ATTR_LOCAL_MESH_POWER_MODE: local mesh STA link-specific power mode
+ * defined in &enum nl80211_mesh_power_mode.
+ *
+ * @NL80211_ATTR_ACL_POLICY: ACL policy, see &enum nl80211_acl_policy,
+ * carried in a u32 attribute
+ *
+ * @NL80211_ATTR_MAC_ADDRS: Array of nested MAC addresses, used for
+ * MAC ACL.
+ *
+ * @NL80211_ATTR_MAC_ACL_MAX: u32 attribute to advertise the maximum
+ * number of MAC addresses that a device can support for MAC
+ * ACL.
+ *
+ * @NL80211_ATTR_RADAR_EVENT: Type of radar event for notification to userspace,
+ * contains a value of enum nl80211_radar_event (u32).
+ *
+ * @NL80211_ATTR_EXT_CAPA: 802.11 extended capabilities that the kernel driver
+ * has and handles. The format is the same as the IE contents. See
+ * 802.11-2012 8.4.2.29 for more information.
+ * @NL80211_ATTR_EXT_CAPA_MASK: Extended capabilities that the kernel driver
+ * has set in the %NL80211_ATTR_EXT_CAPA value, for multibit fields.
+ *
+ * @NL80211_ATTR_STA_CAPABILITY: Station capabilities (u16) are advertised to
+ * the driver, e.g., to enable TDLS power save (PU-APSD).
+ *
+ * @NL80211_ATTR_STA_EXT_CAPABILITY: Station extended capabilities are
+ * advertised to the driver, e.g., to enable TDLS off channel operations
+ * and PU-APSD.
+ *
+ * @NL80211_ATTR_PROTOCOL_FEATURES: global nl80211 feature flags, see
+ * &enum nl80211_protocol_features, the attribute is a u32.
+ *
+ * @NL80211_ATTR_SPLIT_WIPHY_DUMP: flag attribute, userspace supports
+ * receiving the data for a single wiphy split across multiple
+ * messages, given with wiphy dump message
+ *
+ * @NL80211_ATTR_MDID: Mobility Domain Identifier
+ *
+ * @NL80211_ATTR_IE_RIC: Resource Information Container Information
+ * Element
+ *
+ * @NL80211_ATTR_CRIT_PROT_ID: critical protocol identifier requiring increased
+ * reliability, see &enum nl80211_crit_proto_id (u16).
+ * @NL80211_ATTR_MAX_CRIT_PROT_DURATION: duration in milliseconds in which
+ * the connection should have increased reliability (u16).
+ *
+ * @NL80211_ATTR_PEER_AID: Association ID for the peer TDLS station (u16).
+ * This is similar to @NL80211_ATTR_STA_AID but with a difference of being
+ * allowed to be used with the first @NL80211_CMD_SET_STATION command to
+ * update a TDLS peer STA entry.
+ *
+ * @NL80211_ATTR_COALESCE_RULE: Coalesce rule information.
+ *
+ * @NL80211_ATTR_CH_SWITCH_COUNT: u32 attribute specifying the number of TBTT's
+ * until the channel switch event.
+ * @NL80211_ATTR_CH_SWITCH_BLOCK_TX: flag attribute specifying that transmission
+ * must be blocked on the current channel (before the channel switch
+ * operation).
+ * @NL80211_ATTR_CSA_IES: Nested set of attributes containing the IE information
+ * for the time while performing a channel switch.
+ * @NL80211_ATTR_CSA_C_OFF_BEACON: An array of offsets (u16) to the channel
+ * switch counters in the beacons tail (%NL80211_ATTR_BEACON_TAIL).
+ * @NL80211_ATTR_CSA_C_OFF_PRESP: An array of offsets (u16) to the channel
+ * switch counters in the probe response (%NL80211_ATTR_PROBE_RESP).
+ *
+ * @NL80211_ATTR_RXMGMT_FLAGS: flags for nl80211_send_mgmt(), u32.
+ * As specified in the &enum nl80211_rxmgmt_flags.
+ *
+ * @NL80211_ATTR_STA_SUPPORTED_CHANNELS: array of supported channels.
+ *
+ * @NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES: array of supported
+ * supported operating classes.
+ *
+ * @NL80211_ATTR_HANDLE_DFS: A flag indicating whether user space
+ * controls DFS operation in IBSS mode. If the flag is included in
+ * %NL80211_CMD_JOIN_IBSS request, the driver will allow use of DFS
+ * channels and reports radar events to userspace. Userspace is required
+ * to react to radar events, e.g. initiate a channel switch or leave the
+ * IBSS network.
+ *
+ * @NL80211_ATTR_SUPPORT_5_MHZ: A flag indicating that the device supports
+ * 5 MHz channel bandwidth.
+ * @NL80211_ATTR_SUPPORT_10_MHZ: A flag indicating that the device supports
+ * 10 MHz channel bandwidth.
+ *
+ * @NL80211_ATTR_OPMODE_NOTIF: Operating mode field from Operating Mode
+ * Notification Element based on association request when used with
+ * %NL80211_CMD_NEW_STATION or %NL80211_CMD_SET_STATION (only when
+ * %NL80211_FEATURE_FULL_AP_CLIENT_STATE is supported, or with TDLS);
+ * u8 attribute.
+ *
+ * @NL80211_ATTR_VENDOR_ID: The vendor ID, either a 24-bit OUI or, if
+ * %NL80211_VENDOR_ID_IS_LINUX is set, a special Linux ID (not used yet)
+ * @NL80211_ATTR_VENDOR_SUBCMD: vendor sub-command
+ * @NL80211_ATTR_VENDOR_DATA: data for the vendor command, if any; this
+ * attribute is also used for vendor command feature advertisement
+ * @NL80211_ATTR_VENDOR_EVENTS: used for event list advertising in the wiphy
+ * info, containing a nested array of possible events
+ *
+ * @NL80211_ATTR_QOS_MAP: IP DSCP mapping for Interworking QoS mapping. This
+ * data is in the format defined for the payload of the QoS Map Set element
+ * in IEEE Std 802.11-2012, 8.4.2.97.
+ *
+ * @NL80211_ATTR_MAC_HINT: MAC address recommendation as initial BSS
+ * @NL80211_ATTR_WIPHY_FREQ_HINT: frequency of the recommended initial BSS
+ *
+ * @NL80211_ATTR_MAX_AP_ASSOC_STA: Device attribute that indicates how many
+ * associated stations are supported in AP mode (including P2P GO); u32.
+ * Since drivers may not have a fixed limit on the maximum number (e.g.,
+ * other concurrent operations may affect this), drivers are allowed to
+ * advertise values that cannot always be met. In such cases, an attempt
+ * to add a new station entry with @NL80211_CMD_NEW_STATION may fail.
+ *
+ * @NL80211_ATTR_CSA_C_OFFSETS_TX: An array of csa counter offsets (u16) which
+ * should be updated when the frame is transmitted.
+ * @NL80211_ATTR_MAX_CSA_COUNTERS: U8 attribute used to advertise the maximum
+ * supported number of csa counters.
+ *
+ * @NL80211_ATTR_TDLS_PEER_CAPABILITY: flags for TDLS peer capabilities, u32.
+ * As specified in the &enum nl80211_tdls_peer_capability.
+ *
+ * @NL80211_ATTR_SOCKET_OWNER: Flag attribute, if set during interface
+ * creation then the new interface will be owned by the netlink socket
+ * that created it and will be destroyed when the socket is closed.
+ * If set during scheduled scan start then the new scan req will be
+ * owned by the netlink socket that created it and the scheduled scan will
+ * be stopped when the socket is closed.
+ * If set during configuration of regulatory indoor operation then the
+ * regulatory indoor configuration would be owned by the netlink socket
+ * that configured the indoor setting, and the indoor operation would be
+ * cleared when the socket is closed.
+ * If set during NAN interface creation, the interface will be destroyed
+ * if the socket is closed just like any other interface. Moreover, only
+ * the netlink socket that created the interface will be allowed to add
+ * and remove functions. NAN notifications will be sent in unicast to that
+ * socket. Without this attribute, any socket can add functions and the
+ * notifications will be sent to the %NL80211_MCGRP_NAN multicast group.
+ * If set during %NL80211_CMD_ASSOCIATE or %NL80211_CMD_CONNECT the
+ * station will deauthenticate when the socket is closed.
+ *
+ * @NL80211_ATTR_TDLS_INITIATOR: flag attribute indicating the current end is
+ * the TDLS link initiator.
+ *
+ * @NL80211_ATTR_USE_RRM: flag for indicating whether the current connection
+ * shall support Radio Resource Measurements (11k). This attribute can be
+ * used with %NL80211_CMD_ASSOCIATE and %NL80211_CMD_CONNECT requests.
+ * User space applications are expected to use this flag only if the
+ * underlying device supports these minimal RRM features:
+ * %NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES,
+ * %NL80211_FEATURE_QUIET,
+ * Or, if global RRM is supported, see:
+ * %NL80211_EXT_FEATURE_RRM
+ * If this flag is used, driver must add the Power Capabilities IE to the
+ * association request. In addition, it must also set the RRM capability
+ * flag in the association request's Capability Info field.
+ *
+ * @NL80211_ATTR_WIPHY_DYN_ACK: flag attribute used to enable ACK timeout
+ * estimation algorithm (dynack). In order to activate dynack
+ * %NL80211_FEATURE_ACKTO_ESTIMATION feature flag must be set by lower
+ * drivers to indicate dynack capability. Dynack is automatically disabled
+ * setting valid value for coverage class.
+ *
+ * @NL80211_ATTR_TSID: a TSID value (u8 attribute)
+ * @NL80211_ATTR_USER_PRIO: user priority value (u8 attribute)
+ * @NL80211_ATTR_ADMITTED_TIME: admitted time in units of 32 microseconds
+ * (per second) (u16 attribute)
+ *
+ * @NL80211_ATTR_SMPS_MODE: SMPS mode to use (ap mode). see
+ * &enum nl80211_smps_mode.
+ *
+ * @NL80211_ATTR_OPER_CLASS: operating class
+ *
+ * @NL80211_ATTR_MAC_MASK: MAC address mask
+ *
+ * @NL80211_ATTR_WIPHY_SELF_MANAGED_REG: flag attribute indicating this device
+ * is self-managing its regulatory information and any regulatory domain
+ * obtained from it is coming from the device's wiphy and not the global
+ * cfg80211 regdomain.
+ *
+ * @NL80211_ATTR_EXT_FEATURES: extended feature flags contained in a byte
+ * array. The feature flags are identified by their bit index (see &enum
+ * nl80211_ext_feature_index). The bit index is ordered starting at the
+ * least-significant bit of the first byte in the array, ie. bit index 0
+ * is located at bit 0 of byte 0. bit index 25 would be located at bit 1
+ * of byte 3 (u8 array).
+ *
+ * @NL80211_ATTR_SURVEY_RADIO_STATS: Request overall radio statistics to be
+ * returned along with other survey data. If set, @NL80211_CMD_GET_SURVEY
+ * may return a survey entry without a channel indicating global radio
+ * statistics (only some values are valid and make sense.)
+ * For devices that don't return such an entry even then, the information
+ * should be contained in the result as the sum of the respective counters
+ * over all channels.
+ *
+ * @NL80211_ATTR_SCHED_SCAN_DELAY: delay before the first cycle of a
+ * scheduled scan is started. Or the delay before a WoWLAN
+ * net-detect scan is started, counting from the moment the
+ * system is suspended. This value is a u32, in seconds.
+
+ * @NL80211_ATTR_REG_INDOOR: flag attribute, if set indicates that the device
+ * is operating in an indoor environment.
+ *
+ * @NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS: maximum number of scan plans for
+ * scheduled scan supported by the device (u32), a wiphy attribute.
+ * @NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL: maximum interval (in seconds) for
+ * a scan plan (u32), a wiphy attribute.
+ * @NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS: maximum number of iterations in
+ * a scan plan (u32), a wiphy attribute.
+ * @NL80211_ATTR_SCHED_SCAN_PLANS: a list of scan plans for scheduled scan.
+ * Each scan plan defines the number of scan iterations and the interval
+ * between scans. The last scan plan will always run infinitely,
+ * thus it must not specify the number of iterations, only the interval
+ * between scans. The scan plans are executed sequentially.
+ * Each scan plan is a nested attribute of &enum nl80211_sched_scan_plan.
+ * @NL80211_ATTR_PBSS: flag attribute. If set it means operate
+ * in a PBSS. Specified in %NL80211_CMD_CONNECT to request
+ * connecting to a PCP, and in %NL80211_CMD_START_AP to start
+ * a PCP instead of AP. Relevant for DMG networks only.
+ * @NL80211_ATTR_BSS_SELECT: nested attribute for driver supporting the
+ * BSS selection feature. When used with %NL80211_CMD_GET_WIPHY it contains
+ * attributes according &enum nl80211_bss_select_attr to indicate what
+ * BSS selection behaviours are supported. When used with %NL80211_CMD_CONNECT
+ * it contains the behaviour-specific attribute containing the parameters for
+ * BSS selection to be done by driver and/or firmware.
+ *
+ * @NL80211_ATTR_STA_SUPPORT_P2P_PS: whether P2P PS mechanism supported
+ * or not. u8, one of the values of &enum nl80211_sta_p2p_ps_status
+ *
+ * @NL80211_ATTR_PAD: attribute used for padding for 64-bit alignment
+ *
+ * @NL80211_ATTR_IFTYPE_EXT_CAPA: Nested attribute of the following attributes:
+ * %NL80211_ATTR_IFTYPE, %NL80211_ATTR_EXT_CAPA,
+ * %NL80211_ATTR_EXT_CAPA_MASK, to specify the extended capabilities per
+ * interface type.
+ *
+ * @NL80211_ATTR_MU_MIMO_GROUP_DATA: array of 24 bytes that defines a MU-MIMO
+ * groupID for monitor mode.
+ * The first 8 bytes are a mask that defines the membership in each
+ * group (there are 64 groups, group 0 and 63 are reserved),
+ * each bit represents a group and set to 1 for being a member in
+ * that group and 0 for not being a member.
+ * The remaining 16 bytes define the position in each group: 2 bits for
+ * each group.
+ * (smaller group numbers represented on most significant bits and bigger
+ * group numbers on least significant bits.)
+ * This attribute is used only if all interfaces are in monitor mode.
+ * Set this attribute in order to monitor packets using the given MU-MIMO
+ * groupID data.
+ * to turn off that feature set all the bits of the groupID to zero.
+ * @NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR: mac address for the sniffer to follow
+ * when using MU-MIMO air sniffer.
+ * to turn that feature off set an invalid mac address
+ * (e.g. FF:FF:FF:FF:FF:FF)
+ *
+ * @NL80211_ATTR_SCAN_START_TIME_TSF: The time at which the scan was actually
+ * started (u64). The time is the TSF of the BSS the interface that
+ * requested the scan is connected to (if available, otherwise this
+ * attribute must not be included).
+ * @NL80211_ATTR_SCAN_START_TIME_TSF_BSSID: The BSS according to which
+ * %NL80211_ATTR_SCAN_START_TIME_TSF is set.
+ * @NL80211_ATTR_MEASUREMENT_DURATION: measurement duration in TUs (u16). If
+ * %NL80211_ATTR_MEASUREMENT_DURATION_MANDATORY is not set, this is the
+ * maximum measurement duration allowed. This attribute is used with
+ * measurement requests. It can also be used with %NL80211_CMD_TRIGGER_SCAN
+ * if the scan is used for beacon report radio measurement.
+ * @NL80211_ATTR_MEASUREMENT_DURATION_MANDATORY: flag attribute that indicates
+ * that the duration specified with %NL80211_ATTR_MEASUREMENT_DURATION is
+ * mandatory. If this flag is not set, the duration is the maximum duration
+ * and the actual measurement duration may be shorter.
+ *
+ * @NL80211_ATTR_MESH_PEER_AID: Association ID for the mesh peer (u16). This is
+ * used to pull the stored data for mesh peer in power save state.
+ *
+ * @NL80211_ATTR_NAN_MASTER_PREF: the master preference to be used by
+ * %NL80211_CMD_START_NAN and optionally with
+ * %NL80211_CMD_CHANGE_NAN_CONFIG. Its type is u8 and it can't be 0.
+ * Also, values 1 and 255 are reserved for certification purposes and
+ * should not be used during a normal device operation.
+ * @NL80211_ATTR_BANDS: operating bands configuration. This is a u32
+ * bitmask of BIT(NL80211_BAND_*) as described in %enum
+ * nl80211_band. For instance, for NL80211_BAND_2GHZ, bit 0
+ * would be set. This attribute is used with
+ * %NL80211_CMD_START_NAN and %NL80211_CMD_CHANGE_NAN_CONFIG, and
+ * it is optional. If no bands are set, it means don't-care and
+ * the device will decide what to use.
+ * @NL80211_ATTR_NAN_FUNC: a function that can be added to NAN. See
+ * &enum nl80211_nan_func_attributes for description of this nested
+ * attribute.
+ * @NL80211_ATTR_NAN_MATCH: used to report a match. This is a nested attribute.
+ * See &enum nl80211_nan_match_attributes.
+ * @NL80211_ATTR_FILS_KEK: KEK for FILS (Re)Association Request/Response frame
+ * protection.
+ * @NL80211_ATTR_FILS_NONCES: Nonces (part of AAD) for FILS (Re)Association
+ * Request/Response frame protection. This attribute contains the 16 octet
+ * STA Nonce followed by 16 octets of AP Nonce.
+ *
+ * @NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED: Indicates whether or not multicast
+ * packets should be send out as unicast to all stations (flag attribute).
+ *
+ * @NL80211_ATTR_BSSID: The BSSID of the AP. Note that %NL80211_ATTR_MAC is also
+ * used in various commands/events for specifying the BSSID.
+ *
+ * @NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI: Relative RSSI threshold by which
+ * other BSSs has to be better or slightly worse than the current
+ * connected BSS so that they get reported to user space.
+ * This will give an opportunity to userspace to consider connecting to
+ * other matching BSSs which have better or slightly worse RSSI than
+ * the current connected BSS by using an offloaded operation to avoid
+ * unnecessary wakeups.
+ *
+ * @NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST: When present the RSSI level for BSSs in
+ * the specified band is to be adjusted before doing
+ * %NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI based comparision to figure out
+ * better BSSs. The attribute value is a packed structure
+ * value as specified by &struct nl80211_bss_select_rssi_adjust.
+ *
+ * @NL80211_ATTR_TIMEOUT_REASON: The reason for which an operation timed out.
+ * u32 attribute with an &enum nl80211_timeout_reason value. This is used,
+ * e.g., with %NL80211_CMD_CONNECT event.
+ *
+ * @NL80211_ATTR_FILS_ERP_USERNAME: EAP Re-authentication Protocol (ERP)
+ * username part of NAI used to refer keys rRK and rIK. This is used with
+ * %NL80211_CMD_CONNECT.
+ *
+ * @NL80211_ATTR_FILS_ERP_REALM: EAP Re-authentication Protocol (ERP) realm part
+ * of NAI specifying the domain name of the ER server. This is used with
+ * %NL80211_CMD_CONNECT.
+ *
+ * @NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM: Unsigned 16-bit ERP next sequence number
+ * to use in ERP messages. This is used in generating the FILS wrapped data
+ * for FILS authentication and is used with %NL80211_CMD_CONNECT.
+ *
+ * @NL80211_ATTR_FILS_ERP_RRK: ERP re-authentication Root Key (rRK) for the
+ * NAI specified by %NL80211_ATTR_FILS_ERP_USERNAME and
+ * %NL80211_ATTR_FILS_ERP_REALM. This is used for generating rIK and rMSK
+ * from successful FILS authentication and is used with
+ * %NL80211_CMD_CONNECT.
+ *
+ * @NL80211_ATTR_FILS_CACHE_ID: A 2-octet identifier advertized by a FILS AP
+ * identifying the scope of PMKSAs. This is used with
+ * @NL80211_CMD_SET_PMKSA and @NL80211_CMD_DEL_PMKSA.
+ *
+ * @NL80211_ATTR_PMK: attribute for passing PMK key material. Used with
+ * %NL80211_CMD_SET_PMKSA for the PMKSA identified by %NL80211_ATTR_PMKID.
+ * For %NL80211_CMD_CONNECT it is used to provide PSK for offloading 4-way
+ * handshake for WPA/WPA2-PSK networks. For 802.1X authentication it is
+ * used with %NL80211_CMD_SET_PMK. For offloaded FT support this attribute
+ * specifies the PMK-R0 if NL80211_ATTR_PMKR0_NAME is included as well.
+ *
+ * @NL80211_ATTR_SCHED_SCAN_MULTI: flag attribute which user-space shall use to
+ * indicate that it supports multiple active scheduled scan requests.
+ * @NL80211_ATTR_SCHED_SCAN_MAX_REQS: indicates maximum number of scheduled
+ * scan request that may be active for the device (u32).
+ *
+ * @NL80211_ATTR_WANT_1X_4WAY_HS: flag attribute which user-space can include
+ * in %NL80211_CMD_CONNECT to indicate that for 802.1X authentication it
+ * wants to use the supported offload of the 4-way handshake.
+ * @NL80211_ATTR_PMKR0_NAME: PMK-R0 Name for offloaded FT.
+ *
+ * @NUM_NL80211_ATTR: total number of nl80211_attrs available
+ * @NL80211_ATTR_MAX: highest attribute number currently defined
+ * @__NL80211_ATTR_AFTER_LAST: internal use
+ */
+enum nl80211_attrs {
+/* don't change the order or add anything between, this is ABI! */
+ NL80211_ATTR_UNSPEC,
+
+ NL80211_ATTR_WIPHY,
+ NL80211_ATTR_WIPHY_NAME,
+
+ NL80211_ATTR_IFINDEX,
+ NL80211_ATTR_IFNAME,
+ NL80211_ATTR_IFTYPE,
+
+ NL80211_ATTR_MAC,
+
+ NL80211_ATTR_KEY_DATA,
+ NL80211_ATTR_KEY_IDX,
+ NL80211_ATTR_KEY_CIPHER,
+ NL80211_ATTR_KEY_SEQ,
+ NL80211_ATTR_KEY_DEFAULT,
+
+ NL80211_ATTR_BEACON_INTERVAL,
+ NL80211_ATTR_DTIM_PERIOD,
+ NL80211_ATTR_BEACON_HEAD,
+ NL80211_ATTR_BEACON_TAIL,
+
+ NL80211_ATTR_STA_AID,
+ NL80211_ATTR_STA_FLAGS,
+ NL80211_ATTR_STA_LISTEN_INTERVAL,
+ NL80211_ATTR_STA_SUPPORTED_RATES,
+ NL80211_ATTR_STA_VLAN,
+ NL80211_ATTR_STA_INFO,
+
+ NL80211_ATTR_WIPHY_BANDS,
+
+ NL80211_ATTR_MNTR_FLAGS,
+
+ NL80211_ATTR_MESH_ID,
+ NL80211_ATTR_STA_PLINK_ACTION,
+ NL80211_ATTR_MPATH_NEXT_HOP,
+ NL80211_ATTR_MPATH_INFO,
+
+ NL80211_ATTR_BSS_CTS_PROT,
+ NL80211_ATTR_BSS_SHORT_PREAMBLE,
+ NL80211_ATTR_BSS_SHORT_SLOT_TIME,
+
+ NL80211_ATTR_HT_CAPABILITY,
+
+ NL80211_ATTR_SUPPORTED_IFTYPES,
+
+ NL80211_ATTR_REG_ALPHA2,
+ NL80211_ATTR_REG_RULES,
+
+ NL80211_ATTR_MESH_CONFIG,
+
+ NL80211_ATTR_BSS_BASIC_RATES,
+
+ NL80211_ATTR_WIPHY_TXQ_PARAMS,
+ NL80211_ATTR_WIPHY_FREQ,
+ NL80211_ATTR_WIPHY_CHANNEL_TYPE,
+
+ NL80211_ATTR_KEY_DEFAULT_MGMT,
+
+ NL80211_ATTR_MGMT_SUBTYPE,
+ NL80211_ATTR_IE,
+
+ NL80211_ATTR_MAX_NUM_SCAN_SSIDS,
+
+ NL80211_ATTR_SCAN_FREQUENCIES,
+ NL80211_ATTR_SCAN_SSIDS,
+ NL80211_ATTR_GENERATION, /* replaces old SCAN_GENERATION */
+ NL80211_ATTR_BSS,
+
+ NL80211_ATTR_REG_INITIATOR,
+ NL80211_ATTR_REG_TYPE,
+
+ NL80211_ATTR_SUPPORTED_COMMANDS,
+
+ NL80211_ATTR_FRAME,
+ NL80211_ATTR_SSID,
+ NL80211_ATTR_AUTH_TYPE,
+ NL80211_ATTR_REASON_CODE,
+
+ NL80211_ATTR_KEY_TYPE,
+
+ NL80211_ATTR_MAX_SCAN_IE_LEN,
+ NL80211_ATTR_CIPHER_SUITES,
+
+ NL80211_ATTR_FREQ_BEFORE,
+ NL80211_ATTR_FREQ_AFTER,
+
+ NL80211_ATTR_FREQ_FIXED,
+
+
+ NL80211_ATTR_WIPHY_RETRY_SHORT,
+ NL80211_ATTR_WIPHY_RETRY_LONG,
+ NL80211_ATTR_WIPHY_FRAG_THRESHOLD,
+ NL80211_ATTR_WIPHY_RTS_THRESHOLD,
+
+ NL80211_ATTR_TIMED_OUT,
+
+ NL80211_ATTR_USE_MFP,
+
+ NL80211_ATTR_STA_FLAGS2,
+
+ NL80211_ATTR_CONTROL_PORT,
+
+ NL80211_ATTR_TESTDATA,
+
+ NL80211_ATTR_PRIVACY,
+
+ NL80211_ATTR_DISCONNECTED_BY_AP,
+ NL80211_ATTR_STATUS_CODE,
+
+ NL80211_ATTR_CIPHER_SUITES_PAIRWISE,
+ NL80211_ATTR_CIPHER_SUITE_GROUP,
+ NL80211_ATTR_WPA_VERSIONS,
+ NL80211_ATTR_AKM_SUITES,
+
+ NL80211_ATTR_REQ_IE,
+ NL80211_ATTR_RESP_IE,
+
+ NL80211_ATTR_PREV_BSSID,
+
+ NL80211_ATTR_KEY,
+ NL80211_ATTR_KEYS,
+
+ NL80211_ATTR_PID,
+
+ NL80211_ATTR_4ADDR,
+
+ NL80211_ATTR_SURVEY_INFO,
+
+ NL80211_ATTR_PMKID,
+ NL80211_ATTR_MAX_NUM_PMKIDS,
+
+ NL80211_ATTR_DURATION,
+
+ NL80211_ATTR_COOKIE,
+
+ NL80211_ATTR_WIPHY_COVERAGE_CLASS,
+
+ NL80211_ATTR_TX_RATES,
+
+ NL80211_ATTR_FRAME_MATCH,
+
+ NL80211_ATTR_ACK,
+
+ NL80211_ATTR_PS_STATE,
+
+ NL80211_ATTR_CQM,
+
+ NL80211_ATTR_LOCAL_STATE_CHANGE,
+
+ NL80211_ATTR_AP_ISOLATE,
+
+ NL80211_ATTR_WIPHY_TX_POWER_SETTING,
+ NL80211_ATTR_WIPHY_TX_POWER_LEVEL,
+
+ NL80211_ATTR_TX_FRAME_TYPES,
+ NL80211_ATTR_RX_FRAME_TYPES,
+ NL80211_ATTR_FRAME_TYPE,
+
+ NL80211_ATTR_CONTROL_PORT_ETHERTYPE,
+ NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT,
+
+ NL80211_ATTR_SUPPORT_IBSS_RSN,
+
+ NL80211_ATTR_WIPHY_ANTENNA_TX,
+ NL80211_ATTR_WIPHY_ANTENNA_RX,
+
+ NL80211_ATTR_MCAST_RATE,
+
+ NL80211_ATTR_OFFCHANNEL_TX_OK,
+
+ NL80211_ATTR_BSS_HT_OPMODE,
+
+ NL80211_ATTR_KEY_DEFAULT_TYPES,
+
+ NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION,
+
+ NL80211_ATTR_MESH_SETUP,
+
+ NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX,
+ NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX,
+
+ NL80211_ATTR_SUPPORT_MESH_AUTH,
+ NL80211_ATTR_STA_PLINK_STATE,
+
+ NL80211_ATTR_WOWLAN_TRIGGERS,
+ NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED,
+
+ NL80211_ATTR_SCHED_SCAN_INTERVAL,
+
+ NL80211_ATTR_INTERFACE_COMBINATIONS,
+ NL80211_ATTR_SOFTWARE_IFTYPES,
+
+ NL80211_ATTR_REKEY_DATA,
+
+ NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS,
+ NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN,
+
+ NL80211_ATTR_SCAN_SUPP_RATES,
+
+ NL80211_ATTR_HIDDEN_SSID,
+
+ NL80211_ATTR_IE_PROBE_RESP,
+ NL80211_ATTR_IE_ASSOC_RESP,
+
+ NL80211_ATTR_STA_WME,
+ NL80211_ATTR_SUPPORT_AP_UAPSD,
+
+ NL80211_ATTR_ROAM_SUPPORT,
+
+ NL80211_ATTR_SCHED_SCAN_MATCH,
+ NL80211_ATTR_MAX_MATCH_SETS,
+
+ NL80211_ATTR_PMKSA_CANDIDATE,
+
+ NL80211_ATTR_TX_NO_CCK_RATE,
+
+ NL80211_ATTR_TDLS_ACTION,
+ NL80211_ATTR_TDLS_DIALOG_TOKEN,
+ NL80211_ATTR_TDLS_OPERATION,
+ NL80211_ATTR_TDLS_SUPPORT,
+ NL80211_ATTR_TDLS_EXTERNAL_SETUP,
+
+ NL80211_ATTR_DEVICE_AP_SME,
+
+ NL80211_ATTR_DONT_WAIT_FOR_ACK,
+
+ NL80211_ATTR_FEATURE_FLAGS,
+
+ NL80211_ATTR_PROBE_RESP_OFFLOAD,
+
+ NL80211_ATTR_PROBE_RESP,
+
+ NL80211_ATTR_DFS_REGION,
+
+ NL80211_ATTR_DISABLE_HT,
+ NL80211_ATTR_HT_CAPABILITY_MASK,
+
+ NL80211_ATTR_NOACK_MAP,
+
+ NL80211_ATTR_INACTIVITY_TIMEOUT,
+
+ NL80211_ATTR_RX_SIGNAL_DBM,
+
+ NL80211_ATTR_BG_SCAN_PERIOD,
+
+ NL80211_ATTR_WDEV,
+
+ NL80211_ATTR_USER_REG_HINT_TYPE,
+
+ NL80211_ATTR_CONN_FAILED_REASON,
+
+ NL80211_ATTR_AUTH_DATA,
+
+ NL80211_ATTR_VHT_CAPABILITY,
+
+ NL80211_ATTR_SCAN_FLAGS,
+
+ NL80211_ATTR_CHANNEL_WIDTH,
+ NL80211_ATTR_CENTER_FREQ1,
+ NL80211_ATTR_CENTER_FREQ2,
+
+ NL80211_ATTR_P2P_CTWINDOW,
+ NL80211_ATTR_P2P_OPPPS,
+
+ NL80211_ATTR_LOCAL_MESH_POWER_MODE,
+
+ NL80211_ATTR_ACL_POLICY,
+
+ NL80211_ATTR_MAC_ADDRS,
+
+ NL80211_ATTR_MAC_ACL_MAX,
+
+ NL80211_ATTR_RADAR_EVENT,
+
+ NL80211_ATTR_EXT_CAPA,
+ NL80211_ATTR_EXT_CAPA_MASK,
+
+ NL80211_ATTR_STA_CAPABILITY,
+ NL80211_ATTR_STA_EXT_CAPABILITY,
+
+ NL80211_ATTR_PROTOCOL_FEATURES,
+ NL80211_ATTR_SPLIT_WIPHY_DUMP,
+
+ NL80211_ATTR_DISABLE_VHT,
+ NL80211_ATTR_VHT_CAPABILITY_MASK,
+
+ NL80211_ATTR_MDID,
+ NL80211_ATTR_IE_RIC,
+
+ NL80211_ATTR_CRIT_PROT_ID,
+ NL80211_ATTR_MAX_CRIT_PROT_DURATION,
+
+ NL80211_ATTR_PEER_AID,
+
+ NL80211_ATTR_COALESCE_RULE,
+
+ NL80211_ATTR_CH_SWITCH_COUNT,
+ NL80211_ATTR_CH_SWITCH_BLOCK_TX,
+ NL80211_ATTR_CSA_IES,
+ NL80211_ATTR_CSA_C_OFF_BEACON,
+ NL80211_ATTR_CSA_C_OFF_PRESP,
+
+ NL80211_ATTR_RXMGMT_FLAGS,
+
+ NL80211_ATTR_STA_SUPPORTED_CHANNELS,
+
+ NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES,
+
+ NL80211_ATTR_HANDLE_DFS,
+
+ NL80211_ATTR_SUPPORT_5_MHZ,
+ NL80211_ATTR_SUPPORT_10_MHZ,
+
+ NL80211_ATTR_OPMODE_NOTIF,
+
+ NL80211_ATTR_VENDOR_ID,
+ NL80211_ATTR_VENDOR_SUBCMD,
+ NL80211_ATTR_VENDOR_DATA,
+ NL80211_ATTR_VENDOR_EVENTS,
+
+ NL80211_ATTR_QOS_MAP,
+
+ NL80211_ATTR_MAC_HINT,
+ NL80211_ATTR_WIPHY_FREQ_HINT,
+
+ NL80211_ATTR_MAX_AP_ASSOC_STA,
+
+ NL80211_ATTR_TDLS_PEER_CAPABILITY,
+
+ NL80211_ATTR_SOCKET_OWNER,
+
+ NL80211_ATTR_CSA_C_OFFSETS_TX,
+ NL80211_ATTR_MAX_CSA_COUNTERS,
+
+ NL80211_ATTR_TDLS_INITIATOR,
+
+ NL80211_ATTR_USE_RRM,
+
+ NL80211_ATTR_WIPHY_DYN_ACK,
+
+ NL80211_ATTR_TSID,
+ NL80211_ATTR_USER_PRIO,
+ NL80211_ATTR_ADMITTED_TIME,
+
+ NL80211_ATTR_SMPS_MODE,
+
+ NL80211_ATTR_OPER_CLASS,
+
+ NL80211_ATTR_MAC_MASK,
+
+ NL80211_ATTR_WIPHY_SELF_MANAGED_REG,
+
+ NL80211_ATTR_EXT_FEATURES,
+
+ NL80211_ATTR_SURVEY_RADIO_STATS,
+
+ NL80211_ATTR_NETNS_FD,
+
+ NL80211_ATTR_SCHED_SCAN_DELAY,
+
+ NL80211_ATTR_REG_INDOOR,
+
+ NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS,
+ NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL,
+ NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS,
+ NL80211_ATTR_SCHED_SCAN_PLANS,
+
+ NL80211_ATTR_PBSS,
+
+ NL80211_ATTR_BSS_SELECT,
+
+ NL80211_ATTR_STA_SUPPORT_P2P_PS,
+
+ NL80211_ATTR_PAD,
+
+ NL80211_ATTR_IFTYPE_EXT_CAPA,
+
+ NL80211_ATTR_MU_MIMO_GROUP_DATA,
+ NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR,
+
+ NL80211_ATTR_SCAN_START_TIME_TSF,
+ NL80211_ATTR_SCAN_START_TIME_TSF_BSSID,
+ NL80211_ATTR_MEASUREMENT_DURATION,
+ NL80211_ATTR_MEASUREMENT_DURATION_MANDATORY,
+
+ NL80211_ATTR_MESH_PEER_AID,
+
+ NL80211_ATTR_NAN_MASTER_PREF,
+ NL80211_ATTR_BANDS,
+ NL80211_ATTR_NAN_FUNC,
+ NL80211_ATTR_NAN_MATCH,
+
+ NL80211_ATTR_FILS_KEK,
+ NL80211_ATTR_FILS_NONCES,
+
+ NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED,
+
+ NL80211_ATTR_BSSID,
+
+ NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI,
+ NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST,
+
+ NL80211_ATTR_TIMEOUT_REASON,
+
+ NL80211_ATTR_FILS_ERP_USERNAME,
+ NL80211_ATTR_FILS_ERP_REALM,
+ NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM,
+ NL80211_ATTR_FILS_ERP_RRK,
+ NL80211_ATTR_FILS_CACHE_ID,
+
+ NL80211_ATTR_PMK,
+
+ NL80211_ATTR_SCHED_SCAN_MULTI,
+ NL80211_ATTR_SCHED_SCAN_MAX_REQS,
+
+ NL80211_ATTR_WANT_1X_4WAY_HS,
+ NL80211_ATTR_PMKR0_NAME,
+
+ /* add attributes here, update the policy in nl80211.c */
+
+ /* Nest private attributes start here */
+ NL80211_ATTR_KLVDATA = 300,
+ NL80211_ATTR_KLV_TYPE,
+ NL80211_ATTR_KLV_INTVL,
+ NL80211_ATTR_KLV_INDEX,
+ NL80211_ATTR_KLV_TRIG,
+ NL80211_ATTR_KLV_PAYLOAD,
+
+ __NL80211_ATTR_AFTER_LAST,
+ NUM_NL80211_ATTR = __NL80211_ATTR_AFTER_LAST,
+ NL80211_ATTR_MAX = __NL80211_ATTR_AFTER_LAST - 1
+};
+
+/* source-level API compatibility */
+#define NL80211_ATTR_SCAN_GENERATION NL80211_ATTR_GENERATION
+#define NL80211_ATTR_MESH_PARAMS NL80211_ATTR_MESH_CONFIG
+#define NL80211_ATTR_IFACE_SOCKET_OWNER NL80211_ATTR_SOCKET_OWNER
+#define NL80211_ATTR_SAE_DATA NL80211_ATTR_AUTH_DATA
+
+/*
+ * Allow user space programs to use #ifdef on new attributes by defining them
+ * here
+ */
+#define NL80211_CMD_CONNECT NL80211_CMD_CONNECT
+#define NL80211_ATTR_HT_CAPABILITY NL80211_ATTR_HT_CAPABILITY
+#define NL80211_ATTR_BSS_BASIC_RATES NL80211_ATTR_BSS_BASIC_RATES
+#define NL80211_ATTR_WIPHY_TXQ_PARAMS NL80211_ATTR_WIPHY_TXQ_PARAMS
+#define NL80211_ATTR_WIPHY_FREQ NL80211_ATTR_WIPHY_FREQ
+#define NL80211_ATTR_WIPHY_CHANNEL_TYPE NL80211_ATTR_WIPHY_CHANNEL_TYPE
+#define NL80211_ATTR_MGMT_SUBTYPE NL80211_ATTR_MGMT_SUBTYPE
+#define NL80211_ATTR_IE NL80211_ATTR_IE
+#define NL80211_ATTR_REG_INITIATOR NL80211_ATTR_REG_INITIATOR
+#define NL80211_ATTR_REG_TYPE NL80211_ATTR_REG_TYPE
+#define NL80211_ATTR_FRAME NL80211_ATTR_FRAME
+#define NL80211_ATTR_SSID NL80211_ATTR_SSID
+#define NL80211_ATTR_AUTH_TYPE NL80211_ATTR_AUTH_TYPE
+#define NL80211_ATTR_REASON_CODE NL80211_ATTR_REASON_CODE
+#define NL80211_ATTR_CIPHER_SUITES_PAIRWISE NL80211_ATTR_CIPHER_SUITES_PAIRWISE
+#define NL80211_ATTR_CIPHER_SUITE_GROUP NL80211_ATTR_CIPHER_SUITE_GROUP
+#define NL80211_ATTR_WPA_VERSIONS NL80211_ATTR_WPA_VERSIONS
+#define NL80211_ATTR_AKM_SUITES NL80211_ATTR_AKM_SUITES
+#define NL80211_ATTR_KEY NL80211_ATTR_KEY
+#define NL80211_ATTR_KEYS NL80211_ATTR_KEYS
+#define NL80211_ATTR_FEATURE_FLAGS NL80211_ATTR_FEATURE_FLAGS
+
+#define NL80211_MAX_SUPP_RATES 32
+#define NL80211_MAX_SUPP_HT_RATES 77
+#define NL80211_MAX_SUPP_REG_RULES 64
+#define NL80211_TKIP_DATA_OFFSET_ENCR_KEY 0
+#define NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY 16
+#define NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY 24
+#define NL80211_HT_CAPABILITY_LEN 26
+#define NL80211_VHT_CAPABILITY_LEN 12
+
+#define NL80211_MAX_NR_CIPHER_SUITES 5
+#define NL80211_MAX_NR_AKM_SUITES 2
+
+#define NL80211_MIN_REMAIN_ON_CHANNEL_TIME 10
+
+/* default RSSI threshold for scan results if none specified. */
+#define NL80211_SCAN_RSSI_THOLD_OFF -300
+
+#define NL80211_CQM_TXE_MAX_INTVL 1800
+
+/**
+ * enum nl80211_iftype - (virtual) interface types
+ *
+ * @NL80211_IFTYPE_UNSPECIFIED: unspecified type, driver decides
+ * @NL80211_IFTYPE_ADHOC: independent BSS member
+ * @NL80211_IFTYPE_STATION: managed BSS member
+ * @NL80211_IFTYPE_AP: access point
+ * @NL80211_IFTYPE_AP_VLAN: VLAN interface for access points; VLAN interfaces
+ * are a bit special in that they must always be tied to a pre-existing
+ * AP type interface.
+ * @NL80211_IFTYPE_WDS: wireless distribution interface
+ * @NL80211_IFTYPE_MONITOR: monitor interface receiving all frames
+ * @NL80211_IFTYPE_MESH_POINT: mesh point
+ * @NL80211_IFTYPE_P2P_CLIENT: P2P client
+ * @NL80211_IFTYPE_P2P_GO: P2P group owner
+ * @NL80211_IFTYPE_P2P_DEVICE: P2P device interface type, this is not a netdev
+ * and therefore can't be created in the normal ways, use the
+ * %NL80211_CMD_START_P2P_DEVICE and %NL80211_CMD_STOP_P2P_DEVICE
+ * commands to create and destroy one
+ * @NL80211_IF_TYPE_OCB: Outside Context of a BSS
+ * This mode corresponds to the MIB variable dot11OCBActivated=true
+ * @NL80211_IFTYPE_NAN: NAN device interface type (not a netdev)
+ * @NL80211_IFTYPE_MAX: highest interface type number currently defined
+ * @NUM_NL80211_IFTYPES: number of defined interface types
+ *
+ * These values are used with the %NL80211_ATTR_IFTYPE
+ * to set the type of an interface.
+ *
+ */
+enum nl80211_iftype {
+ NL80211_IFTYPE_UNSPECIFIED,
+ NL80211_IFTYPE_ADHOC,
+ NL80211_IFTYPE_STATION,
+ NL80211_IFTYPE_AP,
+ NL80211_IFTYPE_AP_VLAN,
+ NL80211_IFTYPE_WDS,
+ NL80211_IFTYPE_MONITOR,
+ NL80211_IFTYPE_MESH_POINT,
+ NL80211_IFTYPE_P2P_CLIENT,
+ NL80211_IFTYPE_P2P_GO,
+ NL80211_IFTYPE_P2P_DEVICE,
+ NL80211_IFTYPE_OCB,
+ NL80211_IFTYPE_NAN,
+
+ /* keep last */
+ NUM_NL80211_IFTYPES,
+ NL80211_IFTYPE_MAX = NUM_NL80211_IFTYPES - 1
+};
+
+/**
+ * enum nl80211_sta_flags - station flags
+ *
+ * Station flags. When a station is added to an AP interface, it is
+ * assumed to be already associated (and hence authenticated.)
+ *
+ * @__NL80211_STA_FLAG_INVALID: attribute number 0 is reserved
+ * @NL80211_STA_FLAG_AUTHORIZED: station is authorized (802.1X)
+ * @NL80211_STA_FLAG_SHORT_PREAMBLE: station is capable of receiving frames
+ * with short barker preamble
+ * @NL80211_STA_FLAG_WME: station is WME/QoS capable
+ * @NL80211_STA_FLAG_MFP: station uses management frame protection
+ * @NL80211_STA_FLAG_AUTHENTICATED: station is authenticated
+ * @NL80211_STA_FLAG_TDLS_PEER: station is a TDLS peer -- this flag should
+ * only be used in managed mode (even in the flags mask). Note that the
+ * flag can't be changed, it is only valid while adding a station, and
+ * attempts to change it will silently be ignored (rather than rejected
+ * as errors.)
+ * @NL80211_STA_FLAG_ASSOCIATED: station is associated; used with drivers
+ * that support %NL80211_FEATURE_FULL_AP_CLIENT_STATE to transition a
+ * previously added station into associated state
+ * @NL80211_STA_FLAG_MAX: highest station flag number currently defined
+ * @__NL80211_STA_FLAG_AFTER_LAST: internal use
+ */
+enum nl80211_sta_flags {
+ __NL80211_STA_FLAG_INVALID,
+ NL80211_STA_FLAG_AUTHORIZED,
+ NL80211_STA_FLAG_SHORT_PREAMBLE,
+ NL80211_STA_FLAG_WME,
+ NL80211_STA_FLAG_MFP,
+ NL80211_STA_FLAG_AUTHENTICATED,
+ NL80211_STA_FLAG_TDLS_PEER,
+ NL80211_STA_FLAG_ASSOCIATED,
+
+ /* keep last */
+ __NL80211_STA_FLAG_AFTER_LAST,
+ NL80211_STA_FLAG_MAX = __NL80211_STA_FLAG_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_sta_p2p_ps_status - station support of P2P PS
+ *
+ * @NL80211_P2P_PS_UNSUPPORTED: station doesn't support P2P PS mechanism
+ * @@NL80211_P2P_PS_SUPPORTED: station supports P2P PS mechanism
+ * @NUM_NL80211_P2P_PS_STATUS: number of values
+ */
+enum nl80211_sta_p2p_ps_status {
+ NL80211_P2P_PS_UNSUPPORTED = 0,
+ NL80211_P2P_PS_SUPPORTED,
+
+ NUM_NL80211_P2P_PS_STATUS,
+};
+
+#define NL80211_STA_FLAG_MAX_OLD_API NL80211_STA_FLAG_TDLS_PEER
+
+/**
+ * struct nl80211_sta_flag_update - station flags mask/set
+ * @mask: mask of station flags to set
+ * @set: which values to set them to
+ *
+ * Both mask and set contain bits as per &enum nl80211_sta_flags.
+ */
+struct nl80211_sta_flag_update {
+ __u32 mask;
+ __u32 set;
+} __attribute__((packed));
+
+/**
+ * enum nl80211_rate_info - bitrate information
+ *
+ * These attribute types are used with %NL80211_STA_INFO_TXRATE
+ * when getting information about the bitrate of a station.
+ * There are 2 attributes for bitrate, a legacy one that represents
+ * a 16-bit value, and new one that represents a 32-bit value.
+ * If the rate value fits into 16 bit, both attributes are reported
+ * with the same value. If the rate is too high to fit into 16 bits
+ * (>6.5535Gbps) only 32-bit attribute is included.
+ * User space tools encouraged to use the 32-bit attribute and fall
+ * back to the 16-bit one for compatibility with older kernels.
+ *
+ * @__NL80211_RATE_INFO_INVALID: attribute number 0 is reserved
+ * @NL80211_RATE_INFO_BITRATE: total bitrate (u16, 100kbit/s)
+ * @NL80211_RATE_INFO_MCS: mcs index for 802.11n (u8)
+ * @NL80211_RATE_INFO_40_MHZ_WIDTH: 40 MHz dualchannel bitrate
+ * @NL80211_RATE_INFO_SHORT_GI: 400ns guard interval
+ * @NL80211_RATE_INFO_BITRATE32: total bitrate (u32, 100kbit/s)
+ * @NL80211_RATE_INFO_MAX: highest rate_info number currently defined
+ * @NL80211_RATE_INFO_VHT_MCS: MCS index for VHT (u8)
+ * @NL80211_RATE_INFO_VHT_NSS: number of streams in VHT (u8)
+ * @NL80211_RATE_INFO_80_MHZ_WIDTH: 80 MHz VHT rate
+ * @NL80211_RATE_INFO_80P80_MHZ_WIDTH: unused - 80+80 is treated the
+ * same as 160 for purposes of the bitrates
+ * @NL80211_RATE_INFO_160_MHZ_WIDTH: 160 MHz VHT rate
+ * @NL80211_RATE_INFO_10_MHZ_WIDTH: 10 MHz width - note that this is
+ * a legacy rate and will be reported as the actual bitrate, i.e.
+ * half the base (20 MHz) rate
+ * @NL80211_RATE_INFO_5_MHZ_WIDTH: 5 MHz width - note that this is
+ * a legacy rate and will be reported as the actual bitrate, i.e.
+ * a quarter of the base (20 MHz) rate
+ * @__NL80211_RATE_INFO_AFTER_LAST: internal use
+ */
+enum nl80211_rate_info {
+ __NL80211_RATE_INFO_INVALID,
+ NL80211_RATE_INFO_BITRATE,
+ NL80211_RATE_INFO_MCS,
+ NL80211_RATE_INFO_40_MHZ_WIDTH,
+ NL80211_RATE_INFO_SHORT_GI,
+ NL80211_RATE_INFO_BITRATE32,
+ NL80211_RATE_INFO_VHT_MCS,
+ NL80211_RATE_INFO_VHT_NSS,
+ NL80211_RATE_INFO_80_MHZ_WIDTH,
+ NL80211_RATE_INFO_80P80_MHZ_WIDTH,
+ NL80211_RATE_INFO_160_MHZ_WIDTH,
+ NL80211_RATE_INFO_10_MHZ_WIDTH,
+ NL80211_RATE_INFO_5_MHZ_WIDTH,
+
+ /* keep last */
+ __NL80211_RATE_INFO_AFTER_LAST,
+ NL80211_RATE_INFO_MAX = __NL80211_RATE_INFO_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_sta_bss_param - BSS information collected by STA
+ *
+ * These attribute types are used with %NL80211_STA_INFO_BSS_PARAM
+ * when getting information about the bitrate of a station.
+ *
+ * @__NL80211_STA_BSS_PARAM_INVALID: attribute number 0 is reserved
+ * @NL80211_STA_BSS_PARAM_CTS_PROT: whether CTS protection is enabled (flag)
+ * @NL80211_STA_BSS_PARAM_SHORT_PREAMBLE: whether short preamble is enabled
+ * (flag)
+ * @NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME: whether short slot time is enabled
+ * (flag)
+ * @NL80211_STA_BSS_PARAM_DTIM_PERIOD: DTIM period for beaconing (u8)
+ * @NL80211_STA_BSS_PARAM_BEACON_INTERVAL: Beacon interval (u16)
+ * @NL80211_STA_BSS_PARAM_MAX: highest sta_bss_param number currently defined
+ * @__NL80211_STA_BSS_PARAM_AFTER_LAST: internal use
+ */
+enum nl80211_sta_bss_param {
+ __NL80211_STA_BSS_PARAM_INVALID,
+ NL80211_STA_BSS_PARAM_CTS_PROT,
+ NL80211_STA_BSS_PARAM_SHORT_PREAMBLE,
+ NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME,
+ NL80211_STA_BSS_PARAM_DTIM_PERIOD,
+ NL80211_STA_BSS_PARAM_BEACON_INTERVAL,
+
+ /* keep last */
+ __NL80211_STA_BSS_PARAM_AFTER_LAST,
+ NL80211_STA_BSS_PARAM_MAX = __NL80211_STA_BSS_PARAM_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_sta_info - station information
+ *
+ * These attribute types are used with %NL80211_ATTR_STA_INFO
+ * when getting information about a station.
+ *
+ * @__NL80211_STA_INFO_INVALID: attribute number 0 is reserved
+ * @NL80211_STA_INFO_INACTIVE_TIME: time since last activity (u32, msecs)
+ * @NL80211_STA_INFO_RX_BYTES: total received bytes (MPDU length)
+ * (u32, from this station)
+ * @NL80211_STA_INFO_TX_BYTES: total transmitted bytes (MPDU length)
+ * (u32, to this station)
+ * @NL80211_STA_INFO_RX_BYTES64: total received bytes (MPDU length)
+ * (u64, from this station)
+ * @NL80211_STA_INFO_TX_BYTES64: total transmitted bytes (MPDU length)
+ * (u64, to this station)
+ * @NL80211_STA_INFO_SIGNAL: signal strength of last received PPDU (u8, dBm)
+ * @NL80211_STA_INFO_TX_BITRATE: current unicast tx rate, nested attribute
+ * containing info as possible, see &enum nl80211_rate_info
+ * @NL80211_STA_INFO_RX_PACKETS: total received packet (MSDUs and MMPDUs)
+ * (u32, from this station)
+ * @NL80211_STA_INFO_TX_PACKETS: total transmitted packets (MSDUs and MMPDUs)
+ * (u32, to this station)
+ * @NL80211_STA_INFO_TX_RETRIES: total retries (MPDUs) (u32, to this station)
+ * @NL80211_STA_INFO_TX_FAILED: total failed packets (MPDUs)
+ * (u32, to this station)
+ * @NL80211_STA_INFO_SIGNAL_AVG: signal strength average (u8, dBm)
+ * @NL80211_STA_INFO_LLID: the station's mesh LLID
+ * @NL80211_STA_INFO_PLID: the station's mesh PLID
+ * @NL80211_STA_INFO_PLINK_STATE: peer link state for the station
+ * (see %enum nl80211_plink_state)
+ * @NL80211_STA_INFO_RX_BITRATE: last unicast data frame rx rate, nested
+ * attribute, like NL80211_STA_INFO_TX_BITRATE.
+ * @NL80211_STA_INFO_BSS_PARAM: current station's view of BSS, nested attribute
+ * containing info as possible, see &enum nl80211_sta_bss_param
+ * @NL80211_STA_INFO_CONNECTED_TIME: time since the station is last connected
+ * @NL80211_STA_INFO_STA_FLAGS: Contains a struct nl80211_sta_flag_update.
+ * @NL80211_STA_INFO_BEACON_LOSS: count of times beacon loss was detected (u32)
+ * @NL80211_STA_INFO_T_OFFSET: timing offset with respect to this STA (s64)
+ * @NL80211_STA_INFO_LOCAL_PM: local mesh STA link-specific power mode
+ * @NL80211_STA_INFO_PEER_PM: peer mesh STA link-specific power mode
+ * @NL80211_STA_INFO_NONPEER_PM: neighbor mesh STA power save mode towards
+ * non-peer STA
+ * @NL80211_STA_INFO_CHAIN_SIGNAL: per-chain signal strength of last PPDU
+ * Contains a nested array of signal strength attributes (u8, dBm)
+ * @NL80211_STA_INFO_CHAIN_SIGNAL_AVG: per-chain signal strength average
+ * Same format as NL80211_STA_INFO_CHAIN_SIGNAL.
+ * @NL80211_STA_EXPECTED_THROUGHPUT: expected throughput considering also the
+ * 802.11 header (u32, kbps)
+ * @NL80211_STA_INFO_RX_DROP_MISC: RX packets dropped for unspecified reasons
+ * (u64)
+ * @NL80211_STA_INFO_BEACON_RX: number of beacons received from this peer (u64)
+ * @NL80211_STA_INFO_BEACON_SIGNAL_AVG: signal strength average
+ * for beacons only (u8, dBm)
+ * @NL80211_STA_INFO_TID_STATS: per-TID statistics (see &enum nl80211_tid_stats)
+ * This is a nested attribute where each the inner attribute number is the
+ * TID+1 and the special TID 16 (i.e. value 17) is used for non-QoS frames;
+ * each one of those is again nested with &enum nl80211_tid_stats
+ * attributes carrying the actual values.
+ * @NL80211_STA_INFO_RX_DURATION: aggregate PPDU duration for all frames
+ * received from the station (u64, usec)
+ * @NL80211_STA_INFO_PAD: attribute used for padding for 64-bit alignment
+ * @__NL80211_STA_INFO_AFTER_LAST: internal
+ * @NL80211_STA_INFO_MAX: highest possible station info attribute
+ */
+enum nl80211_sta_info {
+ __NL80211_STA_INFO_INVALID,
+ NL80211_STA_INFO_INACTIVE_TIME,
+ NL80211_STA_INFO_RX_BYTES,
+ NL80211_STA_INFO_TX_BYTES,
+ NL80211_STA_INFO_LLID,
+ NL80211_STA_INFO_PLID,
+ NL80211_STA_INFO_PLINK_STATE,
+ NL80211_STA_INFO_SIGNAL,
+ NL80211_STA_INFO_TX_BITRATE,
+ NL80211_STA_INFO_RX_PACKETS,
+ NL80211_STA_INFO_TX_PACKETS,
+ NL80211_STA_INFO_TX_RETRIES,
+ NL80211_STA_INFO_TX_FAILED,
+ NL80211_STA_INFO_SIGNAL_AVG,
+ NL80211_STA_INFO_RX_BITRATE,
+ NL80211_STA_INFO_BSS_PARAM,
+ NL80211_STA_INFO_CONNECTED_TIME,
+ NL80211_STA_INFO_STA_FLAGS,
+ NL80211_STA_INFO_BEACON_LOSS,
+ NL80211_STA_INFO_T_OFFSET,
+ NL80211_STA_INFO_LOCAL_PM,
+ NL80211_STA_INFO_PEER_PM,
+ NL80211_STA_INFO_NONPEER_PM,
+ NL80211_STA_INFO_RX_BYTES64,
+ NL80211_STA_INFO_TX_BYTES64,
+ NL80211_STA_INFO_CHAIN_SIGNAL,
+ NL80211_STA_INFO_CHAIN_SIGNAL_AVG,
+ NL80211_STA_INFO_EXPECTED_THROUGHPUT,
+ NL80211_STA_INFO_RX_DROP_MISC,
+ NL80211_STA_INFO_BEACON_RX,
+ NL80211_STA_INFO_BEACON_SIGNAL_AVG,
+ NL80211_STA_INFO_TID_STATS,
+ NL80211_STA_INFO_RX_DURATION,
+ NL80211_STA_INFO_PAD,
+
+ /* keep last */
+ __NL80211_STA_INFO_AFTER_LAST,
+ NL80211_STA_INFO_MAX = __NL80211_STA_INFO_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_tid_stats - per TID statistics attributes
+ * @__NL80211_TID_STATS_INVALID: attribute number 0 is reserved
+ * @NL80211_TID_STATS_RX_MSDU: number of MSDUs received (u64)
+ * @NL80211_TID_STATS_TX_MSDU: number of MSDUs transmitted (or
+ * attempted to transmit; u64)
+ * @NL80211_TID_STATS_TX_MSDU_RETRIES: number of retries for
+ * transmitted MSDUs (not counting the first attempt; u64)
+ * @NL80211_TID_STATS_TX_MSDU_FAILED: number of failed transmitted
+ * MSDUs (u64)
+ * @NL80211_TID_STATS_PAD: attribute used for padding for 64-bit alignment
+ * @NUM_NL80211_TID_STATS: number of attributes here
+ * @NL80211_TID_STATS_MAX: highest numbered attribute here
+ */
+enum nl80211_tid_stats {
+ __NL80211_TID_STATS_INVALID,
+ NL80211_TID_STATS_RX_MSDU,
+ NL80211_TID_STATS_TX_MSDU,
+ NL80211_TID_STATS_TX_MSDU_RETRIES,
+ NL80211_TID_STATS_TX_MSDU_FAILED,
+ NL80211_TID_STATS_PAD,
+
+ /* keep last */
+ NUM_NL80211_TID_STATS,
+ NL80211_TID_STATS_MAX = NUM_NL80211_TID_STATS - 1
+};
+
+/**
+ * enum nl80211_mpath_flags - nl80211 mesh path flags
+ *
+ * @NL80211_MPATH_FLAG_ACTIVE: the mesh path is active
+ * @NL80211_MPATH_FLAG_RESOLVING: the mesh path discovery process is running
+ * @NL80211_MPATH_FLAG_SN_VALID: the mesh path contains a valid SN
+ * @NL80211_MPATH_FLAG_FIXED: the mesh path has been manually set
+ * @NL80211_MPATH_FLAG_RESOLVED: the mesh path discovery process succeeded
+ */
+enum nl80211_mpath_flags {
+ NL80211_MPATH_FLAG_ACTIVE = 1<<0,
+ NL80211_MPATH_FLAG_RESOLVING = 1<<1,
+ NL80211_MPATH_FLAG_SN_VALID = 1<<2,
+ NL80211_MPATH_FLAG_FIXED = 1<<3,
+ NL80211_MPATH_FLAG_RESOLVED = 1<<4,
+};
+
+/**
+ * enum nl80211_mpath_info - mesh path information
+ *
+ * These attribute types are used with %NL80211_ATTR_MPATH_INFO when getting
+ * information about a mesh path.
+ *
+ * @__NL80211_MPATH_INFO_INVALID: attribute number 0 is reserved
+ * @NL80211_MPATH_INFO_FRAME_QLEN: number of queued frames for this destination
+ * @NL80211_MPATH_INFO_SN: destination sequence number
+ * @NL80211_MPATH_INFO_METRIC: metric (cost) of this mesh path
+ * @NL80211_MPATH_INFO_EXPTIME: expiration time for the path, in msec from now
+ * @NL80211_MPATH_INFO_FLAGS: mesh path flags, enumerated in
+ * &enum nl80211_mpath_flags;
+ * @NL80211_MPATH_INFO_DISCOVERY_TIMEOUT: total path discovery timeout, in msec
+ * @NL80211_MPATH_INFO_DISCOVERY_RETRIES: mesh path discovery retries
+ * @NL80211_MPATH_INFO_MAX: highest mesh path information attribute number
+ * currently defind
+ * @__NL80211_MPATH_INFO_AFTER_LAST: internal use
+ */
+enum nl80211_mpath_info {
+ __NL80211_MPATH_INFO_INVALID,
+ NL80211_MPATH_INFO_FRAME_QLEN,
+ NL80211_MPATH_INFO_SN,
+ NL80211_MPATH_INFO_METRIC,
+ NL80211_MPATH_INFO_EXPTIME,
+ NL80211_MPATH_INFO_FLAGS,
+ NL80211_MPATH_INFO_DISCOVERY_TIMEOUT,
+ NL80211_MPATH_INFO_DISCOVERY_RETRIES,
+
+ /* keep last */
+ __NL80211_MPATH_INFO_AFTER_LAST,
+ NL80211_MPATH_INFO_MAX = __NL80211_MPATH_INFO_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_band_attr - band attributes
+ * @__NL80211_BAND_ATTR_INVALID: attribute number 0 is reserved
+ * @NL80211_BAND_ATTR_FREQS: supported frequencies in this band,
+ * an array of nested frequency attributes
+ * @NL80211_BAND_ATTR_RATES: supported bitrates in this band,
+ * an array of nested bitrate attributes
+ * @NL80211_BAND_ATTR_HT_MCS_SET: 16-byte attribute containing the MCS set as
+ * defined in 802.11n
+ * @NL80211_BAND_ATTR_HT_CAPA: HT capabilities, as in the HT information IE
+ * @NL80211_BAND_ATTR_HT_AMPDU_FACTOR: A-MPDU factor, as in 11n
+ * @NL80211_BAND_ATTR_HT_AMPDU_DENSITY: A-MPDU density, as in 11n
+ * @NL80211_BAND_ATTR_VHT_MCS_SET: 32-byte attribute containing the MCS set as
+ * defined in 802.11ac
+ * @NL80211_BAND_ATTR_VHT_CAPA: VHT capabilities, as in the HT information IE
+ * @NL80211_BAND_ATTR_MAX: highest band attribute currently defined
+ * @__NL80211_BAND_ATTR_AFTER_LAST: internal use
+ */
+enum nl80211_band_attr {
+ __NL80211_BAND_ATTR_INVALID,
+ NL80211_BAND_ATTR_FREQS,
+ NL80211_BAND_ATTR_RATES,
+
+ NL80211_BAND_ATTR_HT_MCS_SET,
+ NL80211_BAND_ATTR_HT_CAPA,
+ NL80211_BAND_ATTR_HT_AMPDU_FACTOR,
+ NL80211_BAND_ATTR_HT_AMPDU_DENSITY,
+
+ NL80211_BAND_ATTR_VHT_MCS_SET,
+ NL80211_BAND_ATTR_VHT_CAPA,
+
+ /* keep last */
+ __NL80211_BAND_ATTR_AFTER_LAST,
+ NL80211_BAND_ATTR_MAX = __NL80211_BAND_ATTR_AFTER_LAST - 1
+};
+
+#define NL80211_BAND_ATTR_HT_CAPA NL80211_BAND_ATTR_HT_CAPA
+
+/**
+ * enum nl80211_frequency_attr - frequency attributes
+ * @__NL80211_FREQUENCY_ATTR_INVALID: attribute number 0 is reserved
+ * @NL80211_FREQUENCY_ATTR_FREQ: Frequency in MHz
+ * @NL80211_FREQUENCY_ATTR_DISABLED: Channel is disabled in current
+ * regulatory domain.
+ * @NL80211_FREQUENCY_ATTR_NO_IR: no mechanisms that initiate radiation
+ * are permitted on this channel, this includes sending probe
+ * requests, or modes of operation that require beaconing.
+ * @NL80211_FREQUENCY_ATTR_RADAR: Radar detection is mandatory
+ * on this channel in current regulatory domain.
+ * @NL80211_FREQUENCY_ATTR_MAX_TX_POWER: Maximum transmission power in mBm
+ * (100 * dBm).
+ * @NL80211_FREQUENCY_ATTR_DFS_STATE: current state for DFS
+ * (enum nl80211_dfs_state)
+ * @NL80211_FREQUENCY_ATTR_DFS_TIME: time in miliseconds for how long
+ * this channel is in this DFS state.
+ * @NL80211_FREQUENCY_ATTR_NO_HT40_MINUS: HT40- isn't possible with this
+ * channel as the control channel
+ * @NL80211_FREQUENCY_ATTR_NO_HT40_PLUS: HT40+ isn't possible with this
+ * channel as the control channel
+ * @NL80211_FREQUENCY_ATTR_NO_80MHZ: any 80 MHz channel using this channel
+ * as the primary or any of the secondary channels isn't possible,
+ * this includes 80+80 channels
+ * @NL80211_FREQUENCY_ATTR_NO_160MHZ: any 160 MHz (but not 80+80) channel
+ * using this channel as the primary or any of the secondary channels
+ * isn't possible
+ * @NL80211_FREQUENCY_ATTR_DFS_CAC_TIME: DFS CAC time in milliseconds.
+ * @NL80211_FREQUENCY_ATTR_INDOOR_ONLY: Only indoor use is permitted on this
+ * channel. A channel that has the INDOOR_ONLY attribute can only be
+ * used when there is a clear assessment that the device is operating in
+ * an indoor surroundings, i.e., it is connected to AC power (and not
+ * through portable DC inverters) or is under the control of a master
+ * that is acting as an AP and is connected to AC power.
+ * @NL80211_FREQUENCY_ATTR_IR_CONCURRENT: IR operation is allowed on this
+ * channel if it's connected concurrently to a BSS on the same channel on
+ * the 2 GHz band or to a channel in the same UNII band (on the 5 GHz
+ * band), and IEEE80211_CHAN_RADAR is not set. Instantiating a GO or TDLS
+ * off-channel on a channel that has the IR_CONCURRENT attribute set can be
+ * done when there is a clear assessment that the device is operating under
+ * the guidance of an authorized master, i.e., setting up a GO or TDLS
+ * off-channel while the device is also connected to an AP with DFS and
+ * radar detection on the UNII band (it is up to user-space, i.e.,
+ * wpa_supplicant to perform the required verifications). Using this
+ * attribute for IR is disallowed for master interfaces (IBSS, AP).
+ * @NL80211_FREQUENCY_ATTR_NO_20MHZ: 20 MHz operation is not allowed
+ * on this channel in current regulatory domain.
+ * @NL80211_FREQUENCY_ATTR_NO_10MHZ: 10 MHz operation is not allowed
+ * on this channel in current regulatory domain.
+ * @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number
+ * currently defined
+ * @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use
+ *
+ * See https://apps.fcc.gov/eas/comments/GetPublishedDocument.html?id=327&tn=528122
+ * for more information on the FCC description of the relaxations allowed
+ * by NL80211_FREQUENCY_ATTR_INDOOR_ONLY and
+ * NL80211_FREQUENCY_ATTR_IR_CONCURRENT.
+ */
+enum nl80211_frequency_attr {
+ __NL80211_FREQUENCY_ATTR_INVALID,
+ NL80211_FREQUENCY_ATTR_FREQ,
+ NL80211_FREQUENCY_ATTR_DISABLED,
+ NL80211_FREQUENCY_ATTR_NO_IR,
+ __NL80211_FREQUENCY_ATTR_NO_IBSS,
+ NL80211_FREQUENCY_ATTR_RADAR,
+ NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
+ NL80211_FREQUENCY_ATTR_DFS_STATE,
+ NL80211_FREQUENCY_ATTR_DFS_TIME,
+ NL80211_FREQUENCY_ATTR_NO_HT40_MINUS,
+ NL80211_FREQUENCY_ATTR_NO_HT40_PLUS,
+ NL80211_FREQUENCY_ATTR_NO_80MHZ,
+ NL80211_FREQUENCY_ATTR_NO_160MHZ,
+ NL80211_FREQUENCY_ATTR_DFS_CAC_TIME,
+ NL80211_FREQUENCY_ATTR_INDOOR_ONLY,
+ NL80211_FREQUENCY_ATTR_IR_CONCURRENT,
+ NL80211_FREQUENCY_ATTR_NO_20MHZ,
+ NL80211_FREQUENCY_ATTR_NO_10MHZ,
+
+ /* keep last */
+ __NL80211_FREQUENCY_ATTR_AFTER_LAST,
+ NL80211_FREQUENCY_ATTR_MAX = __NL80211_FREQUENCY_ATTR_AFTER_LAST - 1
+};
+
+#define NL80211_FREQUENCY_ATTR_MAX_TX_POWER NL80211_FREQUENCY_ATTR_MAX_TX_POWER
+#define NL80211_FREQUENCY_ATTR_PASSIVE_SCAN NL80211_FREQUENCY_ATTR_NO_IR
+#define NL80211_FREQUENCY_ATTR_NO_IBSS NL80211_FREQUENCY_ATTR_NO_IR
+#define NL80211_FREQUENCY_ATTR_NO_IR NL80211_FREQUENCY_ATTR_NO_IR
+#define NL80211_FREQUENCY_ATTR_GO_CONCURRENT \
+ NL80211_FREQUENCY_ATTR_IR_CONCURRENT
+
+/**
+ * enum nl80211_bitrate_attr - bitrate attributes
+ * @__NL80211_BITRATE_ATTR_INVALID: attribute number 0 is reserved
+ * @NL80211_BITRATE_ATTR_RATE: Bitrate in units of 100 kbps
+ * @NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE: Short preamble supported
+ * in 2.4 GHz band.
+ * @NL80211_BITRATE_ATTR_MAX: highest bitrate attribute number
+ * currently defined
+ * @__NL80211_BITRATE_ATTR_AFTER_LAST: internal use
+ */
+enum nl80211_bitrate_attr {
+ __NL80211_BITRATE_ATTR_INVALID,
+ NL80211_BITRATE_ATTR_RATE,
+ NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE,
+
+ /* keep last */
+ __NL80211_BITRATE_ATTR_AFTER_LAST,
+ NL80211_BITRATE_ATTR_MAX = __NL80211_BITRATE_ATTR_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_initiator - Indicates the initiator of a reg domain request
+ * @NL80211_REGDOM_SET_BY_CORE: Core queried CRDA for a dynamic world
+ * regulatory domain.
+ * @NL80211_REGDOM_SET_BY_USER: User asked the wireless core to set the
+ * regulatory domain.
+ * @NL80211_REGDOM_SET_BY_DRIVER: a wireless drivers has hinted to the
+ * wireless core it thinks its knows the regulatory domain we should be in.
+ * @NL80211_REGDOM_SET_BY_COUNTRY_IE: the wireless core has received an
+ * 802.11 country information element with regulatory information it
+ * thinks we should consider. cfg80211 only processes the country
+ * code from the IE, and relies on the regulatory domain information
+ * structure passed by userspace (CRDA) from our wireless-regdb.
+ * If a channel is enabled but the country code indicates it should
+ * be disabled we disable the channel and re-enable it upon disassociation.
+ */
+enum nl80211_reg_initiator {
+ NL80211_REGDOM_SET_BY_CORE,
+ NL80211_REGDOM_SET_BY_USER,
+ NL80211_REGDOM_SET_BY_DRIVER,
+ NL80211_REGDOM_SET_BY_COUNTRY_IE,
+};
+
+/**
+ * enum nl80211_reg_type - specifies the type of regulatory domain
+ * @NL80211_REGDOM_TYPE_COUNTRY: the regulatory domain set is one that pertains
+ * to a specific country. When this is set you can count on the
+ * ISO / IEC 3166 alpha2 country code being valid.
+ * @NL80211_REGDOM_TYPE_WORLD: the regulatory set domain is the world regulatory
+ * domain.
+ * @NL80211_REGDOM_TYPE_CUSTOM_WORLD: the regulatory domain set is a custom
+ * driver specific world regulatory domain. These do not apply system-wide
+ * and are only applicable to the individual devices which have requested
+ * them to be applied.
+ * @NL80211_REGDOM_TYPE_INTERSECTION: the regulatory domain set is the product
+ * of an intersection between two regulatory domains -- the previously
+ * set regulatory domain on the system and the last accepted regulatory
+ * domain request to be processed.
+ */
+enum nl80211_reg_type {
+ NL80211_REGDOM_TYPE_COUNTRY,
+ NL80211_REGDOM_TYPE_WORLD,
+ NL80211_REGDOM_TYPE_CUSTOM_WORLD,
+ NL80211_REGDOM_TYPE_INTERSECTION,
+};
+
+/**
+ * enum nl80211_reg_rule_attr - regulatory rule attributes
+ * @__NL80211_REG_RULE_ATTR_INVALID: attribute number 0 is reserved
+ * @NL80211_ATTR_REG_RULE_FLAGS: a set of flags which specify additional
+ * considerations for a given frequency range. These are the
+ * &enum nl80211_reg_rule_flags.
+ * @NL80211_ATTR_FREQ_RANGE_START: starting frequencry for the regulatory
+ * rule in KHz. This is not a center of frequency but an actual regulatory
+ * band edge.
+ * @NL80211_ATTR_FREQ_RANGE_END: ending frequency for the regulatory rule
+ * in KHz. This is not a center a frequency but an actual regulatory
+ * band edge.
+ * @NL80211_ATTR_FREQ_RANGE_MAX_BW: maximum allowed bandwidth for this
+ * frequency range, in KHz.
+ * @NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN: the maximum allowed antenna gain
+ * for a given frequency range. The value is in mBi (100 * dBi).
+ * If you don't have one then don't send this.
+ * @NL80211_ATTR_POWER_RULE_MAX_EIRP: the maximum allowed EIRP for
+ * a given frequency range. The value is in mBm (100 * dBm).
+ * @NL80211_ATTR_DFS_CAC_TIME: DFS CAC time in milliseconds.
+ * If not present or 0 default CAC time will be used.
+ * @NL80211_REG_RULE_ATTR_MAX: highest regulatory rule attribute number
+ * currently defined
+ * @__NL80211_REG_RULE_ATTR_AFTER_LAST: internal use
+ */
+enum nl80211_reg_rule_attr {
+ __NL80211_REG_RULE_ATTR_INVALID,
+ NL80211_ATTR_REG_RULE_FLAGS,
+
+ NL80211_ATTR_FREQ_RANGE_START,
+ NL80211_ATTR_FREQ_RANGE_END,
+ NL80211_ATTR_FREQ_RANGE_MAX_BW,
+
+ NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN,
+ NL80211_ATTR_POWER_RULE_MAX_EIRP,
+
+ NL80211_ATTR_DFS_CAC_TIME,
+
+ /* keep last */
+ __NL80211_REG_RULE_ATTR_AFTER_LAST,
+ NL80211_REG_RULE_ATTR_MAX = __NL80211_REG_RULE_ATTR_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_sched_scan_match_attr - scheduled scan match attributes
+ * @__NL80211_SCHED_SCAN_MATCH_ATTR_INVALID: attribute number 0 is reserved
+ * @NL80211_SCHED_SCAN_MATCH_ATTR_SSID: SSID to be used for matching,
+ * only report BSS with matching SSID.
+ * (This cannot be used together with BSSID.)
+ * @NL80211_SCHED_SCAN_MATCH_ATTR_RSSI: RSSI threshold (in dBm) for reporting a
+ * BSS in scan results. Filtering is turned off if not specified. Note that
+ * if this attribute is in a match set of its own, then it is treated as
+ * the default value for all matchsets with an SSID, rather than being a
+ * matchset of its own without an RSSI filter. This is due to problems with
+ * how this API was implemented in the past. Also, due to the same problem,
+ * the only way to create a matchset with only an RSSI filter (with this
+ * attribute) is if there's only a single matchset with the RSSI attribute.
+ * @NL80211_SCHED_SCAN_MATCH_ATTR_RELATIVE_RSSI: Flag indicating whether
+ * %NL80211_SCHED_SCAN_MATCH_ATTR_RSSI to be used as absolute RSSI or
+ * relative to current bss's RSSI.
+ * @NL80211_SCHED_SCAN_MATCH_ATTR_RSSI_ADJUST: When present the RSSI level for
+ * BSS-es in the specified band is to be adjusted before doing
+ * RSSI-based BSS selection. The attribute value is a packed structure
+ * value as specified by &struct nl80211_bss_select_rssi_adjust.
+ * @NL80211_SCHED_SCAN_MATCH_ATTR_BSSID: BSSID to be used for matching
+ * (this cannot be used together with SSID).
+ * @NL80211_SCHED_SCAN_MATCH_ATTR_MAX: highest scheduled scan filter
+ * attribute number currently defined
+ * @__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST: internal use
+ */
+enum nl80211_sched_scan_match_attr {
+ __NL80211_SCHED_SCAN_MATCH_ATTR_INVALID,
+
+ NL80211_SCHED_SCAN_MATCH_ATTR_SSID,
+ NL80211_SCHED_SCAN_MATCH_ATTR_RSSI,
+ NL80211_SCHED_SCAN_MATCH_ATTR_RELATIVE_RSSI,
+ NL80211_SCHED_SCAN_MATCH_ATTR_RSSI_ADJUST,
+ NL80211_SCHED_SCAN_MATCH_ATTR_BSSID,
+
+ /* keep last */
+ __NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST,
+ NL80211_SCHED_SCAN_MATCH_ATTR_MAX =
+ __NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST - 1
+};
+
+/* only for backward compatibility */
+#define NL80211_ATTR_SCHED_SCAN_MATCH_SSID NL80211_SCHED_SCAN_MATCH_ATTR_SSID
+
+/**
+ * enum nl80211_reg_rule_flags - regulatory rule flags
+ *
+ * @NL80211_RRF_NO_OFDM: OFDM modulation not allowed
+ * @NL80211_RRF_NO_CCK: CCK modulation not allowed
+ * @NL80211_RRF_NO_INDOOR: indoor operation not allowed
+ * @NL80211_RRF_NO_OUTDOOR: outdoor operation not allowed
+ * @NL80211_RRF_DFS: DFS support is required to be used
+ * @NL80211_RRF_PTP_ONLY: this is only for Point To Point links
+ * @NL80211_RRF_PTMP_ONLY: this is only for Point To Multi Point links
+ * @NL80211_RRF_NO_IR: no mechanisms that initiate radiation are allowed,
+ * this includes probe requests or modes of operation that require
+ * beaconing.
+ * @NL80211_RRF_AUTO_BW: maximum available bandwidth should be calculated
+ * base on contiguous rules and wider channels will be allowed to cross
+ * multiple contiguous/overlapping frequency ranges.
+ * @NL80211_RRF_IR_CONCURRENT: See &NL80211_FREQUENCY_ATTR_IR_CONCURRENT
+ * @NL80211_RRF_NO_HT40MINUS: channels can't be used in HT40- operation
+ * @NL80211_RRF_NO_HT40PLUS: channels can't be used in HT40+ operation
+ * @NL80211_RRF_NO_80MHZ: 80MHz operation not allowed
+ * @NL80211_RRF_NO_160MHZ: 160MHz operation not allowed
+ */
+enum nl80211_reg_rule_flags {
+ NL80211_RRF_NO_OFDM = 1<<0,
+ NL80211_RRF_NO_CCK = 1<<1,
+ NL80211_RRF_NO_INDOOR = 1<<2,
+ NL80211_RRF_NO_OUTDOOR = 1<<3,
+ NL80211_RRF_DFS = 1<<4,
+ NL80211_RRF_PTP_ONLY = 1<<5,
+ NL80211_RRF_PTMP_ONLY = 1<<6,
+ NL80211_RRF_NO_IR = 1<<7,
+ __NL80211_RRF_NO_IBSS = 1<<8,
+ NL80211_RRF_AUTO_BW = 1<<11,
+ NL80211_RRF_IR_CONCURRENT = 1<<12,
+ NL80211_RRF_NO_HT40MINUS = 1<<13,
+ NL80211_RRF_NO_HT40PLUS = 1<<14,
+ NL80211_RRF_NO_80MHZ = 1<<15,
+ NL80211_RRF_NO_160MHZ = 1<<16,
+};
+
+#define NL80211_RRF_PASSIVE_SCAN NL80211_RRF_NO_IR
+#define NL80211_RRF_NO_IBSS NL80211_RRF_NO_IR
+#define NL80211_RRF_NO_IR NL80211_RRF_NO_IR
+#define NL80211_RRF_NO_HT40 (NL80211_RRF_NO_HT40MINUS |\
+ NL80211_RRF_NO_HT40PLUS)
+#define NL80211_RRF_GO_CONCURRENT NL80211_RRF_IR_CONCURRENT
+
+/* For backport compatibility with older userspace */
+#define NL80211_RRF_NO_IR_ALL (NL80211_RRF_NO_IR | __NL80211_RRF_NO_IBSS)
+
+/**
+ * enum nl80211_dfs_regions - regulatory DFS regions
+ *
+ * @NL80211_DFS_UNSET: Country has no DFS master region specified
+ * @NL80211_DFS_FCC: Country follows DFS master rules from FCC
+ * @NL80211_DFS_ETSI: Country follows DFS master rules from ETSI
+ * @NL80211_DFS_JP: Country follows DFS master rules from JP/MKK/Telec
+ */
+enum nl80211_dfs_regions {
+ NL80211_DFS_UNSET = 0,
+ NL80211_DFS_FCC = 1,
+ NL80211_DFS_ETSI = 2,
+ NL80211_DFS_JP = 3,
+};
+
+/**
+ * enum nl80211_user_reg_hint_type - type of user regulatory hint
+ *
+ * @NL80211_USER_REG_HINT_USER: a user sent the hint. This is always
+ * assumed if the attribute is not set.
+ * @NL80211_USER_REG_HINT_CELL_BASE: the hint comes from a cellular
+ * base station. Device drivers that have been tested to work
+ * properly to support this type of hint can enable these hints
+ * by setting the NL80211_FEATURE_CELL_BASE_REG_HINTS feature
+ * capability on the struct wiphy. The wireless core will
+ * ignore all cell base station hints until at least one device
+ * present has been registered with the wireless core that
+ * has listed NL80211_FEATURE_CELL_BASE_REG_HINTS as a
+ * supported feature.
+ * @NL80211_USER_REG_HINT_INDOOR: a user sent an hint indicating that the
+ * platform is operating in an indoor environment.
+ */
+enum nl80211_user_reg_hint_type {
+ NL80211_USER_REG_HINT_USER = 0,
+ NL80211_USER_REG_HINT_CELL_BASE = 1,
+ NL80211_USER_REG_HINT_INDOOR = 2,
+};
+
+/**
+ * enum nl80211_survey_info - survey information
+ *
+ * These attribute types are used with %NL80211_ATTR_SURVEY_INFO
+ * when getting information about a survey.
+ *
+ * @__NL80211_SURVEY_INFO_INVALID: attribute number 0 is reserved
+ * @NL80211_SURVEY_INFO_FREQUENCY: center frequency of channel
+ * @NL80211_SURVEY_INFO_NOISE: noise level of channel (u8, dBm)
+ * @NL80211_SURVEY_INFO_IN_USE: channel is currently being used
+ * @NL80211_SURVEY_INFO_TIME: amount of time (in ms) that the radio
+ * was turned on (on channel or globally)
+ * @NL80211_SURVEY_INFO_TIME_BUSY: amount of the time the primary
+ * channel was sensed busy (either due to activity or energy detect)
+ * @NL80211_SURVEY_INFO_TIME_EXT_BUSY: amount of time the extension
+ * channel was sensed busy
+ * @NL80211_SURVEY_INFO_TIME_RX: amount of time the radio spent
+ * receiving data (on channel or globally)
+ * @NL80211_SURVEY_INFO_TIME_TX: amount of time the radio spent
+ * transmitting data (on channel or globally)
+ * @NL80211_SURVEY_INFO_TIME_SCAN: time the radio spent for scan
+ * (on this channel or globally)
+ * @NL80211_SURVEY_INFO_PAD: attribute used for padding for 64-bit alignment
+ * @NL80211_SURVEY_INFO_MAX: highest survey info attribute number
+ * currently defined
+ * @__NL80211_SURVEY_INFO_AFTER_LAST: internal use
+ */
+enum nl80211_survey_info {
+ __NL80211_SURVEY_INFO_INVALID,
+ NL80211_SURVEY_INFO_FREQUENCY,
+ NL80211_SURVEY_INFO_NOISE,
+ NL80211_SURVEY_INFO_IN_USE,
+ NL80211_SURVEY_INFO_TIME,
+ NL80211_SURVEY_INFO_TIME_BUSY,
+ NL80211_SURVEY_INFO_TIME_EXT_BUSY,
+ NL80211_SURVEY_INFO_TIME_RX,
+ NL80211_SURVEY_INFO_TIME_TX,
+ NL80211_SURVEY_INFO_TIME_SCAN,
+ NL80211_SURVEY_INFO_PAD,
+
+ /* keep last */
+ __NL80211_SURVEY_INFO_AFTER_LAST,
+ NL80211_SURVEY_INFO_MAX = __NL80211_SURVEY_INFO_AFTER_LAST - 1
+};
+
+/* keep old names for compatibility */
+#define NL80211_SURVEY_INFO_CHANNEL_TIME NL80211_SURVEY_INFO_TIME
+#define NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY NL80211_SURVEY_INFO_TIME_BUSY
+#define NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY NL80211_SURVEY_INFO_TIME_EXT_BUSY
+#define NL80211_SURVEY_INFO_CHANNEL_TIME_RX NL80211_SURVEY_INFO_TIME_RX
+#define NL80211_SURVEY_INFO_CHANNEL_TIME_TX NL80211_SURVEY_INFO_TIME_TX
+
+/**
+ * enum nl80211_mntr_flags - monitor configuration flags
+ *
+ * Monitor configuration flags.
+ *
+ * @__NL80211_MNTR_FLAG_INVALID: reserved
+ *
+ * @NL80211_MNTR_FLAG_FCSFAIL: pass frames with bad FCS
+ * @NL80211_MNTR_FLAG_PLCPFAIL: pass frames with bad PLCP
+ * @NL80211_MNTR_FLAG_CONTROL: pass control frames
+ * @NL80211_MNTR_FLAG_OTHER_BSS: disable BSSID filtering
+ * @NL80211_MNTR_FLAG_COOK_FRAMES: report frames after processing.
+ * overrides all other flags.
+ * @NL80211_MNTR_FLAG_ACTIVE: use the configured MAC address
+ * and ACK incoming unicast packets.
+ *
+ * @__NL80211_MNTR_FLAG_AFTER_LAST: internal use
+ * @NL80211_MNTR_FLAG_MAX: highest possible monitor flag
+ */
+enum nl80211_mntr_flags {
+ __NL80211_MNTR_FLAG_INVALID,
+ NL80211_MNTR_FLAG_FCSFAIL,
+ NL80211_MNTR_FLAG_PLCPFAIL,
+ NL80211_MNTR_FLAG_CONTROL,
+ NL80211_MNTR_FLAG_OTHER_BSS,
+ NL80211_MNTR_FLAG_COOK_FRAMES,
+ NL80211_MNTR_FLAG_ACTIVE,
+
+ /* keep last */
+ __NL80211_MNTR_FLAG_AFTER_LAST,
+ NL80211_MNTR_FLAG_MAX = __NL80211_MNTR_FLAG_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_mesh_power_mode - mesh power save modes
+ *
+ * @NL80211_MESH_POWER_UNKNOWN: The mesh power mode of the mesh STA is
+ * not known or has not been set yet.
+ * @NL80211_MESH_POWER_ACTIVE: Active mesh power mode. The mesh STA is
+ * in Awake state all the time.
+ * @NL80211_MESH_POWER_LIGHT_SLEEP: Light sleep mode. The mesh STA will
+ * alternate between Active and Doze states, but will wake up for
+ * neighbor's beacons.
+ * @NL80211_MESH_POWER_DEEP_SLEEP: Deep sleep mode. The mesh STA will
+ * alternate between Active and Doze states, but may not wake up
+ * for neighbor's beacons.
+ *
+ * @__NL80211_MESH_POWER_AFTER_LAST - internal use
+ * @NL80211_MESH_POWER_MAX - highest possible power save level
+ */
+
+enum nl80211_mesh_power_mode {
+ NL80211_MESH_POWER_UNKNOWN,
+ NL80211_MESH_POWER_ACTIVE,
+ NL80211_MESH_POWER_LIGHT_SLEEP,
+ NL80211_MESH_POWER_DEEP_SLEEP,
+
+ __NL80211_MESH_POWER_AFTER_LAST,
+ NL80211_MESH_POWER_MAX = __NL80211_MESH_POWER_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_meshconf_params - mesh configuration parameters
+ *
+ * Mesh configuration parameters. These can be changed while the mesh is
+ * active.
+ *
+ * @__NL80211_MESHCONF_INVALID: internal use
+ *
+ * @NL80211_MESHCONF_RETRY_TIMEOUT: specifies the initial retry timeout in
+ * millisecond units, used by the Peer Link Open message
+ *
+ * @NL80211_MESHCONF_CONFIRM_TIMEOUT: specifies the initial confirm timeout, in
+ * millisecond units, used by the peer link management to close a peer link
+ *
+ * @NL80211_MESHCONF_HOLDING_TIMEOUT: specifies the holding timeout, in
+ * millisecond units
+ *
+ * @NL80211_MESHCONF_MAX_PEER_LINKS: maximum number of peer links allowed
+ * on this mesh interface
+ *
+ * @NL80211_MESHCONF_MAX_RETRIES: specifies the maximum number of peer link
+ * open retries that can be sent to establish a new peer link instance in a
+ * mesh
+ *
+ * @NL80211_MESHCONF_TTL: specifies the value of TTL field set at a source mesh
+ * point.
+ *
+ * @NL80211_MESHCONF_AUTO_OPEN_PLINKS: whether we should automatically open
+ * peer links when we detect compatible mesh peers. Disabled if
+ * @NL80211_MESH_SETUP_USERSPACE_MPM or @NL80211_MESH_SETUP_USERSPACE_AMPE are
+ * set.
+ *
+ * @NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES: the number of action frames
+ * containing a PREQ that an MP can send to a particular destination (path
+ * target)
+ *
+ * @NL80211_MESHCONF_PATH_REFRESH_TIME: how frequently to refresh mesh paths
+ * (in milliseconds)
+ *
+ * @NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT: minimum length of time to wait
+ * until giving up on a path discovery (in milliseconds)
+ *
+ * @NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT: The time (in TUs) for which mesh
+ * points receiving a PREQ shall consider the forwarding information from
+ * the root to be valid. (TU = time unit)
+ *
+ * @NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL: The minimum interval of time (in
+ * TUs) during which an MP can send only one action frame containing a PREQ
+ * reference element
+ *
+ * @NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME: The interval of time (in TUs)
+ * that it takes for an HWMP information element to propagate across the
+ * mesh
+ *
+ * @NL80211_MESHCONF_HWMP_ROOTMODE: whether root mode is enabled or not
+ *
+ * @NL80211_MESHCONF_ELEMENT_TTL: specifies the value of TTL field set at a
+ * source mesh point for path selection elements.
+ *
+ * @NL80211_MESHCONF_HWMP_RANN_INTERVAL: The interval of time (in TUs) between
+ * root announcements are transmitted.
+ *
+ * @NL80211_MESHCONF_GATE_ANNOUNCEMENTS: Advertise that this mesh station has
+ * access to a broader network beyond the MBSS. This is done via Root
+ * Announcement frames.
+ *
+ * @NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL: The minimum interval of time (in
+ * TUs) during which a mesh STA can send only one Action frame containing a
+ * PERR element.
+ *
+ * @NL80211_MESHCONF_FORWARDING: set Mesh STA as forwarding or non-forwarding
+ * or forwarding entity (default is TRUE - forwarding entity)
+ *
+ * @NL80211_MESHCONF_RSSI_THRESHOLD: RSSI threshold in dBm. This specifies the
+ * threshold for average signal strength of candidate station to establish
+ * a peer link.
+ *
+ * @NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR: maximum number of neighbors
+ * to synchronize to for 11s default synchronization method
+ * (see 11C.12.2.2)
+ *
+ * @NL80211_MESHCONF_HT_OPMODE: set mesh HT protection mode.
+ *
+ * @NL80211_MESHCONF_ATTR_MAX: highest possible mesh configuration attribute
+ *
+ * @NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT: The time (in TUs) for
+ * which mesh STAs receiving a proactive PREQ shall consider the forwarding
+ * information to the root mesh STA to be valid.
+ *
+ * @NL80211_MESHCONF_HWMP_ROOT_INTERVAL: The interval of time (in TUs) between
+ * proactive PREQs are transmitted.
+ *
+ * @NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL: The minimum interval of time
+ * (in TUs) during which a mesh STA can send only one Action frame
+ * containing a PREQ element for root path confirmation.
+ *
+ * @NL80211_MESHCONF_POWER_MODE: Default mesh power mode for new peer links.
+ * type &enum nl80211_mesh_power_mode (u32)
+ *
+ * @NL80211_MESHCONF_AWAKE_WINDOW: awake window duration (in TUs)
+ *
+ * @NL80211_MESHCONF_PLINK_TIMEOUT: If no tx activity is seen from a STA we've
+ * established peering with for longer than this time (in seconds), then
+ * remove it from the STA's list of peers. You may set this to 0 to disable
+ * the removal of the STA. Default is 30 minutes.
+ *
+ * @__NL80211_MESHCONF_ATTR_AFTER_LAST: internal use
+ */
+enum nl80211_meshconf_params {
+ __NL80211_MESHCONF_INVALID,
+ NL80211_MESHCONF_RETRY_TIMEOUT,
+ NL80211_MESHCONF_CONFIRM_TIMEOUT,
+ NL80211_MESHCONF_HOLDING_TIMEOUT,
+ NL80211_MESHCONF_MAX_PEER_LINKS,
+ NL80211_MESHCONF_MAX_RETRIES,
+ NL80211_MESHCONF_TTL,
+ NL80211_MESHCONF_AUTO_OPEN_PLINKS,
+ NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
+ NL80211_MESHCONF_PATH_REFRESH_TIME,
+ NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
+ NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
+ NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
+ NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
+ NL80211_MESHCONF_HWMP_ROOTMODE,
+ NL80211_MESHCONF_ELEMENT_TTL,
+ NL80211_MESHCONF_HWMP_RANN_INTERVAL,
+ NL80211_MESHCONF_GATE_ANNOUNCEMENTS,
+ NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL,
+ NL80211_MESHCONF_FORWARDING,
+ NL80211_MESHCONF_RSSI_THRESHOLD,
+ NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR,
+ NL80211_MESHCONF_HT_OPMODE,
+ NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT,
+ NL80211_MESHCONF_HWMP_ROOT_INTERVAL,
+ NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL,
+ NL80211_MESHCONF_POWER_MODE,
+ NL80211_MESHCONF_AWAKE_WINDOW,
+ NL80211_MESHCONF_PLINK_TIMEOUT,
+
+ /* keep last */
+ __NL80211_MESHCONF_ATTR_AFTER_LAST,
+ NL80211_MESHCONF_ATTR_MAX = __NL80211_MESHCONF_ATTR_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_mesh_setup_params - mesh setup parameters
+ *
+ * Mesh setup parameters. These are used to start/join a mesh and cannot be
+ * changed while the mesh is active.
+ *
+ * @__NL80211_MESH_SETUP_INVALID: Internal use
+ *
+ * @NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL: Enable this option to use a
+ * vendor specific path selection algorithm or disable it to use the
+ * default HWMP.
+ *
+ * @NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC: Enable this option to use a
+ * vendor specific path metric or disable it to use the default Airtime
+ * metric.
+ *
+ * @NL80211_MESH_SETUP_IE: Information elements for this mesh, for instance, a
+ * robust security network ie, or a vendor specific information element
+ * that vendors will use to identify the path selection methods and
+ * metrics in use.
+ *
+ * @NL80211_MESH_SETUP_USERSPACE_AUTH: Enable this option if an authentication
+ * daemon will be authenticating mesh candidates.
+ *
+ * @NL80211_MESH_SETUP_USERSPACE_AMPE: Enable this option if an authentication
+ * daemon will be securing peer link frames. AMPE is a secured version of
+ * Mesh Peering Management (MPM) and is implemented with the assistance of
+ * a userspace daemon. When this flag is set, the kernel will send peer
+ * management frames to a userspace daemon that will implement AMPE
+ * functionality (security capabilities selection, key confirmation, and
+ * key management). When the flag is unset (default), the kernel can
+ * autonomously complete (unsecured) mesh peering without the need of a
+ * userspace daemon.
+ *
+ * @NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC: Enable this option to use a
+ * vendor specific synchronization method or disable it to use the default
+ * neighbor offset synchronization
+ *
+ * @NL80211_MESH_SETUP_USERSPACE_MPM: Enable this option if userspace will
+ * implement an MPM which handles peer allocation and state.
+ *
+ * @NL80211_MESH_SETUP_AUTH_PROTOCOL: Inform the kernel of the authentication
+ * method (u8, as defined in IEEE 8.4.2.100.6, e.g. 0x1 for SAE).
+ * Default is no authentication method required.
+ *
+ * @NL80211_MESH_SETUP_ATTR_MAX: highest possible mesh setup attribute number
+ *
+ * @__NL80211_MESH_SETUP_ATTR_AFTER_LAST: Internal use
+ */
+enum nl80211_mesh_setup_params {
+ __NL80211_MESH_SETUP_INVALID,
+ NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL,
+ NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC,
+ NL80211_MESH_SETUP_IE,
+ NL80211_MESH_SETUP_USERSPACE_AUTH,
+ NL80211_MESH_SETUP_USERSPACE_AMPE,
+ NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC,
+ NL80211_MESH_SETUP_USERSPACE_MPM,
+ NL80211_MESH_SETUP_AUTH_PROTOCOL,
+
+ /* keep last */
+ __NL80211_MESH_SETUP_ATTR_AFTER_LAST,
+ NL80211_MESH_SETUP_ATTR_MAX = __NL80211_MESH_SETUP_ATTR_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_txq_attr - TX queue parameter attributes
+ * @__NL80211_TXQ_ATTR_INVALID: Attribute number 0 is reserved
+ * @NL80211_TXQ_ATTR_AC: AC identifier (NL80211_AC_*)
+ * @NL80211_TXQ_ATTR_TXOP: Maximum burst time in units of 32 usecs, 0 meaning
+ * disabled
+ * @NL80211_TXQ_ATTR_CWMIN: Minimum contention window [a value of the form
+ * 2^n-1 in the range 1..32767]
+ * @NL80211_TXQ_ATTR_CWMAX: Maximum contention window [a value of the form
+ * 2^n-1 in the range 1..32767]
+ * @NL80211_TXQ_ATTR_AIFS: Arbitration interframe space [0..255]
+ * @__NL80211_TXQ_ATTR_AFTER_LAST: Internal
+ * @NL80211_TXQ_ATTR_MAX: Maximum TXQ attribute number
+ */
+enum nl80211_txq_attr {
+ __NL80211_TXQ_ATTR_INVALID,
+ NL80211_TXQ_ATTR_AC,
+ NL80211_TXQ_ATTR_TXOP,
+ NL80211_TXQ_ATTR_CWMIN,
+ NL80211_TXQ_ATTR_CWMAX,
+ NL80211_TXQ_ATTR_AIFS,
+
+ /* keep last */
+ __NL80211_TXQ_ATTR_AFTER_LAST,
+ NL80211_TXQ_ATTR_MAX = __NL80211_TXQ_ATTR_AFTER_LAST - 1
+};
+
+enum nl80211_ac {
+ NL80211_AC_VO,
+ NL80211_AC_VI,
+ NL80211_AC_BE,
+ NL80211_AC_BK,
+ NL80211_NUM_ACS
+};
+
+/* backward compat */
+#define NL80211_TXQ_ATTR_QUEUE NL80211_TXQ_ATTR_AC
+#define NL80211_TXQ_Q_VO NL80211_AC_VO
+#define NL80211_TXQ_Q_VI NL80211_AC_VI
+#define NL80211_TXQ_Q_BE NL80211_AC_BE
+#define NL80211_TXQ_Q_BK NL80211_AC_BK
+
+/**
+ * enum nl80211_channel_type - channel type
+ * @NL80211_CHAN_NO_HT: 20 MHz, non-HT channel
+ * @NL80211_CHAN_HT20: 20 MHz HT channel
+ * @NL80211_CHAN_HT40MINUS: HT40 channel, secondary channel
+ * below the control channel
+ * @NL80211_CHAN_HT40PLUS: HT40 channel, secondary channel
+ * above the control channel
+ */
+enum nl80211_channel_type {
+ NL80211_CHAN_NO_HT,
+ NL80211_CHAN_HT20,
+ NL80211_CHAN_HT40MINUS,
+ NL80211_CHAN_HT40PLUS
+};
+
+/**
+ * enum nl80211_chan_width - channel width definitions
+ *
+ * These values are used with the %NL80211_ATTR_CHANNEL_WIDTH
+ * attribute.
+ *
+ * @NL80211_CHAN_WIDTH_20_NOHT: 20 MHz, non-HT channel
+ * @NL80211_CHAN_WIDTH_20: 20 MHz HT channel
+ * @NL80211_CHAN_WIDTH_40: 40 MHz channel, the %NL80211_ATTR_CENTER_FREQ1
+ * attribute must be provided as well
+ * @NL80211_CHAN_WIDTH_80: 80 MHz channel, the %NL80211_ATTR_CENTER_FREQ1
+ * attribute must be provided as well
+ * @NL80211_CHAN_WIDTH_80P80: 80+80 MHz channel, the %NL80211_ATTR_CENTER_FREQ1
+ * and %NL80211_ATTR_CENTER_FREQ2 attributes must be provided as well
+ * @NL80211_CHAN_WIDTH_160: 160 MHz channel, the %NL80211_ATTR_CENTER_FREQ1
+ * attribute must be provided as well
+ * @NL80211_CHAN_WIDTH_5: 5 MHz OFDM channel
+ * @NL80211_CHAN_WIDTH_10: 10 MHz OFDM channel
+ */
+enum nl80211_chan_width {
+ NL80211_CHAN_WIDTH_20_NOHT,
+ NL80211_CHAN_WIDTH_20,
+ NL80211_CHAN_WIDTH_40,
+ NL80211_CHAN_WIDTH_80,
+ NL80211_CHAN_WIDTH_80P80,
+ NL80211_CHAN_WIDTH_160,
+ NL80211_CHAN_WIDTH_5,
+ NL80211_CHAN_WIDTH_10,
+};
+
+/**
+ * enum nl80211_bss_scan_width - control channel width for a BSS
+ *
+ * These values are used with the %NL80211_BSS_CHAN_WIDTH attribute.
+ *
+ * @NL80211_BSS_CHAN_WIDTH_20: control channel is 20 MHz wide or compatible
+ * @NL80211_BSS_CHAN_WIDTH_10: control channel is 10 MHz wide
+ * @NL80211_BSS_CHAN_WIDTH_5: control channel is 5 MHz wide
+ */
+enum nl80211_bss_scan_width {
+ NL80211_BSS_CHAN_WIDTH_20,
+ NL80211_BSS_CHAN_WIDTH_10,
+ NL80211_BSS_CHAN_WIDTH_5,
+};
+
+/**
+ * enum nl80211_bss - netlink attributes for a BSS
+ *
+ * @__NL80211_BSS_INVALID: invalid
+ * @NL80211_BSS_BSSID: BSSID of the BSS (6 octets)
+ * @NL80211_BSS_FREQUENCY: frequency in MHz (u32)
+ * @NL80211_BSS_TSF: TSF of the received probe response/beacon (u64)
+ * (if @NL80211_BSS_PRESP_DATA is present then this is known to be
+ * from a probe response, otherwise it may be from the same beacon
+ * that the NL80211_BSS_BEACON_TSF will be from)
+ * @NL80211_BSS_BEACON_INTERVAL: beacon interval of the (I)BSS (u16)
+ * @NL80211_BSS_CAPABILITY: capability field (CPU order, u16)
+ * @NL80211_BSS_INFORMATION_ELEMENTS: binary attribute containing the
+ * raw information elements from the probe response/beacon (bin);
+ * if the %NL80211_BSS_BEACON_IES attribute is present and the data is
+ * different then the IEs here are from a Probe Response frame; otherwise
+ * they are from a Beacon frame.
+ * However, if the driver does not indicate the source of the IEs, these
+ * IEs may be from either frame subtype.
+ * If present, the @NL80211_BSS_PRESP_DATA attribute indicates that the
+ * data here is known to be from a probe response, without any heuristics.
+ * @NL80211_BSS_SIGNAL_MBM: signal strength of probe response/beacon
+ * in mBm (100 * dBm) (s32)
+ * @NL80211_BSS_SIGNAL_UNSPEC: signal strength of the probe response/beacon
+ * in unspecified units, scaled to 0..100 (u8)
+ * @NL80211_BSS_STATUS: status, if this BSS is "used"
+ * @NL80211_BSS_SEEN_MS_AGO: age of this BSS entry in ms
+ * @NL80211_BSS_BEACON_IES: binary attribute containing the raw information
+ * elements from a Beacon frame (bin); not present if no Beacon frame has
+ * yet been received
+ * @NL80211_BSS_CHAN_WIDTH: channel width of the control channel
+ * (u32, enum nl80211_bss_scan_width)
+ * @NL80211_BSS_BEACON_TSF: TSF of the last received beacon (u64)
+ * (not present if no beacon frame has been received yet)
+ * @NL80211_BSS_PRESP_DATA: the data in @NL80211_BSS_INFORMATION_ELEMENTS and
+ * @NL80211_BSS_TSF is known to be from a probe response (flag attribute)
+ * @NL80211_BSS_LAST_SEEN_BOOTTIME: CLOCK_BOOTTIME timestamp when this entry
+ * was last updated by a received frame. The value is expected to be
+ * accurate to about 10ms. (u64, nanoseconds)
+ * @NL80211_BSS_PAD: attribute used for padding for 64-bit alignment
+ * @NL80211_BSS_PARENT_TSF: the time at the start of reception of the first
+ * octet of the timestamp field of the last beacon/probe received for
+ * this BSS. The time is the TSF of the BSS specified by
+ * @NL80211_BSS_PARENT_BSSID. (u64).
+ * @NL80211_BSS_PARENT_BSSID: the BSS according to which @NL80211_BSS_PARENT_TSF
+ * is set.
+ * @__NL80211_BSS_AFTER_LAST: internal
+ * @NL80211_BSS_MAX: highest BSS attribute
+ */
+enum nl80211_bss {
+ __NL80211_BSS_INVALID,
+ NL80211_BSS_BSSID,
+ NL80211_BSS_FREQUENCY,
+ NL80211_BSS_TSF,
+ NL80211_BSS_BEACON_INTERVAL,
+ NL80211_BSS_CAPABILITY,
+ NL80211_BSS_INFORMATION_ELEMENTS,
+ NL80211_BSS_SIGNAL_MBM,
+ NL80211_BSS_SIGNAL_UNSPEC,
+ NL80211_BSS_STATUS,
+ NL80211_BSS_SEEN_MS_AGO,
+ NL80211_BSS_BEACON_IES,
+ NL80211_BSS_CHAN_WIDTH,
+ NL80211_BSS_BEACON_TSF,
+ NL80211_BSS_PRESP_DATA,
+ NL80211_BSS_LAST_SEEN_BOOTTIME,
+ NL80211_BSS_PAD,
+ NL80211_BSS_PARENT_TSF,
+ NL80211_BSS_PARENT_BSSID,
+
+ /* keep last */
+ __NL80211_BSS_AFTER_LAST,
+ NL80211_BSS_MAX = __NL80211_BSS_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_bss_status - BSS "status"
+ * @NL80211_BSS_STATUS_AUTHENTICATED: Authenticated with this BSS.
+ * Note that this is no longer used since cfg80211 no longer
+ * keeps track of whether or not authentication was done with
+ * a given BSS.
+ * @NL80211_BSS_STATUS_ASSOCIATED: Associated with this BSS.
+ * @NL80211_BSS_STATUS_IBSS_JOINED: Joined to this IBSS.
+ *
+ * The BSS status is a BSS attribute in scan dumps, which
+ * indicates the status the interface has wrt. this BSS.
+ */
+enum nl80211_bss_status {
+ NL80211_BSS_STATUS_AUTHENTICATED,
+ NL80211_BSS_STATUS_ASSOCIATED,
+ NL80211_BSS_STATUS_IBSS_JOINED,
+};
+
+/**
+ * enum nl80211_auth_type - AuthenticationType
+ *
+ * @NL80211_AUTHTYPE_OPEN_SYSTEM: Open System authentication
+ * @NL80211_AUTHTYPE_SHARED_KEY: Shared Key authentication (WEP only)
+ * @NL80211_AUTHTYPE_FT: Fast BSS Transition (IEEE 802.11r)
+ * @NL80211_AUTHTYPE_NETWORK_EAP: Network EAP (some Cisco APs and mainly LEAP)
+ * @NL80211_AUTHTYPE_SAE: Simultaneous authentication of equals
+ * @NL80211_AUTHTYPE_FILS_SK: Fast Initial Link Setup shared key
+ * @NL80211_AUTHTYPE_FILS_SK_PFS: Fast Initial Link Setup shared key with PFS
+ * @NL80211_AUTHTYPE_FILS_PK: Fast Initial Link Setup public key
+ * @__NL80211_AUTHTYPE_NUM: internal
+ * @NL80211_AUTHTYPE_MAX: maximum valid auth algorithm
+ * @NL80211_AUTHTYPE_AUTOMATIC: determine automatically (if necessary by
+ * trying multiple times); this is invalid in netlink -- leave out
+ * the attribute for this on CONNECT commands.
+ */
+enum nl80211_auth_type {
+ NL80211_AUTHTYPE_OPEN_SYSTEM,
+ NL80211_AUTHTYPE_SHARED_KEY,
+ NL80211_AUTHTYPE_FT,
+ NL80211_AUTHTYPE_NETWORK_EAP,
+ NL80211_AUTHTYPE_SAE,
+ NL80211_AUTHTYPE_FILS_SK,
+ NL80211_AUTHTYPE_FILS_SK_PFS,
+ NL80211_AUTHTYPE_FILS_PK,
+
+ /* keep last */
+ __NL80211_AUTHTYPE_NUM,
+ NL80211_AUTHTYPE_MAX = __NL80211_AUTHTYPE_NUM - 1,
+ NL80211_AUTHTYPE_AUTOMATIC
+};
+
+/**
+ * enum nl80211_key_type - Key Type
+ * @NL80211_KEYTYPE_GROUP: Group (broadcast/multicast) key
+ * @NL80211_KEYTYPE_PAIRWISE: Pairwise (unicast/individual) key
+ * @NL80211_KEYTYPE_PEERKEY: PeerKey (DLS)
+ * @NUM_NL80211_KEYTYPES: number of defined key types
+ */
+enum nl80211_key_type {
+ NL80211_KEYTYPE_GROUP,
+ NL80211_KEYTYPE_PAIRWISE,
+ NL80211_KEYTYPE_PEERKEY,
+
+ NUM_NL80211_KEYTYPES
+};
+
+/**
+ * enum nl80211_mfp - Management frame protection state
+ * @NL80211_MFP_NO: Management frame protection not used
+ * @NL80211_MFP_REQUIRED: Management frame protection required
+ */
+enum nl80211_mfp {
+ NL80211_MFP_NO,
+ NL80211_MFP_REQUIRED,
+};
+
+enum nl80211_wpa_versions {
+ NL80211_WPA_VERSION_1 = 1 << 0,
+ NL80211_WPA_VERSION_2 = 1 << 1,
+};
+
+/**
+ * enum nl80211_key_default_types - key default types
+ * @__NL80211_KEY_DEFAULT_TYPE_INVALID: invalid
+ * @NL80211_KEY_DEFAULT_TYPE_UNICAST: key should be used as default
+ * unicast key
+ * @NL80211_KEY_DEFAULT_TYPE_MULTICAST: key should be used as default
+ * multicast key
+ * @NUM_NL80211_KEY_DEFAULT_TYPES: number of default types
+ */
+enum nl80211_key_default_types {
+ __NL80211_KEY_DEFAULT_TYPE_INVALID,
+ NL80211_KEY_DEFAULT_TYPE_UNICAST,
+ NL80211_KEY_DEFAULT_TYPE_MULTICAST,
+
+ NUM_NL80211_KEY_DEFAULT_TYPES
+};
+
+/**
+ * enum nl80211_key_attributes - key attributes
+ * @__NL80211_KEY_INVALID: invalid
+ * @NL80211_KEY_DATA: (temporal) key data; for TKIP this consists of
+ * 16 bytes encryption key followed by 8 bytes each for TX and RX MIC
+ * keys
+ * @NL80211_KEY_IDX: key ID (u8, 0-3)
+ * @NL80211_KEY_CIPHER: key cipher suite (u32, as defined by IEEE 802.11
+ * section 7.3.2.25.1, e.g. 0x000FAC04)
+ * @NL80211_KEY_SEQ: transmit key sequence number (IV/PN) for TKIP and
+ * CCMP keys, each six bytes in little endian
+ * @NL80211_KEY_DEFAULT: flag indicating default key
+ * @NL80211_KEY_DEFAULT_MGMT: flag indicating default management key
+ * @NL80211_KEY_TYPE: the key type from enum nl80211_key_type, if not
+ * specified the default depends on whether a MAC address was
+ * given with the command using the key or not (u32)
+ * @NL80211_KEY_DEFAULT_TYPES: A nested attribute containing flags
+ * attributes, specifying what a key should be set as default as.
+ * See &enum nl80211_key_default_types.
+ * @__NL80211_KEY_AFTER_LAST: internal
+ * @NL80211_KEY_MAX: highest key attribute
+ */
+enum nl80211_key_attributes {
+ __NL80211_KEY_INVALID,
+ NL80211_KEY_DATA,
+ NL80211_KEY_IDX,
+ NL80211_KEY_CIPHER,
+ NL80211_KEY_SEQ,
+ NL80211_KEY_DEFAULT,
+ NL80211_KEY_DEFAULT_MGMT,
+ NL80211_KEY_TYPE,
+ NL80211_KEY_DEFAULT_TYPES,
+
+ /* keep last */
+ __NL80211_KEY_AFTER_LAST,
+ NL80211_KEY_MAX = __NL80211_KEY_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_tx_rate_attributes - TX rate set attributes
+ * @__NL80211_TXRATE_INVALID: invalid
+ * @NL80211_TXRATE_LEGACY: Legacy (non-MCS) rates allowed for TX rate selection
+ * in an array of rates as defined in IEEE 802.11 7.3.2.2 (u8 values with
+ * 1 = 500 kbps) but without the IE length restriction (at most
+ * %NL80211_MAX_SUPP_RATES in a single array).
+ * @NL80211_TXRATE_HT: HT (MCS) rates allowed for TX rate selection
+ * in an array of MCS numbers.
+ * @NL80211_TXRATE_VHT: VHT rates allowed for TX rate selection,
+ * see &struct nl80211_txrate_vht
+ * @NL80211_TXRATE_GI: configure GI, see &enum nl80211_txrate_gi
+ * @__NL80211_TXRATE_AFTER_LAST: internal
+ * @NL80211_TXRATE_MAX: highest TX rate attribute
+ */
+enum nl80211_tx_rate_attributes {
+ __NL80211_TXRATE_INVALID,
+ NL80211_TXRATE_LEGACY,
+ NL80211_TXRATE_HT,
+ NL80211_TXRATE_VHT,
+ NL80211_TXRATE_GI,
+
+ /* keep last */
+ __NL80211_TXRATE_AFTER_LAST,
+ NL80211_TXRATE_MAX = __NL80211_TXRATE_AFTER_LAST - 1
+};
+
+#define NL80211_TXRATE_MCS NL80211_TXRATE_HT
+#define NL80211_VHT_NSS_MAX 8
+
+/**
+ * struct nl80211_txrate_vht - VHT MCS/NSS txrate bitmap
+ * @mcs: MCS bitmap table for each NSS (array index 0 for 1 stream, etc.)
+ */
+struct nl80211_txrate_vht {
+ __u16 mcs[NL80211_VHT_NSS_MAX];
+};
+
+enum nl80211_txrate_gi {
+ NL80211_TXRATE_DEFAULT_GI,
+ NL80211_TXRATE_FORCE_SGI,
+ NL80211_TXRATE_FORCE_LGI,
+};
+
+/**
+ * enum nl80211_band - Frequency band
+ * @NL80211_BAND_2GHZ: 2.4 GHz ISM band
+ * @NL80211_BAND_5GHZ: around 5 GHz band (4.9 - 5.7 GHz)
+ * @NL80211_BAND_60GHZ: around 60 GHz band (58.32 - 64.80 GHz)
+ * @NUM_NL80211_BANDS: number of bands, avoid using this in userspace
+ * since newer kernel versions may support more bands
+ */
+enum nl80211_band {
+ NL80211_BAND_2GHZ,
+ NL80211_BAND_5GHZ,
+ NL80211_BAND_60GHZ,
+
+ NUM_NL80211_BANDS,
+};
+
+/**
+ * enum nl80211_ps_state - powersave state
+ * @NL80211_PS_DISABLED: powersave is disabled
+ * @NL80211_PS_ENABLED: powersave is enabled
+ */
+enum nl80211_ps_state {
+ NL80211_PS_DISABLED,
+ NL80211_PS_ENABLED,
+};
+
+/**
+ * enum nl80211_attr_cqm - connection quality monitor attributes
+ * @__NL80211_ATTR_CQM_INVALID: invalid
+ * @NL80211_ATTR_CQM_RSSI_THOLD: RSSI threshold in dBm. This value specifies
+ * the threshold for the RSSI level at which an event will be sent. Zero
+ * to disable. Alternatively, if %NL80211_EXT_FEATURE_CQM_RSSI_LIST is
+ * set, multiple values can be supplied as a low-to-high sorted array of
+ * threshold values in dBm. Events will be sent when the RSSI value
+ * crosses any of the thresholds.
+ * @NL80211_ATTR_CQM_RSSI_HYST: RSSI hysteresis in dBm. This value specifies
+ * the minimum amount the RSSI level must change after an event before a
+ * new event may be issued (to reduce effects of RSSI oscillation).
+ * @NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT: RSSI threshold event
+ * @NL80211_ATTR_CQM_PKT_LOSS_EVENT: a u32 value indicating that this many
+ * consecutive packets were not acknowledged by the peer
+ * @NL80211_ATTR_CQM_TXE_RATE: TX error rate in %. Minimum % of TX failures
+ * during the given %NL80211_ATTR_CQM_TXE_INTVL before an
+ * %NL80211_CMD_NOTIFY_CQM with reported %NL80211_ATTR_CQM_TXE_RATE and
+ * %NL80211_ATTR_CQM_TXE_PKTS is generated.
+ * @NL80211_ATTR_CQM_TXE_PKTS: number of attempted packets in a given
+ * %NL80211_ATTR_CQM_TXE_INTVL before %NL80211_ATTR_CQM_TXE_RATE is
+ * checked.
+ * @NL80211_ATTR_CQM_TXE_INTVL: interval in seconds. Specifies the periodic
+ * interval in which %NL80211_ATTR_CQM_TXE_PKTS and
+ * %NL80211_ATTR_CQM_TXE_RATE must be satisfied before generating an
+ * %NL80211_CMD_NOTIFY_CQM. Set to 0 to turn off TX error reporting.
+ * @NL80211_ATTR_CQM_BEACON_LOSS_EVENT: flag attribute that's set in a beacon
+ * loss event
+ * @NL80211_ATTR_CQM_RSSI_LEVEL: the RSSI value in dBm that triggered the
+ * RSSI threshold event.
+ * @__NL80211_ATTR_CQM_AFTER_LAST: internal
+ * @NL80211_ATTR_CQM_MAX: highest key attribute
+ */
+enum nl80211_attr_cqm {
+ __NL80211_ATTR_CQM_INVALID,
+ NL80211_ATTR_CQM_RSSI_THOLD,
+ NL80211_ATTR_CQM_RSSI_HYST,
+ NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT,
+ NL80211_ATTR_CQM_PKT_LOSS_EVENT,
+ NL80211_ATTR_CQM_TXE_RATE,
+ NL80211_ATTR_CQM_TXE_PKTS,
+ NL80211_ATTR_CQM_TXE_INTVL,
+ NL80211_ATTR_CQM_BEACON_LOSS_EVENT,
+ NL80211_ATTR_CQM_RSSI_LEVEL,
+
+ /* keep last */
+ __NL80211_ATTR_CQM_AFTER_LAST,
+ NL80211_ATTR_CQM_MAX = __NL80211_ATTR_CQM_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_cqm_rssi_threshold_event - RSSI threshold event
+ * @NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW: The RSSI level is lower than the
+ * configured threshold
+ * @NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH: The RSSI is higher than the
+ * configured threshold
+ * @NL80211_CQM_RSSI_BEACON_LOSS_EVENT: (reserved, never sent)
+ */
+enum nl80211_cqm_rssi_threshold_event {
+ NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
+ NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
+ NL80211_CQM_RSSI_BEACON_LOSS_EVENT,
+};
+
+
+/**
+ * enum nl80211_tx_power_setting - TX power adjustment
+ * @NL80211_TX_POWER_AUTOMATIC: automatically determine transmit power
+ * @NL80211_TX_POWER_LIMITED: limit TX power by the mBm parameter
+ * @NL80211_TX_POWER_FIXED: fix TX power to the mBm parameter
+ */
+enum nl80211_tx_power_setting {
+ NL80211_TX_POWER_AUTOMATIC,
+ NL80211_TX_POWER_LIMITED,
+ NL80211_TX_POWER_FIXED,
+};
+
+/**
+ * enum nl80211_packet_pattern_attr - packet pattern attribute
+ * @__NL80211_PKTPAT_INVALID: invalid number for nested attribute
+ * @NL80211_PKTPAT_PATTERN: the pattern, values where the mask has
+ * a zero bit are ignored
+ * @NL80211_PKTPAT_MASK: pattern mask, must be long enough to have
+ * a bit for each byte in the pattern. The lowest-order bit corresponds
+ * to the first byte of the pattern, but the bytes of the pattern are
+ * in a little-endian-like format, i.e. the 9th byte of the pattern
+ * corresponds to the lowest-order bit in the second byte of the mask.
+ * For example: The match 00:xx:00:00:xx:00:00:00:00:xx:xx:xx (where
+ * xx indicates "don't care") would be represented by a pattern of
+ * twelve zero bytes, and a mask of "0xed,0x01".
+ * Note that the pattern matching is done as though frames were not
+ * 802.11 frames but 802.3 frames, i.e. the frame is fully unpacked
+ * first (including SNAP header unpacking) and then matched.
+ * @NL80211_PKTPAT_OFFSET: packet offset, pattern is matched after
+ * these fixed number of bytes of received packet
+ * @NL80211_ACTION: pattern action which can be either to wake up
+ * on this pattern or drop it and avoid wake up. This can be used to
+ * specify an excpetion/blacklist pattern that shouldn't cause wakeup
+ * despite the packet matching another wowlan pattern. For example:
+ * configure all IPv4 multicast to wake up except certain type of packets
+ * This can be either NL80211_WOWLAN_ACTION_ALLOW or DROP.
+ * If this attribute is missing the default would be ALLOW.
+ * @NUM_NL80211_PKTPAT: number of attributes
+ * @MAX_NL80211_PKTPAT: max attribute number
+ */
+enum nl80211_packet_pattern_attr {
+ __NL80211_PKTPAT_INVALID,
+ NL80211_PKTPAT_MASK,
+ NL80211_PKTPAT_PATTERN,
+ NL80211_PKTPAT_OFFSET,
+ NL80211_PKTPAT_ACTION = NL80211_PKTPAT_PATTERN + 10,
+
+ NUM_NL80211_PKTPAT,
+ MAX_NL80211_PKTPAT = NUM_NL80211_PKTPAT - 1,
+};
+
+
+/**
+ * enum nl80211_wowlan_action - WoWLAN packet pattern action
+ * @NL80211_WOWLAN_ACTION_ALLOW: this pattern should wake up the host
+ * and the packet should be forwarded to the host unless this packet
+ * matches a DROP rule.
+ * @NL80211_WOWLAN_ACTION_DROP: a packet containing this pattern shouldn't
+ * wake up the host.
+ */
+enum nl80211_wowlan_action {
+ NL80211_WOWLAN_ACTION_ALLOW,
+ NL80211_WOWLAN_ACTION_DROP,
+
+ /* keep last */
+ NUM_NL80211_WOWLAN_ACTION,
+ MAX_NL80211_WOWLAN_ACTION = NUM_NL80211_WOWLAN_ACTION - 1,
+};
+
+/**
+ * struct nl80211_pattern_support - packet pattern support information
+ * @max_patterns: maximum number of patterns supported
+ * @min_pattern_len: minimum length of each pattern
+ * @max_pattern_len: maximum length of each pattern
+ * @max_pkt_offset: maximum Rx packet offset
+ *
+ * This struct is carried in %NL80211_WOWLAN_TRIG_PKT_PATTERN when
+ * that is part of %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED or in
+ * %NL80211_ATTR_COALESCE_RULE_PKT_PATTERN when that is part of
+ * %NL80211_ATTR_COALESCE_RULE in the capability information given
+ * by the kernel to userspace.
+ */
+struct nl80211_pattern_support {
+ __u32 max_patterns;
+ __u32 min_pattern_len;
+ __u32 max_pattern_len;
+ __u32 max_pkt_offset;
+} __attribute__((packed));
+
+/* only for backward compatibility */
+#define __NL80211_WOWLAN_PKTPAT_INVALID __NL80211_PKTPAT_INVALID
+#define NL80211_WOWLAN_PKTPAT_MASK NL80211_PKTPAT_MASK
+#define NL80211_WOWLAN_PKTPAT_PATTERN NL80211_PKTPAT_PATTERN
+#define NL80211_WOWLAN_PKTPAT_OFFSET NL80211_PKTPAT_OFFSET
+#define NL80211_WOWLAN_ACTION NL80211_PKTPAT_ACTION
+#define NUM_NL80211_WOWLAN_PKTPAT NUM_NL80211_PKTPAT
+#define MAX_NL80211_WOWLAN_PKTPAT MAX_NL80211_PKTPAT
+#define nl80211_wowlan_pattern_support nl80211_pattern_support
+
+/**
+ * enum nl80211_wowlan_triggers - WoWLAN trigger definitions
+ * @__NL80211_WOWLAN_TRIG_INVALID: invalid number for nested attributes
+ * @NL80211_WOWLAN_TRIG_ANY: wake up on any activity, do not really put
+ * the chip into a special state -- works best with chips that have
+ * support for low-power operation already (flag)
+ * Note that this mode is incompatible with all of the others, if
+ * any others are even supported by the device.
+ * @NL80211_WOWLAN_TRIG_DISCONNECT: wake up on disconnect, the way disconnect
+ * is detected is implementation-specific (flag)
+ * @NL80211_WOWLAN_TRIG_MAGIC_PKT: wake up on magic packet (6x 0xff, followed
+ * by 16 repetitions of MAC addr, anywhere in payload) (flag)
+ * @NL80211_WOWLAN_TRIG_PKT_PATTERN: wake up on the specified packet patterns
+ * which are passed in an array of nested attributes, each nested attribute
+ * defining a with attributes from &struct nl80211_wowlan_trig_pkt_pattern.
+ * Each pattern defines a wakeup packet. Packet offset is associated with
+ * each pattern which is used while matching the pattern. The matching is
+ * done on the MSDU, i.e. as though the packet was an 802.3 packet, so the
+ * pattern matching is done after the packet is converted to the MSDU.
+ *
+ * In %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED, it is a binary attribute
+ * carrying a &struct nl80211_pattern_support.
+ *
+ * When reporting wakeup. it is a u32 attribute containing the 0-based
+ * index of the pattern that caused the wakeup, in the patterns passed
+ * to the kernel when configuring.
+ * @NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED: Not a real trigger, and cannot be
+ * used when setting, used only to indicate that GTK rekeying is supported
+ * by the device (flag)
+ * @NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE: wake up on GTK rekey failure (if
+ * done by the device) (flag)
+ * @NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST: wake up on EAP Identity Request
+ * packet (flag)
+ * @NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE: wake up on 4-way handshake (flag)
+ * @NL80211_WOWLAN_TRIG_RFKILL_RELEASE: wake up when rfkill is released
+ * (on devices that have rfkill in the device) (flag)
+ * @NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211: For wakeup reporting only, contains
+ * the 802.11 packet that caused the wakeup, e.g. a deauth frame. The frame
+ * may be truncated, the @NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN
+ * attribute contains the original length.
+ * @NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN: Original length of the 802.11
+ * packet, may be bigger than the @NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211
+ * attribute if the packet was truncated somewhere.
+ * @NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023: For wakeup reporting only, contains the
+ * 802.11 packet that caused the wakeup, e.g. a magic packet. The frame may
+ * be truncated, the @NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023_LEN attribute
+ * contains the original length.
+ * @NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023_LEN: Original length of the 802.3
+ * packet, may be bigger than the @NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023
+ * attribute if the packet was truncated somewhere.
+ * @NL80211_WOWLAN_TRIG_TCP_CONNECTION: TCP connection wake, see DOC section
+ * "TCP connection wakeup" for more details. This is a nested attribute
+ * containing the exact information for establishing and keeping alive
+ * the TCP connection.
+ * @NL80211_WOWLAN_TRIG_TCP_WAKEUP_MATCH: For wakeup reporting only, the
+ * wakeup packet was received on the TCP connection
+ * @NL80211_WOWLAN_TRIG_WAKEUP_TCP_CONNLOST: For wakeup reporting only, the
+ * TCP connection was lost or failed to be established
+ * @NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS: For wakeup reporting only,
+ * the TCP connection ran out of tokens to use for data to send to the
+ * service
+ * @NL80211_WOWLAN_TRIG_NET_DETECT: wake up when a configured network
+ * is detected. This is a nested attribute that contains the
+ * same attributes used with @NL80211_CMD_START_SCHED_SCAN. It
+ * specifies how the scan is performed (e.g. the interval, the
+ * channels to scan and the initial delay) as well as the scan
+ * results that will trigger a wake (i.e. the matchsets). This
+ * attribute is also sent in a response to
+ * @NL80211_CMD_GET_WIPHY, indicating the number of match sets
+ * supported by the driver (u32).
+ * @NL80211_WOWLAN_TRIG_NET_DETECT_RESULTS: nested attribute
+ * containing an array with information about what triggered the
+ * wake up. If no elements are present in the array, it means
+ * that the information is not available. If more than one
+ * element is present, it means that more than one match
+ * occurred.
+ * Each element in the array is a nested attribute that contains
+ * one optional %NL80211_ATTR_SSID attribute and one optional
+ * %NL80211_ATTR_SCAN_FREQUENCIES attribute. At least one of
+ * these attributes must be present. If
+ * %NL80211_ATTR_SCAN_FREQUENCIES contains more than one
+ * frequency, it means that the match occurred in more than one
+ * channel.
+ * @NUM_NL80211_WOWLAN_TRIG: number of wake on wireless triggers
+ * @MAX_NL80211_WOWLAN_TRIG: highest wowlan trigger attribute number
+ *
+ * These nested attributes are used to configure the wakeup triggers and
+ * to report the wakeup reason(s).
+ */
+enum nl80211_wowlan_triggers {
+ __NL80211_WOWLAN_TRIG_INVALID,
+ NL80211_WOWLAN_TRIG_ANY,
+ NL80211_WOWLAN_TRIG_DISCONNECT,
+ NL80211_WOWLAN_TRIG_MAGIC_PKT,
+ NL80211_WOWLAN_TRIG_PKT_PATTERN,
+ NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED,
+ NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE,
+ NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST,
+ NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE,
+ NL80211_WOWLAN_TRIG_RFKILL_RELEASE,
+ NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211,
+ NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN,
+ NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023,
+ NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023_LEN,
+ NL80211_WOWLAN_TRIG_TCP_CONNECTION,
+ NL80211_WOWLAN_TRIG_WAKEUP_TCP_MATCH,
+ NL80211_WOWLAN_TRIG_WAKEUP_TCP_CONNLOST,
+ NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS,
+ NL80211_WOWLAN_TRIG_NET_DETECT,
+ NL80211_WOWLAN_TRIG_NET_DETECT_RESULTS,
+
+ /* keep last */
+ NUM_NL80211_WOWLAN_TRIG,
+ MAX_NL80211_WOWLAN_TRIG = NUM_NL80211_WOWLAN_TRIG - 1
+};
+
+/**
+ * DOC: TCP connection wakeup
+ *
+ * Some devices can establish a TCP connection in order to be woken up by a
+ * packet coming in from outside their network segment, or behind NAT. If
+ * configured, the device will establish a TCP connection to the given
+ * service, and periodically send data to that service. The first data
+ * packet is usually transmitted after SYN/ACK, also ACKing the SYN/ACK.
+ * The data packets can optionally include a (little endian) sequence
+ * number (in the TCP payload!) that is generated by the device, and, also
+ * optionally, a token from a list of tokens. This serves as a keep-alive
+ * with the service, and for NATed connections, etc.
+ *
+ * During this keep-alive period, the server doesn't send any data to the
+ * client. When receiving data, it is compared against the wakeup pattern
+ * (and mask) and if it matches, the host is woken up. Similarly, if the
+ * connection breaks or cannot be established to start with, the host is
+ * also woken up.
+ *
+ * Developer's note: ARP offload is required for this, otherwise TCP
+ * response packets might not go through correctly.
+ */
+
+/**
+ * struct nl80211_wowlan_tcp_data_seq - WoWLAN TCP data sequence
+ * @start: starting value
+ * @offset: offset of sequence number in packet
+ * @len: length of the sequence value to write, 1 through 4
+ *
+ * Note: don't confuse with the TCP sequence number(s), this is for the
+ * keepalive packet payload. The actual value is written into the packet
+ * in little endian.
+ */
+struct nl80211_wowlan_tcp_data_seq {
+ __u32 start, offset, len;
+};
+
+/**
+ * struct nl80211_wowlan_tcp_data_token - WoWLAN TCP data token config
+ * @offset: offset of token in packet
+ * @len: length of each token
+ * @token_stream: stream of data to be used for the tokens, the length must
+ * be a multiple of @len for this to make sense
+ */
+struct nl80211_wowlan_tcp_data_token {
+ __u32 offset, len;
+ __u8 token_stream[];
+};
+
+/**
+ * struct nl80211_wowlan_tcp_data_token_feature - data token features
+ * @min_len: minimum token length
+ * @max_len: maximum token length
+ * @bufsize: total available token buffer size (max size of @token_stream)
+ */
+struct nl80211_wowlan_tcp_data_token_feature {
+ __u32 min_len, max_len, bufsize;
+};
+
+/**
+ * enum nl80211_wowlan_tcp_attrs - WoWLAN TCP connection parameters
+ * @__NL80211_WOWLAN_TCP_INVALID: invalid number for nested attributes
+ * @NL80211_WOWLAN_TCP_SRC_IPV4: source IPv4 address (in network byte order)
+ * @NL80211_WOWLAN_TCP_DST_IPV4: destination IPv4 address
+ * (in network byte order)
+ * @NL80211_WOWLAN_TCP_DST_MAC: destination MAC address, this is given because
+ * route lookup when configured might be invalid by the time we suspend,
+ * and doing a route lookup when suspending is no longer possible as it
+ * might require ARP querying.
+ * @NL80211_WOWLAN_TCP_SRC_PORT: source port (u16); optional, if not given a
+ * socket and port will be allocated
+ * @NL80211_WOWLAN_TCP_DST_PORT: destination port (u16)
+ * @NL80211_WOWLAN_TCP_DATA_PAYLOAD: data packet payload, at least one byte.
+ * For feature advertising, a u32 attribute holding the maximum length
+ * of the data payload.
+ * @NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ: data packet sequence configuration
+ * (if desired), a &struct nl80211_wowlan_tcp_data_seq. For feature
+ * advertising it is just a flag
+ * @NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN: data packet token configuration,
+ * see &struct nl80211_wowlan_tcp_data_token and for advertising see
+ * &struct nl80211_wowlan_tcp_data_token_feature.
+ * @NL80211_WOWLAN_TCP_DATA_INTERVAL: data interval in seconds, maximum
+ * interval in feature advertising (u32)
+ * @NL80211_WOWLAN_TCP_WAKE_PAYLOAD: wake packet payload, for advertising a
+ * u32 attribute holding the maximum length
+ * @NL80211_WOWLAN_TCP_WAKE_MASK: Wake packet payload mask, not used for
+ * feature advertising. The mask works like @NL80211_PKTPAT_MASK
+ * but on the TCP payload only.
+ * @NUM_NL80211_WOWLAN_TCP: number of TCP attributes
+ * @MAX_NL80211_WOWLAN_TCP: highest attribute number
+ */
+enum nl80211_wowlan_tcp_attrs {
+ __NL80211_WOWLAN_TCP_INVALID,
+ NL80211_WOWLAN_TCP_SRC_IPV4,
+ NL80211_WOWLAN_TCP_DST_IPV4,
+ NL80211_WOWLAN_TCP_DST_MAC,
+ NL80211_WOWLAN_TCP_SRC_PORT,
+ NL80211_WOWLAN_TCP_DST_PORT,
+ NL80211_WOWLAN_TCP_DATA_PAYLOAD,
+ NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ,
+ NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN,
+ NL80211_WOWLAN_TCP_DATA_INTERVAL,
+ NL80211_WOWLAN_TCP_WAKE_PAYLOAD,
+ NL80211_WOWLAN_TCP_WAKE_MASK,
+
+ /* keep last */
+ NUM_NL80211_WOWLAN_TCP,
+ MAX_NL80211_WOWLAN_TCP = NUM_NL80211_WOWLAN_TCP - 1
+};
+
+/**
+ * struct nl80211_coalesce_rule_support - coalesce rule support information
+ * @max_rules: maximum number of rules supported
+ * @pat: packet pattern support information
+ * @max_delay: maximum supported coalescing delay in msecs
+ *
+ * This struct is carried in %NL80211_ATTR_COALESCE_RULE in the
+ * capability information given by the kernel to userspace.
+ */
+struct nl80211_coalesce_rule_support {
+ __u32 max_rules;
+ struct nl80211_pattern_support pat;
+ __u32 max_delay;
+} __attribute__((packed));
+
+/**
+ * enum nl80211_attr_coalesce_rule - coalesce rule attribute
+ * @__NL80211_COALESCE_RULE_INVALID: invalid number for nested attribute
+ * @NL80211_ATTR_COALESCE_RULE_DELAY: delay in msecs used for packet coalescing
+ * @NL80211_ATTR_COALESCE_RULE_CONDITION: condition for packet coalescence,
+ * see &enum nl80211_coalesce_condition.
+ * @NL80211_ATTR_COALESCE_RULE_PKT_PATTERN: packet offset, pattern is matched
+ * after these fixed number of bytes of received packet
+ * @NUM_NL80211_ATTR_COALESCE_RULE: number of attributes
+ * @NL80211_ATTR_COALESCE_RULE_MAX: max attribute number
+ */
+enum nl80211_attr_coalesce_rule {
+ __NL80211_COALESCE_RULE_INVALID,
+ NL80211_ATTR_COALESCE_RULE_DELAY,
+ NL80211_ATTR_COALESCE_RULE_CONDITION,
+ NL80211_ATTR_COALESCE_RULE_PKT_PATTERN,
+
+ /* keep last */
+ NUM_NL80211_ATTR_COALESCE_RULE,
+ NL80211_ATTR_COALESCE_RULE_MAX = NUM_NL80211_ATTR_COALESCE_RULE - 1
+};
+
+/**
+ * enum nl80211_coalesce_condition - coalesce rule conditions
+ * @NL80211_COALESCE_CONDITION_MATCH: coalaesce Rx packets when patterns
+ * in a rule are matched.
+ * @NL80211_COALESCE_CONDITION_NO_MATCH: coalesce Rx packets when patterns
+ * in a rule are not matched.
+ */
+enum nl80211_coalesce_condition {
+ NL80211_COALESCE_CONDITION_MATCH,
+ NL80211_COALESCE_CONDITION_NO_MATCH
+};
+
+/**
+ * enum nl80211_iface_limit_attrs - limit attributes
+ * @NL80211_IFACE_LIMIT_UNSPEC: (reserved)
+ * @NL80211_IFACE_LIMIT_MAX: maximum number of interfaces that
+ * can be chosen from this set of interface types (u32)
+ * @NL80211_IFACE_LIMIT_TYPES: nested attribute containing a
+ * flag attribute for each interface type in this set
+ * @NUM_NL80211_IFACE_LIMIT: number of attributes
+ * @MAX_NL80211_IFACE_LIMIT: highest attribute number
+ */
+enum nl80211_iface_limit_attrs {
+ NL80211_IFACE_LIMIT_UNSPEC,
+ NL80211_IFACE_LIMIT_MAX,
+ NL80211_IFACE_LIMIT_TYPES,
+
+ /* keep last */
+ NUM_NL80211_IFACE_LIMIT,
+ MAX_NL80211_IFACE_LIMIT = NUM_NL80211_IFACE_LIMIT - 1
+};
+
+/**
+ * enum nl80211_if_combination_attrs -- interface combination attributes
+ *
+ * @NL80211_IFACE_COMB_UNSPEC: (reserved)
+ * @NL80211_IFACE_COMB_LIMITS: Nested attributes containing the limits
+ * for given interface types, see &enum nl80211_iface_limit_attrs.
+ * @NL80211_IFACE_COMB_MAXNUM: u32 attribute giving the total number of
+ * interfaces that can be created in this group. This number doesn't
+ * apply to interfaces purely managed in software, which are listed
+ * in a separate attribute %NL80211_ATTR_INTERFACES_SOFTWARE.
+ * @NL80211_IFACE_COMB_STA_AP_BI_MATCH: flag attribute specifying that
+ * beacon intervals within this group must be all the same even for
+ * infrastructure and AP/GO combinations, i.e. the GO(s) must adopt
+ * the infrastructure network's beacon interval.
+ * @NL80211_IFACE_COMB_NUM_CHANNELS: u32 attribute specifying how many
+ * different channels may be used within this group.
+ * @NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS: u32 attribute containing the bitmap
+ * of supported channel widths for radar detection.
+ * @NL80211_IFACE_COMB_RADAR_DETECT_REGIONS: u32 attribute containing the bitmap
+ * of supported regulatory regions for radar detection.
+ * @NL80211_IFACE_COMB_BI_MIN_GCD: u32 attribute specifying the minimum GCD of
+ * different beacon intervals supported by all the interface combinations
+ * in this group (if not present, all beacon intervals be identical).
+ * @NUM_NL80211_IFACE_COMB: number of attributes
+ * @MAX_NL80211_IFACE_COMB: highest attribute number
+ *
+ * Examples:
+ * limits = [ #{STA} <= 1, #{AP} <= 1 ], matching BI, channels = 1, max = 2
+ * => allows an AP and a STA that must match BIs
+ *
+ * numbers = [ #{AP, P2P-GO} <= 8 ], BI min gcd, channels = 1, max = 8,
+ * => allows 8 of AP/GO that can have BI gcd >= min gcd
+ *
+ * numbers = [ #{STA} <= 2 ], channels = 2, max = 2
+ * => allows two STAs on different channels
+ *
+ * numbers = [ #{STA} <= 1, #{P2P-client,P2P-GO} <= 3 ], max = 4
+ * => allows a STA plus three P2P interfaces
+ *
+ * The list of these four possiblities could completely be contained
+ * within the %NL80211_ATTR_INTERFACE_COMBINATIONS attribute to indicate
+ * that any of these groups must match.
+ *
+ * "Combinations" of just a single interface will not be listed here,
+ * a single interface of any valid interface type is assumed to always
+ * be possible by itself. This means that implicitly, for each valid
+ * interface type, the following group always exists:
+ * numbers = [ #{<type>} <= 1 ], channels = 1, max = 1
+ */
+enum nl80211_if_combination_attrs {
+ NL80211_IFACE_COMB_UNSPEC,
+ NL80211_IFACE_COMB_LIMITS,
+ NL80211_IFACE_COMB_MAXNUM,
+ NL80211_IFACE_COMB_STA_AP_BI_MATCH,
+ NL80211_IFACE_COMB_NUM_CHANNELS,
+ NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS,
+ NL80211_IFACE_COMB_RADAR_DETECT_REGIONS,
+ NL80211_IFACE_COMB_BI_MIN_GCD,
+
+ /* keep last */
+ NUM_NL80211_IFACE_COMB,
+ MAX_NL80211_IFACE_COMB = NUM_NL80211_IFACE_COMB - 1
+};
+
+
+/**
+ * enum nl80211_plink_state - state of a mesh peer link finite state machine
+ *
+ * @NL80211_PLINK_LISTEN: initial state, considered the implicit
+ * state of non existant mesh peer links
+ * @NL80211_PLINK_OPN_SNT: mesh plink open frame has been sent to
+ * this mesh peer
+ * @NL80211_PLINK_OPN_RCVD: mesh plink open frame has been received
+ * from this mesh peer
+ * @NL80211_PLINK_CNF_RCVD: mesh plink confirm frame has been
+ * received from this mesh peer
+ * @NL80211_PLINK_ESTAB: mesh peer link is established
+ * @NL80211_PLINK_HOLDING: mesh peer link is being closed or cancelled
+ * @NL80211_PLINK_BLOCKED: all frames transmitted from this mesh
+ * plink are discarded
+ * @NUM_NL80211_PLINK_STATES: number of peer link states
+ * @MAX_NL80211_PLINK_STATES: highest numerical value of plink states
+ */
+enum nl80211_plink_state {
+ NL80211_PLINK_LISTEN,
+ NL80211_PLINK_OPN_SNT,
+ NL80211_PLINK_OPN_RCVD,
+ NL80211_PLINK_CNF_RCVD,
+ NL80211_PLINK_ESTAB,
+ NL80211_PLINK_HOLDING,
+ NL80211_PLINK_BLOCKED,
+
+ /* keep last */
+ NUM_NL80211_PLINK_STATES,
+ MAX_NL80211_PLINK_STATES = NUM_NL80211_PLINK_STATES - 1
+};
+
+/**
+ * enum nl80211_plink_action - actions to perform in mesh peers
+ *
+ * @NL80211_PLINK_ACTION_NO_ACTION: perform no action
+ * @NL80211_PLINK_ACTION_OPEN: start mesh peer link establishment
+ * @NL80211_PLINK_ACTION_BLOCK: block traffic from this mesh peer
+ * @NUM_NL80211_PLINK_ACTIONS: number of possible actions
+ */
+enum plink_actions {
+ NL80211_PLINK_ACTION_NO_ACTION,
+ NL80211_PLINK_ACTION_OPEN,
+ NL80211_PLINK_ACTION_BLOCK,
+
+ NUM_NL80211_PLINK_ACTIONS,
+};
+
+
+#define NL80211_KCK_LEN 16
+#define NL80211_KEK_LEN 16
+#define NL80211_REPLAY_CTR_LEN 8
+
+/**
+ * enum nl80211_rekey_data - attributes for GTK rekey offload
+ * @__NL80211_REKEY_DATA_INVALID: invalid number for nested attributes
+ * @NL80211_REKEY_DATA_KEK: key encryption key (binary)
+ * @NL80211_REKEY_DATA_KCK: key confirmation key (binary)
+ * @NL80211_REKEY_DATA_REPLAY_CTR: replay counter (binary)
+ * @NUM_NL80211_REKEY_DATA: number of rekey attributes (internal)
+ * @MAX_NL80211_REKEY_DATA: highest rekey attribute (internal)
+ */
+enum nl80211_rekey_data {
+ __NL80211_REKEY_DATA_INVALID,
+ NL80211_REKEY_DATA_KEK,
+ NL80211_REKEY_DATA_KCK,
+ NL80211_REKEY_DATA_REPLAY_CTR,
+
+ /* keep last */
+ NUM_NL80211_REKEY_DATA,
+ MAX_NL80211_REKEY_DATA = NUM_NL80211_REKEY_DATA - 1
+};
+
+/**
+ * enum nl80211_hidden_ssid - values for %NL80211_ATTR_HIDDEN_SSID
+ * @NL80211_HIDDEN_SSID_NOT_IN_USE: do not hide SSID (i.e., broadcast it in
+ * Beacon frames)
+ * @NL80211_HIDDEN_SSID_ZERO_LEN: hide SSID by using zero-length SSID element
+ * in Beacon frames
+ * @NL80211_HIDDEN_SSID_ZERO_CONTENTS: hide SSID by using correct length of SSID
+ * element in Beacon frames but zero out each byte in the SSID
+ */
+enum nl80211_hidden_ssid {
+ NL80211_HIDDEN_SSID_NOT_IN_USE,
+ NL80211_HIDDEN_SSID_ZERO_LEN,
+ NL80211_HIDDEN_SSID_ZERO_CONTENTS
+};
+
+/**
+ * enum nl80211_sta_wme_attr - station WME attributes
+ * @__NL80211_STA_WME_INVALID: invalid number for nested attribute
+ * @NL80211_STA_WME_UAPSD_QUEUES: bitmap of uapsd queues. the format
+ * is the same as the AC bitmap in the QoS info field.
+ * @NL80211_STA_WME_MAX_SP: max service period. the format is the same
+ * as the MAX_SP field in the QoS info field (but already shifted down).
+ * @__NL80211_STA_WME_AFTER_LAST: internal
+ * @NL80211_STA_WME_MAX: highest station WME attribute
+ */
+enum nl80211_sta_wme_attr {
+ __NL80211_STA_WME_INVALID,
+ NL80211_STA_WME_UAPSD_QUEUES,
+ NL80211_STA_WME_MAX_SP,
+
+ /* keep last */
+ __NL80211_STA_WME_AFTER_LAST,
+ NL80211_STA_WME_MAX = __NL80211_STA_WME_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_pmksa_candidate_attr - attributes for PMKSA caching candidates
+ * @__NL80211_PMKSA_CANDIDATE_INVALID: invalid number for nested attributes
+ * @NL80211_PMKSA_CANDIDATE_INDEX: candidate index (u32; the smaller, the higher
+ * priority)
+ * @NL80211_PMKSA_CANDIDATE_BSSID: candidate BSSID (6 octets)
+ * @NL80211_PMKSA_CANDIDATE_PREAUTH: RSN pre-authentication supported (flag)
+ * @NUM_NL80211_PMKSA_CANDIDATE: number of PMKSA caching candidate attributes
+ * (internal)
+ * @MAX_NL80211_PMKSA_CANDIDATE: highest PMKSA caching candidate attribute
+ * (internal)
+ */
+enum nl80211_pmksa_candidate_attr {
+ __NL80211_PMKSA_CANDIDATE_INVALID,
+ NL80211_PMKSA_CANDIDATE_INDEX,
+ NL80211_PMKSA_CANDIDATE_BSSID,
+ NL80211_PMKSA_CANDIDATE_PREAUTH,
+
+ /* keep last */
+ NUM_NL80211_PMKSA_CANDIDATE,
+ MAX_NL80211_PMKSA_CANDIDATE = NUM_NL80211_PMKSA_CANDIDATE - 1
+};
+
+/**
+ * enum nl80211_tdls_operation - values for %NL80211_ATTR_TDLS_OPERATION
+ * @NL80211_TDLS_DISCOVERY_REQ: Send a TDLS discovery request
+ * @NL80211_TDLS_SETUP: Setup TDLS link
+ * @NL80211_TDLS_TEARDOWN: Teardown a TDLS link which is already established
+ * @NL80211_TDLS_ENABLE_LINK: Enable TDLS link
+ * @NL80211_TDLS_DISABLE_LINK: Disable TDLS link
+ */
+enum nl80211_tdls_operation {
+ NL80211_TDLS_DISCOVERY_REQ,
+ NL80211_TDLS_SETUP,
+ NL80211_TDLS_TEARDOWN,
+ NL80211_TDLS_ENABLE_LINK,
+ NL80211_TDLS_DISABLE_LINK,
+};
+
+/*
+ * enum nl80211_ap_sme_features - device-integrated AP features
+ * Reserved for future use, no bits are defined in
+ * NL80211_ATTR_DEVICE_AP_SME yet.
+enum nl80211_ap_sme_features {
+};
+ */
+
+/**
+ * enum nl80211_feature_flags - device/driver features
+ * @NL80211_FEATURE_SK_TX_STATUS: This driver supports reflecting back
+ * TX status to the socket error queue when requested with the
+ * socket option.
+ * @NL80211_FEATURE_HT_IBSS: This driver supports IBSS with HT datarates.
+ * @NL80211_FEATURE_INACTIVITY_TIMER: This driver takes care of freeing up
+ * the connected inactive stations in AP mode.
+ * @NL80211_FEATURE_CELL_BASE_REG_HINTS: This driver has been tested
+ * to work properly to suppport receiving regulatory hints from
+ * cellular base stations.
+ * @NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL: (no longer available, only
+ * here to reserve the value for API/ABI compatibility)
+ * @NL80211_FEATURE_SAE: This driver supports simultaneous authentication of
+ * equals (SAE) with user space SME (NL80211_CMD_AUTHENTICATE) in station
+ * mode
+ * @NL80211_FEATURE_LOW_PRIORITY_SCAN: This driver supports low priority scan
+ * @NL80211_FEATURE_SCAN_FLUSH: Scan flush is supported
+ * @NL80211_FEATURE_AP_SCAN: Support scanning using an AP vif
+ * @NL80211_FEATURE_VIF_TXPOWER: The driver supports per-vif TX power setting
+ * @NL80211_FEATURE_NEED_OBSS_SCAN: The driver expects userspace to perform
+ * OBSS scans and generate 20/40 BSS coex reports. This flag is used only
+ * for drivers implementing the CONNECT API, for AUTH/ASSOC it is implied.
+ * @NL80211_FEATURE_P2P_GO_CTWIN: P2P GO implementation supports CT Window
+ * setting
+ * @NL80211_FEATURE_P2P_GO_OPPPS: P2P GO implementation supports opportunistic
+ * powersave
+ * @NL80211_FEATURE_FULL_AP_CLIENT_STATE: The driver supports full state
+ * transitions for AP clients. Without this flag (and if the driver
+ * doesn't have the AP SME in the device) the driver supports adding
+ * stations only when they're associated and adds them in associated
+ * state (to later be transitioned into authorized), with this flag
+ * they should be added before even sending the authentication reply
+ * and then transitioned into authenticated, associated and authorized
+ * states using station flags.
+ * Note that even for drivers that support this, the default is to add
+ * stations in authenticated/associated state, so to add unauthenticated
+ * stations the authenticated/associated bits have to be set in the mask.
+ * @NL80211_FEATURE_ADVERTISE_CHAN_LIMITS: cfg80211 advertises channel limits
+ * (HT40, VHT 80/160 MHz) if this flag is set
+ * @NL80211_FEATURE_USERSPACE_MPM: This driver supports a userspace Mesh
+ * Peering Management entity which may be implemented by registering for
+ * beacons or NL80211_CMD_NEW_PEER_CANDIDATE events. The mesh beacon is
+ * still generated by the driver.
+ * @NL80211_FEATURE_ACTIVE_MONITOR: This driver supports an active monitor
+ * interface. An active monitor interface behaves like a normal monitor
+ * interface, but gets added to the driver. It ensures that incoming
+ * unicast packets directed at the configured interface address get ACKed.
+ * @NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE: This driver supports dynamic
+ * channel bandwidth change (e.g., HT 20 <-> 40 MHz channel) during the
+ * lifetime of a BSS.
+ * @NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES: This device adds a DS Parameter
+ * Set IE to probe requests.
+ * @NL80211_FEATURE_WFA_TPC_IE_IN_PROBES: This device adds a WFA TPC Report IE
+ * to probe requests.
+ * @NL80211_FEATURE_QUIET: This device, in client mode, supports Quiet Period
+ * requests sent to it by an AP.
+ * @NL80211_FEATURE_TX_POWER_INSERTION: This device is capable of inserting the
+ * current tx power value into the TPC Report IE in the spectrum
+ * management TPC Report action frame, and in the Radio Measurement Link
+ * Measurement Report action frame.
+ * @NL80211_FEATURE_ACKTO_ESTIMATION: This driver supports dynamic ACK timeout
+ * estimation (dynack). %NL80211_ATTR_WIPHY_DYN_ACK flag attribute is used
+ * to enable dynack.
+ * @NL80211_FEATURE_STATIC_SMPS: Device supports static spatial
+ * multiplexing powersave, ie. can turn off all but one chain
+ * even on HT connections that should be using more chains.
+ * @NL80211_FEATURE_DYNAMIC_SMPS: Device supports dynamic spatial
+ * multiplexing powersave, ie. can turn off all but one chain
+ * and then wake the rest up as required after, for example,
+ * rts/cts handshake.
+ * @NL80211_FEATURE_SUPPORTS_WMM_ADMISSION: the device supports setting up WMM
+ * TSPEC sessions (TID aka TSID 0-7) with the %NL80211_CMD_ADD_TX_TS
+ * command. Standard IEEE 802.11 TSPEC setup is not yet supported, it
+ * needs to be able to handle Block-Ack agreements and other things.
+ * @NL80211_FEATURE_MAC_ON_CREATE: Device supports configuring
+ * the vif's MAC address upon creation.
+ * See 'macaddr' field in the vif_params (cfg80211.h).
+ * @NL80211_FEATURE_TDLS_CHANNEL_SWITCH: Driver supports channel switching when
+ * operating as a TDLS peer.
+ * @NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR: This device/driver supports using a
+ * random MAC address during scan (if the device is unassociated); the
+ * %NL80211_SCAN_FLAG_RANDOM_ADDR flag may be set for scans and the MAC
+ * address mask/value will be used.
+ * @NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR: This device/driver supports
+ * using a random MAC address for every scan iteration during scheduled
+ * scan (while not associated), the %NL80211_SCAN_FLAG_RANDOM_ADDR may
+ * be set for scheduled scan and the MAC address mask/value will be used.
+ * @NL80211_FEATURE_ND_RANDOM_MAC_ADDR: This device/driver supports using a
+ * random MAC address for every scan iteration during "net detect", i.e.
+ * scan in unassociated WoWLAN, the %NL80211_SCAN_FLAG_RANDOM_ADDR may
+ * be set for scheduled scan and the MAC address mask/value will be used.
+ */
+enum nl80211_feature_flags {
+ NL80211_FEATURE_SK_TX_STATUS = 1 << 0,
+ NL80211_FEATURE_HT_IBSS = 1 << 1,
+ NL80211_FEATURE_INACTIVITY_TIMER = 1 << 2,
+ NL80211_FEATURE_CELL_BASE_REG_HINTS = 1 << 3,
+ NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL = 1 << 4,
+ NL80211_FEATURE_SAE = 1 << 5,
+ NL80211_FEATURE_LOW_PRIORITY_SCAN = 1 << 6,
+ NL80211_FEATURE_SCAN_FLUSH = 1 << 7,
+ NL80211_FEATURE_AP_SCAN = 1 << 8,
+ NL80211_FEATURE_VIF_TXPOWER = 1 << 9,
+ NL80211_FEATURE_NEED_OBSS_SCAN = 1 << 10,
+ NL80211_FEATURE_P2P_GO_CTWIN = 1 << 11,
+ NL80211_FEATURE_P2P_GO_OPPPS = 1 << 12,
+ /* bit 13 is reserved */
+ NL80211_FEATURE_ADVERTISE_CHAN_LIMITS = 1 << 14,
+ NL80211_FEATURE_FULL_AP_CLIENT_STATE = 1 << 15,
+ NL80211_FEATURE_USERSPACE_MPM = 1 << 16,
+ NL80211_FEATURE_ACTIVE_MONITOR = 1 << 17,
+ NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE = 1 << 18,
+ NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES = 1 << 19,
+ NL80211_FEATURE_WFA_TPC_IE_IN_PROBES = 1 << 20,
+ NL80211_FEATURE_QUIET = 1 << 21,
+ NL80211_FEATURE_TX_POWER_INSERTION = 1 << 22,
+ NL80211_FEATURE_ACKTO_ESTIMATION = 1 << 23,
+ NL80211_FEATURE_STATIC_SMPS = 1 << 24,
+ NL80211_FEATURE_DYNAMIC_SMPS = 1 << 25,
+ NL80211_FEATURE_SUPPORTS_WMM_ADMISSION = 1 << 26,
+ NL80211_FEATURE_MAC_ON_CREATE = 1 << 27,
+ NL80211_FEATURE_TDLS_CHANNEL_SWITCH = 1 << 28,
+ NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR = 1 << 29,
+ NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR = 1 << 30,
+ NL80211_FEATURE_ND_RANDOM_MAC_ADDR = 1 << 31,
+};
+
+/**
+ * enum nl80211_ext_feature_index - bit index of extended features.
+ * @NL80211_EXT_FEATURE_VHT_IBSS: This driver supports IBSS with VHT datarates.
+ * @NL80211_EXT_FEATURE_RRM: This driver supports RRM. When featured, user can
+ * can request to use RRM (see %NL80211_ATTR_USE_RRM) with
+ * %NL80211_CMD_ASSOCIATE and %NL80211_CMD_CONNECT requests, which will set
+ * the ASSOC_REQ_USE_RRM flag in the association request even if
+ * NL80211_FEATURE_QUIET is not advertized.
+ * @NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER: This device supports MU-MIMO air
+ * sniffer which means that it can be configured to hear packets from
+ * certain groups which can be configured by the
+ * %NL80211_ATTR_MU_MIMO_GROUP_DATA attribute,
+ * or can be configured to follow a station by configuring the
+ * %NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR attribute.
+ * @NL80211_EXT_FEATURE_SCAN_START_TIME: This driver includes the actual
+ * time the scan started in scan results event. The time is the TSF of
+ * the BSS that the interface that requested the scan is connected to
+ * (if available).
+ * @NL80211_EXT_FEATURE_BSS_PARENT_TSF: Per BSS, this driver reports the
+ * time the last beacon/probe was received. The time is the TSF of the
+ * BSS that the interface that requested the scan is connected to
+ * (if available).
+ * @NL80211_EXT_FEATURE_SET_SCAN_DWELL: This driver supports configuration of
+ * channel dwell time.
+ * @NL80211_EXT_FEATURE_BEACON_RATE_LEGACY: Driver supports beacon rate
+ * configuration (AP/mesh), supporting a legacy (non HT/VHT) rate.
+ * @NL80211_EXT_FEATURE_BEACON_RATE_HT: Driver supports beacon rate
+ * configuration (AP/mesh) with HT rates.
+ * @NL80211_EXT_FEATURE_BEACON_RATE_VHT: Driver supports beacon rate
+ * configuration (AP/mesh) with VHT rates.
+ * @NL80211_EXT_FEATURE_FILS_STA: This driver supports Fast Initial Link Setup
+ * with user space SME (NL80211_CMD_AUTHENTICATE) in station mode.
+ * @NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA: This driver supports randomized TA
+ * in @NL80211_CMD_FRAME while not associated.
+ * @NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA_CONNECTED: This driver supports
+ * randomized TA in @NL80211_CMD_FRAME while associated.
+ * @NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI: The driver supports sched_scan
+ * for reporting BSSs with better RSSI than the current connected BSS
+ * (%NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI).
+ * @NL80211_EXT_FEATURE_CQM_RSSI_LIST: With this driver the
+ * %NL80211_ATTR_CQM_RSSI_THOLD attribute accepts a list of zero or more
+ * RSSI threshold values to monitor rather than exactly one threshold.
+ * @NL80211_EXT_FEATURE_FILS_SK_OFFLOAD: Driver SME supports FILS shared key
+ * authentication with %NL80211_CMD_CONNECT.
+ * @NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK: Device wants to do 4-way
+ * handshake with PSK in station mode (PSK is passed as part of the connect
+ * and associate commands), doing it in the host might not be supported.
+ * @NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X: Device wants to do doing 4-way
+ * handshake with 802.1X in station mode (will pass EAP frames to the host
+ * and accept the set_pmk/del_pmk commands), doing it in the host might not
+ * be supported.
+ *
+ * @NUM_NL80211_EXT_FEATURES: number of extended features.
+ * @MAX_NL80211_EXT_FEATURES: highest extended feature index.
+ */
+enum nl80211_ext_feature_index {
+ NL80211_EXT_FEATURE_VHT_IBSS,
+ NL80211_EXT_FEATURE_RRM,
+ NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER,
+ NL80211_EXT_FEATURE_SCAN_START_TIME,
+ NL80211_EXT_FEATURE_BSS_PARENT_TSF,
+ NL80211_EXT_FEATURE_SET_SCAN_DWELL,
+ NL80211_EXT_FEATURE_BEACON_RATE_LEGACY,
+ NL80211_EXT_FEATURE_BEACON_RATE_HT,
+ NL80211_EXT_FEATURE_BEACON_RATE_VHT,
+ NL80211_EXT_FEATURE_FILS_STA,
+ NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA,
+ NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA_CONNECTED,
+ NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI,
+ NL80211_EXT_FEATURE_CQM_RSSI_LIST,
+ NL80211_EXT_FEATURE_FILS_SK_OFFLOAD,
+ NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK,
+ NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X,
+
+ /* add new features before the definition below */
+ NUM_NL80211_EXT_FEATURES,
+ MAX_NL80211_EXT_FEATURES = NUM_NL80211_EXT_FEATURES - 1
+};
+
+/**
+ * enum nl80211_probe_resp_offload_support_attr - optional supported
+ * protocols for probe-response offloading by the driver/FW.
+ * To be used with the %NL80211_ATTR_PROBE_RESP_OFFLOAD attribute.
+ * Each enum value represents a bit in the bitmap of supported
+ * protocols. Typically a subset of probe-requests belonging to a
+ * supported protocol will be excluded from offload and uploaded
+ * to the host.
+ *
+ * @NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS: Support for WPS ver. 1
+ * @NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2: Support for WPS ver. 2
+ * @NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P: Support for P2P
+ * @NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U: Support for 802.11u
+ */
+enum nl80211_probe_resp_offload_support_attr {
+ NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS = 1<<0,
+ NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 = 1<<1,
+ NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P = 1<<2,
+ NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U = 1<<3,
+};
+
+/**
+ * enum nl80211_connect_failed_reason - connection request failed reasons
+ * @NL80211_CONN_FAIL_MAX_CLIENTS: Maximum number of clients that can be
+ * handled by the AP is reached.
+ * @NL80211_CONN_FAIL_BLOCKED_CLIENT: Connection request is rejected due to ACL.
+ */
+enum nl80211_connect_failed_reason {
+ NL80211_CONN_FAIL_MAX_CLIENTS,
+ NL80211_CONN_FAIL_BLOCKED_CLIENT,
+};
+
+/**
+ * enum nl80211_timeout_reason - timeout reasons
+ *
+ * @NL80211_TIMEOUT_UNSPECIFIED: Timeout reason unspecified.
+ * @NL80211_TIMEOUT_SCAN: Scan (AP discovery) timed out.
+ * @NL80211_TIMEOUT_AUTH: Authentication timed out.
+ * @NL80211_TIMEOUT_ASSOC: Association timed out.
+ */
+enum nl80211_timeout_reason {
+ NL80211_TIMEOUT_UNSPECIFIED,
+ NL80211_TIMEOUT_SCAN,
+ NL80211_TIMEOUT_AUTH,
+ NL80211_TIMEOUT_ASSOC,
+};
+
+/**
+ * enum nl80211_scan_flags - scan request control flags
+ *
+ * Scan request control flags are used to control the handling
+ * of NL80211_CMD_TRIGGER_SCAN and NL80211_CMD_START_SCHED_SCAN
+ * requests.
+ *
+ * @NL80211_SCAN_FLAG_LOW_PRIORITY: scan request has low priority
+ * @NL80211_SCAN_FLAG_FLUSH: flush cache before scanning
+ * @NL80211_SCAN_FLAG_AP: force a scan even if the interface is configured
+ * as AP and the beaconing has already been configured. This attribute is
+ * dangerous because will destroy stations performance as a lot of frames
+ * will be lost while scanning off-channel, therefore it must be used only
+ * when really needed
+ * @NL80211_SCAN_FLAG_RANDOM_ADDR: use a random MAC address for this scan (or
+ * for scheduled scan: a different one for every scan iteration). When the
+ * flag is set, depending on device capabilities the @NL80211_ATTR_MAC and
+ * @NL80211_ATTR_MAC_MASK attributes may also be given in which case only
+ * the masked bits will be preserved from the MAC address and the remainder
+ * randomised. If the attributes are not given full randomisation (46 bits,
+ * locally administered 1, multicast 0) is assumed.
+ * This flag must not be requested when the feature isn't supported, check
+ * the nl80211 feature flags for the device.
+ */
+enum nl80211_scan_flags {
+ NL80211_SCAN_FLAG_LOW_PRIORITY = 1<<0,
+ NL80211_SCAN_FLAG_FLUSH = 1<<1,
+ NL80211_SCAN_FLAG_AP = 1<<2,
+ NL80211_SCAN_FLAG_RANDOM_ADDR = 1<<3,
+};
+
+/**
+ * enum nl80211_acl_policy - access control policy
+ *
+ * Access control policy is applied on a MAC list set by
+ * %NL80211_CMD_START_AP and %NL80211_CMD_SET_MAC_ACL, to
+ * be used with %NL80211_ATTR_ACL_POLICY.
+ *
+ * @NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED: Deny stations which are
+ * listed in ACL, i.e. allow all the stations which are not listed
+ * in ACL to authenticate.
+ * @NL80211_ACL_POLICY_DENY_UNLESS_LISTED: Allow the stations which are listed
+ * in ACL, i.e. deny all the stations which are not listed in ACL.
+ */
+enum nl80211_acl_policy {
+ NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED,
+ NL80211_ACL_POLICY_DENY_UNLESS_LISTED,
+};
+
+/**
+ * enum nl80211_smps_mode - SMPS mode
+ *
+ * Requested SMPS mode (for AP mode)
+ *
+ * @NL80211_SMPS_OFF: SMPS off (use all antennas).
+ * @NL80211_SMPS_STATIC: static SMPS (use a single antenna)
+ * @NL80211_SMPS_DYNAMIC: dynamic smps (start with a single antenna and
+ * turn on other antennas after CTS/RTS).
+ */
+enum nl80211_smps_mode {
+ NL80211_SMPS_OFF,
+ NL80211_SMPS_STATIC,
+ NL80211_SMPS_DYNAMIC,
+
+ __NL80211_SMPS_AFTER_LAST,
+ NL80211_SMPS_MAX = __NL80211_SMPS_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_radar_event - type of radar event for DFS operation
+ *
+ * Type of event to be used with NL80211_ATTR_RADAR_EVENT to inform userspace
+ * about detected radars or success of the channel available check (CAC)
+ *
+ * @NL80211_RADAR_DETECTED: A radar pattern has been detected. The channel is
+ * now unusable.
+ * @NL80211_RADAR_CAC_FINISHED: Channel Availability Check has been finished,
+ * the channel is now available.
+ * @NL80211_RADAR_CAC_ABORTED: Channel Availability Check has been aborted, no
+ * change to the channel status.
+ * @NL80211_RADAR_NOP_FINISHED: The Non-Occupancy Period for this channel is
+ * over, channel becomes usable.
+ * @NL80211_RADAR_PRE_CAC_EXPIRED: Channel Availability Check done on this
+ * non-operating channel is expired and no longer valid. New CAC must
+ * be done on this channel before starting the operation. This is not
+ * applicable for ETSI dfs domain where pre-CAC is valid for ever.
+ */
+enum nl80211_radar_event {
+ NL80211_RADAR_DETECTED,
+ NL80211_RADAR_CAC_FINISHED,
+ NL80211_RADAR_CAC_ABORTED,
+ NL80211_RADAR_NOP_FINISHED,
+ NL80211_RADAR_PRE_CAC_EXPIRED,
+};
+
+/**
+ * enum nl80211_dfs_state - DFS states for channels
+ *
+ * Channel states used by the DFS code.
+ *
+ * @NL80211_DFS_USABLE: The channel can be used, but channel availability
+ * check (CAC) must be performed before using it for AP or IBSS.
+ * @NL80211_DFS_UNAVAILABLE: A radar has been detected on this channel, it
+ * is therefore marked as not available.
+ * @NL80211_DFS_AVAILABLE: The channel has been CAC checked and is available.
+ */
+enum nl80211_dfs_state {
+ NL80211_DFS_USABLE,
+ NL80211_DFS_UNAVAILABLE,
+ NL80211_DFS_AVAILABLE,
+};
+
+/**
+ * enum enum nl80211_protocol_features - nl80211 protocol features
+ * @NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP: nl80211 supports splitting
+ * wiphy dumps (if requested by the application with the attribute
+ * %NL80211_ATTR_SPLIT_WIPHY_DUMP. Also supported is filtering the
+ * wiphy dump by %NL80211_ATTR_WIPHY, %NL80211_ATTR_IFINDEX or
+ * %NL80211_ATTR_WDEV.
+ */
+enum nl80211_protocol_features {
+ NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP = 1 << 0,
+};
+
+/**
+ * enum nl80211_crit_proto_id - nl80211 critical protocol identifiers
+ *
+ * @NL80211_CRIT_PROTO_UNSPEC: protocol unspecified.
+ * @NL80211_CRIT_PROTO_DHCP: BOOTP or DHCPv6 protocol.
+ * @NL80211_CRIT_PROTO_EAPOL: EAPOL protocol.
+ * @NL80211_CRIT_PROTO_APIPA: APIPA protocol.
+ * @NUM_NL80211_CRIT_PROTO: must be kept last.
+ */
+enum nl80211_crit_proto_id {
+ NL80211_CRIT_PROTO_UNSPEC,
+ NL80211_CRIT_PROTO_DHCP,
+ NL80211_CRIT_PROTO_EAPOL,
+ NL80211_CRIT_PROTO_APIPA,
+ /* add other protocols before this one */
+ NUM_NL80211_CRIT_PROTO
+};
+
+/* maximum duration for critical protocol measures */
+#define NL80211_CRIT_PROTO_MAX_DURATION 5000 /* msec */
+
+/**
+ * enum nl80211_rxmgmt_flags - flags for received management frame.
+ *
+ * Used by cfg80211_rx_mgmt()
+ *
+ * @NL80211_RXMGMT_FLAG_ANSWERED: frame was answered by device/driver.
+ */
+enum nl80211_rxmgmt_flags {
+ NL80211_RXMGMT_FLAG_ANSWERED = 1 << 0,
+};
+
+/*
+ * If this flag is unset, the lower 24 bits are an OUI, if set
+ * a Linux nl80211 vendor ID is used (no such IDs are allocated
+ * yet, so that's not valid so far)
+ */
+#define NL80211_VENDOR_ID_IS_LINUX 0x80000000
+
+/**
+ * struct nl80211_vendor_cmd_info - vendor command data
+ * @vendor_id: If the %NL80211_VENDOR_ID_IS_LINUX flag is clear, then the
+ * value is a 24-bit OUI; if it is set then a separately allocated ID
+ * may be used, but no such IDs are allocated yet. New IDs should be
+ * added to this file when needed.
+ * @subcmd: sub-command ID for the command
+ */
+struct nl80211_vendor_cmd_info {
+ __u32 vendor_id;
+ __u32 subcmd;
+};
+
+/**
+ * enum nl80211_tdls_peer_capability - TDLS peer flags.
+ *
+ * Used by tdls_mgmt() to determine which conditional elements need
+ * to be added to TDLS Setup frames.
+ *
+ * @NL80211_TDLS_PEER_HT: TDLS peer is HT capable.
+ * @NL80211_TDLS_PEER_VHT: TDLS peer is VHT capable.
+ * @NL80211_TDLS_PEER_WMM: TDLS peer is WMM capable.
+ */
+enum nl80211_tdls_peer_capability {
+ NL80211_TDLS_PEER_HT = 1<<0,
+ NL80211_TDLS_PEER_VHT = 1<<1,
+ NL80211_TDLS_PEER_WMM = 1<<2,
+};
+
+/**
+ * enum nl80211_sched_scan_plan - scanning plan for scheduled scan
+ * @__NL80211_SCHED_SCAN_PLAN_INVALID: attribute number 0 is reserved
+ * @NL80211_SCHED_SCAN_PLAN_INTERVAL: interval between scan iterations. In
+ * seconds (u32).
+ * @NL80211_SCHED_SCAN_PLAN_ITERATIONS: number of scan iterations in this
+ * scan plan (u32). The last scan plan must not specify this attribute
+ * because it will run infinitely. A value of zero is invalid as it will
+ * make the scan plan meaningless.
+ * @NL80211_SCHED_SCAN_PLAN_MAX: highest scheduled scan plan attribute number
+ * currently defined
+ * @__NL80211_SCHED_SCAN_PLAN_AFTER_LAST: internal use
+ */
+enum nl80211_sched_scan_plan {
+ __NL80211_SCHED_SCAN_PLAN_INVALID,
+ NL80211_SCHED_SCAN_PLAN_INTERVAL,
+ NL80211_SCHED_SCAN_PLAN_ITERATIONS,
+
+ /* keep last */
+ __NL80211_SCHED_SCAN_PLAN_AFTER_LAST,
+ NL80211_SCHED_SCAN_PLAN_MAX =
+ __NL80211_SCHED_SCAN_PLAN_AFTER_LAST - 1
+};
+
+/**
+ * struct nl80211_bss_select_rssi_adjust - RSSI adjustment parameters.
+ *
+ * @band: band of BSS that must match for RSSI value adjustment. The value
+ * of this field is according to &enum nl80211_band.
+ * @delta: value used to adjust the RSSI value of matching BSS in dB.
+ */
+struct nl80211_bss_select_rssi_adjust {
+ __u8 band;
+ __s8 delta;
+} __attribute__((packed));
+
+/**
+ * enum nl80211_bss_select_attr - attributes for bss selection.
+ *
+ * @__NL80211_BSS_SELECT_ATTR_INVALID: reserved.
+ * @NL80211_BSS_SELECT_ATTR_RSSI: Flag indicating only RSSI-based BSS selection
+ * is requested.
+ * @NL80211_BSS_SELECT_ATTR_BAND_PREF: attribute indicating BSS
+ * selection should be done such that the specified band is preferred.
+ * When there are multiple BSS-es in the preferred band, the driver
+ * shall use RSSI-based BSS selection as a second step. The value of
+ * this attribute is according to &enum nl80211_band (u32).
+ * @NL80211_BSS_SELECT_ATTR_RSSI_ADJUST: When present the RSSI level for
+ * BSS-es in the specified band is to be adjusted before doing
+ * RSSI-based BSS selection. The attribute value is a packed structure
+ * value as specified by &struct nl80211_bss_select_rssi_adjust.
+ * @NL80211_BSS_SELECT_ATTR_MAX: highest bss select attribute number.
+ * @__NL80211_BSS_SELECT_ATTR_AFTER_LAST: internal use.
+ *
+ * One and only one of these attributes are found within %NL80211_ATTR_BSS_SELECT
+ * for %NL80211_CMD_CONNECT. It specifies the required BSS selection behaviour
+ * which the driver shall use.
+ */
+enum nl80211_bss_select_attr {
+ __NL80211_BSS_SELECT_ATTR_INVALID,
+ NL80211_BSS_SELECT_ATTR_RSSI,
+ NL80211_BSS_SELECT_ATTR_BAND_PREF,
+ NL80211_BSS_SELECT_ATTR_RSSI_ADJUST,
+
+ /* keep last */
+ __NL80211_BSS_SELECT_ATTR_AFTER_LAST,
+ NL80211_BSS_SELECT_ATTR_MAX = __NL80211_BSS_SELECT_ATTR_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_nan_function_type - NAN function type
+ *
+ * Defines the function type of a NAN function
+ *
+ * @NL80211_NAN_FUNC_PUBLISH: function is publish
+ * @NL80211_NAN_FUNC_SUBSCRIBE: function is subscribe
+ * @NL80211_NAN_FUNC_FOLLOW_UP: function is follow-up
+ */
+enum nl80211_nan_function_type {
+ NL80211_NAN_FUNC_PUBLISH,
+ NL80211_NAN_FUNC_SUBSCRIBE,
+ NL80211_NAN_FUNC_FOLLOW_UP,
+
+ /* keep last */
+ __NL80211_NAN_FUNC_TYPE_AFTER_LAST,
+ NL80211_NAN_FUNC_MAX_TYPE = __NL80211_NAN_FUNC_TYPE_AFTER_LAST - 1,
+};
+
+/**
+ * enum nl80211_nan_publish_type - NAN publish tx type
+ *
+ * Defines how to send publish Service Discovery Frames
+ *
+ * @NL80211_NAN_SOLICITED_PUBLISH: publish function is solicited
+ * @NL80211_NAN_UNSOLICITED_PUBLISH: publish function is unsolicited
+ */
+enum nl80211_nan_publish_type {
+ NL80211_NAN_SOLICITED_PUBLISH = 1 << 0,
+ NL80211_NAN_UNSOLICITED_PUBLISH = 1 << 1,
+};
+
+/**
+ * enum nl80211_nan_func_term_reason - NAN functions termination reason
+ *
+ * Defines termination reasons of a NAN function
+ *
+ * @NL80211_NAN_FUNC_TERM_REASON_USER_REQUEST: requested by user
+ * @NL80211_NAN_FUNC_TERM_REASON_TTL_EXPIRED: timeout
+ * @NL80211_NAN_FUNC_TERM_REASON_ERROR: errored
+ */
+enum nl80211_nan_func_term_reason {
+ NL80211_NAN_FUNC_TERM_REASON_USER_REQUEST,
+ NL80211_NAN_FUNC_TERM_REASON_TTL_EXPIRED,
+ NL80211_NAN_FUNC_TERM_REASON_ERROR,
+};
+
+#define NL80211_NAN_FUNC_SERVICE_ID_LEN 6
+#define NL80211_NAN_FUNC_SERVICE_SPEC_INFO_MAX_LEN 0xff
+#define NL80211_NAN_FUNC_SRF_MAX_LEN 0xff
+
+/**
+ * enum nl80211_nan_func_attributes - NAN function attributes
+ * @__NL80211_NAN_FUNC_INVALID: invalid
+ * @NL80211_NAN_FUNC_TYPE: &enum nl80211_nan_function_type (u8).
+ * @NL80211_NAN_FUNC_SERVICE_ID: 6 bytes of the service ID hash as
+ * specified in NAN spec. This is a binary attribute.
+ * @NL80211_NAN_FUNC_PUBLISH_TYPE: relevant if the function's type is
+ * publish. Defines the transmission type for the publish Service Discovery
+ * Frame, see &enum nl80211_nan_publish_type. Its type is u8.
+ * @NL80211_NAN_FUNC_PUBLISH_BCAST: relevant if the function is a solicited
+ * publish. Should the solicited publish Service Discovery Frame be sent to
+ * the NAN Broadcast address. This is a flag.
+ * @NL80211_NAN_FUNC_SUBSCRIBE_ACTIVE: relevant if the function's type is
+ * subscribe. Is the subscribe active. This is a flag.
+ * @NL80211_NAN_FUNC_FOLLOW_UP_ID: relevant if the function's type is follow up.
+ * The instance ID for the follow up Service Discovery Frame. This is u8.
+ * @NL80211_NAN_FUNC_FOLLOW_UP_REQ_ID: relevant if the function's type
+ * is follow up. This is a u8.
+ * The requestor instance ID for the follow up Service Discovery Frame.
+ * @NL80211_NAN_FUNC_FOLLOW_UP_DEST: the MAC address of the recipient of the
+ * follow up Service Discovery Frame. This is a binary attribute.
+ * @NL80211_NAN_FUNC_CLOSE_RANGE: is this function limited for devices in a
+ * close range. The range itself (RSSI) is defined by the device.
+ * This is a flag.
+ * @NL80211_NAN_FUNC_TTL: strictly positive number of DWs this function should
+ * stay active. If not present infinite TTL is assumed. This is a u32.
+ * @NL80211_NAN_FUNC_SERVICE_INFO: array of bytes describing the service
+ * specific info. This is a binary attribute.
+ * @NL80211_NAN_FUNC_SRF: Service Receive Filter. This is a nested attribute.
+ * See &enum nl80211_nan_srf_attributes.
+ * @NL80211_NAN_FUNC_RX_MATCH_FILTER: Receive Matching filter. This is a nested
+ * attribute. It is a list of binary values.
+ * @NL80211_NAN_FUNC_TX_MATCH_FILTER: Transmit Matching filter. This is a
+ * nested attribute. It is a list of binary values.
+ * @NL80211_NAN_FUNC_INSTANCE_ID: The instance ID of the function.
+ * Its type is u8 and it cannot be 0.
+ * @NL80211_NAN_FUNC_TERM_REASON: NAN function termination reason.
+ * See &enum nl80211_nan_func_term_reason.
+ *
+ * @NUM_NL80211_NAN_FUNC_ATTR: internal
+ * @NL80211_NAN_FUNC_ATTR_MAX: highest NAN function attribute
+ */
+enum nl80211_nan_func_attributes {
+ __NL80211_NAN_FUNC_INVALID,
+ NL80211_NAN_FUNC_TYPE,
+ NL80211_NAN_FUNC_SERVICE_ID,
+ NL80211_NAN_FUNC_PUBLISH_TYPE,
+ NL80211_NAN_FUNC_PUBLISH_BCAST,
+ NL80211_NAN_FUNC_SUBSCRIBE_ACTIVE,
+ NL80211_NAN_FUNC_FOLLOW_UP_ID,
+ NL80211_NAN_FUNC_FOLLOW_UP_REQ_ID,
+ NL80211_NAN_FUNC_FOLLOW_UP_DEST,
+ NL80211_NAN_FUNC_CLOSE_RANGE,
+ NL80211_NAN_FUNC_TTL,
+ NL80211_NAN_FUNC_SERVICE_INFO,
+ NL80211_NAN_FUNC_SRF,
+ NL80211_NAN_FUNC_RX_MATCH_FILTER,
+ NL80211_NAN_FUNC_TX_MATCH_FILTER,
+ NL80211_NAN_FUNC_INSTANCE_ID,
+ NL80211_NAN_FUNC_TERM_REASON,
+
+ /* keep last */
+ NUM_NL80211_NAN_FUNC_ATTR,
+ NL80211_NAN_FUNC_ATTR_MAX = NUM_NL80211_NAN_FUNC_ATTR - 1
+};
+
+/**
+ * enum nl80211_nan_srf_attributes - NAN Service Response filter attributes
+ * @__NL80211_NAN_SRF_INVALID: invalid
+ * @NL80211_NAN_SRF_INCLUDE: present if the include bit of the SRF set.
+ * This is a flag.
+ * @NL80211_NAN_SRF_BF: Bloom Filter. Present if and only if
+ * &NL80211_NAN_SRF_MAC_ADDRS isn't present. This attribute is binary.
+ * @NL80211_NAN_SRF_BF_IDX: index of the Bloom Filter. Mandatory if
+ * &NL80211_NAN_SRF_BF is present. This is a u8.
+ * @NL80211_NAN_SRF_MAC_ADDRS: list of MAC addresses for the SRF. Present if
+ * and only if &NL80211_NAN_SRF_BF isn't present. This is a nested
+ * attribute. Each nested attribute is a MAC address.
+ * @NUM_NL80211_NAN_SRF_ATTR: internal
+ * @NL80211_NAN_SRF_ATTR_MAX: highest NAN SRF attribute
+ */
+enum nl80211_nan_srf_attributes {
+ __NL80211_NAN_SRF_INVALID,
+ NL80211_NAN_SRF_INCLUDE,
+ NL80211_NAN_SRF_BF,
+ NL80211_NAN_SRF_BF_IDX,
+ NL80211_NAN_SRF_MAC_ADDRS,
+
+ /* keep last */
+ NUM_NL80211_NAN_SRF_ATTR,
+ NL80211_NAN_SRF_ATTR_MAX = NUM_NL80211_NAN_SRF_ATTR - 1,
+};
+
+/**
+ * enum nl80211_nan_match_attributes - NAN match attributes
+ * @__NL80211_NAN_MATCH_INVALID: invalid
+ * @NL80211_NAN_MATCH_FUNC_LOCAL: the local function that had the
+ * match. This is a nested attribute.
+ * See &enum nl80211_nan_func_attributes.
+ * @NL80211_NAN_MATCH_FUNC_PEER: the peer function
+ * that caused the match. This is a nested attribute.
+ * See &enum nl80211_nan_func_attributes.
+ *
+ * @NUM_NL80211_NAN_MATCH_ATTR: internal
+ * @NL80211_NAN_MATCH_ATTR_MAX: highest NAN match attribute
+ */
+enum nl80211_nan_match_attributes {
+ __NL80211_NAN_MATCH_INVALID,
+ NL80211_NAN_MATCH_FUNC_LOCAL,
+ NL80211_NAN_MATCH_FUNC_PEER,
+
+ /* keep last */
+ NUM_NL80211_NAN_MATCH_ATTR,
+ NL80211_NAN_MATCH_ATTR_MAX = NUM_NL80211_NAN_MATCH_ATTR - 1
+};
+
+#endif /* __LINUX_NL80211_H */
diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h
new file mode 100644
index 0000000..d56bb00
--- /dev/null
+++ b/include/uapi/linux/pci_regs.h
@@ -0,0 +1,1006 @@
+/*
+ * pci_regs.h
+ *
+ * PCI standard defines
+ * Copyright 1994, Drew Eckhardt
+ * Copyright 1997--1999 Martin Mares <mj@ucw.cz>
+ *
+ * For more information, please consult the following manuals (look at
+ * http://www.pcisig.com/ for how to get them):
+ *
+ * PCI BIOS Specification
+ * PCI Local Bus Specification
+ * PCI to PCI Bridge Specification
+ * PCI System Design Guide
+ *
+ * For HyperTransport information, please consult the following manuals
+ * from http://www.hypertransport.org
+ *
+ * The HyperTransport I/O Link Specification
+ */
+
+#ifndef LINUX_PCI_REGS_H
+#define LINUX_PCI_REGS_H
+
+/*
+ * Conventional PCI and PCI-X Mode 1 devices have 256 bytes of
+ * configuration space. PCI-X Mode 2 and PCIe devices have 4096 bytes of
+ * configuration space.
+ */
+#define PCI_CFG_SPACE_SIZE 256
+#define PCI_CFG_SPACE_EXP_SIZE 4096
+
+/*
+ * Under PCI, each device has 256 bytes of configuration address space,
+ * of which the first 64 bytes are standardized as follows:
+ */
+#define PCI_STD_HEADER_SIZEOF 64
+#define PCI_VENDOR_ID 0x00 /* 16 bits */
+#define PCI_DEVICE_ID 0x02 /* 16 bits */
+#define PCI_COMMAND 0x04 /* 16 bits */
+#define PCI_COMMAND_IO 0x1 /* Enable response in I/O space */
+#define PCI_COMMAND_MEMORY 0x2 /* Enable response in Memory space */
+#define PCI_COMMAND_MASTER 0x4 /* Enable bus mastering */
+#define PCI_COMMAND_SPECIAL 0x8 /* Enable response to special cycles */
+#define PCI_COMMAND_INVALIDATE 0x10 /* Use memory write and invalidate */
+#define PCI_COMMAND_VGA_PALETTE 0x20 /* Enable palette snooping */
+#define PCI_COMMAND_PARITY 0x40 /* Enable parity checking */
+#define PCI_COMMAND_WAIT 0x80 /* Enable address/data stepping */
+#define PCI_COMMAND_SERR 0x100 /* Enable SERR */
+#define PCI_COMMAND_FAST_BACK 0x200 /* Enable back-to-back writes */
+#define PCI_COMMAND_INTX_DISABLE 0x400 /* INTx Emulation Disable */
+
+#define PCI_STATUS 0x06 /* 16 bits */
+#define PCI_STATUS_INTERRUPT 0x08 /* Interrupt status */
+#define PCI_STATUS_CAP_LIST 0x10 /* Support Capability List */
+#define PCI_STATUS_66MHZ 0x20 /* Support 66 MHz PCI 2.1 bus */
+#define PCI_STATUS_UDF 0x40 /* Support User Definable Features [obsolete] */
+#define PCI_STATUS_FAST_BACK 0x80 /* Accept fast-back to back */
+#define PCI_STATUS_PARITY 0x100 /* Detected parity error */
+#define PCI_STATUS_DEVSEL_MASK 0x600 /* DEVSEL timing */
+#define PCI_STATUS_DEVSEL_FAST 0x000
+#define PCI_STATUS_DEVSEL_MEDIUM 0x200
+#define PCI_STATUS_DEVSEL_SLOW 0x400
+#define PCI_STATUS_SIG_TARGET_ABORT 0x800 /* Set on target abort */
+#define PCI_STATUS_REC_TARGET_ABORT 0x1000 /* Master ack of " */
+#define PCI_STATUS_REC_MASTER_ABORT 0x2000 /* Set on master abort */
+#define PCI_STATUS_SIG_SYSTEM_ERROR 0x4000 /* Set when we drive SERR */
+#define PCI_STATUS_DETECTED_PARITY 0x8000 /* Set on parity error */
+
+#define PCI_CLASS_REVISION 0x08 /* High 24 bits are class, low 8 revision */
+#define PCI_REVISION_ID 0x08 /* Revision ID */
+#define PCI_CLASS_PROG 0x09 /* Reg. Level Programming Interface */
+#define PCI_CLASS_DEVICE 0x0a /* Device class */
+
+#define PCI_CACHE_LINE_SIZE 0x0c /* 8 bits */
+#define PCI_LATENCY_TIMER 0x0d /* 8 bits */
+#define PCI_HEADER_TYPE 0x0e /* 8 bits */
+#define PCI_HEADER_TYPE_NORMAL 0
+#define PCI_HEADER_TYPE_BRIDGE 1
+#define PCI_HEADER_TYPE_CARDBUS 2
+
+#define PCI_BIST 0x0f /* 8 bits */
+#define PCI_BIST_CODE_MASK 0x0f /* Return result */
+#define PCI_BIST_START 0x40 /* 1 to start BIST, 2 secs or less */
+#define PCI_BIST_CAPABLE 0x80 /* 1 if BIST capable */
+
+/*
+ * Base addresses specify locations in memory or I/O space.
+ * Decoded size can be determined by writing a value of
+ * 0xffffffff to the register, and reading it back. Only
+ * 1 bits are decoded.
+ */
+#define PCI_BASE_ADDRESS_0 0x10 /* 32 bits */
+#define PCI_BASE_ADDRESS_1 0x14 /* 32 bits [htype 0,1 only] */
+#define PCI_BASE_ADDRESS_2 0x18 /* 32 bits [htype 0 only] */
+#define PCI_BASE_ADDRESS_3 0x1c /* 32 bits */
+#define PCI_BASE_ADDRESS_4 0x20 /* 32 bits */
+#define PCI_BASE_ADDRESS_5 0x24 /* 32 bits */
+#define PCI_BASE_ADDRESS_SPACE 0x01 /* 0 = memory, 1 = I/O */
+#define PCI_BASE_ADDRESS_SPACE_IO 0x01
+#define PCI_BASE_ADDRESS_SPACE_MEMORY 0x00
+#define PCI_BASE_ADDRESS_MEM_TYPE_MASK 0x06
+#define PCI_BASE_ADDRESS_MEM_TYPE_32 0x00 /* 32 bit address */
+#define PCI_BASE_ADDRESS_MEM_TYPE_1M 0x02 /* Below 1M [obsolete] */
+#define PCI_BASE_ADDRESS_MEM_TYPE_64 0x04 /* 64 bit address */
+#define PCI_BASE_ADDRESS_MEM_PREFETCH 0x08 /* prefetchable? */
+#define PCI_BASE_ADDRESS_MEM_MASK (~0x0fUL)
+#define PCI_BASE_ADDRESS_IO_MASK (~0x03UL)
+/* bit 1 is reserved if address_space = 1 */
+
+/* Header type 0 (normal devices) */
+#define PCI_CARDBUS_CIS 0x28
+#define PCI_SUBSYSTEM_VENDOR_ID 0x2c
+#define PCI_SUBSYSTEM_ID 0x2e
+#define PCI_ROM_ADDRESS 0x30 /* Bits 31..11 are address, 10..1 reserved */
+#define PCI_ROM_ADDRESS_ENABLE 0x01
+#define PCI_ROM_ADDRESS_MASK (~0x7ffU)
+
+#define PCI_CAPABILITY_LIST 0x34 /* Offset of first capability list entry */
+
+/* 0x35-0x3b are reserved */
+#define PCI_INTERRUPT_LINE 0x3c /* 8 bits */
+#define PCI_INTERRUPT_PIN 0x3d /* 8 bits */
+#define PCI_MIN_GNT 0x3e /* 8 bits */
+#define PCI_MAX_LAT 0x3f /* 8 bits */
+
+/* Header type 1 (PCI-to-PCI bridges) */
+#define PCI_PRIMARY_BUS 0x18 /* Primary bus number */
+#define PCI_SECONDARY_BUS 0x19 /* Secondary bus number */
+#define PCI_SUBORDINATE_BUS 0x1a /* Highest bus number behind the bridge */
+#define PCI_SEC_LATENCY_TIMER 0x1b /* Latency timer for secondary interface */
+#define PCI_IO_BASE 0x1c /* I/O range behind the bridge */
+#define PCI_IO_LIMIT 0x1d
+#define PCI_IO_RANGE_TYPE_MASK 0x0fUL /* I/O bridging type */
+#define PCI_IO_RANGE_TYPE_16 0x00
+#define PCI_IO_RANGE_TYPE_32 0x01
+#define PCI_IO_RANGE_MASK (~0x0fUL) /* Standard 4K I/O windows */
+#define PCI_IO_1K_RANGE_MASK (~0x03UL) /* Intel 1K I/O windows */
+#define PCI_SEC_STATUS 0x1e /* Secondary status register, only bit 14 used */
+#define PCI_MEMORY_BASE 0x20 /* Memory range behind */
+#define PCI_MEMORY_LIMIT 0x22
+#define PCI_MEMORY_RANGE_TYPE_MASK 0x0fUL
+#define PCI_MEMORY_RANGE_MASK (~0x0fUL)
+#define PCI_PREF_MEMORY_BASE 0x24 /* Prefetchable memory range behind */
+#define PCI_PREF_MEMORY_LIMIT 0x26
+#define PCI_PREF_RANGE_TYPE_MASK 0x0fUL
+#define PCI_PREF_RANGE_TYPE_32 0x00
+#define PCI_PREF_RANGE_TYPE_64 0x01
+#define PCI_PREF_RANGE_MASK (~0x0fUL)
+#define PCI_PREF_BASE_UPPER32 0x28 /* Upper half of prefetchable memory range */
+#define PCI_PREF_LIMIT_UPPER32 0x2c
+#define PCI_IO_BASE_UPPER16 0x30 /* Upper half of I/O addresses */
+#define PCI_IO_LIMIT_UPPER16 0x32
+/* 0x34 same as for htype 0 */
+/* 0x35-0x3b is reserved */
+#define PCI_ROM_ADDRESS1 0x38 /* Same as PCI_ROM_ADDRESS, but for htype 1 */
+/* 0x3c-0x3d are same as for htype 0 */
+#define PCI_BRIDGE_CONTROL 0x3e
+#define PCI_BRIDGE_CTL_PARITY 0x01 /* Enable parity detection on secondary interface */
+#define PCI_BRIDGE_CTL_SERR 0x02 /* The same for SERR forwarding */
+#define PCI_BRIDGE_CTL_ISA 0x04 /* Enable ISA mode */
+#define PCI_BRIDGE_CTL_VGA 0x08 /* Forward VGA addresses */
+#define PCI_BRIDGE_CTL_MASTER_ABORT 0x20 /* Report master aborts */
+#define PCI_BRIDGE_CTL_BUS_RESET 0x40 /* Secondary bus reset */
+#define PCI_BRIDGE_CTL_FAST_BACK 0x80 /* Fast Back2Back enabled on secondary interface */
+
+/* Header type 2 (CardBus bridges) */
+#define PCI_CB_CAPABILITY_LIST 0x14
+/* 0x15 reserved */
+#define PCI_CB_SEC_STATUS 0x16 /* Secondary status */
+#define PCI_CB_PRIMARY_BUS 0x18 /* PCI bus number */
+#define PCI_CB_CARD_BUS 0x19 /* CardBus bus number */
+#define PCI_CB_SUBORDINATE_BUS 0x1a /* Subordinate bus number */
+#define PCI_CB_LATENCY_TIMER 0x1b /* CardBus latency timer */
+#define PCI_CB_MEMORY_BASE_0 0x1c
+#define PCI_CB_MEMORY_LIMIT_0 0x20
+#define PCI_CB_MEMORY_BASE_1 0x24
+#define PCI_CB_MEMORY_LIMIT_1 0x28
+#define PCI_CB_IO_BASE_0 0x2c
+#define PCI_CB_IO_BASE_0_HI 0x2e
+#define PCI_CB_IO_LIMIT_0 0x30
+#define PCI_CB_IO_LIMIT_0_HI 0x32
+#define PCI_CB_IO_BASE_1 0x34
+#define PCI_CB_IO_BASE_1_HI 0x36
+#define PCI_CB_IO_LIMIT_1 0x38
+#define PCI_CB_IO_LIMIT_1_HI 0x3a
+#define PCI_CB_IO_RANGE_MASK (~0x03UL)
+/* 0x3c-0x3d are same as for htype 0 */
+#define PCI_CB_BRIDGE_CONTROL 0x3e
+#define PCI_CB_BRIDGE_CTL_PARITY 0x01 /* Similar to standard bridge control register */
+#define PCI_CB_BRIDGE_CTL_SERR 0x02
+#define PCI_CB_BRIDGE_CTL_ISA 0x04
+#define PCI_CB_BRIDGE_CTL_VGA 0x08
+#define PCI_CB_BRIDGE_CTL_MASTER_ABORT 0x20
+#define PCI_CB_BRIDGE_CTL_CB_RESET 0x40 /* CardBus reset */
+#define PCI_CB_BRIDGE_CTL_16BIT_INT 0x80 /* Enable interrupt for 16-bit cards */
+#define PCI_CB_BRIDGE_CTL_PREFETCH_MEM0 0x100 /* Prefetch enable for both memory regions */
+#define PCI_CB_BRIDGE_CTL_PREFETCH_MEM1 0x200
+#define PCI_CB_BRIDGE_CTL_POST_WRITES 0x400
+#define PCI_CB_SUBSYSTEM_VENDOR_ID 0x40
+#define PCI_CB_SUBSYSTEM_ID 0x42
+#define PCI_CB_LEGACY_MODE_BASE 0x44 /* 16-bit PC Card legacy mode base address (ExCa) */
+/* 0x48-0x7f reserved */
+
+/* Capability lists */
+
+#define PCI_CAP_LIST_ID 0 /* Capability ID */
+#define PCI_CAP_ID_PM 0x01 /* Power Management */
+#define PCI_CAP_ID_AGP 0x02 /* Accelerated Graphics Port */
+#define PCI_CAP_ID_VPD 0x03 /* Vital Product Data */
+#define PCI_CAP_ID_SLOTID 0x04 /* Slot Identification */
+#define PCI_CAP_ID_MSI 0x05 /* Message Signalled Interrupts */
+#define PCI_CAP_ID_CHSWP 0x06 /* CompactPCI HotSwap */
+#define PCI_CAP_ID_PCIX 0x07 /* PCI-X */
+#define PCI_CAP_ID_HT 0x08 /* HyperTransport */
+#define PCI_CAP_ID_VNDR 0x09 /* Vendor-Specific */
+#define PCI_CAP_ID_DBG 0x0A /* Debug port */
+#define PCI_CAP_ID_CCRC 0x0B /* CompactPCI Central Resource Control */
+#define PCI_CAP_ID_SHPC 0x0C /* PCI Standard Hot-Plug Controller */
+#define PCI_CAP_ID_SSVID 0x0D /* Bridge subsystem vendor/device ID */
+#define PCI_CAP_ID_AGP3 0x0E /* AGP Target PCI-PCI bridge */
+#define PCI_CAP_ID_SECDEV 0x0F /* Secure Device */
+#define PCI_CAP_ID_EXP 0x10 /* PCI Express */
+#define PCI_CAP_ID_MSIX 0x11 /* MSI-X */
+#define PCI_CAP_ID_SATA 0x12 /* SATA Data/Index Conf. */
+#define PCI_CAP_ID_AF 0x13 /* PCI Advanced Features */
+#define PCI_CAP_ID_EA 0x14 /* PCI Enhanced Allocation */
+#define PCI_CAP_ID_MAX PCI_CAP_ID_EA
+#define PCI_CAP_LIST_NEXT 1 /* Next capability in the list */
+#define PCI_CAP_FLAGS 2 /* Capability defined flags (16 bits) */
+#define PCI_CAP_SIZEOF 4
+
+/* Power Management Registers */
+
+#define PCI_PM_PMC 2 /* PM Capabilities Register */
+#define PCI_PM_CAP_VER_MASK 0x0007 /* Version */
+#define PCI_PM_CAP_PME_CLOCK 0x0008 /* PME clock required */
+#define PCI_PM_CAP_RESERVED 0x0010 /* Reserved field */
+#define PCI_PM_CAP_DSI 0x0020 /* Device specific initialization */
+#define PCI_PM_CAP_AUX_POWER 0x01C0 /* Auxiliary power support mask */
+#define PCI_PM_CAP_D1 0x0200 /* D1 power state support */
+#define PCI_PM_CAP_D2 0x0400 /* D2 power state support */
+#define PCI_PM_CAP_PME 0x0800 /* PME pin supported */
+#define PCI_PM_CAP_PME_MASK 0xF800 /* PME Mask of all supported states */
+#define PCI_PM_CAP_PME_D0 0x0800 /* PME# from D0 */
+#define PCI_PM_CAP_PME_D1 0x1000 /* PME# from D1 */
+#define PCI_PM_CAP_PME_D2 0x2000 /* PME# from D2 */
+#define PCI_PM_CAP_PME_D3 0x4000 /* PME# from D3 (hot) */
+#define PCI_PM_CAP_PME_D3cold 0x8000 /* PME# from D3 (cold) */
+#define PCI_PM_CAP_PME_SHIFT 11 /* Start of the PME Mask in PMC */
+#define PCI_PM_CTRL 4 /* PM control and status register */
+#define PCI_PM_CTRL_STATE_MASK 0x0003 /* Current power state (D0 to D3) */
+#define PCI_PM_CTRL_NO_SOFT_RESET 0x0008 /* No reset for D3hot->D0 */
+#define PCI_PM_CTRL_PME_ENABLE 0x0100 /* PME pin enable */
+#define PCI_PM_CTRL_DATA_SEL_MASK 0x1e00 /* Data select (??) */
+#define PCI_PM_CTRL_DATA_SCALE_MASK 0x6000 /* Data scale (??) */
+#define PCI_PM_CTRL_PME_STATUS 0x8000 /* PME pin status */
+#define PCI_PM_PPB_EXTENSIONS 6 /* PPB support extensions (??) */
+#define PCI_PM_PPB_B2_B3 0x40 /* Stop clock when in D3hot (??) */
+#define PCI_PM_BPCC_ENABLE 0x80 /* Bus power/clock control enable (??) */
+#define PCI_PM_DATA_REGISTER 7 /* (??) */
+#define PCI_PM_SIZEOF 8
+
+/* AGP registers */
+
+#define PCI_AGP_VERSION 2 /* BCD version number */
+#define PCI_AGP_RFU 3 /* Rest of capability flags */
+#define PCI_AGP_STATUS 4 /* Status register */
+#define PCI_AGP_STATUS_RQ_MASK 0xff000000 /* Maximum number of requests - 1 */
+#define PCI_AGP_STATUS_SBA 0x0200 /* Sideband addressing supported */
+#define PCI_AGP_STATUS_64BIT 0x0020 /* 64-bit addressing supported */
+#define PCI_AGP_STATUS_FW 0x0010 /* FW transfers supported */
+#define PCI_AGP_STATUS_RATE4 0x0004 /* 4x transfer rate supported */
+#define PCI_AGP_STATUS_RATE2 0x0002 /* 2x transfer rate supported */
+#define PCI_AGP_STATUS_RATE1 0x0001 /* 1x transfer rate supported */
+#define PCI_AGP_COMMAND 8 /* Control register */
+#define PCI_AGP_COMMAND_RQ_MASK 0xff000000 /* Master: Maximum number of requests */
+#define PCI_AGP_COMMAND_SBA 0x0200 /* Sideband addressing enabled */
+#define PCI_AGP_COMMAND_AGP 0x0100 /* Allow processing of AGP transactions */
+#define PCI_AGP_COMMAND_64BIT 0x0020 /* Allow processing of 64-bit addresses */
+#define PCI_AGP_COMMAND_FW 0x0010 /* Force FW transfers */
+#define PCI_AGP_COMMAND_RATE4 0x0004 /* Use 4x rate */
+#define PCI_AGP_COMMAND_RATE2 0x0002 /* Use 2x rate */
+#define PCI_AGP_COMMAND_RATE1 0x0001 /* Use 1x rate */
+#define PCI_AGP_SIZEOF 12
+
+/* Vital Product Data */
+
+#define PCI_VPD_ADDR 2 /* Address to access (15 bits!) */
+#define PCI_VPD_ADDR_MASK 0x7fff /* Address mask */
+#define PCI_VPD_ADDR_F 0x8000 /* Write 0, 1 indicates completion */
+#define PCI_VPD_DATA 4 /* 32-bits of data returned here */
+#define PCI_CAP_VPD_SIZEOF 8
+
+/* Slot Identification */
+
+#define PCI_SID_ESR 2 /* Expansion Slot Register */
+#define PCI_SID_ESR_NSLOTS 0x1f /* Number of expansion slots available */
+#define PCI_SID_ESR_FIC 0x20 /* First In Chassis Flag */
+#define PCI_SID_CHASSIS_NR 3 /* Chassis Number */
+
+/* Message Signalled Interrupts registers */
+
+#define PCI_MSI_FLAGS 2 /* Message Control */
+#define PCI_MSI_FLAGS_ENABLE 0x0001 /* MSI feature enabled */
+#define PCI_MSI_FLAGS_QMASK 0x000e /* Maximum queue size available */
+#define PCI_MSI_FLAGS_QSIZE 0x0070 /* Message queue size configured */
+#define PCI_MSI_FLAGS_64BIT 0x0080 /* 64-bit addresses allowed */
+#define PCI_MSI_FLAGS_MASKBIT 0x0100 /* Per-vector masking capable */
+#define PCI_MSI_RFU 3 /* Rest of capability flags */
+#define PCI_MSI_ADDRESS_LO 4 /* Lower 32 bits */
+#define PCI_MSI_ADDRESS_HI 8 /* Upper 32 bits (if PCI_MSI_FLAGS_64BIT set) */
+#define PCI_MSI_DATA_32 8 /* 16 bits of data for 32-bit devices */
+#define PCI_MSI_MASK_32 12 /* Mask bits register for 32-bit devices */
+#define PCI_MSI_PENDING_32 16 /* Pending intrs for 32-bit devices */
+#define PCI_MSI_DATA_64 12 /* 16 bits of data for 64-bit devices */
+#define PCI_MSI_MASK_64 16 /* Mask bits register for 64-bit devices */
+#define PCI_MSI_PENDING_64 20 /* Pending intrs for 64-bit devices */
+
+/* MSI-X registers */
+#define PCI_MSIX_FLAGS 2 /* Message Control */
+#define PCI_MSIX_FLAGS_QSIZE 0x07FF /* Table size */
+#define PCI_MSIX_FLAGS_MASKALL 0x4000 /* Mask all vectors for this function */
+#define PCI_MSIX_FLAGS_ENABLE 0x8000 /* MSI-X enable */
+#define PCI_MSIX_TABLE 4 /* Table offset */
+#define PCI_MSIX_TABLE_BIR 0x00000007 /* BAR index */
+#define PCI_MSIX_TABLE_OFFSET 0xfffffff8 /* Offset into specified BAR */
+#define PCI_MSIX_PBA 8 /* Pending Bit Array offset */
+#define PCI_MSIX_PBA_BIR 0x00000007 /* BAR index */
+#define PCI_MSIX_PBA_OFFSET 0xfffffff8 /* Offset into specified BAR */
+#define PCI_MSIX_FLAGS_BIRMASK PCI_MSIX_PBA_BIR /* deprecated */
+#define PCI_CAP_MSIX_SIZEOF 12 /* size of MSIX registers */
+
+/* MSI-X Table entry format */
+#define PCI_MSIX_ENTRY_SIZE 16
+#define PCI_MSIX_ENTRY_LOWER_ADDR 0
+#define PCI_MSIX_ENTRY_UPPER_ADDR 4
+#define PCI_MSIX_ENTRY_DATA 8
+#define PCI_MSIX_ENTRY_VECTOR_CTRL 12
+#define PCI_MSIX_ENTRY_CTRL_MASKBIT 1
+
+/* CompactPCI Hotswap Register */
+
+#define PCI_CHSWP_CSR 2 /* Control and Status Register */
+#define PCI_CHSWP_DHA 0x01 /* Device Hiding Arm */
+#define PCI_CHSWP_EIM 0x02 /* ENUM# Signal Mask */
+#define PCI_CHSWP_PIE 0x04 /* Pending Insert or Extract */
+#define PCI_CHSWP_LOO 0x08 /* LED On / Off */
+#define PCI_CHSWP_PI 0x30 /* Programming Interface */
+#define PCI_CHSWP_EXT 0x40 /* ENUM# status - extraction */
+#define PCI_CHSWP_INS 0x80 /* ENUM# status - insertion */
+
+/* PCI Advanced Feature registers */
+
+#define PCI_AF_LENGTH 2
+#define PCI_AF_CAP 3
+#define PCI_AF_CAP_TP 0x01
+#define PCI_AF_CAP_FLR 0x02
+#define PCI_AF_CTRL 4
+#define PCI_AF_CTRL_FLR 0x01
+#define PCI_AF_STATUS 5
+#define PCI_AF_STATUS_TP 0x01
+#define PCI_CAP_AF_SIZEOF 6 /* size of AF registers */
+
+/* PCI Enhanced Allocation registers */
+
+#define PCI_EA_NUM_ENT 2 /* Number of Capability Entries */
+#define PCI_EA_NUM_ENT_MASK 0x3f /* Num Entries Mask */
+#define PCI_EA_FIRST_ENT 4 /* First EA Entry in List */
+#define PCI_EA_FIRST_ENT_BRIDGE 8 /* First EA Entry for Bridges */
+#define PCI_EA_ES 0x00000007 /* Entry Size */
+#define PCI_EA_BEI 0x000000f0 /* BAR Equivalent Indicator */
+/* 0-5 map to BARs 0-5 respectively */
+#define PCI_EA_BEI_BAR0 0
+#define PCI_EA_BEI_BAR5 5
+#define PCI_EA_BEI_BRIDGE 6 /* Resource behind bridge */
+#define PCI_EA_BEI_ENI 7 /* Equivalent Not Indicated */
+#define PCI_EA_BEI_ROM 8 /* Expansion ROM */
+/* 9-14 map to VF BARs 0-5 respectively */
+#define PCI_EA_BEI_VF_BAR0 9
+#define PCI_EA_BEI_VF_BAR5 14
+#define PCI_EA_BEI_RESERVED 15 /* Reserved - Treat like ENI */
+#define PCI_EA_PP 0x0000ff00 /* Primary Properties */
+#define PCI_EA_SP 0x00ff0000 /* Secondary Properties */
+#define PCI_EA_P_MEM 0x00 /* Non-Prefetch Memory */
+#define PCI_EA_P_MEM_PREFETCH 0x01 /* Prefetchable Memory */
+#define PCI_EA_P_IO 0x02 /* I/O Space */
+#define PCI_EA_P_VF_MEM_PREFETCH 0x03 /* VF Prefetchable Memory */
+#define PCI_EA_P_VF_MEM 0x04 /* VF Non-Prefetch Memory */
+#define PCI_EA_P_BRIDGE_MEM 0x05 /* Bridge Non-Prefetch Memory */
+#define PCI_EA_P_BRIDGE_MEM_PREFETCH 0x06 /* Bridge Prefetchable Memory */
+#define PCI_EA_P_BRIDGE_IO 0x07 /* Bridge I/O Space */
+/* 0x08-0xfc reserved */
+#define PCI_EA_P_MEM_RESERVED 0xfd /* Reserved Memory */
+#define PCI_EA_P_IO_RESERVED 0xfe /* Reserved I/O Space */
+#define PCI_EA_P_UNAVAILABLE 0xff /* Entry Unavailable */
+#define PCI_EA_WRITABLE 0x40000000 /* Writable: 1 = RW, 0 = HwInit */
+#define PCI_EA_ENABLE 0x80000000 /* Enable for this entry */
+#define PCI_EA_BASE 4 /* Base Address Offset */
+#define PCI_EA_MAX_OFFSET 8 /* MaxOffset (resource length) */
+/* bit 0 is reserved */
+#define PCI_EA_IS_64 0x00000002 /* 64-bit field flag */
+#define PCI_EA_FIELD_MASK 0xfffffffc /* For Base & Max Offset */
+
+/* PCI-X registers (Type 0 (non-bridge) devices) */
+
+#define PCI_X_CMD 2 /* Modes & Features */
+#define PCI_X_CMD_DPERR_E 0x0001 /* Data Parity Error Recovery Enable */
+#define PCI_X_CMD_ERO 0x0002 /* Enable Relaxed Ordering */
+#define PCI_X_CMD_READ_512 0x0000 /* 512 byte maximum read byte count */
+#define PCI_X_CMD_READ_1K 0x0004 /* 1Kbyte maximum read byte count */
+#define PCI_X_CMD_READ_2K 0x0008 /* 2Kbyte maximum read byte count */
+#define PCI_X_CMD_READ_4K 0x000c /* 4Kbyte maximum read byte count */
+#define PCI_X_CMD_MAX_READ 0x000c /* Max Memory Read Byte Count */
+ /* Max # of outstanding split transactions */
+#define PCI_X_CMD_SPLIT_1 0x0000 /* Max 1 */
+#define PCI_X_CMD_SPLIT_2 0x0010 /* Max 2 */
+#define PCI_X_CMD_SPLIT_3 0x0020 /* Max 3 */
+#define PCI_X_CMD_SPLIT_4 0x0030 /* Max 4 */
+#define PCI_X_CMD_SPLIT_8 0x0040 /* Max 8 */
+#define PCI_X_CMD_SPLIT_12 0x0050 /* Max 12 */
+#define PCI_X_CMD_SPLIT_16 0x0060 /* Max 16 */
+#define PCI_X_CMD_SPLIT_32 0x0070 /* Max 32 */
+#define PCI_X_CMD_MAX_SPLIT 0x0070 /* Max Outstanding Split Transactions */
+#define PCI_X_CMD_VERSION(x) (((x) >> 12) & 3) /* Version */
+#define PCI_X_STATUS 4 /* PCI-X capabilities */
+#define PCI_X_STATUS_DEVFN 0x000000ff /* A copy of devfn */
+#define PCI_X_STATUS_BUS 0x0000ff00 /* A copy of bus nr */
+#define PCI_X_STATUS_64BIT 0x00010000 /* 64-bit device */
+#define PCI_X_STATUS_133MHZ 0x00020000 /* 133 MHz capable */
+#define PCI_X_STATUS_SPL_DISC 0x00040000 /* Split Completion Discarded */
+#define PCI_X_STATUS_UNX_SPL 0x00080000 /* Unexpected Split Completion */
+#define PCI_X_STATUS_COMPLEX 0x00100000 /* Device Complexity */
+#define PCI_X_STATUS_MAX_READ 0x00600000 /* Designed Max Memory Read Count */
+#define PCI_X_STATUS_MAX_SPLIT 0x03800000 /* Designed Max Outstanding Split Transactions */
+#define PCI_X_STATUS_MAX_CUM 0x1c000000 /* Designed Max Cumulative Read Size */
+#define PCI_X_STATUS_SPL_ERR 0x20000000 /* Rcvd Split Completion Error Msg */
+#define PCI_X_STATUS_266MHZ 0x40000000 /* 266 MHz capable */
+#define PCI_X_STATUS_533MHZ 0x80000000 /* 533 MHz capable */
+#define PCI_X_ECC_CSR 8 /* ECC control and status */
+#define PCI_CAP_PCIX_SIZEOF_V0 8 /* size of registers for Version 0 */
+#define PCI_CAP_PCIX_SIZEOF_V1 24 /* size for Version 1 */
+#define PCI_CAP_PCIX_SIZEOF_V2 PCI_CAP_PCIX_SIZEOF_V1 /* Same for v2 */
+
+/* PCI-X registers (Type 1 (bridge) devices) */
+
+#define PCI_X_BRIDGE_SSTATUS 2 /* Secondary Status */
+#define PCI_X_SSTATUS_64BIT 0x0001 /* Secondary AD interface is 64 bits */
+#define PCI_X_SSTATUS_133MHZ 0x0002 /* 133 MHz capable */
+#define PCI_X_SSTATUS_FREQ 0x03c0 /* Secondary Bus Mode and Frequency */
+#define PCI_X_SSTATUS_VERS 0x3000 /* PCI-X Capability Version */
+#define PCI_X_SSTATUS_V1 0x1000 /* Mode 2, not Mode 1 */
+#define PCI_X_SSTATUS_V2 0x2000 /* Mode 1 or Modes 1 and 2 */
+#define PCI_X_SSTATUS_266MHZ 0x4000 /* 266 MHz capable */
+#define PCI_X_SSTATUS_533MHZ 0x8000 /* 533 MHz capable */
+#define PCI_X_BRIDGE_STATUS 4 /* Bridge Status */
+
+/* PCI Bridge Subsystem ID registers */
+
+#define PCI_SSVID_VENDOR_ID 4 /* PCI Bridge subsystem vendor ID */
+#define PCI_SSVID_DEVICE_ID 6 /* PCI Bridge subsystem device ID */
+
+/* PCI Express capability registers */
+
+#define PCI_EXP_FLAGS 2 /* Capabilities register */
+#define PCI_EXP_FLAGS_VERS 0x000f /* Capability version */
+#define PCI_EXP_FLAGS_TYPE 0x00f0 /* Device/Port type */
+#define PCI_EXP_TYPE_ENDPOINT 0x0 /* Express Endpoint */
+#define PCI_EXP_TYPE_LEG_END 0x1 /* Legacy Endpoint */
+#define PCI_EXP_TYPE_ROOT_PORT 0x4 /* Root Port */
+#define PCI_EXP_TYPE_UPSTREAM 0x5 /* Upstream Port */
+#define PCI_EXP_TYPE_DOWNSTREAM 0x6 /* Downstream Port */
+#define PCI_EXP_TYPE_PCI_BRIDGE 0x7 /* PCIe to PCI/PCI-X Bridge */
+#define PCI_EXP_TYPE_PCIE_BRIDGE 0x8 /* PCI/PCI-X to PCIe Bridge */
+#define PCI_EXP_TYPE_RC_END 0x9 /* Root Complex Integrated Endpoint */
+#define PCI_EXP_TYPE_RC_EC 0xa /* Root Complex Event Collector */
+#define PCI_EXP_FLAGS_SLOT 0x0100 /* Slot implemented */
+#define PCI_EXP_FLAGS_IRQ 0x3e00 /* Interrupt message number */
+#define PCI_EXP_DEVCAP 4 /* Device capabilities */
+#define PCI_EXP_DEVCAP_PAYLOAD 0x00000007 /* Max_Payload_Size */
+#define PCI_EXP_DEVCAP_PHANTOM 0x00000018 /* Phantom functions */
+#define PCI_EXP_DEVCAP_EXT_TAG 0x00000020 /* Extended tags */
+#define PCI_EXP_DEVCAP_L0S 0x000001c0 /* L0s Acceptable Latency */
+#define PCI_EXP_DEVCAP_L1 0x00000e00 /* L1 Acceptable Latency */
+#define PCI_EXP_DEVCAP_ATN_BUT 0x00001000 /* Attention Button Present */
+#define PCI_EXP_DEVCAP_ATN_IND 0x00002000 /* Attention Indicator Present */
+#define PCI_EXP_DEVCAP_PWR_IND 0x00004000 /* Power Indicator Present */
+#define PCI_EXP_DEVCAP_RBER 0x00008000 /* Role-Based Error Reporting */
+#define PCI_EXP_DEVCAP_PWR_VAL 0x03fc0000 /* Slot Power Limit Value */
+#define PCI_EXP_DEVCAP_PWR_SCL 0x0c000000 /* Slot Power Limit Scale */
+#define PCI_EXP_DEVCAP_FLR 0x10000000 /* Function Level Reset */
+#define PCI_EXP_DEVCTL 8 /* Device Control */
+#define PCI_EXP_DEVCTL_CERE 0x0001 /* Correctable Error Reporting En. */
+#define PCI_EXP_DEVCTL_NFERE 0x0002 /* Non-Fatal Error Reporting Enable */
+#define PCI_EXP_DEVCTL_FERE 0x0004 /* Fatal Error Reporting Enable */
+#define PCI_EXP_DEVCTL_URRE 0x0008 /* Unsupported Request Reporting En. */
+#define PCI_EXP_DEVCTL_RELAX_EN 0x0010 /* Enable relaxed ordering */
+#define PCI_EXP_DEVCTL_PAYLOAD 0x00e0 /* Max_Payload_Size */
+#define PCI_EXP_DEVCTL_EXT_TAG 0x0100 /* Extended Tag Field Enable */
+#define PCI_EXP_DEVCTL_PHANTOM 0x0200 /* Phantom Functions Enable */
+#define PCI_EXP_DEVCTL_AUX_PME 0x0400 /* Auxiliary Power PM Enable */
+#define PCI_EXP_DEVCTL_NOSNOOP_EN 0x0800 /* Enable No Snoop */
+#define PCI_EXP_DEVCTL_READRQ 0x7000 /* Max_Read_Request_Size */
+#define PCI_EXP_DEVCTL_READRQ_128B 0x0000 /* 128 Bytes */
+#define PCI_EXP_DEVCTL_READRQ_256B 0x1000 /* 256 Bytes */
+#define PCI_EXP_DEVCTL_READRQ_512B 0x2000 /* 512 Bytes */
+#define PCI_EXP_DEVCTL_READRQ_1024B 0x3000 /* 1024 Bytes */
+#define PCI_EXP_DEVCTL_BCR_FLR 0x8000 /* Bridge Configuration Retry / FLR */
+#define PCI_EXP_DEVSTA 10 /* Device Status */
+#define PCI_EXP_DEVSTA_CED 0x0001 /* Correctable Error Detected */
+#define PCI_EXP_DEVSTA_NFED 0x0002 /* Non-Fatal Error Detected */
+#define PCI_EXP_DEVSTA_FED 0x0004 /* Fatal Error Detected */
+#define PCI_EXP_DEVSTA_URD 0x0008 /* Unsupported Request Detected */
+#define PCI_EXP_DEVSTA_AUXPD 0x0010 /* AUX Power Detected */
+#define PCI_EXP_DEVSTA_TRPND 0x0020 /* Transactions Pending */
+#define PCI_EXP_LNKCAP 12 /* Link Capabilities */
+#define PCI_EXP_LNKCAP_SLS 0x0000000f /* Supported Link Speeds */
+#define PCI_EXP_LNKCAP_SLS_2_5GB 0x00000001 /* LNKCAP2 SLS Vector bit 0 */
+#define PCI_EXP_LNKCAP_SLS_5_0GB 0x00000002 /* LNKCAP2 SLS Vector bit 1 */
+#define PCI_EXP_LNKCAP_MLW 0x000003f0 /* Maximum Link Width */
+#define PCI_EXP_LNKCAP_ASPMS 0x00000c00 /* ASPM Support */
+#define PCI_EXP_LNKCAP_L0SEL 0x00007000 /* L0s Exit Latency */
+#define PCI_EXP_LNKCAP_L1EL 0x00038000 /* L1 Exit Latency */
+#define PCI_EXP_LNKCAP_CLKPM 0x00040000 /* Clock Power Management */
+#define PCI_EXP_LNKCAP_SDERC 0x00080000 /* Surprise Down Error Reporting Capable */
+#define PCI_EXP_LNKCAP_DLLLARC 0x00100000 /* Data Link Layer Link Active Reporting Capable */
+#define PCI_EXP_LNKCAP_LBNC 0x00200000 /* Link Bandwidth Notification Capability */
+#define PCI_EXP_LNKCAP_PN 0xff000000 /* Port Number */
+#define PCI_EXP_LNKCTL 16 /* Link Control */
+#define PCI_EXP_LNKCTL_ASPMC 0x0003 /* ASPM Control */
+#define PCI_EXP_LNKCTL_ASPM_L0S 0x0001 /* L0s Enable */
+#define PCI_EXP_LNKCTL_ASPM_L1 0x0002 /* L1 Enable */
+#define PCI_EXP_LNKCTL_RCB 0x0008 /* Read Completion Boundary */
+#define PCI_EXP_LNKCTL_LD 0x0010 /* Link Disable */
+#define PCI_EXP_LNKCTL_RL 0x0020 /* Retrain Link */
+#define PCI_EXP_LNKCTL_CCC 0x0040 /* Common Clock Configuration */
+#define PCI_EXP_LNKCTL_ES 0x0080 /* Extended Synch */
+#define PCI_EXP_LNKCTL_CLKREQ_EN 0x0100 /* Enable clkreq */
+#define PCI_EXP_LNKCTL_HAWD 0x0200 /* Hardware Autonomous Width Disable */
+#define PCI_EXP_LNKCTL_LBMIE 0x0400 /* Link Bandwidth Management Interrupt Enable */
+#define PCI_EXP_LNKCTL_LABIE 0x0800 /* Link Autonomous Bandwidth Interrupt Enable */
+#define PCI_EXP_LNKSTA 18 /* Link Status */
+#define PCI_EXP_LNKSTA_CLS 0x000f /* Current Link Speed */
+#define PCI_EXP_LNKSTA_CLS_2_5GB 0x0001 /* Current Link Speed 2.5GT/s */
+#define PCI_EXP_LNKSTA_CLS_5_0GB 0x0002 /* Current Link Speed 5.0GT/s */
+#define PCI_EXP_LNKSTA_CLS_8_0GB 0x0003 /* Current Link Speed 8.0GT/s */
+#define PCI_EXP_LNKSTA_NLW 0x03f0 /* Negotiated Link Width */
+#define PCI_EXP_LNKSTA_NLW_X1 0x0010 /* Current Link Width x1 */
+#define PCI_EXP_LNKSTA_NLW_X2 0x0020 /* Current Link Width x2 */
+#define PCI_EXP_LNKSTA_NLW_X4 0x0040 /* Current Link Width x4 */
+#define PCI_EXP_LNKSTA_NLW_X8 0x0080 /* Current Link Width x8 */
+#define PCI_EXP_LNKSTA_NLW_SHIFT 4 /* start of NLW mask in link status */
+#define PCI_EXP_LNKSTA_LT 0x0800 /* Link Training */
+#define PCI_EXP_LNKSTA_SLC 0x1000 /* Slot Clock Configuration */
+#define PCI_EXP_LNKSTA_DLLLA 0x2000 /* Data Link Layer Link Active */
+#define PCI_EXP_LNKSTA_LBMS 0x4000 /* Link Bandwidth Management Status */
+#define PCI_EXP_LNKSTA_LABS 0x8000 /* Link Autonomous Bandwidth Status */
+#define PCI_CAP_EXP_ENDPOINT_SIZEOF_V1 20 /* v1 endpoints end here */
+#define PCI_EXP_SLTCAP 20 /* Slot Capabilities */
+#define PCI_EXP_SLTCAP_ABP 0x00000001 /* Attention Button Present */
+#define PCI_EXP_SLTCAP_PCP 0x00000002 /* Power Controller Present */
+#define PCI_EXP_SLTCAP_MRLSP 0x00000004 /* MRL Sensor Present */
+#define PCI_EXP_SLTCAP_AIP 0x00000008 /* Attention Indicator Present */
+#define PCI_EXP_SLTCAP_PIP 0x00000010 /* Power Indicator Present */
+#define PCI_EXP_SLTCAP_HPS 0x00000020 /* Hot-Plug Surprise */
+#define PCI_EXP_SLTCAP_HPC 0x00000040 /* Hot-Plug Capable */
+#define PCI_EXP_SLTCAP_SPLV 0x00007f80 /* Slot Power Limit Value */
+#define PCI_EXP_SLTCAP_SPLS 0x00018000 /* Slot Power Limit Scale */
+#define PCI_EXP_SLTCAP_EIP 0x00020000 /* Electromechanical Interlock Present */
+#define PCI_EXP_SLTCAP_NCCS 0x00040000 /* No Command Completed Support */
+#define PCI_EXP_SLTCAP_PSN 0xfff80000 /* Physical Slot Number */
+#define PCI_EXP_SLTCTL 24 /* Slot Control */
+#define PCI_EXP_SLTCTL_ABPE 0x0001 /* Attention Button Pressed Enable */
+#define PCI_EXP_SLTCTL_PFDE 0x0002 /* Power Fault Detected Enable */
+#define PCI_EXP_SLTCTL_MRLSCE 0x0004 /* MRL Sensor Changed Enable */
+#define PCI_EXP_SLTCTL_PDCE 0x0008 /* Presence Detect Changed Enable */
+#define PCI_EXP_SLTCTL_CCIE 0x0010 /* Command Completed Interrupt Enable */
+#define PCI_EXP_SLTCTL_HPIE 0x0020 /* Hot-Plug Interrupt Enable */
+#define PCI_EXP_SLTCTL_AIC 0x00c0 /* Attention Indicator Control */
+#define PCI_EXP_SLTCTL_ATTN_IND_ON 0x0040 /* Attention Indicator on */
+#define PCI_EXP_SLTCTL_ATTN_IND_BLINK 0x0080 /* Attention Indicator blinking */
+#define PCI_EXP_SLTCTL_ATTN_IND_OFF 0x00c0 /* Attention Indicator off */
+#define PCI_EXP_SLTCTL_PIC 0x0300 /* Power Indicator Control */
+#define PCI_EXP_SLTCTL_PWR_IND_ON 0x0100 /* Power Indicator on */
+#define PCI_EXP_SLTCTL_PWR_IND_BLINK 0x0200 /* Power Indicator blinking */
+#define PCI_EXP_SLTCTL_PWR_IND_OFF 0x0300 /* Power Indicator off */
+#define PCI_EXP_SLTCTL_PCC 0x0400 /* Power Controller Control */
+#define PCI_EXP_SLTCTL_PWR_ON 0x0000 /* Power On */
+#define PCI_EXP_SLTCTL_PWR_OFF 0x0400 /* Power Off */
+#define PCI_EXP_SLTCTL_EIC 0x0800 /* Electromechanical Interlock Control */
+#define PCI_EXP_SLTCTL_DLLSCE 0x1000 /* Data Link Layer State Changed Enable */
+#define PCI_EXP_SLTSTA 26 /* Slot Status */
+#define PCI_EXP_SLTSTA_ABP 0x0001 /* Attention Button Pressed */
+#define PCI_EXP_SLTSTA_PFD 0x0002 /* Power Fault Detected */
+#define PCI_EXP_SLTSTA_MRLSC 0x0004 /* MRL Sensor Changed */
+#define PCI_EXP_SLTSTA_PDC 0x0008 /* Presence Detect Changed */
+#define PCI_EXP_SLTSTA_CC 0x0010 /* Command Completed */
+#define PCI_EXP_SLTSTA_MRLSS 0x0020 /* MRL Sensor State */
+#define PCI_EXP_SLTSTA_PDS 0x0040 /* Presence Detect State */
+#define PCI_EXP_SLTSTA_EIS 0x0080 /* Electromechanical Interlock Status */
+#define PCI_EXP_SLTSTA_DLLSC 0x0100 /* Data Link Layer State Changed */
+#define PCI_EXP_RTCTL 28 /* Root Control */
+#define PCI_EXP_RTCTL_SECEE 0x0001 /* System Error on Correctable Error */
+#define PCI_EXP_RTCTL_SENFEE 0x0002 /* System Error on Non-Fatal Error */
+#define PCI_EXP_RTCTL_SEFEE 0x0004 /* System Error on Fatal Error */
+#define PCI_EXP_RTCTL_PMEIE 0x0008 /* PME Interrupt Enable */
+#define PCI_EXP_RTCTL_CRSSVE 0x0010 /* CRS Software Visibility Enable */
+#define PCI_EXP_RTCAP 30 /* Root Capabilities */
+#define PCI_EXP_RTCAP_CRSVIS 0x0001 /* CRS Software Visibility capability */
+#define PCI_EXP_RTSTA 32 /* Root Status */
+#define PCI_EXP_RTSTA_PME 0x00010000 /* PME status */
+#define PCI_EXP_RTSTA_PENDING 0x00020000 /* PME pending */
+/*
+ * The Device Capabilities 2, Device Status 2, Device Control 2,
+ * Link Capabilities 2, Link Status 2, Link Control 2,
+ * Slot Capabilities 2, Slot Status 2, and Slot Control 2 registers
+ * are only present on devices with PCIe Capability version 2.
+ * Use pcie_capability_read_word() and similar interfaces to use them
+ * safely.
+ */
+#define PCI_EXP_DEVCAP2 36 /* Device Capabilities 2 */
+#define PCI_EXP_DEVCAP2_ARI 0x00000020 /* Alternative Routing-ID */
+#define PCI_EXP_DEVCAP2_ATOMIC_ROUTE 0x00000040 /* Atomic Op routing */
+#define PCI_EXP_DEVCAP2_ATOMIC_COMP64 0x00000100 /* Atomic 64-bit compare */
+#define PCI_EXP_DEVCAP2_LTR 0x00000800 /* Latency tolerance reporting */
+#define PCI_EXP_DEVCAP2_OBFF_MASK 0x000c0000 /* OBFF support mechanism */
+#define PCI_EXP_DEVCAP2_OBFF_MSG 0x00040000 /* New message signaling */
+#define PCI_EXP_DEVCAP2_OBFF_WAKE 0x00080000 /* Re-use WAKE# for OBFF */
+#define PCI_EXP_DEVCTL2 40 /* Device Control 2 */
+#define PCI_EXP_DEVCTL2_COMP_TIMEOUT 0x000f /* Completion Timeout Value */
+#define PCI_EXP_DEVCTL2_ARI 0x0020 /* Alternative Routing-ID */
+#define PCI_EXP_DEVCTL2_ATOMIC_REQ 0x0040 /* Set Atomic requests */
+#define PCI_EXP_DEVCTL2_ATOMIC_EGRESS_BLOCK 0x0080 /* Block atomic egress */
+#define PCI_EXP_DEVCTL2_IDO_REQ_EN 0x0100 /* Allow IDO for requests */
+#define PCI_EXP_DEVCTL2_IDO_CMP_EN 0x0200 /* Allow IDO for completions */
+#define PCI_EXP_DEVCTL2_LTR_EN 0x0400 /* Enable LTR mechanism */
+#define PCI_EXP_DEVCTL2_OBFF_MSGA_EN 0x2000 /* Enable OBFF Message type A */
+#define PCI_EXP_DEVCTL2_OBFF_MSGB_EN 0x4000 /* Enable OBFF Message type B */
+#define PCI_EXP_DEVCTL2_OBFF_WAKE_EN 0x6000 /* OBFF using WAKE# signaling */
+#define PCI_EXP_DEVSTA2 42 /* Device Status 2 */
+#define PCI_CAP_EXP_ENDPOINT_SIZEOF_V2 44 /* v2 endpoints end here */
+#define PCI_EXP_LNKCAP2 44 /* Link Capabilities 2 */
+#define PCI_EXP_LNKCAP2_SLS_2_5GB 0x00000002 /* Supported Speed 2.5GT/s */
+#define PCI_EXP_LNKCAP2_SLS_5_0GB 0x00000004 /* Supported Speed 5.0GT/s */
+#define PCI_EXP_LNKCAP2_SLS_8_0GB 0x00000008 /* Supported Speed 8.0GT/s */
+#define PCI_EXP_LNKCAP2_CROSSLINK 0x00000100 /* Crosslink supported */
+#define PCI_EXP_LNKCTL2 48 /* Link Control 2 */
+#define PCI_EXP_LNKSTA2 50 /* Link Status 2 */
+#define PCI_EXP_SLTCAP2 52 /* Slot Capabilities 2 */
+#define PCI_EXP_SLTCTL2 56 /* Slot Control 2 */
+#define PCI_EXP_SLTSTA2 58 /* Slot Status 2 */
+
+/* Extended Capabilities (PCI-X 2.0 and Express) */
+#define PCI_EXT_CAP_ID(header) (header & 0x0000ffff)
+#define PCI_EXT_CAP_VER(header) ((header >> 16) & 0xf)
+#define PCI_EXT_CAP_NEXT(header) ((header >> 20) & 0xffc)
+
+#define PCI_EXT_CAP_ID_ERR 0x01 /* Advanced Error Reporting */
+#define PCI_EXT_CAP_ID_VC 0x02 /* Virtual Channel Capability */
+#define PCI_EXT_CAP_ID_DSN 0x03 /* Device Serial Number */
+#define PCI_EXT_CAP_ID_PWR 0x04 /* Power Budgeting */
+#define PCI_EXT_CAP_ID_RCLD 0x05 /* Root Complex Link Declaration */
+#define PCI_EXT_CAP_ID_RCILC 0x06 /* Root Complex Internal Link Control */
+#define PCI_EXT_CAP_ID_RCEC 0x07 /* Root Complex Event Collector */
+#define PCI_EXT_CAP_ID_MFVC 0x08 /* Multi-Function VC Capability */
+#define PCI_EXT_CAP_ID_VC9 0x09 /* same as _VC */
+#define PCI_EXT_CAP_ID_RCRB 0x0A /* Root Complex RB? */
+#define PCI_EXT_CAP_ID_VNDR 0x0B /* Vendor-Specific */
+#define PCI_EXT_CAP_ID_CAC 0x0C /* Config Access - obsolete */
+#define PCI_EXT_CAP_ID_ACS 0x0D /* Access Control Services */
+#define PCI_EXT_CAP_ID_ARI 0x0E /* Alternate Routing ID */
+#define PCI_EXT_CAP_ID_ATS 0x0F /* Address Translation Services */
+#define PCI_EXT_CAP_ID_SRIOV 0x10 /* Single Root I/O Virtualization */
+#define PCI_EXT_CAP_ID_MRIOV 0x11 /* Multi Root I/O Virtualization */
+#define PCI_EXT_CAP_ID_MCAST 0x12 /* Multicast */
+#define PCI_EXT_CAP_ID_PRI 0x13 /* Page Request Interface */
+#define PCI_EXT_CAP_ID_AMD_XXX 0x14 /* Reserved for AMD */
+#define PCI_EXT_CAP_ID_REBAR 0x15 /* Resizable BAR */
+#define PCI_EXT_CAP_ID_DPA 0x16 /* Dynamic Power Allocation */
+#define PCI_EXT_CAP_ID_TPH 0x17 /* TPH Requester */
+#define PCI_EXT_CAP_ID_LTR 0x18 /* Latency Tolerance Reporting */
+#define PCI_EXT_CAP_ID_SECPCI 0x19 /* Secondary PCIe Capability */
+#define PCI_EXT_CAP_ID_PMUX 0x1A /* Protocol Multiplexing */
+#define PCI_EXT_CAP_ID_PASID 0x1B /* Process Address Space ID */
+#define PCI_EXT_CAP_ID_DPC 0x1D /* Downstream Port Containment */
+#define PCI_EXT_CAP_ID_L1SS 0x1E /* L1 PM Substates */
+#define PCI_EXT_CAP_ID_PTM 0x1F /* Precision Time Measurement */
+#define PCI_EXT_CAP_ID_MAX PCI_EXT_CAP_ID_PTM
+
+#define PCI_EXT_CAP_DSN_SIZEOF 12
+#define PCI_EXT_CAP_MCAST_ENDPOINT_SIZEOF 40
+
+/* Advanced Error Reporting */
+#define PCI_ERR_UNCOR_STATUS 4 /* Uncorrectable Error Status */
+#define PCI_ERR_UNC_UND 0x00000001 /* Undefined */
+#define PCI_ERR_UNC_DLP 0x00000010 /* Data Link Protocol */
+#define PCI_ERR_UNC_SURPDN 0x00000020 /* Surprise Down */
+#define PCI_ERR_UNC_POISON_TLP 0x00001000 /* Poisoned TLP */
+#define PCI_ERR_UNC_FCP 0x00002000 /* Flow Control Protocol */
+#define PCI_ERR_UNC_COMP_TIME 0x00004000 /* Completion Timeout */
+#define PCI_ERR_UNC_COMP_ABORT 0x00008000 /* Completer Abort */
+#define PCI_ERR_UNC_UNX_COMP 0x00010000 /* Unexpected Completion */
+#define PCI_ERR_UNC_RX_OVER 0x00020000 /* Receiver Overflow */
+#define PCI_ERR_UNC_MALF_TLP 0x00040000 /* Malformed TLP */
+#define PCI_ERR_UNC_ECRC 0x00080000 /* ECRC Error Status */
+#define PCI_ERR_UNC_UNSUP 0x00100000 /* Unsupported Request */
+#define PCI_ERR_UNC_ACSV 0x00200000 /* ACS Violation */
+#define PCI_ERR_UNC_INTN 0x00400000 /* internal error */
+#define PCI_ERR_UNC_MCBTLP 0x00800000 /* MC blocked TLP */
+#define PCI_ERR_UNC_ATOMEG 0x01000000 /* Atomic egress blocked */
+#define PCI_ERR_UNC_TLPPRE 0x02000000 /* TLP prefix blocked */
+#define PCI_ERR_UNCOR_MASK 8 /* Uncorrectable Error Mask */
+ /* Same bits as above */
+#define PCI_ERR_UNCOR_SEVER 12 /* Uncorrectable Error Severity */
+ /* Same bits as above */
+#define PCI_ERR_COR_STATUS 16 /* Correctable Error Status */
+#define PCI_ERR_COR_RCVR 0x00000001 /* Receiver Error Status */
+#define PCI_ERR_COR_BAD_TLP 0x00000040 /* Bad TLP Status */
+#define PCI_ERR_COR_BAD_DLLP 0x00000080 /* Bad DLLP Status */
+#define PCI_ERR_COR_REP_ROLL 0x00000100 /* REPLAY_NUM Rollover */
+#define PCI_ERR_COR_REP_TIMER 0x00001000 /* Replay Timer Timeout */
+#define PCI_ERR_COR_ADV_NFAT 0x00002000 /* Advisory Non-Fatal */
+#define PCI_ERR_COR_INTERNAL 0x00004000 /* Corrected Internal */
+#define PCI_ERR_COR_LOG_OVER 0x00008000 /* Header Log Overflow */
+#define PCI_ERR_COR_MASK 20 /* Correctable Error Mask */
+ /* Same bits as above */
+#define PCI_ERR_CAP 24 /* Advanced Error Capabilities */
+#define PCI_ERR_CAP_FEP(x) ((x) & 31) /* First Error Pointer */
+#define PCI_ERR_CAP_ECRC_GENC 0x00000020 /* ECRC Generation Capable */
+#define PCI_ERR_CAP_ECRC_GENE 0x00000040 /* ECRC Generation Enable */
+#define PCI_ERR_CAP_ECRC_CHKC 0x00000080 /* ECRC Check Capable */
+#define PCI_ERR_CAP_ECRC_CHKE 0x00000100 /* ECRC Check Enable */
+#define PCI_ERR_HEADER_LOG 28 /* Header Log Register (16 bytes) */
+#define PCI_ERR_ROOT_COMMAND 44 /* Root Error Command */
+/* Correctable Err Reporting Enable */
+#define PCI_ERR_ROOT_CMD_COR_EN 0x00000001
+/* Non-fatal Err Reporting Enable */
+#define PCI_ERR_ROOT_CMD_NONFATAL_EN 0x00000002
+/* Fatal Err Reporting Enable */
+#define PCI_ERR_ROOT_CMD_FATAL_EN 0x00000004
+#define PCI_ERR_ROOT_STATUS 48
+#define PCI_ERR_ROOT_COR_RCV 0x00000001 /* ERR_COR Received */
+/* Multi ERR_COR Received */
+#define PCI_ERR_ROOT_MULTI_COR_RCV 0x00000002
+/* ERR_FATAL/NONFATAL Received */
+#define PCI_ERR_ROOT_UNCOR_RCV 0x00000004
+/* Multi ERR_FATAL/NONFATAL Received */
+#define PCI_ERR_ROOT_MULTI_UNCOR_RCV 0x00000008
+#define PCI_ERR_ROOT_FIRST_FATAL 0x00000010 /* First Fatal */
+#define PCI_ERR_ROOT_NONFATAL_RCV 0x00000020 /* Non-Fatal Received */
+#define PCI_ERR_ROOT_FATAL_RCV 0x00000040 /* Fatal Received */
+#define PCI_ERR_ROOT_ERR_SRC 52 /* Error Source Identification */
+
+/* Virtual Channel */
+#define PCI_VC_PORT_CAP1 4
+#define PCI_VC_CAP1_EVCC 0x00000007 /* extended VC count */
+#define PCI_VC_CAP1_LPEVCC 0x00000070 /* low prio extended VC count */
+#define PCI_VC_CAP1_ARB_SIZE 0x00000c00
+#define PCI_VC_PORT_CAP2 8
+#define PCI_VC_CAP2_32_PHASE 0x00000002
+#define PCI_VC_CAP2_64_PHASE 0x00000004
+#define PCI_VC_CAP2_128_PHASE 0x00000008
+#define PCI_VC_CAP2_ARB_OFF 0xff000000
+#define PCI_VC_PORT_CTRL 12
+#define PCI_VC_PORT_CTRL_LOAD_TABLE 0x00000001
+#define PCI_VC_PORT_STATUS 14
+#define PCI_VC_PORT_STATUS_TABLE 0x00000001
+#define PCI_VC_RES_CAP 16
+#define PCI_VC_RES_CAP_32_PHASE 0x00000002
+#define PCI_VC_RES_CAP_64_PHASE 0x00000004
+#define PCI_VC_RES_CAP_128_PHASE 0x00000008
+#define PCI_VC_RES_CAP_128_PHASE_TB 0x00000010
+#define PCI_VC_RES_CAP_256_PHASE 0x00000020
+#define PCI_VC_RES_CAP_ARB_OFF 0xff000000
+#define PCI_VC_RES_CTRL 20
+#define PCI_VC_RES_CTRL_LOAD_TABLE 0x00010000
+#define PCI_VC_RES_CTRL_ARB_SELECT 0x000e0000
+#define PCI_VC_RES_CTRL_ID 0x07000000
+#define PCI_VC_RES_CTRL_ENABLE 0x80000000
+#define PCI_VC_RES_STATUS 26
+#define PCI_VC_RES_STATUS_TABLE 0x00000001
+#define PCI_VC_RES_STATUS_NEGO 0x00000002
+#define PCI_CAP_VC_BASE_SIZEOF 0x10
+#define PCI_CAP_VC_PER_VC_SIZEOF 0x0C
+
+/* Power Budgeting */
+#define PCI_PWR_DSR 4 /* Data Select Register */
+#define PCI_PWR_DATA 8 /* Data Register */
+#define PCI_PWR_DATA_BASE(x) ((x) & 0xff) /* Base Power */
+#define PCI_PWR_DATA_SCALE(x) (((x) >> 8) & 3) /* Data Scale */
+#define PCI_PWR_DATA_PM_SUB(x) (((x) >> 10) & 7) /* PM Sub State */
+#define PCI_PWR_DATA_PM_STATE(x) (((x) >> 13) & 3) /* PM State */
+#define PCI_PWR_DATA_TYPE(x) (((x) >> 15) & 7) /* Type */
+#define PCI_PWR_DATA_RAIL(x) (((x) >> 18) & 7) /* Power Rail */
+#define PCI_PWR_CAP 12 /* Capability */
+#define PCI_PWR_CAP_BUDGET(x) ((x) & 1) /* Included in system budget */
+#define PCI_EXT_CAP_PWR_SIZEOF 16
+
+/* Vendor-Specific (VSEC, PCI_EXT_CAP_ID_VNDR) */
+#define PCI_VNDR_HEADER 4 /* Vendor-Specific Header */
+#define PCI_VNDR_HEADER_ID(x) ((x) & 0xffff)
+#define PCI_VNDR_HEADER_REV(x) (((x) >> 16) & 0xf)
+#define PCI_VNDR_HEADER_LEN(x) (((x) >> 20) & 0xfff)
+
+/*
+ * HyperTransport sub capability types
+ *
+ * Unfortunately there are both 3 bit and 5 bit capability types defined
+ * in the HT spec, catering for that is a little messy. You probably don't
+ * want to use these directly, just use pci_find_ht_capability() and it
+ * will do the right thing for you.
+ */
+#define HT_3BIT_CAP_MASK 0xE0
+#define HT_CAPTYPE_SLAVE 0x00 /* Slave/Primary link configuration */
+#define HT_CAPTYPE_HOST 0x20 /* Host/Secondary link configuration */
+
+#define HT_5BIT_CAP_MASK 0xF8
+#define HT_CAPTYPE_IRQ 0x80 /* IRQ Configuration */
+#define HT_CAPTYPE_REMAPPING_40 0xA0 /* 40 bit address remapping */
+#define HT_CAPTYPE_REMAPPING_64 0xA2 /* 64 bit address remapping */
+#define HT_CAPTYPE_UNITID_CLUMP 0x90 /* Unit ID clumping */
+#define HT_CAPTYPE_EXTCONF 0x98 /* Extended Configuration Space Access */
+#define HT_CAPTYPE_MSI_MAPPING 0xA8 /* MSI Mapping Capability */
+#define HT_MSI_FLAGS 0x02 /* Offset to flags */
+#define HT_MSI_FLAGS_ENABLE 0x1 /* Mapping enable */
+#define HT_MSI_FLAGS_FIXED 0x2 /* Fixed mapping only */
+#define HT_MSI_FIXED_ADDR 0x00000000FEE00000ULL /* Fixed addr */
+#define HT_MSI_ADDR_LO 0x04 /* Offset to low addr bits */
+#define HT_MSI_ADDR_LO_MASK 0xFFF00000 /* Low address bit mask */
+#define HT_MSI_ADDR_HI 0x08 /* Offset to high addr bits */
+#define HT_CAPTYPE_DIRECT_ROUTE 0xB0 /* Direct routing configuration */
+#define HT_CAPTYPE_VCSET 0xB8 /* Virtual Channel configuration */
+#define HT_CAPTYPE_ERROR_RETRY 0xC0 /* Retry on error configuration */
+#define HT_CAPTYPE_GEN3 0xD0 /* Generation 3 HyperTransport configuration */
+#define HT_CAPTYPE_PM 0xE0 /* HyperTransport power management configuration */
+#define HT_CAP_SIZEOF_LONG 28 /* slave & primary */
+#define HT_CAP_SIZEOF_SHORT 24 /* host & secondary */
+
+/* Alternative Routing-ID Interpretation */
+#define PCI_ARI_CAP 0x04 /* ARI Capability Register */
+#define PCI_ARI_CAP_MFVC 0x0001 /* MFVC Function Groups Capability */
+#define PCI_ARI_CAP_ACS 0x0002 /* ACS Function Groups Capability */
+#define PCI_ARI_CAP_NFN(x) (((x) >> 8) & 0xff) /* Next Function Number */
+#define PCI_ARI_CTRL 0x06 /* ARI Control Register */
+#define PCI_ARI_CTRL_MFVC 0x0001 /* MFVC Function Groups Enable */
+#define PCI_ARI_CTRL_ACS 0x0002 /* ACS Function Groups Enable */
+#define PCI_ARI_CTRL_FG(x) (((x) >> 4) & 7) /* Function Group */
+#define PCI_EXT_CAP_ARI_SIZEOF 8
+
+/* Address Translation Service */
+#define PCI_ATS_CAP 0x04 /* ATS Capability Register */
+#define PCI_ATS_CAP_QDEP(x) ((x) & 0x1f) /* Invalidate Queue Depth */
+#define PCI_ATS_MAX_QDEP 32 /* Max Invalidate Queue Depth */
+#define PCI_ATS_CTRL 0x06 /* ATS Control Register */
+#define PCI_ATS_CTRL_ENABLE 0x8000 /* ATS Enable */
+#define PCI_ATS_CTRL_STU(x) ((x) & 0x1f) /* Smallest Translation Unit */
+#define PCI_ATS_MIN_STU 12 /* shift of minimum STU block */
+#define PCI_EXT_CAP_ATS_SIZEOF 8
+
+/* Page Request Interface */
+#define PCI_PRI_CTRL 0x04 /* PRI control register */
+#define PCI_PRI_CTRL_ENABLE 0x01 /* Enable */
+#define PCI_PRI_CTRL_RESET 0x02 /* Reset */
+#define PCI_PRI_STATUS 0x06 /* PRI status register */
+#define PCI_PRI_STATUS_RF 0x001 /* Response Failure */
+#define PCI_PRI_STATUS_UPRGI 0x002 /* Unexpected PRG index */
+#define PCI_PRI_STATUS_STOPPED 0x100 /* PRI Stopped */
+#define PCI_PRI_MAX_REQ 0x08 /* PRI max reqs supported */
+#define PCI_PRI_ALLOC_REQ 0x0c /* PRI max reqs allowed */
+#define PCI_EXT_CAP_PRI_SIZEOF 16
+
+/* Process Address Space ID */
+#define PCI_PASID_CAP 0x04 /* PASID feature register */
+#define PCI_PASID_CAP_EXEC 0x02 /* Exec permissions Supported */
+#define PCI_PASID_CAP_PRIV 0x04 /* Privilege Mode Supported */
+#define PCI_PASID_CTRL 0x06 /* PASID control register */
+#define PCI_PASID_CTRL_ENABLE 0x01 /* Enable bit */
+#define PCI_PASID_CTRL_EXEC 0x02 /* Exec permissions Enable */
+#define PCI_PASID_CTRL_PRIV 0x04 /* Privilege Mode Enable */
+#define PCI_EXT_CAP_PASID_SIZEOF 8
+
+/* Single Root I/O Virtualization */
+#define PCI_SRIOV_CAP 0x04 /* SR-IOV Capabilities */
+#define PCI_SRIOV_CAP_VFM 0x01 /* VF Migration Capable */
+#define PCI_SRIOV_CAP_INTR(x) ((x) >> 21) /* Interrupt Message Number */
+#define PCI_SRIOV_CTRL 0x08 /* SR-IOV Control */
+#define PCI_SRIOV_CTRL_VFE 0x01 /* VF Enable */
+#define PCI_SRIOV_CTRL_VFM 0x02 /* VF Migration Enable */
+#define PCI_SRIOV_CTRL_INTR 0x04 /* VF Migration Interrupt Enable */
+#define PCI_SRIOV_CTRL_MSE 0x08 /* VF Memory Space Enable */
+#define PCI_SRIOV_CTRL_ARI 0x10 /* ARI Capable Hierarchy */
+#define PCI_SRIOV_STATUS 0x0a /* SR-IOV Status */
+#define PCI_SRIOV_STATUS_VFM 0x01 /* VF Migration Status */
+#define PCI_SRIOV_INITIAL_VF 0x0c /* Initial VFs */
+#define PCI_SRIOV_TOTAL_VF 0x0e /* Total VFs */
+#define PCI_SRIOV_NUM_VF 0x10 /* Number of VFs */
+#define PCI_SRIOV_FUNC_LINK 0x12 /* Function Dependency Link */
+#define PCI_SRIOV_VF_OFFSET 0x14 /* First VF Offset */
+#define PCI_SRIOV_VF_STRIDE 0x16 /* Following VF Stride */
+#define PCI_SRIOV_VF_DID 0x1a /* VF Device ID */
+#define PCI_SRIOV_SUP_PGSIZE 0x1c /* Supported Page Sizes */
+#define PCI_SRIOV_SYS_PGSIZE 0x20 /* System Page Size */
+#define PCI_SRIOV_BAR 0x24 /* VF BAR0 */
+#define PCI_SRIOV_NUM_BARS 6 /* Number of VF BARs */
+#define PCI_SRIOV_VFM 0x3c /* VF Migration State Array Offset*/
+#define PCI_SRIOV_VFM_BIR(x) ((x) & 7) /* State BIR */
+#define PCI_SRIOV_VFM_OFFSET(x) ((x) & ~7) /* State Offset */
+#define PCI_SRIOV_VFM_UA 0x0 /* Inactive.Unavailable */
+#define PCI_SRIOV_VFM_MI 0x1 /* Dormant.MigrateIn */
+#define PCI_SRIOV_VFM_MO 0x2 /* Active.MigrateOut */
+#define PCI_SRIOV_VFM_AV 0x3 /* Active.Available */
+#define PCI_EXT_CAP_SRIOV_SIZEOF 64
+
+#define PCI_LTR_MAX_SNOOP_LAT 0x4
+#define PCI_LTR_MAX_NOSNOOP_LAT 0x6
+#define PCI_LTR_VALUE_MASK 0x000003ff
+#define PCI_LTR_SCALE_MASK 0x00001c00
+#define PCI_LTR_SCALE_SHIFT 10
+#define PCI_EXT_CAP_LTR_SIZEOF 8
+
+/* Access Control Service */
+#define PCI_ACS_CAP 0x04 /* ACS Capability Register */
+#define PCI_ACS_SV 0x01 /* Source Validation */
+#define PCI_ACS_TB 0x02 /* Translation Blocking */
+#define PCI_ACS_RR 0x04 /* P2P Request Redirect */
+#define PCI_ACS_CR 0x08 /* P2P Completion Redirect */
+#define PCI_ACS_UF 0x10 /* Upstream Forwarding */
+#define PCI_ACS_EC 0x20 /* P2P Egress Control */
+#define PCI_ACS_DT 0x40 /* Direct Translated P2P */
+#define PCI_ACS_EGRESS_BITS 0x05 /* ACS Egress Control Vector Size */
+#define PCI_ACS_CTRL 0x06 /* ACS Control Register */
+#define PCI_ACS_EGRESS_CTL_V 0x08 /* ACS Egress Control Vector */
+
+#define PCI_VSEC_HDR 4 /* extended cap - vendor-specific */
+#define PCI_VSEC_HDR_LEN_SHIFT 20 /* shift for length field */
+
+/* SATA capability */
+#define PCI_SATA_REGS 4 /* SATA REGs specifier */
+#define PCI_SATA_REGS_MASK 0xF /* location - BAR#/inline */
+#define PCI_SATA_REGS_INLINE 0xF /* REGS in config space */
+#define PCI_SATA_SIZEOF_SHORT 8
+#define PCI_SATA_SIZEOF_LONG 16
+
+/* Resizable BARs */
+#define PCI_REBAR_CTRL 8 /* control register */
+#define PCI_REBAR_CTRL_NBAR_MASK (7 << 5) /* mask for # bars */
+#define PCI_REBAR_CTRL_NBAR_SHIFT 5 /* shift for # bars */
+
+/* Dynamic Power Allocation */
+#define PCI_DPA_CAP 4 /* capability register */
+#define PCI_DPA_CAP_SUBSTATE_MASK 0x1F /* # substates - 1 */
+#define PCI_DPA_BASE_SIZEOF 16 /* size with 0 substates */
+
+/* TPH Requester */
+#define PCI_TPH_CAP 4 /* capability register */
+#define PCI_TPH_CAP_LOC_MASK 0x600 /* location mask */
+#define PCI_TPH_LOC_NONE 0x000 /* no location */
+#define PCI_TPH_LOC_CAP 0x200 /* in capability */
+#define PCI_TPH_LOC_MSIX 0x400 /* in MSI-X */
+#define PCI_TPH_CAP_ST_MASK 0x07FF0000 /* st table mask */
+#define PCI_TPH_CAP_ST_SHIFT 16 /* st table shift */
+#define PCI_TPH_BASE_SIZEOF 12 /* size with no st table */
+
+/* Downstream Port Containment */
+#define PCI_EXP_DPC_CAP 4 /* DPC Capability */
+#define PCI_EXP_DPC_CAP_RP_EXT 0x20 /* Root Port Extensions for DPC */
+#define PCI_EXP_DPC_CAP_POISONED_TLP 0x40 /* Poisoned TLP Egress Blocking Supported */
+#define PCI_EXP_DPC_CAP_SW_TRIGGER 0x80 /* Software Triggering Supported */
+#define PCI_EXP_DPC_CAP_DL_ACTIVE 0x1000 /* ERR_COR signal on DL_Active supported */
+
+#define PCI_EXP_DPC_CTL 6 /* DPC control */
+#define PCI_EXP_DPC_CTL_EN_NONFATAL 0x02 /* Enable trigger on ERR_NONFATAL message */
+#define PCI_EXP_DPC_CTL_INT_EN 0x08 /* DPC Interrupt Enable */
+
+#define PCI_EXP_DPC_STATUS 8 /* DPC Status */
+#define PCI_EXP_DPC_STATUS_TRIGGER 0x01 /* Trigger Status */
+#define PCI_EXP_DPC_STATUS_INTERRUPT 0x08 /* Interrupt Status */
+#define PCI_EXP_DPC_RP_BUSY 0x10 /* Root Port Busy */
+
+#define PCI_EXP_DPC_SOURCE_ID 10 /* DPC Source Identifier */
+
+/* Precision Time Measurement */
+#define PCI_PTM_CAP 0x04 /* PTM Capability */
+#define PCI_PTM_CAP_REQ 0x00000001 /* Requester capable */
+#define PCI_PTM_CAP_ROOT 0x00000004 /* Root capable */
+#define PCI_PTM_GRANULARITY_MASK 0x0000FF00 /* Clock granularity */
+#define PCI_PTM_CTRL 0x08 /* PTM Control */
+#define PCI_PTM_CTRL_ENABLE 0x00000001 /* PTM enable */
+#define PCI_PTM_CTRL_ROOT 0x00000002 /* Root select */
+
+/* L1 PM Substates */
+#define PCI_L1SS_CAP 4 /* capability register */
+#define PCI_L1SS_CAP_PCIPM_L1_2 1 /* PCI PM L1.2 Support */
+#define PCI_L1SS_CAP_PCIPM_L1_1 2 /* PCI PM L1.1 Support */
+#define PCI_L1SS_CAP_ASPM_L1_2 4 /* ASPM L1.2 Support */
+#define PCI_L1SS_CAP_ASPM_L1_1 8 /* ASPM L1.1 Support */
+#define PCI_L1SS_CAP_L1_PM_SS 16 /* L1 PM Substates Support */
+#define PCI_L1SS_CTL1 8 /* Control Register 1 */
+#define PCI_L1SS_CTL1_PCIPM_L1_2 1 /* PCI PM L1.2 Enable */
+#define PCI_L1SS_CTL1_PCIPM_L1_1 2 /* PCI PM L1.1 Support */
+#define PCI_L1SS_CTL1_ASPM_L1_2 4 /* ASPM L1.2 Support */
+#define PCI_L1SS_CTL1_ASPM_L1_1 8 /* ASPM L1.1 Support */
+#define PCI_L1SS_CTL1_L1SS_MASK 0x0000000F
+#define PCI_L1SS_CTL2 0xC /* Control Register 2 */
+
+#endif /* LINUX_PCI_REGS_H */
diff --git a/include/uapi/linux/wireless.h b/include/uapi/linux/wireless.h
new file mode 100644
index 0000000..d9ecd7c
--- /dev/null
+++ b/include/uapi/linux/wireless.h
@@ -0,0 +1,1109 @@
+/*
+ * This file define a set of standard wireless extensions
+ *
+ * Version : 22 16.3.07
+ *
+ * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com>
+ * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved.
+ */
+
+#ifndef _UAPI_LINUX_WIRELESS_H
+#define _UAPI_LINUX_WIRELESS_H
+
+/************************** DOCUMENTATION **************************/
+/*
+ * Initial APIs (1996 -> onward) :
+ * -----------------------------
+ * Basically, the wireless extensions are for now a set of standard ioctl
+ * call + /proc/net/wireless
+ *
+ * The entry /proc/net/wireless give statistics and information on the
+ * driver.
+ * This is better than having each driver having its entry because
+ * its centralised and we may remove the driver module safely.
+ *
+ * Ioctl are used to configure the driver and issue commands. This is
+ * better than command line options of insmod because we may want to
+ * change dynamically (while the driver is running) some parameters.
+ *
+ * The ioctl mechanimsm are copied from standard devices ioctl.
+ * We have the list of command plus a structure descibing the
+ * data exchanged...
+ * Note that to add these ioctl, I was obliged to modify :
+ * # net/core/dev.c (two place + add include)
+ * # net/ipv4/af_inet.c (one place + add include)
+ *
+ * /proc/net/wireless is a copy of /proc/net/dev.
+ * We have a structure for data passed from the driver to /proc/net/wireless
+ * Too add this, I've modified :
+ * # net/core/dev.c (two other places)
+ * # include/linux/netdevice.h (one place)
+ * # include/linux/proc_fs.h (one place)
+ *
+ * New driver API (2002 -> onward) :
+ * -------------------------------
+ * This file is only concerned with the user space API and common definitions.
+ * The new driver API is defined and documented in :
+ * # include/net/iw_handler.h
+ *
+ * Note as well that /proc/net/wireless implementation has now moved in :
+ * # net/core/wireless.c
+ *
+ * Wireless Events (2002 -> onward) :
+ * --------------------------------
+ * Events are defined at the end of this file, and implemented in :
+ * # net/core/wireless.c
+ *
+ * Other comments :
+ * --------------
+ * Do not add here things that are redundant with other mechanisms
+ * (drivers init, ifconfig, /proc/net/dev, ...) and with are not
+ * wireless specific.
+ *
+ * These wireless extensions are not magic : each driver has to provide
+ * support for them...
+ *
+ * IMPORTANT NOTE : As everything in the kernel, this is very much a
+ * work in progress. Contact me if you have ideas of improvements...
+ */
+
+/***************************** INCLUDES *****************************/
+
+#include <linux/types.h> /* for __u* and __s* typedefs */
+#include <linux/socket.h> /* for "struct sockaddr" et al */
+#include <linux/if.h> /* for IFNAMSIZ and co... */
+
+/***************************** VERSION *****************************/
+/*
+ * This constant is used to know the availability of the wireless
+ * extensions and to know which version of wireless extensions it is
+ * (there is some stuff that will be added in the future...)
+ * I just plan to increment with each new version.
+ */
+#define WIRELESS_EXT 22
+
+/*
+ * Changes :
+ *
+ * V2 to V3
+ * --------
+ * Alan Cox start some incompatibles changes. I've integrated a bit more.
+ * - Encryption renamed to Encode to avoid US regulation problems
+ * - Frequency changed from float to struct to avoid problems on old 386
+ *
+ * V3 to V4
+ * --------
+ * - Add sensitivity
+ *
+ * V4 to V5
+ * --------
+ * - Missing encoding definitions in range
+ * - Access points stuff
+ *
+ * V5 to V6
+ * --------
+ * - 802.11 support (ESSID ioctls)
+ *
+ * V6 to V7
+ * --------
+ * - define IW_ESSID_MAX_SIZE and IW_MAX_AP
+ *
+ * V7 to V8
+ * --------
+ * - Changed my e-mail address
+ * - More 802.11 support (nickname, rate, rts, frag)
+ * - List index in frequencies
+ *
+ * V8 to V9
+ * --------
+ * - Support for 'mode of operation' (ad-hoc, managed...)
+ * - Support for unicast and multicast power saving
+ * - Change encoding to support larger tokens (>64 bits)
+ * - Updated iw_params (disable, flags) and use it for NWID
+ * - Extracted iw_point from iwreq for clarity
+ *
+ * V9 to V10
+ * ---------
+ * - Add PM capability to range structure
+ * - Add PM modifier : MAX/MIN/RELATIVE
+ * - Add encoding option : IW_ENCODE_NOKEY
+ * - Add TxPower ioctls (work like TxRate)
+ *
+ * V10 to V11
+ * ----------
+ * - Add WE version in range (help backward/forward compatibility)
+ * - Add retry ioctls (work like PM)
+ *
+ * V11 to V12
+ * ----------
+ * - Add SIOCSIWSTATS to get /proc/net/wireless programatically
+ * - Add DEV PRIVATE IOCTL to avoid collisions in SIOCDEVPRIVATE space
+ * - Add new statistics (frag, retry, beacon)
+ * - Add average quality (for user space calibration)
+ *
+ * V12 to V13
+ * ----------
+ * - Document creation of new driver API.
+ * - Extract union iwreq_data from struct iwreq (for new driver API).
+ * - Rename SIOCSIWNAME as SIOCSIWCOMMIT
+ *
+ * V13 to V14
+ * ----------
+ * - Wireless Events support : define struct iw_event
+ * - Define additional specific event numbers
+ * - Add "addr" and "param" fields in union iwreq_data
+ * - AP scanning stuff (SIOCSIWSCAN and friends)
+ *
+ * V14 to V15
+ * ----------
+ * - Add IW_PRIV_TYPE_ADDR for struct sockaddr private arg
+ * - Make struct iw_freq signed (both m & e), add explicit padding
+ * - Add IWEVCUSTOM for driver specific event/scanning token
+ * - Add IW_MAX_GET_SPY for driver returning a lot of addresses
+ * - Add IW_TXPOW_RANGE for range of Tx Powers
+ * - Add IWEVREGISTERED & IWEVEXPIRED events for Access Points
+ * - Add IW_MODE_MONITOR for passive monitor
+ *
+ * V15 to V16
+ * ----------
+ * - Increase the number of bitrates in iw_range to 32 (for 802.11g)
+ * - Increase the number of frequencies in iw_range to 32 (for 802.11b+a)
+ * - Reshuffle struct iw_range for increases, add filler
+ * - Increase IW_MAX_AP to 64 for driver returning a lot of addresses
+ * - Remove IW_MAX_GET_SPY because conflict with enhanced spy support
+ * - Add SIOCSIWTHRSPY/SIOCGIWTHRSPY and "struct iw_thrspy"
+ * - Add IW_ENCODE_TEMP and iw_range->encoding_login_index
+ *
+ * V16 to V17
+ * ----------
+ * - Add flags to frequency -> auto/fixed
+ * - Document (struct iw_quality *)->updated, add new flags (INVALID)
+ * - Wireless Event capability in struct iw_range
+ * - Add support for relative TxPower (yick !)
+ *
+ * V17 to V18 (From Jouni Malinen <j@w1.fi>)
+ * ----------
+ * - Add support for WPA/WPA2
+ * - Add extended encoding configuration (SIOCSIWENCODEEXT and
+ * SIOCGIWENCODEEXT)
+ * - Add SIOCSIWGENIE/SIOCGIWGENIE
+ * - Add SIOCSIWMLME
+ * - Add SIOCSIWPMKSA
+ * - Add struct iw_range bit field for supported encoding capabilities
+ * - Add optional scan request parameters for SIOCSIWSCAN
+ * - Add SIOCSIWAUTH/SIOCGIWAUTH for setting authentication and WPA
+ * related parameters (extensible up to 4096 parameter values)
+ * - Add wireless events: IWEVGENIE, IWEVMICHAELMICFAILURE,
+ * IWEVASSOCREQIE, IWEVASSOCRESPIE, IWEVPMKIDCAND
+ *
+ * V18 to V19
+ * ----------
+ * - Remove (struct iw_point *)->pointer from events and streams
+ * - Remove header includes to help user space
+ * - Increase IW_ENCODING_TOKEN_MAX from 32 to 64
+ * - Add IW_QUAL_ALL_UPDATED and IW_QUAL_ALL_INVALID macros
+ * - Add explicit flag to tell stats are in dBm : IW_QUAL_DBM
+ * - Add IW_IOCTL_IDX() and IW_EVENT_IDX() macros
+ *
+ * V19 to V20
+ * ----------
+ * - RtNetlink requests support (SET/GET)
+ *
+ * V20 to V21
+ * ----------
+ * - Remove (struct net_device *)->get_wireless_stats()
+ * - Change length in ESSID and NICK to strlen() instead of strlen()+1
+ * - Add IW_RETRY_SHORT/IW_RETRY_LONG retry modifiers
+ * - Power/Retry relative values no longer * 100000
+ * - Add explicit flag to tell stats are in 802.11k RCPI : IW_QUAL_RCPI
+ *
+ * V21 to V22
+ * ----------
+ * - Prevent leaking of kernel space in stream on 64 bits.
+ */
+
+/**************************** CONSTANTS ****************************/
+
+/* -------------------------- IOCTL LIST -------------------------- */
+
+/* Wireless Identification */
+#define SIOCSIWCOMMIT 0x8B00 /* Commit pending changes to driver */
+#define SIOCGIWNAME 0x8B01 /* get name == wireless protocol */
+/* SIOCGIWNAME is used to verify the presence of Wireless Extensions.
+ * Common values : "IEEE 802.11-DS", "IEEE 802.11-FH", "IEEE 802.11b"...
+ * Don't put the name of your driver there, it's useless. */
+
+/* Basic operations */
+#define SIOCSIWNWID 0x8B02 /* set network id (pre-802.11) */
+#define SIOCGIWNWID 0x8B03 /* get network id (the cell) */
+#define SIOCSIWFREQ 0x8B04 /* set channel/frequency (Hz) */
+#define SIOCGIWFREQ 0x8B05 /* get channel/frequency (Hz) */
+#define SIOCSIWMODE 0x8B06 /* set operation mode */
+#define SIOCGIWMODE 0x8B07 /* get operation mode */
+#define SIOCSIWSENS 0x8B08 /* set sensitivity (dBm) */
+#define SIOCGIWSENS 0x8B09 /* get sensitivity (dBm) */
+
+/* Informative stuff */
+#define SIOCSIWRANGE 0x8B0A /* Unused */
+#define SIOCGIWRANGE 0x8B0B /* Get range of parameters */
+#define SIOCSIWPRIV 0x8B0C /* Unused */
+#define SIOCGIWPRIV 0x8B0D /* get private ioctl interface info */
+#define SIOCSIWSTATS 0x8B0E /* Unused */
+#define SIOCGIWSTATS 0x8B0F /* Get /proc/net/wireless stats */
+/* SIOCGIWSTATS is strictly used between user space and the kernel, and
+ * is never passed to the driver (i.e. the driver will never see it). */
+
+/* Spy support (statistics per MAC address - used for Mobile IP support) */
+#define SIOCSIWSPY 0x8B10 /* set spy addresses */
+#define SIOCGIWSPY 0x8B11 /* get spy info (quality of link) */
+#define SIOCSIWTHRSPY 0x8B12 /* set spy threshold (spy event) */
+#define SIOCGIWTHRSPY 0x8B13 /* get spy threshold */
+
+/* Access Point manipulation */
+#define SIOCSIWAP 0x8B14 /* set access point MAC addresses */
+#define SIOCGIWAP 0x8B15 /* get access point MAC addresses */
+#define SIOCGIWAPLIST 0x8B17 /* Deprecated in favor of scanning */
+#define SIOCSIWSCAN 0x8B18 /* trigger scanning (list cells) */
+#define SIOCGIWSCAN 0x8B19 /* get scanning results */
+
+/* 802.11 specific support */
+#define SIOCSIWESSID 0x8B1A /* set ESSID (network name) */
+#define SIOCGIWESSID 0x8B1B /* get ESSID */
+#define SIOCSIWNICKN 0x8B1C /* set node name/nickname */
+#define SIOCGIWNICKN 0x8B1D /* get node name/nickname */
+/* As the ESSID and NICKN are strings up to 32 bytes long, it doesn't fit
+ * within the 'iwreq' structure, so we need to use the 'data' member to
+ * point to a string in user space, like it is done for RANGE... */
+
+/* Other parameters useful in 802.11 and some other devices */
+#define SIOCSIWRATE 0x8B20 /* set default bit rate (bps) */
+#define SIOCGIWRATE 0x8B21 /* get default bit rate (bps) */
+#define SIOCSIWRTS 0x8B22 /* set RTS/CTS threshold (bytes) */
+#define SIOCGIWRTS 0x8B23 /* get RTS/CTS threshold (bytes) */
+#define SIOCSIWFRAG 0x8B24 /* set fragmentation thr (bytes) */
+#define SIOCGIWFRAG 0x8B25 /* get fragmentation thr (bytes) */
+#define SIOCSIWTXPOW 0x8B26 /* set transmit power (dBm) */
+#define SIOCGIWTXPOW 0x8B27 /* get transmit power (dBm) */
+#define SIOCSIWRETRY 0x8B28 /* set retry limits and lifetime */
+#define SIOCGIWRETRY 0x8B29 /* get retry limits and lifetime */
+
+/* Encoding stuff (scrambling, hardware security, WEP...) */
+#define SIOCSIWENCODE 0x8B2A /* set encoding token & mode */
+#define SIOCGIWENCODE 0x8B2B /* get encoding token & mode */
+/* Power saving stuff (power management, unicast and multicast) */
+#define SIOCSIWPOWER 0x8B2C /* set Power Management settings */
+#define SIOCGIWPOWER 0x8B2D /* get Power Management settings */
+
+/* WPA : Generic IEEE 802.11 informatiom element (e.g., for WPA/RSN/WMM).
+ * This ioctl uses struct iw_point and data buffer that includes IE id and len
+ * fields. More than one IE may be included in the request. Setting the generic
+ * IE to empty buffer (len=0) removes the generic IE from the driver. Drivers
+ * are allowed to generate their own WPA/RSN IEs, but in these cases, drivers
+ * are required to report the used IE as a wireless event, e.g., when
+ * associating with an AP. */
+#define SIOCSIWGENIE 0x8B30 /* set generic IE */
+#define SIOCGIWGENIE 0x8B31 /* get generic IE */
+
+/* WPA : IEEE 802.11 MLME requests */
+#define SIOCSIWMLME 0x8B16 /* request MLME operation; uses
+ * struct iw_mlme */
+/* WPA : Authentication mode parameters */
+#define SIOCSIWAUTH 0x8B32 /* set authentication mode params */
+#define SIOCGIWAUTH 0x8B33 /* get authentication mode params */
+
+/* WPA : Extended version of encoding configuration */
+#define SIOCSIWENCODEEXT 0x8B34 /* set encoding token & mode */
+#define SIOCGIWENCODEEXT 0x8B35 /* get encoding token & mode */
+
+/* WPA2 : PMKSA cache management */
+#define SIOCSIWPMKSA 0x8B36 /* PMKSA cache operation */
+
+/* -------------------- DEV PRIVATE IOCTL LIST -------------------- */
+
+/* These 32 ioctl are wireless device private, for 16 commands.
+ * Each driver is free to use them for whatever purpose it chooses,
+ * however the driver *must* export the description of those ioctls
+ * with SIOCGIWPRIV and *must* use arguments as defined below.
+ * If you don't follow those rules, DaveM is going to hate you (reason :
+ * it make mixed 32/64bit operation impossible).
+ */
+#define SIOCIWFIRSTPRIV 0x8BE0
+#define SIOCIWLASTPRIV 0x8BFF
+/* Previously, we were using SIOCDEVPRIVATE, but we now have our
+ * separate range because of collisions with other tools such as
+ * 'mii-tool'.
+ * We now have 32 commands, so a bit more space ;-).
+ * Also, all 'even' commands are only usable by root and don't return the
+ * content of ifr/iwr to user (but you are not obliged to use the set/get
+ * convention, just use every other two command). More details in iwpriv.c.
+ * And I repeat : you are not forced to use them with iwpriv, but you
+ * must be compliant with it.
+ */
+
+/* ------------------------- IOCTL STUFF ------------------------- */
+
+/* The first and the last (range) */
+#define SIOCIWFIRST 0x8B00
+#define SIOCIWLAST SIOCIWLASTPRIV /* 0x8BFF */
+#define IW_IOCTL_IDX(cmd) ((cmd) - SIOCIWFIRST)
+#define IW_HANDLER(id, func) \
+ [IW_IOCTL_IDX(id)] = func
+
+/* Odd : get (world access), even : set (root access) */
+#define IW_IS_SET(cmd) (!((cmd) & 0x1))
+#define IW_IS_GET(cmd) ((cmd) & 0x1)
+
+/* ----------------------- WIRELESS EVENTS ----------------------- */
+/* Those are *NOT* ioctls, do not issue request on them !!! */
+/* Most events use the same identifier as ioctl requests */
+
+#define IWEVTXDROP 0x8C00 /* Packet dropped to excessive retry */
+#define IWEVQUAL 0x8C01 /* Quality part of statistics (scan) */
+#define IWEVCUSTOM 0x8C02 /* Driver specific ascii string */
+#define IWEVREGISTERED 0x8C03 /* Discovered a new node (AP mode) */
+#define IWEVEXPIRED 0x8C04 /* Expired a node (AP mode) */
+#define IWEVGENIE 0x8C05 /* Generic IE (WPA, RSN, WMM, ..)
+ * (scan results); This includes id and
+ * length fields. One IWEVGENIE may
+ * contain more than one IE. Scan
+ * results may contain one or more
+ * IWEVGENIE events. */
+#define IWEVMICHAELMICFAILURE 0x8C06 /* Michael MIC failure
+ * (struct iw_michaelmicfailure)
+ */
+#define IWEVASSOCREQIE 0x8C07 /* IEs used in (Re)Association Request.
+ * The data includes id and length
+ * fields and may contain more than one
+ * IE. This event is required in
+ * Managed mode if the driver
+ * generates its own WPA/RSN IE. This
+ * should be sent just before
+ * IWEVREGISTERED event for the
+ * association. */
+#define IWEVASSOCRESPIE 0x8C08 /* IEs used in (Re)Association
+ * Response. The data includes id and
+ * length fields and may contain more
+ * than one IE. This may be sent
+ * between IWEVASSOCREQIE and
+ * IWEVREGISTERED events for the
+ * association. */
+#define IWEVPMKIDCAND 0x8C09 /* PMKID candidate for RSN
+ * pre-authentication
+ * (struct iw_pmkid_cand) */
+
+#define IWEVFIRST 0x8C00
+#define IW_EVENT_IDX(cmd) ((cmd) - IWEVFIRST)
+
+/* ------------------------- PRIVATE INFO ------------------------- */
+/*
+ * The following is used with SIOCGIWPRIV. It allow a driver to define
+ * the interface (name, type of data) for its private ioctl.
+ * Privates ioctl are SIOCIWFIRSTPRIV -> SIOCIWLASTPRIV
+ */
+
+#define IW_PRIV_TYPE_MASK 0x7000 /* Type of arguments */
+#define IW_PRIV_TYPE_NONE 0x0000
+#define IW_PRIV_TYPE_BYTE 0x1000 /* Char as number */
+#define IW_PRIV_TYPE_CHAR 0x2000 /* Char as character */
+#define IW_PRIV_TYPE_INT 0x4000 /* 32 bits int */
+#define IW_PRIV_TYPE_FLOAT 0x5000 /* struct iw_freq */
+#define IW_PRIV_TYPE_ADDR 0x6000 /* struct sockaddr */
+
+#define IW_PRIV_SIZE_FIXED 0x0800 /* Variable or fixed number of args */
+
+#define IW_PRIV_SIZE_MASK 0x07FF /* Max number of those args */
+
+/*
+ * Note : if the number of args is fixed and the size < 16 octets,
+ * instead of passing a pointer we will put args in the iwreq struct...
+ */
+
+/* ----------------------- OTHER CONSTANTS ----------------------- */
+
+/* Maximum frequencies in the range struct */
+#define IW_MAX_FREQUENCIES 32
+/* Note : if you have something like 80 frequencies,
+ * don't increase this constant and don't fill the frequency list.
+ * The user will be able to set by channel anyway... */
+
+/* Maximum bit rates in the range struct */
+#define IW_MAX_BITRATES 32
+
+/* Maximum tx powers in the range struct */
+#define IW_MAX_TXPOWER 8
+/* Note : if you more than 8 TXPowers, just set the max and min or
+ * a few of them in the struct iw_range. */
+
+/* Maximum of address that you may set with SPY */
+#define IW_MAX_SPY 8
+
+/* Maximum of address that you may get in the
+ list of access points in range */
+#define IW_MAX_AP 64
+
+/* Maximum size of the ESSID and NICKN strings */
+#define IW_ESSID_MAX_SIZE 32
+
+/* Modes of operation */
+#define IW_MODE_AUTO 0 /* Let the driver decides */
+#define IW_MODE_ADHOC 1 /* Single cell network */
+#define IW_MODE_INFRA 2 /* Multi cell network, roaming, ... */
+#define IW_MODE_MASTER 3 /* Synchronisation master or Access Point */
+#define IW_MODE_REPEAT 4 /* Wireless Repeater (forwarder) */
+#define IW_MODE_SECOND 5 /* Secondary master/repeater (backup) */
+#define IW_MODE_MONITOR 6 /* Passive monitor (listen only) */
+#define IW_MODE_MESH 7 /* Mesh (IEEE 802.11s) network */
+
+/* Statistics flags (bitmask in updated) */
+#define IW_QUAL_QUAL_UPDATED 0x01 /* Value was updated since last read */
+#define IW_QUAL_LEVEL_UPDATED 0x02
+#define IW_QUAL_NOISE_UPDATED 0x04
+#define IW_QUAL_ALL_UPDATED 0x07
+#define IW_QUAL_DBM 0x08 /* Level + Noise are dBm */
+#define IW_QUAL_QUAL_INVALID 0x10 /* Driver doesn't provide value */
+#define IW_QUAL_LEVEL_INVALID 0x20
+#define IW_QUAL_NOISE_INVALID 0x40
+#define IW_QUAL_RCPI 0x80 /* Level + Noise are 802.11k RCPI */
+#define IW_QUAL_ALL_INVALID 0x70
+
+/* Frequency flags */
+#define IW_FREQ_AUTO 0x00 /* Let the driver decides */
+#define IW_FREQ_FIXED 0x01 /* Force a specific value */
+
+/* Maximum number of size of encoding token available
+ * they are listed in the range structure */
+#define IW_MAX_ENCODING_SIZES 8
+
+/* Maximum size of the encoding token in bytes */
+#define IW_ENCODING_TOKEN_MAX 64 /* 512 bits (for now) */
+
+/* Flags for encoding (along with the token) */
+#define IW_ENCODE_INDEX 0x00FF /* Token index (if needed) */
+#define IW_ENCODE_FLAGS 0xFF00 /* Flags defined below */
+#define IW_ENCODE_MODE 0xF000 /* Modes defined below */
+#define IW_ENCODE_DISABLED 0x8000 /* Encoding disabled */
+#define IW_ENCODE_ENABLED 0x0000 /* Encoding enabled */
+#define IW_ENCODE_RESTRICTED 0x4000 /* Refuse non-encoded packets */
+#define IW_ENCODE_OPEN 0x2000 /* Accept non-encoded packets */
+#define IW_ENCODE_NOKEY 0x0800 /* Key is write only, so not present */
+#define IW_ENCODE_TEMP 0x0400 /* Temporary key */
+
+/* Power management flags available (along with the value, if any) */
+#define IW_POWER_ON 0x0000 /* No details... */
+#define IW_POWER_TYPE 0xF000 /* Type of parameter */
+#define IW_POWER_PERIOD 0x1000 /* Value is a period/duration of */
+#define IW_POWER_TIMEOUT 0x2000 /* Value is a timeout (to go asleep) */
+#define IW_POWER_MODE 0x0F00 /* Power Management mode */
+#define IW_POWER_UNICAST_R 0x0100 /* Receive only unicast messages */
+#define IW_POWER_MULTICAST_R 0x0200 /* Receive only multicast messages */
+#define IW_POWER_ALL_R 0x0300 /* Receive all messages though PM */
+#define IW_POWER_FORCE_S 0x0400 /* Force PM procedure for sending unicast */
+#define IW_POWER_REPEATER 0x0800 /* Repeat broadcast messages in PM period */
+#define IW_POWER_MODIFIER 0x000F /* Modify a parameter */
+#define IW_POWER_MIN 0x0001 /* Value is a minimum */
+#define IW_POWER_MAX 0x0002 /* Value is a maximum */
+#define IW_POWER_RELATIVE 0x0004 /* Value is not in seconds/ms/us */
+
+/* Transmit Power flags available */
+#define IW_TXPOW_TYPE 0x00FF /* Type of value */
+#define IW_TXPOW_DBM 0x0000 /* Value is in dBm */
+#define IW_TXPOW_MWATT 0x0001 /* Value is in mW */
+#define IW_TXPOW_RELATIVE 0x0002 /* Value is in arbitrary units */
+#define IW_TXPOW_RANGE 0x1000 /* Range of value between min/max */
+
+/* Retry limits and lifetime flags available */
+#define IW_RETRY_ON 0x0000 /* No details... */
+#define IW_RETRY_TYPE 0xF000 /* Type of parameter */
+#define IW_RETRY_LIMIT 0x1000 /* Maximum number of retries*/
+#define IW_RETRY_LIFETIME 0x2000 /* Maximum duration of retries in us */
+#define IW_RETRY_MODIFIER 0x00FF /* Modify a parameter */
+#define IW_RETRY_MIN 0x0001 /* Value is a minimum */
+#define IW_RETRY_MAX 0x0002 /* Value is a maximum */
+#define IW_RETRY_RELATIVE 0x0004 /* Value is not in seconds/ms/us */
+#define IW_RETRY_SHORT 0x0010 /* Value is for short packets */
+#define IW_RETRY_LONG 0x0020 /* Value is for long packets */
+
+/* Scanning request flags */
+#define IW_SCAN_DEFAULT 0x0000 /* Default scan of the driver */
+#define IW_SCAN_ALL_ESSID 0x0001 /* Scan all ESSIDs */
+#define IW_SCAN_THIS_ESSID 0x0002 /* Scan only this ESSID */
+#define IW_SCAN_ALL_FREQ 0x0004 /* Scan all Frequencies */
+#define IW_SCAN_THIS_FREQ 0x0008 /* Scan only this Frequency */
+#define IW_SCAN_ALL_MODE 0x0010 /* Scan all Modes */
+#define IW_SCAN_THIS_MODE 0x0020 /* Scan only this Mode */
+#define IW_SCAN_ALL_RATE 0x0040 /* Scan all Bit-Rates */
+#define IW_SCAN_THIS_RATE 0x0080 /* Scan only this Bit-Rate */
+/* struct iw_scan_req scan_type */
+#define IW_SCAN_TYPE_ACTIVE 0
+#define IW_SCAN_TYPE_PASSIVE 1
+/* Maximum size of returned data */
+#define IW_SCAN_MAX_DATA 4096 /* In bytes */
+
+/* Scan capability flags - in (struct iw_range *)->scan_capa */
+#define IW_SCAN_CAPA_NONE 0x00
+#define IW_SCAN_CAPA_ESSID 0x01
+#define IW_SCAN_CAPA_BSSID 0x02
+#define IW_SCAN_CAPA_CHANNEL 0x04
+#define IW_SCAN_CAPA_MODE 0x08
+#define IW_SCAN_CAPA_RATE 0x10
+#define IW_SCAN_CAPA_TYPE 0x20
+#define IW_SCAN_CAPA_TIME 0x40
+
+/* Max number of char in custom event - use multiple of them if needed */
+#define IW_CUSTOM_MAX 256 /* In bytes */
+
+/* Generic information element */
+#define IW_GENERIC_IE_MAX 1024
+
+/* MLME requests (SIOCSIWMLME / struct iw_mlme) */
+#define IW_MLME_DEAUTH 0
+#define IW_MLME_DISASSOC 1
+#define IW_MLME_AUTH 2
+#define IW_MLME_ASSOC 3
+
+/* SIOCSIWAUTH/SIOCGIWAUTH struct iw_param flags */
+#define IW_AUTH_INDEX 0x0FFF
+#define IW_AUTH_FLAGS 0xF000
+/* SIOCSIWAUTH/SIOCGIWAUTH parameters (0 .. 4095)
+ * (IW_AUTH_INDEX mask in struct iw_param flags; this is the index of the
+ * parameter that is being set/get to; value will be read/written to
+ * struct iw_param value field) */
+#define IW_AUTH_WPA_VERSION 0
+#define IW_AUTH_CIPHER_PAIRWISE 1
+#define IW_AUTH_CIPHER_GROUP 2
+#define IW_AUTH_KEY_MGMT 3
+#define IW_AUTH_TKIP_COUNTERMEASURES 4
+#define IW_AUTH_DROP_UNENCRYPTED 5
+#define IW_AUTH_80211_AUTH_ALG 6
+#define IW_AUTH_WPA_ENABLED 7
+#define IW_AUTH_RX_UNENCRYPTED_EAPOL 8
+#define IW_AUTH_ROAMING_CONTROL 9
+#define IW_AUTH_PRIVACY_INVOKED 10
+#define IW_AUTH_CIPHER_GROUP_MGMT 11
+#define IW_AUTH_MFP 12
+
+/* IW_AUTH_WPA_VERSION values (bit field) */
+#define IW_AUTH_WPA_VERSION_DISABLED 0x00000001
+#define IW_AUTH_WPA_VERSION_WPA 0x00000002
+#define IW_AUTH_WPA_VERSION_WPA2 0x00000004
+
+/* IW_AUTH_PAIRWISE_CIPHER, IW_AUTH_GROUP_CIPHER, and IW_AUTH_CIPHER_GROUP_MGMT
+ * values (bit field) */
+#define IW_AUTH_CIPHER_NONE 0x00000001
+#define IW_AUTH_CIPHER_WEP40 0x00000002
+#define IW_AUTH_CIPHER_TKIP 0x00000004
+#define IW_AUTH_CIPHER_CCMP 0x00000008
+#define IW_AUTH_CIPHER_WEP104 0x00000010
+#define IW_AUTH_CIPHER_AES_CMAC 0x00000020
+
+/* IW_AUTH_KEY_MGMT values (bit field) */
+#define IW_AUTH_KEY_MGMT_802_1X 1
+#define IW_AUTH_KEY_MGMT_PSK 2
+
+/* IW_AUTH_80211_AUTH_ALG values (bit field) */
+#define IW_AUTH_ALG_OPEN_SYSTEM 0x00000001
+#define IW_AUTH_ALG_SHARED_KEY 0x00000002
+#define IW_AUTH_ALG_LEAP 0x00000004
+
+/* IW_AUTH_ROAMING_CONTROL values */
+#define IW_AUTH_ROAMING_ENABLE 0 /* driver/firmware based roaming */
+#define IW_AUTH_ROAMING_DISABLE 1 /* user space program used for roaming
+ * control */
+
+/* IW_AUTH_MFP (management frame protection) values */
+#define IW_AUTH_MFP_DISABLED 0 /* MFP disabled */
+#define IW_AUTH_MFP_OPTIONAL 1 /* MFP optional */
+#define IW_AUTH_MFP_REQUIRED 2 /* MFP required */
+
+/* SIOCSIWENCODEEXT definitions */
+#define IW_ENCODE_SEQ_MAX_SIZE 8
+/* struct iw_encode_ext ->alg */
+#define IW_ENCODE_ALG_NONE 0
+#define IW_ENCODE_ALG_WEP 1
+#define IW_ENCODE_ALG_TKIP 2
+#define IW_ENCODE_ALG_CCMP 3
+#define IW_ENCODE_ALG_PMK 4
+#define IW_ENCODE_ALG_AES_CMAC 5
+/* struct iw_encode_ext ->ext_flags */
+#define IW_ENCODE_EXT_TX_SEQ_VALID 0x00000001
+#define IW_ENCODE_EXT_RX_SEQ_VALID 0x00000002
+#define IW_ENCODE_EXT_GROUP_KEY 0x00000004
+#define IW_ENCODE_EXT_SET_TX_KEY 0x00000008
+
+/* IWEVMICHAELMICFAILURE : struct iw_michaelmicfailure ->flags */
+#define IW_MICFAILURE_KEY_ID 0x00000003 /* Key ID 0..3 */
+#define IW_MICFAILURE_GROUP 0x00000004
+#define IW_MICFAILURE_PAIRWISE 0x00000008
+#define IW_MICFAILURE_STAKEY 0x00000010
+#define IW_MICFAILURE_COUNT 0x00000060 /* 1 or 2 (0 = count not supported)
+ */
+
+/* Bit field values for enc_capa in struct iw_range */
+#define IW_ENC_CAPA_WPA 0x00000001
+#define IW_ENC_CAPA_WPA2 0x00000002
+#define IW_ENC_CAPA_CIPHER_TKIP 0x00000004
+#define IW_ENC_CAPA_CIPHER_CCMP 0x00000008
+#define IW_ENC_CAPA_4WAY_HANDSHAKE 0x00000010
+
+/* Event capability macros - in (struct iw_range *)->event_capa
+ * Because we have more than 32 possible events, we use an array of
+ * 32 bit bitmasks. Note : 32 bits = 0x20 = 2^5. */
+#define IW_EVENT_CAPA_BASE(cmd) ((cmd >= SIOCIWFIRSTPRIV) ? \
+ (cmd - SIOCIWFIRSTPRIV + 0x60) : \
+ (cmd - SIOCIWFIRST))
+#define IW_EVENT_CAPA_INDEX(cmd) (IW_EVENT_CAPA_BASE(cmd) >> 5)
+#define IW_EVENT_CAPA_MASK(cmd) (1 << (IW_EVENT_CAPA_BASE(cmd) & 0x1F))
+/* Event capability constants - event autogenerated by the kernel
+ * This list is valid for most 802.11 devices, customise as needed... */
+#define IW_EVENT_CAPA_K_0 (IW_EVENT_CAPA_MASK(0x8B04) | \
+ IW_EVENT_CAPA_MASK(0x8B06) | \
+ IW_EVENT_CAPA_MASK(0x8B1A))
+#define IW_EVENT_CAPA_K_1 (IW_EVENT_CAPA_MASK(0x8B2A))
+/* "Easy" macro to set events in iw_range (less efficient) */
+#define IW_EVENT_CAPA_SET(event_capa, cmd) (event_capa[IW_EVENT_CAPA_INDEX(cmd)] |= IW_EVENT_CAPA_MASK(cmd))
+#define IW_EVENT_CAPA_SET_KERNEL(event_capa) {event_capa[0] |= IW_EVENT_CAPA_K_0; event_capa[1] |= IW_EVENT_CAPA_K_1; }
+
+
+/****************************** TYPES ******************************/
+
+/* --------------------------- SUBTYPES --------------------------- */
+/*
+ * Generic format for most parameters that fit in an int
+ */
+struct iw_param {
+ __s32 value; /* The value of the parameter itself */
+ __u8 fixed; /* Hardware should not use auto select */
+ __u8 disabled; /* Disable the feature */
+ __u16 flags; /* Various specifc flags (if any) */
+};
+
+/*
+ * For all data larger than 16 octets, we need to use a
+ * pointer to memory allocated in user space.
+ */
+struct iw_point {
+ void __user *pointer; /* Pointer to the data (in user space) */
+ __u16 length; /* number of fields or size in bytes */
+ __u16 flags; /* Optional params */
+};
+
+
+/*
+ * A frequency
+ * For numbers lower than 10^9, we encode the number in 'm' and
+ * set 'e' to 0
+ * For number greater than 10^9, we divide it by the lowest power
+ * of 10 to get 'm' lower than 10^9, with 'm'= f / (10^'e')...
+ * The power of 10 is in 'e', the result of the division is in 'm'.
+ */
+struct iw_freq {
+ __s32 m; /* Mantissa */
+ __s16 e; /* Exponent */
+ __u8 i; /* List index (when in range struct) */
+ __u8 flags; /* Flags (fixed/auto) */
+};
+
+/*
+ * Quality of the link
+ */
+struct iw_quality {
+ __u8 qual; /* link quality (%retries, SNR,
+ %missed beacons or better...) */
+ __u8 level; /* signal level (dBm) */
+ __u8 noise; /* noise level (dBm) */
+ __u8 updated; /* Flags to know if updated */
+};
+
+/*
+ * Packet discarded in the wireless adapter due to
+ * "wireless" specific problems...
+ * Note : the list of counter and statistics in net_device_stats
+ * is already pretty exhaustive, and you should use that first.
+ * This is only additional stats...
+ */
+struct iw_discarded {
+ __u32 nwid; /* Rx : Wrong nwid/essid */
+ __u32 code; /* Rx : Unable to code/decode (WEP) */
+ __u32 fragment; /* Rx : Can't perform MAC reassembly */
+ __u32 retries; /* Tx : Max MAC retries num reached */
+ __u32 misc; /* Others cases */
+};
+
+/*
+ * Packet/Time period missed in the wireless adapter due to
+ * "wireless" specific problems...
+ */
+struct iw_missed {
+ __u32 beacon; /* Missed beacons/superframe */
+};
+
+/*
+ * Quality range (for spy threshold)
+ */
+struct iw_thrspy {
+ struct sockaddr addr; /* Source address (hw/mac) */
+ struct iw_quality qual; /* Quality of the link */
+ struct iw_quality low; /* Low threshold */
+ struct iw_quality high; /* High threshold */
+};
+
+/*
+ * Optional data for scan request
+ *
+ * Note: these optional parameters are controlling parameters for the
+ * scanning behavior, these do not apply to getting scan results
+ * (SIOCGIWSCAN). Drivers are expected to keep a local BSS table and
+ * provide a merged results with all BSSes even if the previous scan
+ * request limited scanning to a subset, e.g., by specifying an SSID.
+ * Especially, scan results are required to include an entry for the
+ * current BSS if the driver is in Managed mode and associated with an AP.
+ */
+struct iw_scan_req {
+ __u8 scan_type; /* IW_SCAN_TYPE_{ACTIVE,PASSIVE} */
+ __u8 essid_len;
+ __u8 num_channels; /* num entries in channel_list;
+ * 0 = scan all allowed channels */
+ __u8 flags; /* reserved as padding; use zero, this may
+ * be used in the future for adding flags
+ * to request different scan behavior */
+ struct sockaddr bssid; /* ff:ff:ff:ff:ff:ff for broadcast BSSID or
+ * individual address of a specific BSS */
+
+ /*
+ * Use this ESSID if IW_SCAN_THIS_ESSID flag is used instead of using
+ * the current ESSID. This allows scan requests for specific ESSID
+ * without having to change the current ESSID and potentially breaking
+ * the current association.
+ */
+ __u8 essid[IW_ESSID_MAX_SIZE];
+
+ /*
+ * Optional parameters for changing the default scanning behavior.
+ * These are based on the MLME-SCAN.request from IEEE Std 802.11.
+ * TU is 1.024 ms. If these are set to 0, driver is expected to use
+ * reasonable default values. min_channel_time defines the time that
+ * will be used to wait for the first reply on each channel. If no
+ * replies are received, next channel will be scanned after this. If
+ * replies are received, total time waited on the channel is defined by
+ * max_channel_time.
+ */
+ __u32 min_channel_time; /* in TU */
+ __u32 max_channel_time; /* in TU */
+
+ struct iw_freq channel_list[IW_MAX_FREQUENCIES];
+};
+
+/* ------------------------- WPA SUPPORT ------------------------- */
+
+/*
+ * Extended data structure for get/set encoding (this is used with
+ * SIOCSIWENCODEEXT/SIOCGIWENCODEEXT. struct iw_point and IW_ENCODE_*
+ * flags are used in the same way as with SIOCSIWENCODE/SIOCGIWENCODE and
+ * only the data contents changes (key data -> this structure, including
+ * key data).
+ *
+ * If the new key is the first group key, it will be set as the default
+ * TX key. Otherwise, default TX key index is only changed if
+ * IW_ENCODE_EXT_SET_TX_KEY flag is set.
+ *
+ * Key will be changed with SIOCSIWENCODEEXT in all cases except for
+ * special "change TX key index" operation which is indicated by setting
+ * key_len = 0 and ext_flags |= IW_ENCODE_EXT_SET_TX_KEY.
+ *
+ * tx_seq/rx_seq are only used when respective
+ * IW_ENCODE_EXT_{TX,RX}_SEQ_VALID flag is set in ext_flags. Normal
+ * TKIP/CCMP operation is to set RX seq with SIOCSIWENCODEEXT and start
+ * TX seq from zero whenever key is changed. SIOCGIWENCODEEXT is normally
+ * used only by an Authenticator (AP or an IBSS station) to get the
+ * current TX sequence number. Using TX_SEQ_VALID for SIOCSIWENCODEEXT and
+ * RX_SEQ_VALID for SIOCGIWENCODEEXT are optional, but can be useful for
+ * debugging/testing.
+ */
+struct iw_encode_ext {
+ __u32 ext_flags; /* IW_ENCODE_EXT_* */
+ __u8 tx_seq[IW_ENCODE_SEQ_MAX_SIZE]; /* LSB first */
+ __u8 rx_seq[IW_ENCODE_SEQ_MAX_SIZE]; /* LSB first */
+ struct sockaddr addr; /* ff:ff:ff:ff:ff:ff for broadcast/multicast
+ * (group) keys or unicast address for
+ * individual keys */
+ __u16 alg; /* IW_ENCODE_ALG_* */
+ __u16 key_len;
+ __u8 key[0];
+};
+
+/* SIOCSIWMLME data */
+struct iw_mlme {
+ __u16 cmd; /* IW_MLME_* */
+ __u16 reason_code;
+ struct sockaddr addr;
+};
+
+/* SIOCSIWPMKSA data */
+#define IW_PMKSA_ADD 1
+#define IW_PMKSA_REMOVE 2
+#define IW_PMKSA_FLUSH 3
+
+#define IW_PMKID_LEN 16
+
+struct iw_pmksa {
+ __u32 cmd; /* IW_PMKSA_* */
+ struct sockaddr bssid;
+ __u8 pmkid[IW_PMKID_LEN];
+};
+
+/* IWEVMICHAELMICFAILURE data */
+struct iw_michaelmicfailure {
+ __u32 flags;
+ struct sockaddr src_addr;
+ __u8 tsc[IW_ENCODE_SEQ_MAX_SIZE]; /* LSB first */
+};
+
+/* IWEVPMKIDCAND data */
+#define IW_PMKID_CAND_PREAUTH 0x00000001 /* RNS pre-authentication enabled */
+struct iw_pmkid_cand {
+ __u32 flags; /* IW_PMKID_CAND_* */
+ __u32 index; /* the smaller the index, the higher the
+ * priority */
+ struct sockaddr bssid;
+};
+
+/* ------------------------ WIRELESS STATS ------------------------ */
+/*
+ * Wireless statistics (used for /proc/net/wireless)
+ */
+struct iw_statistics {
+ __u16 status; /* Status
+ * - device dependent for now */
+
+ struct iw_quality qual; /* Quality of the link
+ * (instant/mean/max) */
+ struct iw_discarded discard; /* Packet discarded counts */
+ struct iw_missed miss; /* Packet missed counts */
+};
+
+/* ------------------------ IOCTL REQUEST ------------------------ */
+/*
+ * This structure defines the payload of an ioctl, and is used
+ * below.
+ *
+ * Note that this structure should fit on the memory footprint
+ * of iwreq (which is the same as ifreq), which mean a max size of
+ * 16 octets = 128 bits. Warning, pointers might be 64 bits wide...
+ * You should check this when increasing the structures defined
+ * above in this file...
+ */
+union iwreq_data {
+ /* Config - generic */
+ char name[IFNAMSIZ];
+ /* Name : used to verify the presence of wireless extensions.
+ * Name of the protocol/provider... */
+
+ struct iw_point essid; /* Extended network name */
+ struct iw_param nwid; /* network id (or domain - the cell) */
+ struct iw_freq freq; /* frequency or channel :
+ * 0-1000 = channel
+ * > 1000 = frequency in Hz */
+
+ struct iw_param sens; /* signal level threshold */
+ struct iw_param bitrate; /* default bit rate */
+ struct iw_param txpower; /* default transmit power */
+ struct iw_param rts; /* RTS threshold threshold */
+ struct iw_param frag; /* Fragmentation threshold */
+ __u32 mode; /* Operation mode */
+ struct iw_param retry; /* Retry limits & lifetime */
+
+ struct iw_point encoding; /* Encoding stuff : tokens */
+ struct iw_param power; /* PM duration/timeout */
+ struct iw_quality qual; /* Quality part of statistics */
+
+ struct sockaddr ap_addr; /* Access point address */
+ struct sockaddr addr; /* Destination address (hw/mac) */
+
+ struct iw_param param; /* Other small parameters */
+ struct iw_point data; /* Other large parameters */
+};
+
+/*
+ * The structure to exchange data for ioctl.
+ * This structure is the same as 'struct ifreq', but (re)defined for
+ * convenience...
+ * Do I need to remind you about structure size (32 octets) ?
+ */
+struct iwreq {
+ union
+ {
+ char ifrn_name[IFNAMSIZ]; /* if name, e.g. "eth0" */
+ } ifr_ifrn;
+
+ /* Data part (defined just above) */
+ union iwreq_data u;
+};
+
+/* -------------------------- IOCTL DATA -------------------------- */
+/*
+ * For those ioctl which want to exchange mode data that what could
+ * fit in the above structure...
+ */
+
+/*
+ * Range of parameters
+ */
+
+struct iw_range {
+ /* Informative stuff (to choose between different interface) */
+ __u32 throughput; /* To give an idea... */
+ /* In theory this value should be the maximum benchmarked
+ * TCP/IP throughput, because with most of these devices the
+ * bit rate is meaningless (overhead an co) to estimate how
+ * fast the connection will go and pick the fastest one.
+ * I suggest people to play with Netperf or any benchmark...
+ */
+
+ /* NWID (or domain id) */
+ __u32 min_nwid; /* Minimal NWID we are able to set */
+ __u32 max_nwid; /* Maximal NWID we are able to set */
+
+ /* Old Frequency (backward compat - moved lower ) */
+ __u16 old_num_channels;
+ __u8 old_num_frequency;
+
+ /* Scan capabilities */
+ __u8 scan_capa; /* IW_SCAN_CAPA_* bit field */
+
+ /* Wireless event capability bitmasks */
+ __u32 event_capa[6];
+
+ /* signal level threshold range */
+ __s32 sensitivity;
+
+ /* Quality of link & SNR stuff */
+ /* Quality range (link, level, noise)
+ * If the quality is absolute, it will be in the range [0 ; max_qual],
+ * if the quality is dBm, it will be in the range [max_qual ; 0].
+ * Don't forget that we use 8 bit arithmetics... */
+ struct iw_quality max_qual; /* Quality of the link */
+ /* This should contain the average/typical values of the quality
+ * indicator. This should be the threshold between a "good" and
+ * a "bad" link (example : monitor going from green to orange).
+ * Currently, user space apps like quality monitors don't have any
+ * way to calibrate the measurement. With this, they can split
+ * the range between 0 and max_qual in different quality level
+ * (using a geometric subdivision centered on the average).
+ * I expect that people doing the user space apps will feedback
+ * us on which value we need to put in each driver... */
+ struct iw_quality avg_qual; /* Quality of the link */
+
+ /* Rates */
+ __u8 num_bitrates; /* Number of entries in the list */
+ __s32 bitrate[IW_MAX_BITRATES]; /* list, in bps */
+
+ /* RTS threshold */
+ __s32 min_rts; /* Minimal RTS threshold */
+ __s32 max_rts; /* Maximal RTS threshold */
+
+ /* Frag threshold */
+ __s32 min_frag; /* Minimal frag threshold */
+ __s32 max_frag; /* Maximal frag threshold */
+
+ /* Power Management duration & timeout */
+ __s32 min_pmp; /* Minimal PM period */
+ __s32 max_pmp; /* Maximal PM period */
+ __s32 min_pmt; /* Minimal PM timeout */
+ __s32 max_pmt; /* Maximal PM timeout */
+ __u16 pmp_flags; /* How to decode max/min PM period */
+ __u16 pmt_flags; /* How to decode max/min PM timeout */
+ __u16 pm_capa; /* What PM options are supported */
+
+ /* Encoder stuff */
+ __u16 encoding_size[IW_MAX_ENCODING_SIZES]; /* Different token sizes */
+ __u8 num_encoding_sizes; /* Number of entry in the list */
+ __u8 max_encoding_tokens; /* Max number of tokens */
+ /* For drivers that need a "login/passwd" form */
+ __u8 encoding_login_index; /* token index for login token */
+
+ /* Transmit power */
+ __u16 txpower_capa; /* What options are supported */
+ __u8 num_txpower; /* Number of entries in the list */
+ __s32 txpower[IW_MAX_TXPOWER]; /* list, in bps */
+
+ /* Wireless Extension version info */
+ __u8 we_version_compiled; /* Must be WIRELESS_EXT */
+ __u8 we_version_source; /* Last update of source */
+
+ /* Retry limits and lifetime */
+ __u16 retry_capa; /* What retry options are supported */
+ __u16 retry_flags; /* How to decode max/min retry limit */
+ __u16 r_time_flags; /* How to decode max/min retry life */
+ __s32 min_retry; /* Minimal number of retries */
+ __s32 max_retry; /* Maximal number of retries */
+ __s32 min_r_time; /* Minimal retry lifetime */
+ __s32 max_r_time; /* Maximal retry lifetime */
+
+ /* Frequency */
+ __u16 num_channels; /* Number of channels [0; num - 1] */
+ __u8 num_frequency; /* Number of entry in the list */
+ struct iw_freq freq[IW_MAX_FREQUENCIES]; /* list */
+ /* Note : this frequency list doesn't need to fit channel numbers,
+ * because each entry contain its channel index */
+
+ __u32 enc_capa; /* IW_ENC_CAPA_* bit field */
+};
+
+/*
+ * Private ioctl interface information
+ */
+
+struct iw_priv_args {
+ __u32 cmd; /* Number of the ioctl to issue */
+ __u16 set_args; /* Type and number of args */
+ __u16 get_args; /* Type and number of args */
+ char name[IFNAMSIZ]; /* Name of the extension */
+};
+
+/* ----------------------- WIRELESS EVENTS ----------------------- */
+/*
+ * Wireless events are carried through the rtnetlink socket to user
+ * space. They are encapsulated in the IFLA_WIRELESS field of
+ * a RTM_NEWLINK message.
+ */
+
+/*
+ * A Wireless Event. Contains basically the same data as the ioctl...
+ */
+struct iw_event {
+ __u16 len; /* Real length of this stuff */
+ __u16 cmd; /* Wireless IOCTL */
+ union iwreq_data u; /* IOCTL fixed payload */
+};
+
+/* Size of the Event prefix (including padding and alignement junk) */
+#define IW_EV_LCP_LEN (sizeof(struct iw_event) - sizeof(union iwreq_data))
+/* Size of the various events */
+#define IW_EV_CHAR_LEN (IW_EV_LCP_LEN + IFNAMSIZ)
+#define IW_EV_UINT_LEN (IW_EV_LCP_LEN + sizeof(__u32))
+#define IW_EV_FREQ_LEN (IW_EV_LCP_LEN + sizeof(struct iw_freq))
+#define IW_EV_PARAM_LEN (IW_EV_LCP_LEN + sizeof(struct iw_param))
+#define IW_EV_ADDR_LEN (IW_EV_LCP_LEN + sizeof(struct sockaddr))
+#define IW_EV_QUAL_LEN (IW_EV_LCP_LEN + sizeof(struct iw_quality))
+
+/* iw_point events are special. First, the payload (extra data) come at
+ * the end of the event, so they are bigger than IW_EV_POINT_LEN. Second,
+ * we omit the pointer, so start at an offset. */
+#define IW_EV_POINT_OFF (((char *) &(((struct iw_point *) NULL)->length)) - \
+ (char *) NULL)
+#define IW_EV_POINT_LEN (IW_EV_LCP_LEN + sizeof(struct iw_point) - \
+ IW_EV_POINT_OFF)
+
+
+/* Size of the Event prefix when packed in stream */
+#define IW_EV_LCP_PK_LEN (4)
+/* Size of the various events when packed in stream */
+#define IW_EV_CHAR_PK_LEN (IW_EV_LCP_PK_LEN + IFNAMSIZ)
+#define IW_EV_UINT_PK_LEN (IW_EV_LCP_PK_LEN + sizeof(__u32))
+#define IW_EV_FREQ_PK_LEN (IW_EV_LCP_PK_LEN + sizeof(struct iw_freq))
+#define IW_EV_PARAM_PK_LEN (IW_EV_LCP_PK_LEN + sizeof(struct iw_param))
+#define IW_EV_ADDR_PK_LEN (IW_EV_LCP_PK_LEN + sizeof(struct sockaddr))
+#define IW_EV_QUAL_PK_LEN (IW_EV_LCP_PK_LEN + sizeof(struct iw_quality))
+#define IW_EV_POINT_PK_LEN (IW_EV_LCP_PK_LEN + 4)
+
+#endif /* _UAPI_LINUX_WIRELESS_H */
diff --git a/kconf/Makefile b/kconf/Makefile
new file mode 100644
index 0000000..9edd957
--- /dev/null
+++ b/kconf/Makefile
@@ -0,0 +1,15 @@
+CFLAGS=-Wall -Wmissing-prototypes -Wstrict-prototypes -O2 -fomit-frame-pointer
+
+LXDIALOG := lxdialog/checklist.o lxdialog/inputbox.o lxdialog/menubox.o lxdialog/textbox.o lxdialog/util.o lxdialog/yesno.o
+
+conf: conf.o zconf.tab.o
+mconf_CFLAGS := $(shell ./lxdialog/check-lxdialog.sh -ccflags) -DLOCALE
+mconf_LDFLAGS := $(shell ./lxdialog/check-lxdialog.sh -ldflags $(CC))
+mconf: CFLAGS += $(mconf_CFLAGS)
+
+mconf: mconf.o zconf.tab.o $(LXDIALOG)
+ $(CC) -o mconf $^ $(mconf_LDFLAGS)
+
+.PHONY: clean
+clean:
+ @rm -f mconf conf *.o lxdialog/*.o
diff --git a/kconf/conf.c b/kconf/conf.c
new file mode 100644
index 0000000..6c20431
--- /dev/null
+++ b/kconf/conf.c
@@ -0,0 +1,722 @@
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#include <locale.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <errno.h>
+
+#include "lkc.h"
+
+static void conf(struct menu *menu);
+static void check_conf(struct menu *menu);
+static void xfgets(char *str, int size, FILE *in);
+
+enum input_mode {
+ oldaskconfig,
+ silentoldconfig,
+ oldconfig,
+ allnoconfig,
+ allyesconfig,
+ allmodconfig,
+ alldefconfig,
+ randconfig,
+ defconfig,
+ savedefconfig,
+ listnewconfig,
+ olddefconfig,
+} input_mode = oldaskconfig;
+
+static int indent = 1;
+static int tty_stdio;
+static int valid_stdin = 1;
+static int sync_kconfig;
+static int conf_cnt;
+static char line[128];
+static struct menu *rootEntry;
+
+static void print_help(struct menu *menu)
+{
+ struct gstr help = str_new();
+
+ menu_get_ext_help(menu, &help);
+
+ printf("\n%s\n", str_get(&help));
+ str_free(&help);
+}
+
+static void strip(char *str)
+{
+ char *p = str;
+ int l;
+
+ while ((isspace(*p)))
+ p++;
+ l = strlen(p);
+ if (p != str)
+ memmove(str, p, l + 1);
+ if (!l)
+ return;
+ p = str + l - 1;
+ while ((isspace(*p)))
+ *p-- = 0;
+}
+
+static void check_stdin(void)
+{
+ if (!valid_stdin) {
+ printf(_("aborted!\n\n"));
+ printf(_("Console input/output is redirected. "));
+ printf(_("Run 'make oldconfig' to update configuration.\n\n"));
+ exit(1);
+ }
+}
+
+static int conf_askvalue(struct symbol *sym, const char *def)
+{
+ enum symbol_type type = sym_get_type(sym);
+
+ if (!sym_has_value(sym))
+ printf(_("(NEW) "));
+
+ line[0] = '\n';
+ line[1] = 0;
+
+ if (!sym_is_changable(sym)) {
+ printf("%s\n", def);
+ line[0] = '\n';
+ line[1] = 0;
+ return 0;
+ }
+
+ switch (input_mode) {
+ case oldconfig:
+ case silentoldconfig:
+ if (sym_has_value(sym)) {
+ printf("%s\n", def);
+ return 0;
+ }
+ check_stdin();
+ /* fall through */
+ case oldaskconfig:
+ fflush(stdout);
+ xfgets(line, 128, stdin);
+ if (!tty_stdio)
+ printf("\n");
+ return 1;
+ default:
+ break;
+ }
+
+ switch (type) {
+ case S_INT:
+ case S_HEX:
+ case S_STRING:
+ printf("%s\n", def);
+ return 1;
+ default:
+ ;
+ }
+ printf("%s", line);
+ return 1;
+}
+
+static int conf_string(struct menu *menu)
+{
+ struct symbol *sym = menu->sym;
+ const char *def;
+
+ while (1) {
+ printf("%*s%s ", indent - 1, "", _(menu->prompt->text));
+ printf("(%s) ", sym->name);
+ def = sym_get_string_value(sym);
+ if (sym_get_string_value(sym))
+ printf("[%s] ", def);
+ if (!conf_askvalue(sym, def))
+ return 0;
+ switch (line[0]) {
+ case '\n':
+ break;
+ case '?':
+ /* print help */
+ if (line[1] == '\n') {
+ print_help(menu);
+ def = NULL;
+ break;
+ }
+ /* fall through */
+ default:
+ line[strlen(line)-1] = 0;
+ def = line;
+ }
+ if (def && sym_set_string_value(sym, def))
+ return 0;
+ }
+}
+
+static int conf_sym(struct menu *menu)
+{
+ struct symbol *sym = menu->sym;
+ tristate oldval, newval;
+
+ while (1) {
+ printf("%*s%s ", indent - 1, "", _(menu->prompt->text));
+ if (sym->name)
+ printf("(%s) ", sym->name);
+ putchar('[');
+ oldval = sym_get_tristate_value(sym);
+ switch (oldval) {
+ case no:
+ putchar('N');
+ break;
+ case mod:
+ putchar('M');
+ break;
+ case yes:
+ putchar('Y');
+ break;
+ }
+ if (oldval != no && sym_tristate_within_range(sym, no))
+ printf("/n");
+ if (oldval != mod && sym_tristate_within_range(sym, mod))
+ printf("/m");
+ if (oldval != yes && sym_tristate_within_range(sym, yes))
+ printf("/y");
+ if (menu_has_help(menu))
+ printf("/?");
+ printf("] ");
+ if (!conf_askvalue(sym, sym_get_string_value(sym)))
+ return 0;
+ strip(line);
+
+ switch (line[0]) {
+ case 'n':
+ case 'N':
+ newval = no;
+ if (!line[1] || !strcmp(&line[1], "o"))
+ break;
+ continue;
+ case 'm':
+ case 'M':
+ newval = mod;
+ if (!line[1])
+ break;
+ continue;
+ case 'y':
+ case 'Y':
+ newval = yes;
+ if (!line[1] || !strcmp(&line[1], "es"))
+ break;
+ continue;
+ case 0:
+ newval = oldval;
+ break;
+ case '?':
+ goto help;
+ default:
+ continue;
+ }
+ if (sym_set_tristate_value(sym, newval))
+ return 0;
+help:
+ print_help(menu);
+ }
+}
+
+static int conf_choice(struct menu *menu)
+{
+ struct symbol *sym, *def_sym;
+ struct menu *child;
+ bool is_new;
+
+ sym = menu->sym;
+ is_new = !sym_has_value(sym);
+ if (sym_is_changable(sym)) {
+ conf_sym(menu);
+ sym_calc_value(sym);
+ switch (sym_get_tristate_value(sym)) {
+ case no:
+ return 1;
+ case mod:
+ return 0;
+ case yes:
+ break;
+ }
+ } else {
+ switch (sym_get_tristate_value(sym)) {
+ case no:
+ return 1;
+ case mod:
+ printf("%*s%s\n", indent - 1, "", _(menu_get_prompt(menu)));
+ return 0;
+ case yes:
+ break;
+ }
+ }
+
+ while (1) {
+ int cnt, def;
+
+ printf("%*s%s\n", indent - 1, "", _(menu_get_prompt(menu)));
+ def_sym = sym_get_choice_value(sym);
+ cnt = def = 0;
+ line[0] = 0;
+ for (child = menu->list; child; child = child->next) {
+ if (!menu_is_visible(child))
+ continue;
+ if (!child->sym) {
+ printf("%*c %s\n", indent, '*', _(menu_get_prompt(child)));
+ continue;
+ }
+ cnt++;
+ if (child->sym == def_sym) {
+ def = cnt;
+ printf("%*c", indent, '>');
+ } else
+ printf("%*c", indent, ' ');
+ printf(" %d. %s", cnt, _(menu_get_prompt(child)));
+ if (child->sym->name)
+ printf(" (%s)", child->sym->name);
+ if (!sym_has_value(child->sym))
+ printf(_(" (NEW)"));
+ printf("\n");
+ }
+ printf(_("%*schoice"), indent - 1, "");
+ if (cnt == 1) {
+ printf("[1]: 1\n");
+ goto conf_childs;
+ }
+ printf("[1-%d", cnt);
+ if (menu_has_help(menu))
+ printf("?");
+ printf("]: ");
+ switch (input_mode) {
+ case oldconfig:
+ case silentoldconfig:
+ if (!is_new) {
+ cnt = def;
+ printf("%d\n", cnt);
+ break;
+ }
+ check_stdin();
+ /* fall through */
+ case oldaskconfig:
+ fflush(stdout);
+ xfgets(line, 128, stdin);
+ strip(line);
+ if (line[0] == '?') {
+ print_help(menu);
+ continue;
+ }
+ if (!line[0])
+ cnt = def;
+ else if (isdigit(line[0]))
+ cnt = atoi(line);
+ else
+ continue;
+ break;
+ default:
+ break;
+ }
+
+ conf_childs:
+ for (child = menu->list; child; child = child->next) {
+ if (!child->sym || !menu_is_visible(child))
+ continue;
+ if (!--cnt)
+ break;
+ }
+ if (!child)
+ continue;
+ if (line[0] && line[strlen(line) - 1] == '?') {
+ print_help(child);
+ continue;
+ }
+ sym_set_choice_value(sym, child->sym);
+ for (child = child->list; child; child = child->next) {
+ indent += 2;
+ conf(child);
+ indent -= 2;
+ }
+ return 1;
+ }
+}
+
+static void conf(struct menu *menu)
+{
+ struct symbol *sym;
+ struct property *prop;
+ struct menu *child;
+
+ if (!menu_is_visible(menu))
+ return;
+
+ sym = menu->sym;
+ prop = menu->prompt;
+ if (prop) {
+ const char *prompt;
+
+ switch (prop->type) {
+ case P_MENU:
+ if ((input_mode == silentoldconfig ||
+ input_mode == listnewconfig ||
+ input_mode == olddefconfig) &&
+ rootEntry != menu) {
+ check_conf(menu);
+ return;
+ }
+ /* fall through */
+ case P_COMMENT:
+ prompt = menu_get_prompt(menu);
+ if (prompt)
+ printf("%*c\n%*c %s\n%*c\n",
+ indent, '*',
+ indent, '*', _(prompt),
+ indent, '*');
+ default:
+ ;
+ }
+ }
+
+ if (!sym)
+ goto conf_childs;
+
+ if (sym_is_choice(sym)) {
+ conf_choice(menu);
+ if (sym->curr.tri != mod)
+ return;
+ goto conf_childs;
+ }
+
+ switch (sym->type) {
+ case S_INT:
+ case S_HEX:
+ case S_STRING:
+ conf_string(menu);
+ break;
+ default:
+ conf_sym(menu);
+ break;
+ }
+
+conf_childs:
+ if (sym)
+ indent += 2;
+ for (child = menu->list; child; child = child->next)
+ conf(child);
+ if (sym)
+ indent -= 2;
+}
+
+static void check_conf(struct menu *menu)
+{
+ struct symbol *sym;
+ struct menu *child;
+
+ if (!menu_is_visible(menu))
+ return;
+
+ sym = menu->sym;
+ if (sym && !sym_has_value(sym)) {
+ if (sym_is_changable(sym) ||
+ (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes)) {
+ if (input_mode == listnewconfig) {
+ if (sym->name && !sym_is_choice_value(sym)) {
+ printf("%s%s\n", CONFIG_, sym->name);
+ }
+ } else if (input_mode != olddefconfig) {
+ if (!conf_cnt++)
+ printf(_("*\n* Restart config...\n*\n"));
+ rootEntry = menu_get_parent_menu(menu);
+ conf(rootEntry);
+ }
+ }
+ }
+
+ for (child = menu->list; child; child = child->next)
+ check_conf(child);
+}
+
+static struct option long_opts[] = {
+ {"oldaskconfig", no_argument, NULL, oldaskconfig},
+ {"oldconfig", no_argument, NULL, oldconfig},
+ {"silentoldconfig", no_argument, NULL, silentoldconfig},
+ {"defconfig", optional_argument, NULL, defconfig},
+ {"savedefconfig", required_argument, NULL, savedefconfig},
+ {"allnoconfig", no_argument, NULL, allnoconfig},
+ {"allyesconfig", no_argument, NULL, allyesconfig},
+ {"allmodconfig", no_argument, NULL, allmodconfig},
+ {"alldefconfig", no_argument, NULL, alldefconfig},
+ {"randconfig", no_argument, NULL, randconfig},
+ {"listnewconfig", no_argument, NULL, listnewconfig},
+ {"olddefconfig", no_argument, NULL, olddefconfig},
+ /*
+ * oldnoconfig is an alias of olddefconfig, because people already
+ * are dependent on its behavior(sets new symbols to their default
+ * value but not 'n') with the counter-intuitive name.
+ */
+ {"oldnoconfig", no_argument, NULL, olddefconfig},
+ {NULL, 0, NULL, 0}
+};
+
+static void conf_usage(const char *progname)
+{
+
+ printf("Usage: %s [-s] [option] <kconfig-file>\n", progname);
+ printf("[option] is _one_ of the following:\n");
+ printf(" --listnewconfig List new options\n");
+ printf(" --oldaskconfig Start a new configuration using a line-oriented program\n");
+ printf(" --oldconfig Update a configuration using a provided .config as base\n");
+ printf(" --silentoldconfig Same as oldconfig, but quietly, additionally update deps\n");
+ printf(" --olddefconfig Same as silentoldconfig but sets new symbols to their default value\n");
+ printf(" --oldnoconfig An alias of olddefconfig\n");
+ printf(" --defconfig <file> New config with default defined in <file>\n");
+ printf(" --savedefconfig <file> Save the minimal current configuration to <file>\n");
+ printf(" --allnoconfig New config where all options are answered with no\n");
+ printf(" --allyesconfig New config where all options are answered with yes\n");
+ printf(" --allmodconfig New config where all options are answered with mod\n");
+ printf(" --alldefconfig New config with all symbols set to default\n");
+ printf(" --randconfig New config with random answer to all options\n");
+}
+
+int main(int ac, char **av)
+{
+ const char *progname = av[0];
+ int opt;
+ const char *name, *defconfig_file = NULL /* gcc uninit */;
+ struct stat tmpstat;
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+
+ tty_stdio = isatty(0) && isatty(1) && isatty(2);
+
+ while ((opt = getopt_long(ac, av, "s", long_opts, NULL)) != -1) {
+ if (opt == 's') {
+ conf_set_message_callback(NULL);
+ continue;
+ }
+ input_mode = (enum input_mode)opt;
+ switch (opt) {
+ case silentoldconfig:
+ sync_kconfig = 1;
+ break;
+ case defconfig:
+ case savedefconfig:
+ defconfig_file = optarg;
+ break;
+ case randconfig:
+ {
+ struct timeval now;
+ unsigned int seed;
+ char *seed_env;
+
+ /*
+ * Use microseconds derived seed,
+ * compensate for systems where it may be zero
+ */
+ gettimeofday(&now, NULL);
+ seed = (unsigned int)((now.tv_sec + 1) * (now.tv_usec + 1));
+
+ seed_env = getenv("KCONFIG_SEED");
+ if( seed_env && *seed_env ) {
+ char *endp;
+ int tmp = (int)strtol(seed_env, &endp, 0);
+ if (*endp == '\0') {
+ seed = tmp;
+ }
+ }
+ fprintf( stderr, "KCONFIG_SEED=0x%X\n", seed );
+ srand(seed);
+ break;
+ }
+ case oldaskconfig:
+ case oldconfig:
+ case allnoconfig:
+ case allyesconfig:
+ case allmodconfig:
+ case alldefconfig:
+ case listnewconfig:
+ case olddefconfig:
+ break;
+ case '?':
+ conf_usage(progname);
+ exit(1);
+ break;
+ }
+ }
+ if (ac == optind) {
+ printf(_("%s: Kconfig file missing\n"), av[0]);
+ conf_usage(progname);
+ exit(1);
+ }
+ name = av[optind];
+ conf_parse(name);
+ //zconfdump(stdout);
+ if (sync_kconfig) {
+ name = conf_get_configname();
+ if (stat(name, &tmpstat)) {
+ fprintf(stderr, _("***\n"
+ "*** Configuration file \"%s\" not found!\n"
+ "***\n"
+ "*** Please run some configurator (e.g. \"make oldconfig\" or\n"
+ "*** \"make menuconfig\" or \"make xconfig\").\n"
+ "***\n"), name);
+ exit(1);
+ }
+ }
+
+ switch (input_mode) {
+ case defconfig:
+ if (!defconfig_file)
+ defconfig_file = conf_get_default_confname();
+ if (conf_read(defconfig_file)) {
+ printf(_("***\n"
+ "*** Can't find default configuration \"%s\"!\n"
+ "***\n"), defconfig_file);
+ exit(1);
+ }
+ break;
+ case savedefconfig:
+ case silentoldconfig:
+ case oldaskconfig:
+ case oldconfig:
+ case listnewconfig:
+ case olddefconfig:
+ conf_read(NULL);
+ break;
+ case allnoconfig:
+ case allyesconfig:
+ case allmodconfig:
+ case alldefconfig:
+ case randconfig:
+ name = getenv("KCONFIG_ALLCONFIG");
+ if (!name)
+ break;
+ if ((strcmp(name, "") != 0) && (strcmp(name, "1") != 0)) {
+ if (conf_read_simple(name, S_DEF_USER)) {
+ fprintf(stderr,
+ _("*** Can't read seed configuration \"%s\"!\n"),
+ name);
+ exit(1);
+ }
+ break;
+ }
+ switch (input_mode) {
+ case allnoconfig: name = "allno.config"; break;
+ case allyesconfig: name = "allyes.config"; break;
+ case allmodconfig: name = "allmod.config"; break;
+ case alldefconfig: name = "alldef.config"; break;
+ case randconfig: name = "allrandom.config"; break;
+ default: break;
+ }
+ if (conf_read_simple(name, S_DEF_USER) &&
+ conf_read_simple("all.config", S_DEF_USER)) {
+ fprintf(stderr,
+ _("*** KCONFIG_ALLCONFIG set, but no \"%s\" or \"all.config\" file found\n"),
+ name);
+ exit(1);
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (sync_kconfig) {
+ if (conf_get_changed()) {
+ name = getenv("KCONFIG_NOSILENTUPDATE");
+ if (name && *name) {
+ fprintf(stderr,
+ _("\n*** The configuration requires explicit update.\n\n"));
+ return 1;
+ }
+ }
+ valid_stdin = tty_stdio;
+ }
+
+ switch (input_mode) {
+ case allnoconfig:
+ conf_set_all_new_symbols(def_no);
+ break;
+ case allyesconfig:
+ conf_set_all_new_symbols(def_yes);
+ break;
+ case allmodconfig:
+ conf_set_all_new_symbols(def_mod);
+ break;
+ case alldefconfig:
+ conf_set_all_new_symbols(def_default);
+ break;
+ case randconfig:
+ /* Really nothing to do in this loop */
+ while (conf_set_all_new_symbols(def_random)) ;
+ break;
+ case defconfig:
+ conf_set_all_new_symbols(def_default);
+ break;
+ case savedefconfig:
+ break;
+ case oldaskconfig:
+ rootEntry = &rootmenu;
+ conf(&rootmenu);
+ input_mode = silentoldconfig;
+ /* fall through */
+ case oldconfig:
+ case listnewconfig:
+ case olddefconfig:
+ case silentoldconfig:
+ /* Update until a loop caused no more changes */
+ do {
+ conf_cnt = 0;
+ check_conf(&rootmenu);
+ } while (conf_cnt &&
+ (input_mode != listnewconfig &&
+ input_mode != olddefconfig));
+ break;
+ }
+
+ if (sync_kconfig) {
+ /* silentoldconfig is used during the build so we shall update autoconf.
+ * All other commands are only used to generate a config.
+ */
+ if (conf_get_changed() && conf_write(NULL)) {
+ fprintf(stderr, _("\n*** Error during writing of the configuration.\n\n"));
+ exit(1);
+ }
+ if (conf_write_autoconf()) {
+ fprintf(stderr, _("\n*** Error during update of the configuration.\n\n"));
+ return 1;
+ }
+ } else if (input_mode == savedefconfig) {
+ if (conf_write_defconfig(defconfig_file)) {
+ fprintf(stderr, _("n*** Error while saving defconfig to: %s\n\n"),
+ defconfig_file);
+ return 1;
+ }
+ } else if (input_mode != listnewconfig) {
+ if (conf_write(NULL)) {
+ fprintf(stderr, _("\n*** Error during writing of the configuration.\n\n"));
+ exit(1);
+ }
+ }
+ return 0;
+}
+
+/*
+ * Helper function to facilitate fgets() by Jean Sacren.
+ */
+void xfgets(char *str, int size, FILE *in)
+{
+ if (fgets(str, size, in) == NULL)
+ fprintf(stderr, "\nError in reading or end of file.\n");
+}
diff --git a/kconf/confdata.c b/kconf/confdata.c
new file mode 100644
index 0000000..c814f57
--- /dev/null
+++ b/kconf/confdata.c
@@ -0,0 +1,1248 @@
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#include <sys/stat.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "lkc.h"
+
+struct conf_printer {
+ void (*print_symbol)(FILE *, struct symbol *, const char *, void *);
+ void (*print_comment)(FILE *, const char *, void *);
+};
+
+static void conf_warning(const char *fmt, ...)
+ __attribute__ ((format (printf, 1, 2)));
+
+static void conf_message(const char *fmt, ...)
+ __attribute__ ((format (printf, 1, 2)));
+
+static const char *conf_filename;
+static int conf_lineno, conf_warnings, conf_unsaved;
+
+const char conf_defname[] = "arch/$ARCH/defconfig";
+
+static void conf_warning(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ fprintf(stderr, "%s:%d:warning: ", conf_filename, conf_lineno);
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ va_end(ap);
+ conf_warnings++;
+}
+
+static void conf_default_message_callback(const char *fmt, va_list ap)
+{
+ printf("#\n# ");
+ vprintf(fmt, ap);
+ printf("\n#\n");
+}
+
+static void (*conf_message_callback) (const char *fmt, va_list ap) =
+ conf_default_message_callback;
+void conf_set_message_callback(void (*fn) (const char *fmt, va_list ap))
+{
+ conf_message_callback = fn;
+}
+
+static void conf_message(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ if (conf_message_callback)
+ conf_message_callback(fmt, ap);
+ va_end(ap);
+}
+
+const char *conf_get_configname(void)
+{
+ char *name = getenv("KCONFIG_CONFIG");
+
+ return name ? name : ".config";
+}
+
+const char *conf_get_autoconfig_name(void)
+{
+ char *name = getenv("KCONFIG_AUTOCONFIG");
+
+ return name ? name : "include/config/auto.conf";
+}
+
+static char *conf_expand_value(const char *in)
+{
+ struct symbol *sym;
+ const char *src;
+ static char res_value[SYMBOL_MAXLENGTH];
+ char *dst, name[SYMBOL_MAXLENGTH];
+
+ res_value[0] = 0;
+ dst = name;
+ while ((src = strchr(in, '$'))) {
+ strncat(res_value, in, src - in);
+ src++;
+ dst = name;
+ while (isalnum(*src) || *src == '_')
+ *dst++ = *src++;
+ *dst = 0;
+ sym = sym_lookup(name, 0);
+ sym_calc_value(sym);
+ strcat(res_value, sym_get_string_value(sym));
+ in = src;
+ }
+ strcat(res_value, in);
+
+ return res_value;
+}
+
+char *conf_get_default_confname(void)
+{
+ struct stat buf;
+ static char fullname[PATH_MAX+1];
+ char *env, *name;
+
+ name = conf_expand_value(conf_defname);
+ env = getenv(SRCTREE);
+ if (env) {
+ sprintf(fullname, "%s/%s", env, name);
+ if (!stat(fullname, &buf))
+ return fullname;
+ }
+ return name;
+}
+
+static int conf_set_sym_val(struct symbol *sym, int def, int def_flags, char *p)
+{
+ char *p2;
+
+ switch (sym->type) {
+ case S_TRISTATE:
+ if (p[0] == 'm') {
+ sym->def[def].tri = mod;
+ sym->flags |= def_flags;
+ break;
+ }
+ /* fall through */
+ case S_BOOLEAN:
+ if (p[0] == 'y') {
+ sym->def[def].tri = yes;
+ sym->flags |= def_flags;
+ break;
+ }
+ if (p[0] == 'n') {
+ sym->def[def].tri = no;
+ sym->flags |= def_flags;
+ break;
+ }
+ if (def != S_DEF_AUTO)
+ conf_warning("symbol value '%s' invalid for %s",
+ p, sym->name);
+ return 1;
+ case S_OTHER:
+ if (*p != '"') {
+ for (p2 = p; *p2 && !isspace(*p2); p2++)
+ ;
+ sym->type = S_STRING;
+ goto done;
+ }
+ /* fall through */
+ case S_STRING:
+ if (*p++ != '"')
+ break;
+ for (p2 = p; (p2 = strpbrk(p2, "\"\\")); p2++) {
+ if (*p2 == '"') {
+ *p2 = 0;
+ break;
+ }
+ memmove(p2, p2 + 1, strlen(p2));
+ }
+ if (!p2) {
+ if (def != S_DEF_AUTO)
+ conf_warning("invalid string found");
+ return 1;
+ }
+ /* fall through */
+ case S_INT:
+ case S_HEX:
+ done:
+ if (sym_string_valid(sym, p)) {
+ sym->def[def].val = strdup(p);
+ sym->flags |= def_flags;
+ } else {
+ if (def != S_DEF_AUTO)
+ conf_warning("symbol value '%s' invalid for %s",
+ p, sym->name);
+ return 1;
+ }
+ break;
+ default:
+ ;
+ }
+ return 0;
+}
+
+#define LINE_GROWTH 16
+static int add_byte(int c, char **lineptr, size_t slen, size_t *n)
+{
+ char *nline;
+ size_t new_size = slen + 1;
+ if (new_size > *n) {
+ new_size += LINE_GROWTH - 1;
+ new_size *= 2;
+ nline = realloc(*lineptr, new_size);
+ if (!nline)
+ return -1;
+
+ *lineptr = nline;
+ *n = new_size;
+ }
+
+ (*lineptr)[slen] = c;
+
+ return 0;
+}
+
+static ssize_t compat_getline(char **lineptr, size_t *n, FILE *stream)
+{
+ char *line = *lineptr;
+ size_t slen = 0;
+
+ for (;;) {
+ int c = getc(stream);
+
+ switch (c) {
+ case '\n':
+ if (add_byte(c, &line, slen, n) < 0)
+ goto e_out;
+ slen++;
+ /* fall through */
+ case EOF:
+ if (add_byte('\0', &line, slen, n) < 0)
+ goto e_out;
+ *lineptr = line;
+ if (slen == 0)
+ return -1;
+ return slen;
+ default:
+ if (add_byte(c, &line, slen, n) < 0)
+ goto e_out;
+ slen++;
+ }
+ }
+
+e_out:
+ line[slen-1] = '\0';
+ *lineptr = line;
+ return -1;
+}
+
+int conf_read_simple(const char *name, int def)
+{
+ FILE *in = NULL;
+ char *line = NULL;
+ size_t line_asize = 0;
+ char *p, *p2;
+ struct symbol *sym;
+ int i, def_flags;
+
+ if (name) {
+ in = zconf_fopen(name);
+ } else {
+ struct property *prop;
+
+ name = conf_get_configname();
+ in = zconf_fopen(name);
+ if (in)
+ goto load;
+ sym_add_change_count(1);
+ if (!sym_defconfig_list) {
+ if (modules_sym)
+ sym_calc_value(modules_sym);
+ return 1;
+ }
+
+ for_all_defaults(sym_defconfig_list, prop) {
+ if (expr_calc_value(prop->visible.expr) == no ||
+ prop->expr->type != E_SYMBOL)
+ continue;
+ name = conf_expand_value(prop->expr->left.sym->name);
+ in = zconf_fopen(name);
+ if (in) {
+ conf_message(_("using defaults found in %s"),
+ name);
+ goto load;
+ }
+ }
+ }
+ if (!in)
+ return 1;
+
+load:
+ conf_filename = name;
+ conf_lineno = 0;
+ conf_warnings = 0;
+ conf_unsaved = 0;
+
+ def_flags = SYMBOL_DEF << def;
+ for_all_symbols(i, sym) {
+ sym->flags |= SYMBOL_CHANGED;
+ sym->flags &= ~(def_flags|SYMBOL_VALID);
+ if (sym_is_choice(sym))
+ sym->flags |= def_flags;
+ switch (sym->type) {
+ case S_INT:
+ case S_HEX:
+ case S_STRING:
+ if (sym->def[def].val)
+ free(sym->def[def].val);
+ /* fall through */
+ default:
+ sym->def[def].val = NULL;
+ sym->def[def].tri = no;
+ }
+ }
+
+ while (compat_getline(&line, &line_asize, in) != -1) {
+ conf_lineno++;
+ sym = NULL;
+ if (line[0] == '#') {
+ if (memcmp(line + 2, CONFIG_, strlen(CONFIG_)))
+ continue;
+ p = strchr(line + 2 + strlen(CONFIG_), ' ');
+ if (!p)
+ continue;
+ *p++ = 0;
+ if (strncmp(p, "is not set", 10))
+ continue;
+ if (def == S_DEF_USER) {
+ sym = sym_find(line + 2 + strlen(CONFIG_));
+ if (!sym) {
+ sym_add_change_count(1);
+ goto setsym;
+ }
+ } else {
+ sym = sym_lookup(line + 2 + strlen(CONFIG_), 0);
+ if (sym->type == S_UNKNOWN)
+ sym->type = S_BOOLEAN;
+ }
+ if (sym->flags & def_flags) {
+ conf_warning("override: reassigning to symbol %s", sym->name);
+ }
+ switch (sym->type) {
+ case S_BOOLEAN:
+ case S_TRISTATE:
+ sym->def[def].tri = no;
+ sym->flags |= def_flags;
+ break;
+ default:
+ ;
+ }
+ } else if (memcmp(line, CONFIG_, strlen(CONFIG_)) == 0) {
+ p = strchr(line + strlen(CONFIG_), '=');
+ if (!p)
+ continue;
+ *p++ = 0;
+ p2 = strchr(p, '\n');
+ if (p2) {
+ *p2-- = 0;
+ if (*p2 == '\r')
+ *p2 = 0;
+ }
+ if (def == S_DEF_USER) {
+ sym = sym_find(line + strlen(CONFIG_));
+ if (!sym) {
+ sym_add_change_count(1);
+ goto setsym;
+ }
+ } else {
+ sym = sym_lookup(line + strlen(CONFIG_), 0);
+ if (sym->type == S_UNKNOWN)
+ sym->type = S_OTHER;
+ }
+ if (sym->flags & def_flags) {
+ conf_warning("override: reassigning to symbol %s", sym->name);
+ }
+ if (conf_set_sym_val(sym, def, def_flags, p))
+ continue;
+ } else {
+ if (line[0] != '\r' && line[0] != '\n')
+ conf_warning("unexpected data");
+ continue;
+ }
+setsym:
+ if (sym && sym_is_choice_value(sym)) {
+ struct symbol *cs = prop_get_symbol(sym_get_choice_prop(sym));
+ switch (sym->def[def].tri) {
+ case no:
+ break;
+ case mod:
+ if (cs->def[def].tri == yes) {
+ conf_warning("%s creates inconsistent choice state", sym->name);
+ cs->flags &= ~def_flags;
+ }
+ break;
+ case yes:
+ if (cs->def[def].tri != no)
+ conf_warning("override: %s changes choice state", sym->name);
+ cs->def[def].val = sym;
+ break;
+ }
+ cs->def[def].tri = EXPR_OR(cs->def[def].tri, sym->def[def].tri);
+ }
+ }
+ free(line);
+ fclose(in);
+
+ if (modules_sym)
+ sym_calc_value(modules_sym);
+ return 0;
+}
+
+int conf_read(const char *name)
+{
+ struct symbol *sym;
+ int i;
+
+ sym_set_change_count(0);
+
+ if (conf_read_simple(name, S_DEF_USER))
+ return 1;
+
+ for_all_symbols(i, sym) {
+ sym_calc_value(sym);
+ if (sym_is_choice(sym) || (sym->flags & SYMBOL_AUTO))
+ continue;
+ if (sym_has_value(sym) && (sym->flags & SYMBOL_WRITE)) {
+ /* check that calculated value agrees with saved value */
+ switch (sym->type) {
+ case S_BOOLEAN:
+ case S_TRISTATE:
+ if (sym->def[S_DEF_USER].tri != sym_get_tristate_value(sym))
+ break;
+ if (!sym_is_choice(sym))
+ continue;
+ /* fall through */
+ default:
+ if (!strcmp(sym->curr.val, sym->def[S_DEF_USER].val))
+ continue;
+ break;
+ }
+ } else if (!sym_has_value(sym) && !(sym->flags & SYMBOL_WRITE))
+ /* no previous value and not saved */
+ continue;
+ conf_unsaved++;
+ /* maybe print value in verbose mode... */
+ }
+
+ for_all_symbols(i, sym) {
+ if (sym_has_value(sym) && !sym_is_choice_value(sym)) {
+ /* Reset values of generates values, so they'll appear
+ * as new, if they should become visible, but that
+ * doesn't quite work if the Kconfig and the saved
+ * configuration disagree.
+ */
+ if (sym->visible == no && !conf_unsaved)
+ sym->flags &= ~SYMBOL_DEF_USER;
+ switch (sym->type) {
+ case S_STRING:
+ case S_INT:
+ case S_HEX:
+ /* Reset a string value if it's out of range */
+ if (sym_string_within_range(sym, sym->def[S_DEF_USER].val))
+ break;
+ sym->flags &= ~(SYMBOL_VALID|SYMBOL_DEF_USER);
+ conf_unsaved++;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ sym_add_change_count(conf_warnings || conf_unsaved);
+
+ return 0;
+}
+
+/*
+ * Kconfig configuration printer
+ *
+ * This printer is used when generating the resulting configuration after
+ * kconfig invocation and `defconfig' files. Unset symbol might be omitted by
+ * passing a non-NULL argument to the printer.
+ *
+ */
+static void
+kconfig_print_symbol(FILE *fp, struct symbol *sym, const char *value, void *arg)
+{
+
+ switch (sym->type) {
+ case S_BOOLEAN:
+ case S_TRISTATE:
+ if (*value == 'n') {
+ bool skip_unset = (arg != NULL);
+
+ if (!skip_unset)
+ fprintf(fp, "# %s%s is not set\n",
+ CONFIG_, sym->name);
+ return;
+ }
+ break;
+ default:
+ break;
+ }
+
+ fprintf(fp, "%s%s=%s\n", CONFIG_, sym->name, value);
+}
+
+static void
+kconfig_print_comment(FILE *fp, const char *value, void *arg)
+{
+ const char *p = value;
+ size_t l;
+
+ for (;;) {
+ l = strcspn(p, "\n");
+ fprintf(fp, "#");
+ if (l) {
+ fprintf(fp, " ");
+ xfwrite(p, l, 1, fp);
+ p += l;
+ }
+ fprintf(fp, "\n");
+ if (*p++ == '\0')
+ break;
+ }
+}
+
+static struct conf_printer kconfig_printer_cb =
+{
+ .print_symbol = kconfig_print_symbol,
+ .print_comment = kconfig_print_comment,
+};
+
+/*
+ * Header printer
+ *
+ * This printer is used when generating the `include/generated/autoconf.h' file.
+ */
+static void
+header_print_symbol(FILE *fp, struct symbol *sym, const char *value, void *arg)
+{
+
+ switch (sym->type) {
+ case S_BOOLEAN:
+ case S_TRISTATE: {
+ const char *suffix = "";
+
+ switch (*value) {
+ case 'n':
+ break;
+ case 'm':
+ suffix = "_MODULE";
+ /* fall through */
+ default:
+ fprintf(fp, "#define %s%s%s 1\n",
+ CONFIG_, sym->name, suffix);
+ }
+ break;
+ }
+ case S_HEX: {
+ const char *prefix = "";
+
+ if (value[0] != '0' || (value[1] != 'x' && value[1] != 'X'))
+ prefix = "0x";
+ fprintf(fp, "#define %s%s %s%s\n",
+ CONFIG_, sym->name, prefix, value);
+ break;
+ }
+ case S_STRING:
+ case S_INT:
+ fprintf(fp, "#define %s%s %s\n",
+ CONFIG_, sym->name, value);
+ break;
+ default:
+ break;
+ }
+
+}
+
+static void
+header_print_comment(FILE *fp, const char *value, void *arg)
+{
+ const char *p = value;
+ size_t l;
+
+ fprintf(fp, "/*\n");
+ for (;;) {
+ l = strcspn(p, "\n");
+ fprintf(fp, " *");
+ if (l) {
+ fprintf(fp, " ");
+ xfwrite(p, l, 1, fp);
+ p += l;
+ }
+ fprintf(fp, "\n");
+ if (*p++ == '\0')
+ break;
+ }
+ fprintf(fp, " */\n");
+}
+
+static struct conf_printer header_printer_cb =
+{
+ .print_symbol = header_print_symbol,
+ .print_comment = header_print_comment,
+};
+
+/*
+ * Tristate printer
+ *
+ * This printer is used when generating the `include/config/tristate.conf' file.
+ */
+static void
+tristate_print_symbol(FILE *fp, struct symbol *sym, const char *value, void *arg)
+{
+
+ if (sym->type == S_TRISTATE && *value != 'n')
+ fprintf(fp, "%s%s=%c\n", CONFIG_, sym->name, (char)toupper(*value));
+}
+
+static struct conf_printer tristate_printer_cb =
+{
+ .print_symbol = tristate_print_symbol,
+ .print_comment = kconfig_print_comment,
+};
+
+static void conf_write_symbol(FILE *fp, struct symbol *sym,
+ struct conf_printer *printer, void *printer_arg)
+{
+ const char *str;
+
+ switch (sym->type) {
+ case S_OTHER:
+ case S_UNKNOWN:
+ break;
+ case S_STRING:
+ str = sym_get_string_value(sym);
+ str = sym_escape_string_value(str);
+ printer->print_symbol(fp, sym, str, printer_arg);
+ free((void *)str);
+ break;
+ default:
+ str = sym_get_string_value(sym);
+ printer->print_symbol(fp, sym, str, printer_arg);
+ }
+}
+
+static void
+conf_write_heading(FILE *fp, struct conf_printer *printer, void *printer_arg)
+{
+ char buf[256];
+
+ snprintf(buf, sizeof(buf),
+ "\n"
+ "Automatically generated file; DO NOT EDIT.\n"
+ "%s\n",
+ rootmenu.prompt->text);
+
+ printer->print_comment(fp, buf, printer_arg);
+}
+
+/*
+ * Write out a minimal config.
+ * All values that has default values are skipped as this is redundant.
+ */
+int conf_write_defconfig(const char *filename)
+{
+ struct symbol *sym;
+ struct menu *menu;
+ FILE *out;
+
+ out = fopen(filename, "w");
+ if (!out)
+ return 1;
+
+ sym_clear_all_valid();
+
+ /* Traverse all menus to find all relevant symbols */
+ menu = rootmenu.list;
+
+ while (menu != NULL)
+ {
+ sym = menu->sym;
+ if (sym == NULL) {
+ if (!menu_is_visible(menu))
+ goto next_menu;
+ } else if (!sym_is_choice(sym)) {
+ sym_calc_value(sym);
+ if (!(sym->flags & SYMBOL_WRITE))
+ goto next_menu;
+ sym->flags &= ~SYMBOL_WRITE;
+ /* If we cannot change the symbol - skip */
+ if (!sym_is_changable(sym))
+ goto next_menu;
+ /* If symbol equals to default value - skip */
+ if (strcmp(sym_get_string_value(sym), sym_get_string_default(sym)) == 0)
+ goto next_menu;
+
+ /*
+ * If symbol is a choice value and equals to the
+ * default for a choice - skip.
+ * But only if value is bool and equal to "y" and
+ * choice is not "optional".
+ * (If choice is "optional" then all values can be "n")
+ */
+ if (sym_is_choice_value(sym)) {
+ struct symbol *cs;
+ struct symbol *ds;
+
+ cs = prop_get_symbol(sym_get_choice_prop(sym));
+ ds = sym_choice_default(cs);
+ if (!sym_is_optional(cs) && sym == ds) {
+ if ((sym->type == S_BOOLEAN) &&
+ sym_get_tristate_value(sym) == yes)
+ goto next_menu;
+ }
+ }
+ conf_write_symbol(out, sym, &kconfig_printer_cb, NULL);
+ }
+next_menu:
+ if (menu->list != NULL) {
+ menu = menu->list;
+ }
+ else if (menu->next != NULL) {
+ menu = menu->next;
+ } else {
+ while ((menu = menu->parent)) {
+ if (menu->next != NULL) {
+ menu = menu->next;
+ break;
+ }
+ }
+ }
+ }
+ fclose(out);
+ return 0;
+}
+
+int conf_write(const char *name)
+{
+ FILE *out;
+ struct symbol *sym;
+ struct menu *menu;
+ const char *basename;
+ const char *str;
+ char dirname[PATH_MAX+1], tmpname[PATH_MAX+1], newname[PATH_MAX+1];
+ char *env;
+
+ dirname[0] = 0;
+ if (name && name[0]) {
+ struct stat st;
+ char *slash;
+
+ if (!stat(name, &st) && S_ISDIR(st.st_mode)) {
+ strcpy(dirname, name);
+ strcat(dirname, "/");
+ basename = conf_get_configname();
+ } else if ((slash = strrchr(name, '/'))) {
+ int size = slash - name + 1;
+ memcpy(dirname, name, size);
+ dirname[size] = 0;
+ if (slash[1])
+ basename = slash + 1;
+ else
+ basename = conf_get_configname();
+ } else
+ basename = name;
+ } else
+ basename = conf_get_configname();
+
+ sprintf(newname, "%s%s", dirname, basename);
+ env = getenv("KCONFIG_OVERWRITECONFIG");
+ if (!env || !*env) {
+ sprintf(tmpname, "%s.tmpconfig.%d", dirname, (int)getpid());
+ out = fopen(tmpname, "w");
+ } else {
+ *tmpname = 0;
+ out = fopen(newname, "w");
+ }
+ if (!out)
+ return 1;
+
+ conf_write_heading(out, &kconfig_printer_cb, NULL);
+
+ if (!conf_get_changed())
+ sym_clear_all_valid();
+
+ menu = rootmenu.list;
+ while (menu) {
+ sym = menu->sym;
+ if (!sym) {
+ if (!menu_is_visible(menu))
+ goto next;
+ str = menu_get_prompt(menu);
+ fprintf(out, "\n"
+ "#\n"
+ "# %s\n"
+ "#\n", str);
+ } else if (!(sym->flags & SYMBOL_CHOICE)) {
+ sym_calc_value(sym);
+ if (!(sym->flags & SYMBOL_WRITE))
+ goto next;
+ sym->flags &= ~SYMBOL_WRITE;
+
+ conf_write_symbol(out, sym, &kconfig_printer_cb, NULL);
+ }
+
+next:
+ if (menu->list) {
+ menu = menu->list;
+ continue;
+ }
+ if (menu->next)
+ menu = menu->next;
+ else while ((menu = menu->parent)) {
+ if (menu->next) {
+ menu = menu->next;
+ break;
+ }
+ }
+ }
+ fclose(out);
+
+ if (*tmpname) {
+ strcat(dirname, basename);
+ strcat(dirname, ".old");
+ rename(newname, dirname);
+ if (rename(tmpname, newname))
+ return 1;
+ }
+
+ conf_message(_("configuration written to %s"), newname);
+
+ sym_set_change_count(0);
+
+ return 0;
+}
+
+static int conf_split_config(void)
+{
+ const char *name;
+ char path[PATH_MAX+1];
+ char *s, *d, c;
+ struct symbol *sym;
+ struct stat sb;
+ int res, i, fd;
+
+ name = conf_get_autoconfig_name();
+ conf_read_simple(name, S_DEF_AUTO);
+
+ if (chdir("include/config"))
+ return 1;
+
+ res = 0;
+ for_all_symbols(i, sym) {
+ sym_calc_value(sym);
+ if ((sym->flags & SYMBOL_AUTO) || !sym->name)
+ continue;
+ if (sym->flags & SYMBOL_WRITE) {
+ if (sym->flags & SYMBOL_DEF_AUTO) {
+ /*
+ * symbol has old and new value,
+ * so compare them...
+ */
+ switch (sym->type) {
+ case S_BOOLEAN:
+ case S_TRISTATE:
+ if (sym_get_tristate_value(sym) ==
+ sym->def[S_DEF_AUTO].tri)
+ continue;
+ break;
+ case S_STRING:
+ case S_HEX:
+ case S_INT:
+ if (!strcmp(sym_get_string_value(sym),
+ sym->def[S_DEF_AUTO].val))
+ continue;
+ break;
+ default:
+ break;
+ }
+ } else {
+ /*
+ * If there is no old value, only 'no' (unset)
+ * is allowed as new value.
+ */
+ switch (sym->type) {
+ case S_BOOLEAN:
+ case S_TRISTATE:
+ if (sym_get_tristate_value(sym) == no)
+ continue;
+ break;
+ default:
+ break;
+ }
+ }
+ } else if (!(sym->flags & SYMBOL_DEF_AUTO))
+ /* There is neither an old nor a new value. */
+ continue;
+ /* else
+ * There is an old value, but no new value ('no' (unset)
+ * isn't saved in auto.conf, so the old value is always
+ * different from 'no').
+ */
+
+ /* Replace all '_' and append ".h" */
+ s = sym->name;
+ d = path;
+ while ((c = *s++)) {
+ c = tolower(c);
+ *d++ = (c == '_') ? '/' : c;
+ }
+ strcpy(d, ".h");
+
+ /* Assume directory path already exists. */
+ fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ if (fd == -1) {
+ if (errno != ENOENT) {
+ res = 1;
+ break;
+ }
+ /*
+ * Create directory components,
+ * unless they exist already.
+ */
+ d = path;
+ while ((d = strchr(d, '/'))) {
+ *d = 0;
+ if (stat(path, &sb) && mkdir(path, 0755)) {
+ res = 1;
+ goto out;
+ }
+ *d++ = '/';
+ }
+ /* Try it again. */
+ fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ if (fd == -1) {
+ res = 1;
+ break;
+ }
+ }
+ close(fd);
+ }
+out:
+ if (chdir("../.."))
+ return 1;
+
+ return res;
+}
+
+int conf_write_autoconf(void)
+{
+ struct symbol *sym;
+ const char *name;
+ FILE *out, *tristate, *out_h;
+ int i;
+
+ sym_clear_all_valid();
+
+ file_write_dep("include/config/auto.conf.cmd");
+
+ if (conf_split_config())
+ return 1;
+
+ out = fopen(".tmpconfig", "w");
+ if (!out)
+ return 1;
+
+ tristate = fopen(".tmpconfig_tristate", "w");
+ if (!tristate) {
+ fclose(out);
+ return 1;
+ }
+
+ out_h = fopen(".tmpconfig.h", "w");
+ if (!out_h) {
+ fclose(out);
+ fclose(tristate);
+ return 1;
+ }
+
+ conf_write_heading(out, &kconfig_printer_cb, NULL);
+
+ conf_write_heading(tristate, &tristate_printer_cb, NULL);
+
+ conf_write_heading(out_h, &header_printer_cb, NULL);
+
+ for_all_symbols(i, sym) {
+ sym_calc_value(sym);
+ if (!(sym->flags & SYMBOL_WRITE) || !sym->name)
+ continue;
+
+ /* write symbol to auto.conf, tristate and header files */
+ conf_write_symbol(out, sym, &kconfig_printer_cb, (void *)1);
+
+ conf_write_symbol(tristate, sym, &tristate_printer_cb, (void *)1);
+
+ conf_write_symbol(out_h, sym, &header_printer_cb, NULL);
+ }
+ fclose(out);
+ fclose(tristate);
+ fclose(out_h);
+
+ name = getenv("KCONFIG_AUTOHEADER");
+ if (!name)
+ name = "include/generated/autoconf.h";
+ if (rename(".tmpconfig.h", name))
+ return 1;
+ name = getenv("KCONFIG_TRISTATE");
+ if (!name)
+ name = "include/config/tristate.conf";
+ if (rename(".tmpconfig_tristate", name))
+ return 1;
+ name = conf_get_autoconfig_name();
+ /*
+ * This must be the last step, kbuild has a dependency on auto.conf
+ * and this marks the successful completion of the previous steps.
+ */
+ if (rename(".tmpconfig", name))
+ return 1;
+
+ return 0;
+}
+
+static int sym_change_count;
+static void (*conf_changed_callback)(void);
+
+void sym_set_change_count(int count)
+{
+ int _sym_change_count = sym_change_count;
+ sym_change_count = count;
+ if (conf_changed_callback &&
+ (bool)_sym_change_count != (bool)count)
+ conf_changed_callback();
+}
+
+void sym_add_change_count(int count)
+{
+ sym_set_change_count(count + sym_change_count);
+}
+
+bool conf_get_changed(void)
+{
+ return sym_change_count;
+}
+
+void conf_set_changed_callback(void (*fn)(void))
+{
+ conf_changed_callback = fn;
+}
+
+static bool randomize_choice_values(struct symbol *csym)
+{
+ struct property *prop;
+ struct symbol *sym;
+ struct expr *e;
+ int cnt, def;
+
+ /*
+ * If choice is mod then we may have more items selected
+ * and if no then no-one.
+ * In both cases stop.
+ */
+ if (csym->curr.tri != yes)
+ return false;
+
+ prop = sym_get_choice_prop(csym);
+
+ /* count entries in choice block */
+ cnt = 0;
+ expr_list_for_each_sym(prop->expr, e, sym)
+ cnt++;
+
+ /*
+ * find a random value and set it to yes,
+ * set the rest to no so we have only one set
+ */
+ def = (rand() % cnt);
+
+ cnt = 0;
+ expr_list_for_each_sym(prop->expr, e, sym) {
+ if (def == cnt++) {
+ sym->def[S_DEF_USER].tri = yes;
+ csym->def[S_DEF_USER].val = sym;
+ }
+ else {
+ sym->def[S_DEF_USER].tri = no;
+ }
+ sym->flags |= SYMBOL_DEF_USER;
+ /* clear VALID to get value calculated */
+ sym->flags &= ~SYMBOL_VALID;
+ }
+ csym->flags |= SYMBOL_DEF_USER;
+ /* clear VALID to get value calculated */
+ csym->flags &= ~(SYMBOL_VALID);
+
+ return true;
+}
+
+void set_all_choice_values(struct symbol *csym)
+{
+ struct property *prop;
+ struct symbol *sym;
+ struct expr *e;
+
+ prop = sym_get_choice_prop(csym);
+
+ /*
+ * Set all non-assinged choice values to no
+ */
+ expr_list_for_each_sym(prop->expr, e, sym) {
+ if (!sym_has_value(sym))
+ sym->def[S_DEF_USER].tri = no;
+ }
+ csym->flags |= SYMBOL_DEF_USER;
+ /* clear VALID to get value calculated */
+ csym->flags &= ~(SYMBOL_VALID | SYMBOL_NEED_SET_CHOICE_VALUES);
+}
+
+bool conf_set_all_new_symbols(enum conf_def_mode mode)
+{
+ struct symbol *sym, *csym;
+ int i, cnt, pby, pty, ptm; /* pby: probability of boolean = y
+ * pty: probability of tristate = y
+ * ptm: probability of tristate = m
+ */
+
+ pby = 50; pty = ptm = 33; /* can't go as the default in switch-case
+ * below, otherwise gcc whines about
+ * -Wmaybe-uninitialized */
+ if (mode == def_random) {
+ int n, p[3];
+ char *env = getenv("KCONFIG_PROBABILITY");
+ n = 0;
+ while( env && *env ) {
+ char *endp;
+ int tmp = strtol( env, &endp, 10 );
+ if( tmp >= 0 && tmp <= 100 ) {
+ p[n++] = tmp;
+ } else {
+ errno = ERANGE;
+ perror( "KCONFIG_PROBABILITY" );
+ exit( 1 );
+ }
+ env = (*endp == ':') ? endp+1 : endp;
+ if( n >=3 ) {
+ break;
+ }
+ }
+ switch( n ) {
+ case 1:
+ pby = p[0]; ptm = pby/2; pty = pby-ptm;
+ break;
+ case 2:
+ pty = p[0]; ptm = p[1]; pby = pty + ptm;
+ break;
+ case 3:
+ pby = p[0]; pty = p[1]; ptm = p[2];
+ break;
+ }
+
+ if( pty+ptm > 100 ) {
+ errno = ERANGE;
+ perror( "KCONFIG_PROBABILITY" );
+ exit( 1 );
+ }
+ }
+ bool has_changed = false;
+
+ for_all_symbols(i, sym) {
+ if (sym_has_value(sym) || (sym->flags & SYMBOL_VALID))
+ continue;
+ switch (sym_get_type(sym)) {
+ case S_BOOLEAN:
+ case S_TRISTATE:
+ has_changed = true;
+ switch (mode) {
+ case def_yes:
+ sym->def[S_DEF_USER].tri = yes;
+ break;
+ case def_mod:
+ sym->def[S_DEF_USER].tri = mod;
+ break;
+ case def_no:
+ if (sym->flags & SYMBOL_ALLNOCONFIG_Y)
+ sym->def[S_DEF_USER].tri = yes;
+ else
+ sym->def[S_DEF_USER].tri = no;
+ break;
+ case def_random:
+ sym->def[S_DEF_USER].tri = no;
+ cnt = rand() % 100;
+ if (sym->type == S_TRISTATE) {
+ if (cnt < pty)
+ sym->def[S_DEF_USER].tri = yes;
+ else if (cnt < (pty+ptm))
+ sym->def[S_DEF_USER].tri = mod;
+ } else if (cnt < pby)
+ sym->def[S_DEF_USER].tri = yes;
+ break;
+ default:
+ continue;
+ }
+ if (!(sym_is_choice(sym) && mode == def_random))
+ sym->flags |= SYMBOL_DEF_USER;
+ break;
+ default:
+ break;
+ }
+
+ }
+
+ sym_clear_all_valid();
+
+ /*
+ * We have different type of choice blocks.
+ * If curr.tri equals to mod then we can select several
+ * choice symbols in one block.
+ * In this case we do nothing.
+ * If curr.tri equals yes then only one symbol can be
+ * selected in a choice block and we set it to yes,
+ * and the rest to no.
+ */
+ if (mode != def_random) {
+ for_all_symbols(i, csym) {
+ if ((sym_is_choice(csym) && !sym_has_value(csym)) ||
+ sym_is_choice_value(csym))
+ csym->flags |= SYMBOL_NEED_SET_CHOICE_VALUES;
+ }
+ }
+
+ for_all_symbols(i, csym) {
+ if (sym_has_value(csym) || !sym_is_choice(csym))
+ continue;
+
+ sym_calc_value(csym);
+ if (mode == def_random)
+ has_changed = randomize_choice_values(csym);
+ else {
+ set_all_choice_values(csym);
+ has_changed = true;
+ }
+ }
+
+ return has_changed;
+}
diff --git a/kconf/expr.c b/kconf/expr.c
new file mode 100644
index 0000000..667d1aa
--- /dev/null
+++ b/kconf/expr.c
@@ -0,0 +1,1206 @@
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lkc.h"
+
+#define DEBUG_EXPR 0
+
+static int expr_eq(struct expr *e1, struct expr *e2);
+static struct expr *expr_eliminate_yn(struct expr *e);
+
+struct expr *expr_alloc_symbol(struct symbol *sym)
+{
+ struct expr *e = xcalloc(1, sizeof(*e));
+ e->type = E_SYMBOL;
+ e->left.sym = sym;
+ return e;
+}
+
+struct expr *expr_alloc_one(enum expr_type type, struct expr *ce)
+{
+ struct expr *e = xcalloc(1, sizeof(*e));
+ e->type = type;
+ e->left.expr = ce;
+ return e;
+}
+
+struct expr *expr_alloc_two(enum expr_type type, struct expr *e1, struct expr *e2)
+{
+ struct expr *e = xcalloc(1, sizeof(*e));
+ e->type = type;
+ e->left.expr = e1;
+ e->right.expr = e2;
+ return e;
+}
+
+struct expr *expr_alloc_comp(enum expr_type type, struct symbol *s1, struct symbol *s2)
+{
+ struct expr *e = xcalloc(1, sizeof(*e));
+ e->type = type;
+ e->left.sym = s1;
+ e->right.sym = s2;
+ return e;
+}
+
+struct expr *expr_alloc_and(struct expr *e1, struct expr *e2)
+{
+ if (!e1)
+ return e2;
+ return e2 ? expr_alloc_two(E_AND, e1, e2) : e1;
+}
+
+struct expr *expr_alloc_or(struct expr *e1, struct expr *e2)
+{
+ if (!e1)
+ return e2;
+ return e2 ? expr_alloc_two(E_OR, e1, e2) : e1;
+}
+
+struct expr *expr_copy(const struct expr *org)
+{
+ struct expr *e;
+
+ if (!org)
+ return NULL;
+
+ e = xmalloc(sizeof(*org));
+ memcpy(e, org, sizeof(*org));
+ switch (org->type) {
+ case E_SYMBOL:
+ e->left = org->left;
+ break;
+ case E_NOT:
+ e->left.expr = expr_copy(org->left.expr);
+ break;
+ case E_EQUAL:
+ case E_GEQ:
+ case E_GTH:
+ case E_LEQ:
+ case E_LTH:
+ case E_UNEQUAL:
+ e->left.sym = org->left.sym;
+ e->right.sym = org->right.sym;
+ break;
+ case E_AND:
+ case E_OR:
+ case E_LIST:
+ e->left.expr = expr_copy(org->left.expr);
+ e->right.expr = expr_copy(org->right.expr);
+ break;
+ default:
+ printf("can't copy type %d\n", e->type);
+ free(e);
+ e = NULL;
+ break;
+ }
+
+ return e;
+}
+
+void expr_free(struct expr *e)
+{
+ if (!e)
+ return;
+
+ switch (e->type) {
+ case E_SYMBOL:
+ break;
+ case E_NOT:
+ expr_free(e->left.expr);
+ return;
+ case E_EQUAL:
+ case E_GEQ:
+ case E_GTH:
+ case E_LEQ:
+ case E_LTH:
+ case E_UNEQUAL:
+ break;
+ case E_OR:
+ case E_AND:
+ expr_free(e->left.expr);
+ expr_free(e->right.expr);
+ break;
+ default:
+ printf("how to free type %d?\n", e->type);
+ break;
+ }
+ free(e);
+}
+
+static int trans_count;
+
+#define e1 (*ep1)
+#define e2 (*ep2)
+
+static void __expr_eliminate_eq(enum expr_type type, struct expr **ep1, struct expr **ep2)
+{
+ if (e1->type == type) {
+ __expr_eliminate_eq(type, &e1->left.expr, &e2);
+ __expr_eliminate_eq(type, &e1->right.expr, &e2);
+ return;
+ }
+ if (e2->type == type) {
+ __expr_eliminate_eq(type, &e1, &e2->left.expr);
+ __expr_eliminate_eq(type, &e1, &e2->right.expr);
+ return;
+ }
+ if (e1->type == E_SYMBOL && e2->type == E_SYMBOL &&
+ e1->left.sym == e2->left.sym &&
+ (e1->left.sym == &symbol_yes || e1->left.sym == &symbol_no))
+ return;
+ if (!expr_eq(e1, e2))
+ return;
+ trans_count++;
+ expr_free(e1); expr_free(e2);
+ switch (type) {
+ case E_OR:
+ e1 = expr_alloc_symbol(&symbol_no);
+ e2 = expr_alloc_symbol(&symbol_no);
+ break;
+ case E_AND:
+ e1 = expr_alloc_symbol(&symbol_yes);
+ e2 = expr_alloc_symbol(&symbol_yes);
+ break;
+ default:
+ ;
+ }
+}
+
+void expr_eliminate_eq(struct expr **ep1, struct expr **ep2)
+{
+ if (!e1 || !e2)
+ return;
+ switch (e1->type) {
+ case E_OR:
+ case E_AND:
+ __expr_eliminate_eq(e1->type, ep1, ep2);
+ default:
+ ;
+ }
+ if (e1->type != e2->type) switch (e2->type) {
+ case E_OR:
+ case E_AND:
+ __expr_eliminate_eq(e2->type, ep1, ep2);
+ default:
+ ;
+ }
+ e1 = expr_eliminate_yn(e1);
+ e2 = expr_eliminate_yn(e2);
+}
+
+#undef e1
+#undef e2
+
+static int expr_eq(struct expr *e1, struct expr *e2)
+{
+ int res, old_count;
+
+ if (e1->type != e2->type)
+ return 0;
+ switch (e1->type) {
+ case E_EQUAL:
+ case E_GEQ:
+ case E_GTH:
+ case E_LEQ:
+ case E_LTH:
+ case E_UNEQUAL:
+ return e1->left.sym == e2->left.sym && e1->right.sym == e2->right.sym;
+ case E_SYMBOL:
+ return e1->left.sym == e2->left.sym;
+ case E_NOT:
+ return expr_eq(e1->left.expr, e2->left.expr);
+ case E_AND:
+ case E_OR:
+ e1 = expr_copy(e1);
+ e2 = expr_copy(e2);
+ old_count = trans_count;
+ expr_eliminate_eq(&e1, &e2);
+ res = (e1->type == E_SYMBOL && e2->type == E_SYMBOL &&
+ e1->left.sym == e2->left.sym);
+ expr_free(e1);
+ expr_free(e2);
+ trans_count = old_count;
+ return res;
+ case E_LIST:
+ case E_RANGE:
+ case E_NONE:
+ /* panic */;
+ }
+
+ if (DEBUG_EXPR) {
+ expr_fprint(e1, stdout);
+ printf(" = ");
+ expr_fprint(e2, stdout);
+ printf(" ?\n");
+ }
+
+ return 0;
+}
+
+static struct expr *expr_eliminate_yn(struct expr *e)
+{
+ struct expr *tmp;
+
+ if (e) switch (e->type) {
+ case E_AND:
+ e->left.expr = expr_eliminate_yn(e->left.expr);
+ e->right.expr = expr_eliminate_yn(e->right.expr);
+ if (e->left.expr->type == E_SYMBOL) {
+ if (e->left.expr->left.sym == &symbol_no) {
+ expr_free(e->left.expr);
+ expr_free(e->right.expr);
+ e->type = E_SYMBOL;
+ e->left.sym = &symbol_no;
+ e->right.expr = NULL;
+ return e;
+ } else if (e->left.expr->left.sym == &symbol_yes) {
+ free(e->left.expr);
+ tmp = e->right.expr;
+ *e = *(e->right.expr);
+ free(tmp);
+ return e;
+ }
+ }
+ if (e->right.expr->type == E_SYMBOL) {
+ if (e->right.expr->left.sym == &symbol_no) {
+ expr_free(e->left.expr);
+ expr_free(e->right.expr);
+ e->type = E_SYMBOL;
+ e->left.sym = &symbol_no;
+ e->right.expr = NULL;
+ return e;
+ } else if (e->right.expr->left.sym == &symbol_yes) {
+ free(e->right.expr);
+ tmp = e->left.expr;
+ *e = *(e->left.expr);
+ free(tmp);
+ return e;
+ }
+ }
+ break;
+ case E_OR:
+ e->left.expr = expr_eliminate_yn(e->left.expr);
+ e->right.expr = expr_eliminate_yn(e->right.expr);
+ if (e->left.expr->type == E_SYMBOL) {
+ if (e->left.expr->left.sym == &symbol_no) {
+ free(e->left.expr);
+ tmp = e->right.expr;
+ *e = *(e->right.expr);
+ free(tmp);
+ return e;
+ } else if (e->left.expr->left.sym == &symbol_yes) {
+ expr_free(e->left.expr);
+ expr_free(e->right.expr);
+ e->type = E_SYMBOL;
+ e->left.sym = &symbol_yes;
+ e->right.expr = NULL;
+ return e;
+ }
+ }
+ if (e->right.expr->type == E_SYMBOL) {
+ if (e->right.expr->left.sym == &symbol_no) {
+ free(e->right.expr);
+ tmp = e->left.expr;
+ *e = *(e->left.expr);
+ free(tmp);
+ return e;
+ } else if (e->right.expr->left.sym == &symbol_yes) {
+ expr_free(e->left.expr);
+ expr_free(e->right.expr);
+ e->type = E_SYMBOL;
+ e->left.sym = &symbol_yes;
+ e->right.expr = NULL;
+ return e;
+ }
+ }
+ break;
+ default:
+ ;
+ }
+ return e;
+}
+
+/*
+ * bool FOO!=n => FOO
+ */
+struct expr *expr_trans_bool(struct expr *e)
+{
+ if (!e)
+ return NULL;
+ switch (e->type) {
+ case E_AND:
+ case E_OR:
+ case E_NOT:
+ e->left.expr = expr_trans_bool(e->left.expr);
+ e->right.expr = expr_trans_bool(e->right.expr);
+ break;
+ case E_UNEQUAL:
+ // FOO!=n -> FOO
+ if (e->left.sym->type == S_TRISTATE) {
+ if (e->right.sym == &symbol_no) {
+ e->type = E_SYMBOL;
+ e->right.sym = NULL;
+ }
+ }
+ break;
+ default:
+ ;
+ }
+ return e;
+}
+
+/*
+ * e1 || e2 -> ?
+ */
+static struct expr *expr_join_or(struct expr *e1, struct expr *e2)
+{
+ struct expr *tmp;
+ struct symbol *sym1, *sym2;
+
+ if (expr_eq(e1, e2))
+ return expr_copy(e1);
+ if (e1->type != E_EQUAL && e1->type != E_UNEQUAL && e1->type != E_SYMBOL && e1->type != E_NOT)
+ return NULL;
+ if (e2->type != E_EQUAL && e2->type != E_UNEQUAL && e2->type != E_SYMBOL && e2->type != E_NOT)
+ return NULL;
+ if (e1->type == E_NOT) {
+ tmp = e1->left.expr;
+ if (tmp->type != E_EQUAL && tmp->type != E_UNEQUAL && tmp->type != E_SYMBOL)
+ return NULL;
+ sym1 = tmp->left.sym;
+ } else
+ sym1 = e1->left.sym;
+ if (e2->type == E_NOT) {
+ if (e2->left.expr->type != E_SYMBOL)
+ return NULL;
+ sym2 = e2->left.expr->left.sym;
+ } else
+ sym2 = e2->left.sym;
+ if (sym1 != sym2)
+ return NULL;
+ if (sym1->type != S_BOOLEAN && sym1->type != S_TRISTATE)
+ return NULL;
+ if (sym1->type == S_TRISTATE) {
+ if (e1->type == E_EQUAL && e2->type == E_EQUAL &&
+ ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_mod) ||
+ (e1->right.sym == &symbol_mod && e2->right.sym == &symbol_yes))) {
+ // (a='y') || (a='m') -> (a!='n')
+ return expr_alloc_comp(E_UNEQUAL, sym1, &symbol_no);
+ }
+ if (e1->type == E_EQUAL && e2->type == E_EQUAL &&
+ ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_no) ||
+ (e1->right.sym == &symbol_no && e2->right.sym == &symbol_yes))) {
+ // (a='y') || (a='n') -> (a!='m')
+ return expr_alloc_comp(E_UNEQUAL, sym1, &symbol_mod);
+ }
+ if (e1->type == E_EQUAL && e2->type == E_EQUAL &&
+ ((e1->right.sym == &symbol_mod && e2->right.sym == &symbol_no) ||
+ (e1->right.sym == &symbol_no && e2->right.sym == &symbol_mod))) {
+ // (a='m') || (a='n') -> (a!='y')
+ return expr_alloc_comp(E_UNEQUAL, sym1, &symbol_yes);
+ }
+ }
+ if (sym1->type == S_BOOLEAN && sym1 == sym2) {
+ if ((e1->type == E_NOT && e1->left.expr->type == E_SYMBOL && e2->type == E_SYMBOL) ||
+ (e2->type == E_NOT && e2->left.expr->type == E_SYMBOL && e1->type == E_SYMBOL))
+ return expr_alloc_symbol(&symbol_yes);
+ }
+
+ if (DEBUG_EXPR) {
+ printf("optimize (");
+ expr_fprint(e1, stdout);
+ printf(") || (");
+ expr_fprint(e2, stdout);
+ printf(")?\n");
+ }
+ return NULL;
+}
+
+static struct expr *expr_join_and(struct expr *e1, struct expr *e2)
+{
+ struct expr *tmp;
+ struct symbol *sym1, *sym2;
+
+ if (expr_eq(e1, e2))
+ return expr_copy(e1);
+ if (e1->type != E_EQUAL && e1->type != E_UNEQUAL && e1->type != E_SYMBOL && e1->type != E_NOT)
+ return NULL;
+ if (e2->type != E_EQUAL && e2->type != E_UNEQUAL && e2->type != E_SYMBOL && e2->type != E_NOT)
+ return NULL;
+ if (e1->type == E_NOT) {
+ tmp = e1->left.expr;
+ if (tmp->type != E_EQUAL && tmp->type != E_UNEQUAL && tmp->type != E_SYMBOL)
+ return NULL;
+ sym1 = tmp->left.sym;
+ } else
+ sym1 = e1->left.sym;
+ if (e2->type == E_NOT) {
+ if (e2->left.expr->type != E_SYMBOL)
+ return NULL;
+ sym2 = e2->left.expr->left.sym;
+ } else
+ sym2 = e2->left.sym;
+ if (sym1 != sym2)
+ return NULL;
+ if (sym1->type != S_BOOLEAN && sym1->type != S_TRISTATE)
+ return NULL;
+
+ if ((e1->type == E_SYMBOL && e2->type == E_EQUAL && e2->right.sym == &symbol_yes) ||
+ (e2->type == E_SYMBOL && e1->type == E_EQUAL && e1->right.sym == &symbol_yes))
+ // (a) && (a='y') -> (a='y')
+ return expr_alloc_comp(E_EQUAL, sym1, &symbol_yes);
+
+ if ((e1->type == E_SYMBOL && e2->type == E_UNEQUAL && e2->right.sym == &symbol_no) ||
+ (e2->type == E_SYMBOL && e1->type == E_UNEQUAL && e1->right.sym == &symbol_no))
+ // (a) && (a!='n') -> (a)
+ return expr_alloc_symbol(sym1);
+
+ if ((e1->type == E_SYMBOL && e2->type == E_UNEQUAL && e2->right.sym == &symbol_mod) ||
+ (e2->type == E_SYMBOL && e1->type == E_UNEQUAL && e1->right.sym == &symbol_mod))
+ // (a) && (a!='m') -> (a='y')
+ return expr_alloc_comp(E_EQUAL, sym1, &symbol_yes);
+
+ if (sym1->type == S_TRISTATE) {
+ if (e1->type == E_EQUAL && e2->type == E_UNEQUAL) {
+ // (a='b') && (a!='c') -> 'b'='c' ? 'n' : a='b'
+ sym2 = e1->right.sym;
+ if ((e2->right.sym->flags & SYMBOL_CONST) && (sym2->flags & SYMBOL_CONST))
+ return sym2 != e2->right.sym ? expr_alloc_comp(E_EQUAL, sym1, sym2)
+ : expr_alloc_symbol(&symbol_no);
+ }
+ if (e1->type == E_UNEQUAL && e2->type == E_EQUAL) {
+ // (a='b') && (a!='c') -> 'b'='c' ? 'n' : a='b'
+ sym2 = e2->right.sym;
+ if ((e1->right.sym->flags & SYMBOL_CONST) && (sym2->flags & SYMBOL_CONST))
+ return sym2 != e1->right.sym ? expr_alloc_comp(E_EQUAL, sym1, sym2)
+ : expr_alloc_symbol(&symbol_no);
+ }
+ if (e1->type == E_UNEQUAL && e2->type == E_UNEQUAL &&
+ ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_no) ||
+ (e1->right.sym == &symbol_no && e2->right.sym == &symbol_yes)))
+ // (a!='y') && (a!='n') -> (a='m')
+ return expr_alloc_comp(E_EQUAL, sym1, &symbol_mod);
+
+ if (e1->type == E_UNEQUAL && e2->type == E_UNEQUAL &&
+ ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_mod) ||
+ (e1->right.sym == &symbol_mod && e2->right.sym == &symbol_yes)))
+ // (a!='y') && (a!='m') -> (a='n')
+ return expr_alloc_comp(E_EQUAL, sym1, &symbol_no);
+
+ if (e1->type == E_UNEQUAL && e2->type == E_UNEQUAL &&
+ ((e1->right.sym == &symbol_mod && e2->right.sym == &symbol_no) ||
+ (e1->right.sym == &symbol_no && e2->right.sym == &symbol_mod)))
+ // (a!='m') && (a!='n') -> (a='m')
+ return expr_alloc_comp(E_EQUAL, sym1, &symbol_yes);
+
+ if ((e1->type == E_SYMBOL && e2->type == E_EQUAL && e2->right.sym == &symbol_mod) ||
+ (e2->type == E_SYMBOL && e1->type == E_EQUAL && e1->right.sym == &symbol_mod) ||
+ (e1->type == E_SYMBOL && e2->type == E_UNEQUAL && e2->right.sym == &symbol_yes) ||
+ (e2->type == E_SYMBOL && e1->type == E_UNEQUAL && e1->right.sym == &symbol_yes))
+ return NULL;
+ }
+
+ if (DEBUG_EXPR) {
+ printf("optimize (");
+ expr_fprint(e1, stdout);
+ printf(") && (");
+ expr_fprint(e2, stdout);
+ printf(")?\n");
+ }
+ return NULL;
+}
+
+static void expr_eliminate_dups1(enum expr_type type, struct expr **ep1, struct expr **ep2)
+{
+#define e1 (*ep1)
+#define e2 (*ep2)
+ struct expr *tmp;
+
+ if (e1->type == type) {
+ expr_eliminate_dups1(type, &e1->left.expr, &e2);
+ expr_eliminate_dups1(type, &e1->right.expr, &e2);
+ return;
+ }
+ if (e2->type == type) {
+ expr_eliminate_dups1(type, &e1, &e2->left.expr);
+ expr_eliminate_dups1(type, &e1, &e2->right.expr);
+ return;
+ }
+ if (e1 == e2)
+ return;
+
+ switch (e1->type) {
+ case E_OR: case E_AND:
+ expr_eliminate_dups1(e1->type, &e1, &e1);
+ default:
+ ;
+ }
+
+ switch (type) {
+ case E_OR:
+ tmp = expr_join_or(e1, e2);
+ if (tmp) {
+ expr_free(e1); expr_free(e2);
+ e1 = expr_alloc_symbol(&symbol_no);
+ e2 = tmp;
+ trans_count++;
+ }
+ break;
+ case E_AND:
+ tmp = expr_join_and(e1, e2);
+ if (tmp) {
+ expr_free(e1); expr_free(e2);
+ e1 = expr_alloc_symbol(&symbol_yes);
+ e2 = tmp;
+ trans_count++;
+ }
+ break;
+ default:
+ ;
+ }
+#undef e1
+#undef e2
+}
+
+struct expr *expr_eliminate_dups(struct expr *e)
+{
+ int oldcount;
+ if (!e)
+ return e;
+
+ oldcount = trans_count;
+ while (1) {
+ trans_count = 0;
+ switch (e->type) {
+ case E_OR: case E_AND:
+ expr_eliminate_dups1(e->type, &e, &e);
+ default:
+ ;
+ }
+ if (!trans_count)
+ break;
+ e = expr_eliminate_yn(e);
+ }
+ trans_count = oldcount;
+ return e;
+}
+
+struct expr *expr_transform(struct expr *e)
+{
+ struct expr *tmp;
+
+ if (!e)
+ return NULL;
+ switch (e->type) {
+ case E_EQUAL:
+ case E_GEQ:
+ case E_GTH:
+ case E_LEQ:
+ case E_LTH:
+ case E_UNEQUAL:
+ case E_SYMBOL:
+ case E_LIST:
+ break;
+ default:
+ e->left.expr = expr_transform(e->left.expr);
+ e->right.expr = expr_transform(e->right.expr);
+ }
+
+ switch (e->type) {
+ case E_EQUAL:
+ if (e->left.sym->type != S_BOOLEAN)
+ break;
+ if (e->right.sym == &symbol_no) {
+ e->type = E_NOT;
+ e->left.expr = expr_alloc_symbol(e->left.sym);
+ e->right.sym = NULL;
+ break;
+ }
+ if (e->right.sym == &symbol_mod) {
+ printf("boolean symbol %s tested for 'm'? test forced to 'n'\n", e->left.sym->name);
+ e->type = E_SYMBOL;
+ e->left.sym = &symbol_no;
+ e->right.sym = NULL;
+ break;
+ }
+ if (e->right.sym == &symbol_yes) {
+ e->type = E_SYMBOL;
+ e->right.sym = NULL;
+ break;
+ }
+ break;
+ case E_UNEQUAL:
+ if (e->left.sym->type != S_BOOLEAN)
+ break;
+ if (e->right.sym == &symbol_no) {
+ e->type = E_SYMBOL;
+ e->right.sym = NULL;
+ break;
+ }
+ if (e->right.sym == &symbol_mod) {
+ printf("boolean symbol %s tested for 'm'? test forced to 'y'\n", e->left.sym->name);
+ e->type = E_SYMBOL;
+ e->left.sym = &symbol_yes;
+ e->right.sym = NULL;
+ break;
+ }
+ if (e->right.sym == &symbol_yes) {
+ e->type = E_NOT;
+ e->left.expr = expr_alloc_symbol(e->left.sym);
+ e->right.sym = NULL;
+ break;
+ }
+ break;
+ case E_NOT:
+ switch (e->left.expr->type) {
+ case E_NOT:
+ // !!a -> a
+ tmp = e->left.expr->left.expr;
+ free(e->left.expr);
+ free(e);
+ e = tmp;
+ e = expr_transform(e);
+ break;
+ case E_EQUAL:
+ case E_UNEQUAL:
+ // !a='x' -> a!='x'
+ tmp = e->left.expr;
+ free(e);
+ e = tmp;
+ e->type = e->type == E_EQUAL ? E_UNEQUAL : E_EQUAL;
+ break;
+ case E_LEQ:
+ case E_GEQ:
+ // !a<='x' -> a>'x'
+ tmp = e->left.expr;
+ free(e);
+ e = tmp;
+ e->type = e->type == E_LEQ ? E_GTH : E_LTH;
+ break;
+ case E_LTH:
+ case E_GTH:
+ // !a<'x' -> a>='x'
+ tmp = e->left.expr;
+ free(e);
+ e = tmp;
+ e->type = e->type == E_LTH ? E_GEQ : E_LEQ;
+ break;
+ case E_OR:
+ // !(a || b) -> !a && !b
+ tmp = e->left.expr;
+ e->type = E_AND;
+ e->right.expr = expr_alloc_one(E_NOT, tmp->right.expr);
+ tmp->type = E_NOT;
+ tmp->right.expr = NULL;
+ e = expr_transform(e);
+ break;
+ case E_AND:
+ // !(a && b) -> !a || !b
+ tmp = e->left.expr;
+ e->type = E_OR;
+ e->right.expr = expr_alloc_one(E_NOT, tmp->right.expr);
+ tmp->type = E_NOT;
+ tmp->right.expr = NULL;
+ e = expr_transform(e);
+ break;
+ case E_SYMBOL:
+ if (e->left.expr->left.sym == &symbol_yes) {
+ // !'y' -> 'n'
+ tmp = e->left.expr;
+ free(e);
+ e = tmp;
+ e->type = E_SYMBOL;
+ e->left.sym = &symbol_no;
+ break;
+ }
+ if (e->left.expr->left.sym == &symbol_mod) {
+ // !'m' -> 'm'
+ tmp = e->left.expr;
+ free(e);
+ e = tmp;
+ e->type = E_SYMBOL;
+ e->left.sym = &symbol_mod;
+ break;
+ }
+ if (e->left.expr->left.sym == &symbol_no) {
+ // !'n' -> 'y'
+ tmp = e->left.expr;
+ free(e);
+ e = tmp;
+ e->type = E_SYMBOL;
+ e->left.sym = &symbol_yes;
+ break;
+ }
+ break;
+ default:
+ ;
+ }
+ break;
+ default:
+ ;
+ }
+ return e;
+}
+
+int expr_contains_symbol(struct expr *dep, struct symbol *sym)
+{
+ if (!dep)
+ return 0;
+
+ switch (dep->type) {
+ case E_AND:
+ case E_OR:
+ return expr_contains_symbol(dep->left.expr, sym) ||
+ expr_contains_symbol(dep->right.expr, sym);
+ case E_SYMBOL:
+ return dep->left.sym == sym;
+ case E_EQUAL:
+ case E_GEQ:
+ case E_GTH:
+ case E_LEQ:
+ case E_LTH:
+ case E_UNEQUAL:
+ return dep->left.sym == sym ||
+ dep->right.sym == sym;
+ case E_NOT:
+ return expr_contains_symbol(dep->left.expr, sym);
+ default:
+ ;
+ }
+ return 0;
+}
+
+bool expr_depends_symbol(struct expr *dep, struct symbol *sym)
+{
+ if (!dep)
+ return false;
+
+ switch (dep->type) {
+ case E_AND:
+ return expr_depends_symbol(dep->left.expr, sym) ||
+ expr_depends_symbol(dep->right.expr, sym);
+ case E_SYMBOL:
+ return dep->left.sym == sym;
+ case E_EQUAL:
+ if (dep->left.sym == sym) {
+ if (dep->right.sym == &symbol_yes || dep->right.sym == &symbol_mod)
+ return true;
+ }
+ break;
+ case E_UNEQUAL:
+ if (dep->left.sym == sym) {
+ if (dep->right.sym == &symbol_no)
+ return true;
+ }
+ break;
+ default:
+ ;
+ }
+ return false;
+}
+
+struct expr *expr_trans_compare(struct expr *e, enum expr_type type, struct symbol *sym)
+{
+ struct expr *e1, *e2;
+
+ if (!e) {
+ e = expr_alloc_symbol(sym);
+ if (type == E_UNEQUAL)
+ e = expr_alloc_one(E_NOT, e);
+ return e;
+ }
+ switch (e->type) {
+ case E_AND:
+ e1 = expr_trans_compare(e->left.expr, E_EQUAL, sym);
+ e2 = expr_trans_compare(e->right.expr, E_EQUAL, sym);
+ if (sym == &symbol_yes)
+ e = expr_alloc_two(E_AND, e1, e2);
+ if (sym == &symbol_no)
+ e = expr_alloc_two(E_OR, e1, e2);
+ if (type == E_UNEQUAL)
+ e = expr_alloc_one(E_NOT, e);
+ return e;
+ case E_OR:
+ e1 = expr_trans_compare(e->left.expr, E_EQUAL, sym);
+ e2 = expr_trans_compare(e->right.expr, E_EQUAL, sym);
+ if (sym == &symbol_yes)
+ e = expr_alloc_two(E_OR, e1, e2);
+ if (sym == &symbol_no)
+ e = expr_alloc_two(E_AND, e1, e2);
+ if (type == E_UNEQUAL)
+ e = expr_alloc_one(E_NOT, e);
+ return e;
+ case E_NOT:
+ return expr_trans_compare(e->left.expr, type == E_EQUAL ? E_UNEQUAL : E_EQUAL, sym);
+ case E_UNEQUAL:
+ case E_LTH:
+ case E_LEQ:
+ case E_GTH:
+ case E_GEQ:
+ case E_EQUAL:
+ if (type == E_EQUAL) {
+ if (sym == &symbol_yes)
+ return expr_copy(e);
+ if (sym == &symbol_mod)
+ return expr_alloc_symbol(&symbol_no);
+ if (sym == &symbol_no)
+ return expr_alloc_one(E_NOT, expr_copy(e));
+ } else {
+ if (sym == &symbol_yes)
+ return expr_alloc_one(E_NOT, expr_copy(e));
+ if (sym == &symbol_mod)
+ return expr_alloc_symbol(&symbol_yes);
+ if (sym == &symbol_no)
+ return expr_copy(e);
+ }
+ break;
+ case E_SYMBOL:
+ return expr_alloc_comp(type, e->left.sym, sym);
+ case E_LIST:
+ case E_RANGE:
+ case E_NONE:
+ /* panic */;
+ }
+ return NULL;
+}
+
+enum string_value_kind {
+ k_string,
+ k_signed,
+ k_unsigned,
+ k_invalid
+};
+
+union string_value {
+ unsigned long long u;
+ signed long long s;
+};
+
+static enum string_value_kind expr_parse_string(const char *str,
+ enum symbol_type type,
+ union string_value *val)
+{
+ char *tail;
+ enum string_value_kind kind;
+
+ errno = 0;
+ switch (type) {
+ case S_BOOLEAN:
+ case S_TRISTATE:
+ return k_string;
+ case S_INT:
+ val->s = strtoll(str, &tail, 10);
+ kind = k_signed;
+ break;
+ case S_HEX:
+ val->u = strtoull(str, &tail, 16);
+ kind = k_unsigned;
+ break;
+ case S_STRING:
+ case S_UNKNOWN:
+ val->s = strtoll(str, &tail, 0);
+ kind = k_signed;
+ break;
+ default:
+ return k_invalid;
+ }
+ return !errno && !*tail && tail > str && isxdigit(tail[-1])
+ ? kind : k_string;
+}
+
+tristate expr_calc_value(struct expr *e)
+{
+ tristate val1, val2;
+ const char *str1, *str2;
+ enum string_value_kind k1 = k_string, k2 = k_string;
+ union string_value lval = {}, rval = {};
+ int res;
+
+ if (!e)
+ return yes;
+
+ switch (e->type) {
+ case E_SYMBOL:
+ sym_calc_value(e->left.sym);
+ return e->left.sym->curr.tri;
+ case E_AND:
+ val1 = expr_calc_value(e->left.expr);
+ val2 = expr_calc_value(e->right.expr);
+ return EXPR_AND(val1, val2);
+ case E_OR:
+ val1 = expr_calc_value(e->left.expr);
+ val2 = expr_calc_value(e->right.expr);
+ return EXPR_OR(val1, val2);
+ case E_NOT:
+ val1 = expr_calc_value(e->left.expr);
+ return EXPR_NOT(val1);
+ case E_EQUAL:
+ case E_GEQ:
+ case E_GTH:
+ case E_LEQ:
+ case E_LTH:
+ case E_UNEQUAL:
+ break;
+ default:
+ printf("expr_calc_value: %d?\n", e->type);
+ return no;
+ }
+
+ sym_calc_value(e->left.sym);
+ sym_calc_value(e->right.sym);
+ str1 = sym_get_string_value(e->left.sym);
+ str2 = sym_get_string_value(e->right.sym);
+
+ if (e->left.sym->type != S_STRING || e->right.sym->type != S_STRING) {
+ k1 = expr_parse_string(str1, e->left.sym->type, &lval);
+ k2 = expr_parse_string(str2, e->right.sym->type, &rval);
+ }
+
+ if (k1 == k_string || k2 == k_string)
+ res = strcmp(str1, str2);
+ else if (k1 == k_invalid || k2 == k_invalid) {
+ if (e->type != E_EQUAL && e->type != E_UNEQUAL) {
+ printf("Cannot compare \"%s\" and \"%s\"\n", str1, str2);
+ return no;
+ }
+ res = strcmp(str1, str2);
+ } else if (k1 == k_unsigned || k2 == k_unsigned)
+ res = (lval.u > rval.u) - (lval.u < rval.u);
+ else /* if (k1 == k_signed && k2 == k_signed) */
+ res = (lval.s > rval.s) - (lval.s < rval.s);
+
+ switch(e->type) {
+ case E_EQUAL:
+ return res ? no : yes;
+ case E_GEQ:
+ return res >= 0 ? yes : no;
+ case E_GTH:
+ return res > 0 ? yes : no;
+ case E_LEQ:
+ return res <= 0 ? yes : no;
+ case E_LTH:
+ return res < 0 ? yes : no;
+ case E_UNEQUAL:
+ return res ? yes : no;
+ default:
+ printf("expr_calc_value: relation %d?\n", e->type);
+ return no;
+ }
+}
+
+static int expr_compare_type(enum expr_type t1, enum expr_type t2)
+{
+ if (t1 == t2)
+ return 0;
+ switch (t1) {
+ case E_LEQ:
+ case E_LTH:
+ case E_GEQ:
+ case E_GTH:
+ if (t2 == E_EQUAL || t2 == E_UNEQUAL)
+ return 1;
+ case E_EQUAL:
+ case E_UNEQUAL:
+ if (t2 == E_NOT)
+ return 1;
+ case E_NOT:
+ if (t2 == E_AND)
+ return 1;
+ case E_AND:
+ if (t2 == E_OR)
+ return 1;
+ case E_OR:
+ if (t2 == E_LIST)
+ return 1;
+ case E_LIST:
+ if (t2 == 0)
+ return 1;
+ default:
+ return -1;
+ }
+ printf("[%dgt%d?]", t1, t2);
+ return 0;
+}
+
+static inline struct expr *
+expr_get_leftmost_symbol(const struct expr *e)
+{
+
+ if (e == NULL)
+ return NULL;
+
+ while (e->type != E_SYMBOL)
+ e = e->left.expr;
+
+ return expr_copy(e);
+}
+
+/*
+ * Given expression `e1' and `e2', returns the leaf of the longest
+ * sub-expression of `e1' not containing 'e2.
+ */
+struct expr *expr_simplify_unmet_dep(struct expr *e1, struct expr *e2)
+{
+ struct expr *ret;
+
+ switch (e1->type) {
+ case E_OR:
+ return expr_alloc_and(
+ expr_simplify_unmet_dep(e1->left.expr, e2),
+ expr_simplify_unmet_dep(e1->right.expr, e2));
+ case E_AND: {
+ struct expr *e;
+ e = expr_alloc_and(expr_copy(e1), expr_copy(e2));
+ e = expr_eliminate_dups(e);
+ ret = (!expr_eq(e, e1)) ? e1 : NULL;
+ expr_free(e);
+ break;
+ }
+ default:
+ ret = e1;
+ break;
+ }
+
+ return expr_get_leftmost_symbol(ret);
+}
+
+void expr_print(struct expr *e, void (*fn)(void *, struct symbol *, const char *), void *data, int prevtoken)
+{
+ if (!e) {
+ fn(data, NULL, "y");
+ return;
+ }
+
+ if (expr_compare_type(prevtoken, e->type) > 0)
+ fn(data, NULL, "(");
+ switch (e->type) {
+ case E_SYMBOL:
+ if (e->left.sym->name)
+ fn(data, e->left.sym, e->left.sym->name);
+ else
+ fn(data, NULL, "<choice>");
+ break;
+ case E_NOT:
+ fn(data, NULL, "!");
+ expr_print(e->left.expr, fn, data, E_NOT);
+ break;
+ case E_EQUAL:
+ if (e->left.sym->name)
+ fn(data, e->left.sym, e->left.sym->name);
+ else
+ fn(data, NULL, "<choice>");
+ fn(data, NULL, "=");
+ fn(data, e->right.sym, e->right.sym->name);
+ break;
+ case E_LEQ:
+ case E_LTH:
+ if (e->left.sym->name)
+ fn(data, e->left.sym, e->left.sym->name);
+ else
+ fn(data, NULL, "<choice>");
+ fn(data, NULL, e->type == E_LEQ ? "<=" : "<");
+ fn(data, e->right.sym, e->right.sym->name);
+ break;
+ case E_GEQ:
+ case E_GTH:
+ if (e->left.sym->name)
+ fn(data, e->left.sym, e->left.sym->name);
+ else
+ fn(data, NULL, "<choice>");
+ fn(data, NULL, e->type == E_LEQ ? ">=" : ">");
+ fn(data, e->right.sym, e->right.sym->name);
+ break;
+ case E_UNEQUAL:
+ if (e->left.sym->name)
+ fn(data, e->left.sym, e->left.sym->name);
+ else
+ fn(data, NULL, "<choice>");
+ fn(data, NULL, "!=");
+ fn(data, e->right.sym, e->right.sym->name);
+ break;
+ case E_OR:
+ expr_print(e->left.expr, fn, data, E_OR);
+ fn(data, NULL, " || ");
+ expr_print(e->right.expr, fn, data, E_OR);
+ break;
+ case E_AND:
+ expr_print(e->left.expr, fn, data, E_AND);
+ fn(data, NULL, " && ");
+ expr_print(e->right.expr, fn, data, E_AND);
+ break;
+ case E_LIST:
+ fn(data, e->right.sym, e->right.sym->name);
+ if (e->left.expr) {
+ fn(data, NULL, " ^ ");
+ expr_print(e->left.expr, fn, data, E_LIST);
+ }
+ break;
+ case E_RANGE:
+ fn(data, NULL, "[");
+ fn(data, e->left.sym, e->left.sym->name);
+ fn(data, NULL, " ");
+ fn(data, e->right.sym, e->right.sym->name);
+ fn(data, NULL, "]");
+ break;
+ default:
+ {
+ char buf[32];
+ sprintf(buf, "<unknown type %d>", e->type);
+ fn(data, NULL, buf);
+ break;
+ }
+ }
+ if (expr_compare_type(prevtoken, e->type) > 0)
+ fn(data, NULL, ")");
+}
+
+static void expr_print_file_helper(void *data, struct symbol *sym, const char *str)
+{
+ xfwrite(str, strlen(str), 1, data);
+}
+
+void expr_fprint(struct expr *e, FILE *out)
+{
+ expr_print(e, expr_print_file_helper, out, E_NONE);
+}
+
+static void expr_print_gstr_helper(void *data, struct symbol *sym, const char *str)
+{
+ struct gstr *gs = (struct gstr*)data;
+ const char *sym_str = NULL;
+
+ if (sym)
+ sym_str = sym_get_string_value(sym);
+
+ if (gs->max_width) {
+ unsigned extra_length = strlen(str);
+ const char *last_cr = strrchr(gs->s, '\n');
+ unsigned last_line_length;
+
+ if (sym_str)
+ extra_length += 4 + strlen(sym_str);
+
+ if (!last_cr)
+ last_cr = gs->s;
+
+ last_line_length = strlen(gs->s) - (last_cr - gs->s);
+
+ if ((last_line_length + extra_length) > gs->max_width)
+ str_append(gs, "\\\n");
+ }
+
+ str_append(gs, str);
+ if (sym && sym->type != S_UNKNOWN)
+ str_printf(gs, " [=%s]", sym_str);
+}
+
+void expr_gstr_print(struct expr *e, struct gstr *gs)
+{
+ expr_print(e, expr_print_gstr_helper, gs, E_NONE);
+}
diff --git a/kconf/expr.h b/kconf/expr.h
new file mode 100644
index 0000000..973b6f7
--- /dev/null
+++ b/kconf/expr.h
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#ifndef EXPR_H
+#define EXPR_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <assert.h>
+#include <stdio.h>
+#include "list.h"
+#ifndef __cplusplus
+#include <stdbool.h>
+#endif
+
+struct file {
+ struct file *next;
+ struct file *parent;
+ const char *name;
+ int lineno;
+};
+
+typedef enum tristate {
+ no, mod, yes
+} tristate;
+
+enum expr_type {
+ E_NONE, E_OR, E_AND, E_NOT,
+ E_EQUAL, E_UNEQUAL, E_LTH, E_LEQ, E_GTH, E_GEQ,
+ E_LIST, E_SYMBOL, E_RANGE
+};
+
+union expr_data {
+ struct expr *expr;
+ struct symbol *sym;
+};
+
+struct expr {
+ enum expr_type type;
+ union expr_data left, right;
+};
+
+#define EXPR_OR(dep1, dep2) (((dep1)>(dep2))?(dep1):(dep2))
+#define EXPR_AND(dep1, dep2) (((dep1)<(dep2))?(dep1):(dep2))
+#define EXPR_NOT(dep) (2-(dep))
+
+#define expr_list_for_each_sym(l, e, s) \
+ for (e = (l); e && (s = e->right.sym); e = e->left.expr)
+
+struct expr_value {
+ struct expr *expr;
+ tristate tri;
+};
+
+struct symbol_value {
+ void *val;
+ tristate tri;
+};
+
+enum symbol_type {
+ S_UNKNOWN, S_BOOLEAN, S_TRISTATE, S_INT, S_HEX, S_STRING, S_OTHER
+};
+
+/* enum values are used as index to symbol.def[] */
+enum {
+ S_DEF_USER, /* main user value */
+ S_DEF_AUTO, /* values read from auto.conf */
+ S_DEF_DEF3, /* Reserved for UI usage */
+ S_DEF_DEF4, /* Reserved for UI usage */
+ S_DEF_COUNT
+};
+
+struct symbol {
+ struct symbol *next;
+ char *name;
+ enum symbol_type type;
+ struct symbol_value curr;
+ struct symbol_value def[S_DEF_COUNT];
+ tristate visible;
+ int flags;
+ struct property *prop;
+ struct expr_value dir_dep;
+ struct expr_value rev_dep;
+};
+
+#define for_all_symbols(i, sym) for (i = 0; i < SYMBOL_HASHSIZE; i++) for (sym = symbol_hash[i]; sym; sym = sym->next) if (sym->type != S_OTHER)
+
+#define SYMBOL_CONST 0x0001 /* symbol is const */
+#define SYMBOL_CHECK 0x0008 /* used during dependency checking */
+#define SYMBOL_CHOICE 0x0010 /* start of a choice block (null name) */
+#define SYMBOL_CHOICEVAL 0x0020 /* used as a value in a choice block */
+#define SYMBOL_VALID 0x0080 /* set when symbol.curr is calculated */
+#define SYMBOL_OPTIONAL 0x0100 /* choice is optional - values can be 'n' */
+#define SYMBOL_WRITE 0x0200 /* write symbol to file (KCONFIG_CONFIG) */
+#define SYMBOL_CHANGED 0x0400 /* ? */
+#define SYMBOL_AUTO 0x1000 /* value from environment variable */
+#define SYMBOL_CHECKED 0x2000 /* used during dependency checking */
+#define SYMBOL_WARNED 0x8000 /* warning has been issued */
+
+/* Set when symbol.def[] is used */
+#define SYMBOL_DEF 0x10000 /* First bit of SYMBOL_DEF */
+#define SYMBOL_DEF_USER 0x10000 /* symbol.def[S_DEF_USER] is valid */
+#define SYMBOL_DEF_AUTO 0x20000 /* symbol.def[S_DEF_AUTO] is valid */
+#define SYMBOL_DEF3 0x40000 /* symbol.def[S_DEF_3] is valid */
+#define SYMBOL_DEF4 0x80000 /* symbol.def[S_DEF_4] is valid */
+
+/* choice values need to be set before calculating this symbol value */
+#define SYMBOL_NEED_SET_CHOICE_VALUES 0x100000
+
+/* Set symbol to y if allnoconfig; used for symbols that hide others */
+#define SYMBOL_ALLNOCONFIG_Y 0x200000
+
+#define SYMBOL_MAXLENGTH 256
+#define SYMBOL_HASHSIZE 9973
+
+/* A property represent the config options that can be associated
+ * with a config "symbol".
+ * Sample:
+ * config FOO
+ * default y
+ * prompt "foo prompt"
+ * select BAR
+ * config BAZ
+ * int "BAZ Value"
+ * range 1..255
+ */
+enum prop_type {
+ P_UNKNOWN,
+ P_PROMPT, /* prompt "foo prompt" or "BAZ Value" */
+ P_COMMENT, /* text associated with a comment */
+ P_MENU, /* prompt associated with a menuconfig option */
+ P_DEFAULT, /* default y */
+ P_CHOICE, /* choice value */
+ P_SELECT, /* select BAR */
+ P_RANGE, /* range 7..100 (for a symbol) */
+ P_ENV, /* value from environment variable */
+ P_SYMBOL, /* where a symbol is defined */
+};
+
+struct property {
+ struct property *next; /* next property - null if last */
+ struct symbol *sym; /* the symbol for which the property is associated */
+ enum prop_type type; /* type of property */
+ const char *text; /* the prompt value - P_PROMPT, P_MENU, P_COMMENT */
+ struct expr_value visible;
+ struct expr *expr; /* the optional conditional part of the property */
+ struct menu *menu; /* the menu the property are associated with
+ * valid for: P_SELECT, P_RANGE, P_CHOICE,
+ * P_PROMPT, P_DEFAULT, P_MENU, P_COMMENT */
+ struct file *file; /* what file was this property defined */
+ int lineno; /* what lineno was this property defined */
+};
+
+#define for_all_properties(sym, st, tok) \
+ for (st = sym->prop; st; st = st->next) \
+ if (st->type == (tok))
+#define for_all_defaults(sym, st) for_all_properties(sym, st, P_DEFAULT)
+#define for_all_choices(sym, st) for_all_properties(sym, st, P_CHOICE)
+#define for_all_prompts(sym, st) \
+ for (st = sym->prop; st; st = st->next) \
+ if (st->text)
+
+struct menu {
+ struct menu *next;
+ struct menu *parent;
+ struct menu *list;
+ struct symbol *sym;
+ struct property *prompt;
+ struct expr *visibility;
+ struct expr *dep;
+ unsigned int flags;
+ char *help;
+ struct file *file;
+ int lineno;
+ void *data;
+};
+
+#define MENU_CHANGED 0x0001
+#define MENU_ROOT 0x0002
+
+struct jump_key {
+ struct list_head entries;
+ size_t offset;
+ struct menu *target;
+ int index;
+};
+
+#define JUMP_NB 9
+
+extern struct file *file_list;
+extern struct file *current_file;
+struct file *lookup_file(const char *name);
+
+extern struct symbol symbol_yes, symbol_no, symbol_mod;
+extern struct symbol *modules_sym;
+extern struct symbol *sym_defconfig_list;
+extern int cdebug;
+struct expr *expr_alloc_symbol(struct symbol *sym);
+struct expr *expr_alloc_one(enum expr_type type, struct expr *ce);
+struct expr *expr_alloc_two(enum expr_type type, struct expr *e1, struct expr *e2);
+struct expr *expr_alloc_comp(enum expr_type type, struct symbol *s1, struct symbol *s2);
+struct expr *expr_alloc_and(struct expr *e1, struct expr *e2);
+struct expr *expr_alloc_or(struct expr *e1, struct expr *e2);
+struct expr *expr_copy(const struct expr *org);
+void expr_free(struct expr *e);
+void expr_eliminate_eq(struct expr **ep1, struct expr **ep2);
+tristate expr_calc_value(struct expr *e);
+struct expr *expr_trans_bool(struct expr *e);
+struct expr *expr_eliminate_dups(struct expr *e);
+struct expr *expr_transform(struct expr *e);
+int expr_contains_symbol(struct expr *dep, struct symbol *sym);
+bool expr_depends_symbol(struct expr *dep, struct symbol *sym);
+struct expr *expr_trans_compare(struct expr *e, enum expr_type type, struct symbol *sym);
+struct expr *expr_simplify_unmet_dep(struct expr *e1, struct expr *e2);
+
+void expr_fprint(struct expr *e, FILE *out);
+struct gstr; /* forward */
+void expr_gstr_print(struct expr *e, struct gstr *gs);
+
+static inline int expr_is_yes(struct expr *e)
+{
+ return !e || (e->type == E_SYMBOL && e->left.sym == &symbol_yes);
+}
+
+static inline int expr_is_no(struct expr *e)
+{
+ return e && (e->type == E_SYMBOL && e->left.sym == &symbol_no);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* EXPR_H */
diff --git a/kconf/list.h b/kconf/list.h
new file mode 100644
index 0000000..2cf23f0
--- /dev/null
+++ b/kconf/list.h
@@ -0,0 +1,131 @@
+#ifndef LIST_H
+#define LIST_H
+
+/*
+ * Copied from include/linux/...
+ */
+
+#undef offsetof
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+
+/**
+ * container_of - cast a member of a structure out to the containing structure
+ * @ptr: the pointer to the member.
+ * @type: the type of the container struct this is embedded in.
+ * @member: the name of the member within the struct.
+ *
+ */
+#define container_of(ptr, type, member) ({ \
+ const typeof( ((type *)0)->member ) *__mptr = (ptr); \
+ (type *)( (char *)__mptr - offsetof(type,member) );})
+
+
+struct list_head {
+ struct list_head *next, *prev;
+};
+
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+ struct list_head name = LIST_HEAD_INIT(name)
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr: the &struct list_head pointer.
+ * @type: the type of the struct this is embedded in.
+ * @member: the name of the list_head within the struct.
+ */
+#define list_entry(ptr, type, member) \
+ container_of(ptr, type, member)
+
+/**
+ * list_for_each_entry - iterate over list of given type
+ * @pos: the type * to use as a loop cursor.
+ * @head: the head for your list.
+ * @member: the name of the list_head within the struct.
+ */
+#define list_for_each_entry(pos, head, member) \
+ for (pos = list_entry((head)->next, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = list_entry(pos->member.next, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @pos: the type * to use as a loop cursor.
+ * @n: another type * to use as temporary storage
+ * @head: the head for your list.
+ * @member: the name of the list_head within the struct.
+ */
+#define list_for_each_entry_safe(pos, n, head, member) \
+ for (pos = list_entry((head)->next, typeof(*pos), member), \
+ n = list_entry(pos->member.next, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+/**
+ * list_empty - tests whether a list is empty
+ * @head: the list to test.
+ */
+static inline int list_empty(const struct list_head *head)
+{
+ return head->next == head;
+}
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_add(struct list_head *_new,
+ struct list_head *prev,
+ struct list_head *next)
+{
+ next->prev = _new;
+ _new->next = next;
+ _new->prev = prev;
+ prev->next = _new;
+}
+
+/**
+ * list_add_tail - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+static inline void list_add_tail(struct list_head *_new, struct list_head *head)
+{
+ __list_add(_new, head->prev, head);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_del(struct list_head *prev, struct list_head *next)
+{
+ next->prev = prev;
+ prev->next = next;
+}
+
+#define LIST_POISON1 ((void *) 0x00100100)
+#define LIST_POISON2 ((void *) 0x00200200)
+/**
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ * Note: list_empty() on entry does not return true after this, the entry is
+ * in an undefined state.
+ */
+static inline void list_del(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+ entry->next = (struct list_head*)LIST_POISON1;
+ entry->prev = (struct list_head*)LIST_POISON2;
+}
+#endif
diff --git a/kconf/lkc.h b/kconf/lkc.h
new file mode 100644
index 0000000..91ca126
--- /dev/null
+++ b/kconf/lkc.h
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#ifndef LKC_H
+#define LKC_H
+
+#include "expr.h"
+
+#ifndef KBUILD_NO_NLS
+# include <libintl.h>
+#else
+static inline const char *gettext(const char *txt) { return txt; }
+static inline void textdomain(const char *domainname) {}
+static inline void bindtextdomain(const char *name, const char *dir) {}
+static inline char *bind_textdomain_codeset(const char *dn, char *c) { return c; }
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "lkc_proto.h"
+
+#define SRCTREE "srctree"
+
+#ifndef PACKAGE
+#define PACKAGE "linux"
+#endif
+
+#define LOCALEDIR "/usr/share/locale"
+
+#define _(text) gettext(text)
+#define N_(text) (text)
+
+#ifndef CONFIG_
+#define CONFIG_ "CONFIG_"
+#endif
+static inline const char *CONFIG_prefix(void)
+{
+ return getenv( "CONFIG_" ) ?: CONFIG_;
+}
+#undef CONFIG_
+#define CONFIG_ CONFIG_prefix()
+
+#define TF_COMMAND 0x0001
+#define TF_PARAM 0x0002
+#define TF_OPTION 0x0004
+
+enum conf_def_mode {
+ def_default,
+ def_yes,
+ def_mod,
+ def_no,
+ def_random
+};
+
+#define T_OPT_MODULES 1
+#define T_OPT_DEFCONFIG_LIST 2
+#define T_OPT_ENV 3
+#define T_OPT_ALLNOCONFIG_Y 4
+
+struct kconf_id {
+ int name;
+ int token;
+ unsigned int flags;
+ enum symbol_type stype;
+};
+
+void zconfdump(FILE *out);
+void zconf_starthelp(void);
+FILE *zconf_fopen(const char *name);
+void zconf_initscan(const char *name);
+void zconf_nextfile(const char *name);
+int zconf_lineno(void);
+const char *zconf_curname(void);
+
+/* confdata.c */
+const char *conf_get_configname(void);
+const char *conf_get_autoconfig_name(void);
+char *conf_get_default_confname(void);
+void sym_set_change_count(int count);
+void sym_add_change_count(int count);
+bool conf_set_all_new_symbols(enum conf_def_mode mode);
+void set_all_choice_values(struct symbol *csym);
+
+/* confdata.c and expr.c */
+static inline void xfwrite(const void *str, size_t len, size_t count, FILE *out)
+{
+ assert(len != 0);
+
+ if (fwrite(str, len, count, out) != count)
+ fprintf(stderr, "Error in writing or end of file.\n");
+}
+
+/* menu.c */
+void _menu_init(void);
+void menu_warn(struct menu *menu, const char *fmt, ...);
+struct menu *menu_add_menu(void);
+void menu_end_menu(void);
+void menu_add_entry(struct symbol *sym);
+void menu_end_entry(void);
+void menu_add_dep(struct expr *dep);
+void menu_add_visibility(struct expr *dep);
+struct property *menu_add_prompt(enum prop_type type, char *prompt, struct expr *dep);
+void menu_add_expr(enum prop_type type, struct expr *expr, struct expr *dep);
+void menu_add_symbol(enum prop_type type, struct symbol *sym, struct expr *dep);
+void menu_add_option(int token, char *arg);
+void menu_finalize(struct menu *parent);
+void menu_set_type(int type);
+
+/* util.c */
+struct file *file_lookup(const char *name);
+int file_write_dep(const char *name);
+void *xmalloc(size_t size);
+void *xcalloc(size_t nmemb, size_t size);
+
+struct gstr {
+ size_t len;
+ char *s;
+ /*
+ * when max_width is not zero long lines in string s (if any) get
+ * wrapped not to exceed the max_width value
+ */
+ int max_width;
+};
+struct gstr str_new(void);
+void str_free(struct gstr *gs);
+void str_append(struct gstr *gs, const char *s);
+void str_printf(struct gstr *gs, const char *fmt, ...);
+const char *str_get(struct gstr *gs);
+
+/* symbol.c */
+extern struct expr *sym_env_list;
+
+void sym_init(void);
+void sym_clear_all_valid(void);
+struct symbol *sym_choice_default(struct symbol *sym);
+const char *sym_get_string_default(struct symbol *sym);
+struct symbol *sym_check_deps(struct symbol *sym);
+struct property *prop_alloc(enum prop_type type, struct symbol *sym);
+struct symbol *prop_get_symbol(struct property *prop);
+struct property *sym_get_env_prop(struct symbol *sym);
+
+static inline tristate sym_get_tristate_value(struct symbol *sym)
+{
+ return sym->curr.tri;
+}
+
+
+static inline struct symbol *sym_get_choice_value(struct symbol *sym)
+{
+ return (struct symbol *)sym->curr.val;
+}
+
+static inline bool sym_set_choice_value(struct symbol *ch, struct symbol *chval)
+{
+ return sym_set_tristate_value(chval, yes);
+}
+
+static inline bool sym_is_choice(struct symbol *sym)
+{
+ return sym->flags & SYMBOL_CHOICE ? true : false;
+}
+
+static inline bool sym_is_choice_value(struct symbol *sym)
+{
+ return sym->flags & SYMBOL_CHOICEVAL ? true : false;
+}
+
+static inline bool sym_is_optional(struct symbol *sym)
+{
+ return sym->flags & SYMBOL_OPTIONAL ? true : false;
+}
+
+static inline bool sym_has_value(struct symbol *sym)
+{
+ return sym->flags & SYMBOL_DEF_USER ? true : false;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LKC_H */
diff --git a/kconf/lkc_proto.h b/kconf/lkc_proto.h
new file mode 100644
index 0000000..d539871
--- /dev/null
+++ b/kconf/lkc_proto.h
@@ -0,0 +1,52 @@
+#include <stdarg.h>
+
+/* confdata.c */
+void conf_parse(const char *name);
+int conf_read(const char *name);
+int conf_read_simple(const char *name, int);
+int conf_write_defconfig(const char *name);
+int conf_write(const char *name);
+int conf_write_autoconf(void);
+bool conf_get_changed(void);
+void conf_set_changed_callback(void (*fn)(void));
+void conf_set_message_callback(void (*fn)(const char *fmt, va_list ap));
+
+/* menu.c */
+extern struct menu rootmenu;
+
+bool menu_is_empty(struct menu *menu);
+bool menu_is_visible(struct menu *menu);
+bool menu_has_prompt(struct menu *menu);
+const char * menu_get_prompt(struct menu *menu);
+struct menu * menu_get_root_menu(struct menu *menu);
+struct menu * menu_get_parent_menu(struct menu *menu);
+bool menu_has_help(struct menu *menu);
+const char * menu_get_help(struct menu *menu);
+struct gstr get_relations_str(struct symbol **sym_arr, struct list_head *head);
+void menu_get_ext_help(struct menu *menu, struct gstr *help);
+
+/* symbol.c */
+extern struct symbol * symbol_hash[SYMBOL_HASHSIZE];
+
+struct symbol * sym_lookup(const char *name, int flags);
+struct symbol * sym_find(const char *name);
+const char * sym_expand_string_value(const char *in);
+const char * sym_escape_string_value(const char *in);
+struct symbol ** sym_re_search(const char *pattern);
+const char * sym_type_name(enum symbol_type type);
+void sym_calc_value(struct symbol *sym);
+enum symbol_type sym_get_type(struct symbol *sym);
+bool sym_tristate_within_range(struct symbol *sym,tristate tri);
+bool sym_set_tristate_value(struct symbol *sym,tristate tri);
+tristate sym_toggle_tristate_value(struct symbol *sym);
+bool sym_string_valid(struct symbol *sym, const char *newval);
+bool sym_string_within_range(struct symbol *sym, const char *str);
+bool sym_set_string_value(struct symbol *sym, const char *newval);
+bool sym_is_changable(struct symbol *sym);
+struct property * sym_get_choice_prop(struct symbol *sym);
+const char * sym_get_string_value(struct symbol *sym);
+
+const char * prop_get_type_name(enum prop_type type);
+
+/* expr.c */
+void expr_print(struct expr *e, void (*fn)(void *, struct symbol *, const char *), void *data, int prevtoken);
diff --git a/kconf/lxdialog/check-lxdialog.sh b/kconf/lxdialog/check-lxdialog.sh
new file mode 100755
index 0000000..5075ebf
--- /dev/null
+++ b/kconf/lxdialog/check-lxdialog.sh
@@ -0,0 +1,91 @@
+#!/bin/sh
+# Check ncurses compatibility
+
+# What library to link
+ldflags()
+{
+ pkg-config --libs ncursesw 2>/dev/null && exit
+ pkg-config --libs ncurses 2>/dev/null && exit
+ for ext in so a dll.a dylib ; do
+ for lib in ncursesw ncurses curses ; do
+ $cc -print-file-name=lib${lib}.${ext} | grep -q /
+ if [ $? -eq 0 ]; then
+ echo "-l${lib}"
+ exit
+ fi
+ done
+ done
+ exit 1
+}
+
+# Where is ncurses.h?
+ccflags()
+{
+ if pkg-config --cflags ncursesw 2>/dev/null; then
+ echo '-DCURSES_LOC="<ncurses.h>" -DNCURSES_WIDECHAR=1'
+ elif pkg-config --cflags ncurses 2>/dev/null; then
+ echo '-DCURSES_LOC="<ncurses.h>"'
+ elif [ -f /usr/include/ncursesw/curses.h ]; then
+ echo '-I/usr/include/ncursesw -DCURSES_LOC="<curses.h>"'
+ echo ' -DNCURSES_WIDECHAR=1'
+ elif [ -f /usr/include/ncurses/ncurses.h ]; then
+ echo '-I/usr/include/ncurses -DCURSES_LOC="<ncurses.h>"'
+ elif [ -f /usr/include/ncurses/curses.h ]; then
+ echo '-I/usr/include/ncurses -DCURSES_LOC="<curses.h>"'
+ elif [ -f /usr/include/ncurses.h ]; then
+ echo '-DCURSES_LOC="<ncurses.h>"'
+ else
+ echo '-DCURSES_LOC="<curses.h>"'
+ fi
+}
+
+# Temp file, try to clean up after us
+tmp=.lxdialog.tmp
+trap "rm -f $tmp" 0 1 2 3 15
+
+# Check if we can link to ncurses
+check() {
+ $cc -x c - -o $tmp 2>/dev/null <<'EOF'
+#include CURSES_LOC
+main() {}
+EOF
+ if [ $? != 0 ]; then
+ echo " *** Unable to find the ncurses libraries or the" 1>&2
+ echo " *** required header files." 1>&2
+ echo " *** 'make menuconfig' requires the ncurses libraries." 1>&2
+ echo " *** " 1>&2
+ echo " *** Install ncurses (ncurses-devel) and try again." 1>&2
+ echo " *** " 1>&2
+ exit 1
+ fi
+}
+
+usage() {
+ printf "Usage: $0 [-check compiler options|-ccflags|-ldflags compiler options]\n"
+}
+
+if [ $# -eq 0 ]; then
+ usage
+ exit 1
+fi
+
+cc=""
+case "$1" in
+ "-check")
+ shift
+ cc="$@"
+ check
+ ;;
+ "-ccflags")
+ ccflags
+ ;;
+ "-ldflags")
+ shift
+ cc="$@"
+ ldflags
+ ;;
+ "*")
+ usage
+ exit 1
+ ;;
+esac
diff --git a/kconf/lxdialog/checklist.c b/kconf/lxdialog/checklist.c
new file mode 100644
index 0000000..8d016fa
--- /dev/null
+++ b/kconf/lxdialog/checklist.c
@@ -0,0 +1,332 @@
+/*
+ * checklist.c -- implements the checklist box
+ *
+ * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
+ * Stuart Herbert - S.Herbert@sheffield.ac.uk: radiolist extension
+ * Alessandro Rubini - rubini@ipvvis.unipv.it: merged the two
+ * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com)
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "dialog.h"
+
+static int list_width, check_x, item_x;
+
+/*
+ * Print list item
+ */
+static void print_item(WINDOW * win, int choice, int selected)
+{
+ int i;
+ char *list_item = malloc(list_width + 1);
+
+ strncpy(list_item, item_str(), list_width - item_x);
+ list_item[list_width - item_x] = '\0';
+
+ /* Clear 'residue' of last item */
+ wattrset(win, dlg.menubox.atr);
+ wmove(win, choice, 0);
+ for (i = 0; i < list_width; i++)
+ waddch(win, ' ');
+
+ wmove(win, choice, check_x);
+ wattrset(win, selected ? dlg.check_selected.atr
+ : dlg.check.atr);
+ if (!item_is_tag(':'))
+ wprintw(win, "(%c)", item_is_tag('X') ? 'X' : ' ');
+
+ wattrset(win, selected ? dlg.tag_selected.atr : dlg.tag.atr);
+ mvwaddch(win, choice, item_x, list_item[0]);
+ wattrset(win, selected ? dlg.item_selected.atr : dlg.item.atr);
+ waddstr(win, list_item + 1);
+ if (selected) {
+ wmove(win, choice, check_x + 1);
+ wrefresh(win);
+ }
+ free(list_item);
+}
+
+/*
+ * Print the scroll indicators.
+ */
+static void print_arrows(WINDOW * win, int choice, int item_no, int scroll,
+ int y, int x, int height)
+{
+ wmove(win, y, x);
+
+ if (scroll > 0) {
+ wattrset(win, dlg.uarrow.atr);
+ waddch(win, ACS_UARROW);
+ waddstr(win, "(-)");
+ } else {
+ wattrset(win, dlg.menubox.atr);
+ waddch(win, ACS_HLINE);
+ waddch(win, ACS_HLINE);
+ waddch(win, ACS_HLINE);
+ waddch(win, ACS_HLINE);
+ }
+
+ y = y + height + 1;
+ wmove(win, y, x);
+
+ if ((height < item_no) && (scroll + choice < item_no - 1)) {
+ wattrset(win, dlg.darrow.atr);
+ waddch(win, ACS_DARROW);
+ waddstr(win, "(+)");
+ } else {
+ wattrset(win, dlg.menubox_border.atr);
+ waddch(win, ACS_HLINE);
+ waddch(win, ACS_HLINE);
+ waddch(win, ACS_HLINE);
+ waddch(win, ACS_HLINE);
+ }
+}
+
+/*
+ * Display the termination buttons
+ */
+static void print_buttons(WINDOW * dialog, int height, int width, int selected)
+{
+ int x = width / 2 - 11;
+ int y = height - 2;
+
+ print_button(dialog, gettext("Select"), y, x, selected == 0);
+ print_button(dialog, gettext(" Help "), y, x + 14, selected == 1);
+
+ wmove(dialog, y, x + 1 + 14 * selected);
+ wrefresh(dialog);
+}
+
+/*
+ * Display a dialog box with a list of options that can be turned on or off
+ * in the style of radiolist (only one option turned on at a time).
+ */
+int dialog_checklist(const char *title, const char *prompt, int height,
+ int width, int list_height)
+{
+ int i, x, y, box_x, box_y;
+ int key = 0, button = 0, choice = 0, scroll = 0, max_choice;
+ WINDOW *dialog, *list;
+
+ /* which item to highlight */
+ item_foreach() {
+ if (item_is_tag('X'))
+ choice = item_n();
+ if (item_is_selected()) {
+ choice = item_n();
+ break;
+ }
+ }
+
+do_resize:
+ if (getmaxy(stdscr) < (height + CHECKLIST_HEIGTH_MIN))
+ return -ERRDISPLAYTOOSMALL;
+ if (getmaxx(stdscr) < (width + CHECKLIST_WIDTH_MIN))
+ return -ERRDISPLAYTOOSMALL;
+
+ max_choice = MIN(list_height, item_count());
+
+ /* center dialog box on screen */
+ x = (getmaxx(stdscr) - width) / 2;
+ y = (getmaxy(stdscr) - height) / 2;
+
+ draw_shadow(stdscr, y, x, height, width);
+
+ dialog = newwin(height, width, y, x);
+ keypad(dialog, TRUE);
+
+ draw_box(dialog, 0, 0, height, width,
+ dlg.dialog.atr, dlg.border.atr);
+ wattrset(dialog, dlg.border.atr);
+ mvwaddch(dialog, height - 3, 0, ACS_LTEE);
+ for (i = 0; i < width - 2; i++)
+ waddch(dialog, ACS_HLINE);
+ wattrset(dialog, dlg.dialog.atr);
+ waddch(dialog, ACS_RTEE);
+
+ print_title(dialog, title, width);
+
+ wattrset(dialog, dlg.dialog.atr);
+ print_autowrap(dialog, prompt, width - 2, 1, 3);
+
+ list_width = width - 6;
+ box_y = height - list_height - 5;
+ box_x = (width - list_width) / 2 - 1;
+
+ /* create new window for the list */
+ list = subwin(dialog, list_height, list_width, y + box_y + 1,
+ x + box_x + 1);
+
+ keypad(list, TRUE);
+
+ /* draw a box around the list items */
+ draw_box(dialog, box_y, box_x, list_height + 2, list_width + 2,
+ dlg.menubox_border.atr, dlg.menubox.atr);
+
+ /* Find length of longest item in order to center checklist */
+ check_x = 0;
+ item_foreach()
+ check_x = MAX(check_x, strlen(item_str()) + 4);
+ check_x = MIN(check_x, list_width);
+
+ check_x = (list_width - check_x) / 2;
+ item_x = check_x + 4;
+
+ if (choice >= list_height) {
+ scroll = choice - list_height + 1;
+ choice -= scroll;
+ }
+
+ /* Print the list */
+ for (i = 0; i < max_choice; i++) {
+ item_set(scroll + i);
+ print_item(list, i, i == choice);
+ }
+
+ print_arrows(dialog, choice, item_count(), scroll,
+ box_y, box_x + check_x + 5, list_height);
+
+ print_buttons(dialog, height, width, 0);
+
+ wnoutrefresh(dialog);
+ wnoutrefresh(list);
+ doupdate();
+
+ while (key != KEY_ESC) {
+ key = wgetch(dialog);
+
+ for (i = 0; i < max_choice; i++) {
+ item_set(i + scroll);
+ if (toupper(key) == toupper(item_str()[0]))
+ break;
+ }
+
+ if (i < max_choice || key == KEY_UP || key == KEY_DOWN ||
+ key == '+' || key == '-') {
+ if (key == KEY_UP || key == '-') {
+ if (!choice) {
+ if (!scroll)
+ continue;
+ /* Scroll list down */
+ if (list_height > 1) {
+ /* De-highlight current first item */
+ item_set(scroll);
+ print_item(list, 0, FALSE);
+ scrollok(list, TRUE);
+ wscrl(list, -1);
+ scrollok(list, FALSE);
+ }
+ scroll--;
+ item_set(scroll);
+ print_item(list, 0, TRUE);
+ print_arrows(dialog, choice, item_count(),
+ scroll, box_y, box_x + check_x + 5, list_height);
+
+ wnoutrefresh(dialog);
+ wrefresh(list);
+
+ continue; /* wait for another key press */
+ } else
+ i = choice - 1;
+ } else if (key == KEY_DOWN || key == '+') {
+ if (choice == max_choice - 1) {
+ if (scroll + choice >= item_count() - 1)
+ continue;
+ /* Scroll list up */
+ if (list_height > 1) {
+ /* De-highlight current last item before scrolling up */
+ item_set(scroll + max_choice - 1);
+ print_item(list,
+ max_choice - 1,
+ FALSE);
+ scrollok(list, TRUE);
+ wscrl(list, 1);
+ scrollok(list, FALSE);
+ }
+ scroll++;
+ item_set(scroll + max_choice - 1);
+ print_item(list, max_choice - 1, TRUE);
+
+ print_arrows(dialog, choice, item_count(),
+ scroll, box_y, box_x + check_x + 5, list_height);
+
+ wnoutrefresh(dialog);
+ wrefresh(list);
+
+ continue; /* wait for another key press */
+ } else
+ i = choice + 1;
+ }
+ if (i != choice) {
+ /* De-highlight current item */
+ item_set(scroll + choice);
+ print_item(list, choice, FALSE);
+ /* Highlight new item */
+ choice = i;
+ item_set(scroll + choice);
+ print_item(list, choice, TRUE);
+ wnoutrefresh(dialog);
+ wrefresh(list);
+ }
+ continue; /* wait for another key press */
+ }
+ switch (key) {
+ case 'H':
+ case 'h':
+ case '?':
+ button = 1;
+ /* fall-through */
+ case 'S':
+ case 's':
+ case ' ':
+ case '\n':
+ item_foreach()
+ item_set_selected(0);
+ item_set(scroll + choice);
+ item_set_selected(1);
+ delwin(list);
+ delwin(dialog);
+ return button;
+ case TAB:
+ case KEY_LEFT:
+ case KEY_RIGHT:
+ button = ((key == KEY_LEFT ? --button : ++button) < 0)
+ ? 1 : (button > 1 ? 0 : button);
+
+ print_buttons(dialog, height, width, button);
+ wrefresh(dialog);
+ break;
+ case 'X':
+ case 'x':
+ key = KEY_ESC;
+ break;
+ case KEY_ESC:
+ key = on_key_esc(dialog);
+ break;
+ case KEY_RESIZE:
+ delwin(list);
+ delwin(dialog);
+ on_key_resize();
+ goto do_resize;
+ }
+
+ /* Now, update everything... */
+ doupdate();
+ }
+ delwin(list);
+ delwin(dialog);
+ return key; /* ESC pressed */
+}
diff --git a/kconf/lxdialog/dialog.h b/kconf/lxdialog/dialog.h
new file mode 100644
index 0000000..fcffd5b
--- /dev/null
+++ b/kconf/lxdialog/dialog.h
@@ -0,0 +1,257 @@
+/*
+ * dialog.h -- common declarations for all dialog modules
+ *
+ * AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+
+#ifndef KBUILD_NO_NLS
+# include <libintl.h>
+#else
+# define gettext(Msgid) ((const char *) (Msgid))
+#endif
+
+#ifdef __sun__
+#define CURS_MACROS
+#endif
+#include CURSES_LOC
+
+/*
+ * Colors in ncurses 1.9.9e do not work properly since foreground and
+ * background colors are OR'd rather than separately masked. This version
+ * of dialog was hacked to work with ncurses 1.9.9e, making it incompatible
+ * with standard curses. The simplest fix (to make this work with standard
+ * curses) uses the wbkgdset() function, not used in the original hack.
+ * Turn it off if we're building with 1.9.9e, since it just confuses things.
+ */
+#if defined(NCURSES_VERSION) && defined(_NEED_WRAP) && !defined(GCC_PRINTFLIKE)
+#define OLD_NCURSES 1
+#undef wbkgdset
+#define wbkgdset(w,p) /*nothing */
+#else
+#define OLD_NCURSES 0
+#endif
+
+#define TR(params) _tracef params
+
+#define KEY_ESC 27
+#define TAB 9
+#define MAX_LEN 2048
+#define BUF_SIZE (10*1024)
+#define MIN(x,y) (x < y ? x : y)
+#define MAX(x,y) (x > y ? x : y)
+
+#ifndef ACS_ULCORNER
+#define ACS_ULCORNER '+'
+#endif
+#ifndef ACS_LLCORNER
+#define ACS_LLCORNER '+'
+#endif
+#ifndef ACS_URCORNER
+#define ACS_URCORNER '+'
+#endif
+#ifndef ACS_LRCORNER
+#define ACS_LRCORNER '+'
+#endif
+#ifndef ACS_HLINE
+#define ACS_HLINE '-'
+#endif
+#ifndef ACS_VLINE
+#define ACS_VLINE '|'
+#endif
+#ifndef ACS_LTEE
+#define ACS_LTEE '+'
+#endif
+#ifndef ACS_RTEE
+#define ACS_RTEE '+'
+#endif
+#ifndef ACS_UARROW
+#define ACS_UARROW '^'
+#endif
+#ifndef ACS_DARROW
+#define ACS_DARROW 'v'
+#endif
+
+/* error return codes */
+#define ERRDISPLAYTOOSMALL (KEY_MAX + 1)
+
+/*
+ * Color definitions
+ */
+struct dialog_color {
+ chtype atr; /* Color attribute */
+ int fg; /* foreground */
+ int bg; /* background */
+ int hl; /* highlight this item */
+};
+
+struct subtitle_list {
+ struct subtitle_list *next;
+ const char *text;
+};
+
+struct dialog_info {
+ const char *backtitle;
+ struct subtitle_list *subtitles;
+ struct dialog_color screen;
+ struct dialog_color shadow;
+ struct dialog_color dialog;
+ struct dialog_color title;
+ struct dialog_color border;
+ struct dialog_color button_active;
+ struct dialog_color button_inactive;
+ struct dialog_color button_key_active;
+ struct dialog_color button_key_inactive;
+ struct dialog_color button_label_active;
+ struct dialog_color button_label_inactive;
+ struct dialog_color inputbox;
+ struct dialog_color inputbox_border;
+ struct dialog_color searchbox;
+ struct dialog_color searchbox_title;
+ struct dialog_color searchbox_border;
+ struct dialog_color position_indicator;
+ struct dialog_color menubox;
+ struct dialog_color menubox_border;
+ struct dialog_color item;
+ struct dialog_color item_selected;
+ struct dialog_color tag;
+ struct dialog_color tag_selected;
+ struct dialog_color tag_key;
+ struct dialog_color tag_key_selected;
+ struct dialog_color check;
+ struct dialog_color check_selected;
+ struct dialog_color uarrow;
+ struct dialog_color darrow;
+};
+
+/*
+ * Global variables
+ */
+extern struct dialog_info dlg;
+extern char dialog_input_result[];
+extern int saved_x, saved_y; /* Needed in signal handler in mconf.c */
+
+/*
+ * Function prototypes
+ */
+
+/* item list as used by checklist and menubox */
+void item_reset(void);
+void item_make(const char *fmt, ...);
+void item_add_str(const char *fmt, ...);
+void item_set_tag(char tag);
+void item_set_data(void *p);
+void item_set_selected(int val);
+int item_activate_selected(void);
+void *item_data(void);
+char item_tag(void);
+
+/* item list manipulation for lxdialog use */
+#define MAXITEMSTR 200
+struct dialog_item {
+ char str[MAXITEMSTR]; /* prompt displayed */
+ char tag;
+ void *data; /* pointer to menu item - used by menubox+checklist */
+ int selected; /* Set to 1 by dialog_*() function if selected. */
+};
+
+/* list of lialog_items */
+struct dialog_list {
+ struct dialog_item node;
+ struct dialog_list *next;
+};
+
+extern struct dialog_list *item_cur;
+extern struct dialog_list item_nil;
+extern struct dialog_list *item_head;
+
+int item_count(void);
+void item_set(int n);
+int item_n(void);
+const char *item_str(void);
+int item_is_selected(void);
+int item_is_tag(char tag);
+#define item_foreach() \
+ for (item_cur = item_head ? item_head: item_cur; \
+ item_cur && (item_cur != &item_nil); item_cur = item_cur->next)
+
+/* generic key handlers */
+int on_key_esc(WINDOW *win);
+int on_key_resize(void);
+
+/* minimum (re)size values */
+#define CHECKLIST_HEIGTH_MIN 6 /* For dialog_checklist() */
+#define CHECKLIST_WIDTH_MIN 6
+#define INPUTBOX_HEIGTH_MIN 2 /* For dialog_inputbox() */
+#define INPUTBOX_WIDTH_MIN 2
+#define MENUBOX_HEIGTH_MIN 15 /* For dialog_menu() */
+#define MENUBOX_WIDTH_MIN 65
+#define TEXTBOX_HEIGTH_MIN 8 /* For dialog_textbox() */
+#define TEXTBOX_WIDTH_MIN 8
+#define YESNO_HEIGTH_MIN 4 /* For dialog_yesno() */
+#define YESNO_WIDTH_MIN 4
+#define WINDOW_HEIGTH_MIN 19 /* For init_dialog() */
+#define WINDOW_WIDTH_MIN 80
+
+int init_dialog(const char *backtitle);
+void set_dialog_backtitle(const char *backtitle);
+void set_dialog_subtitles(struct subtitle_list *subtitles);
+void end_dialog(int x, int y);
+void attr_clear(WINDOW * win, int height, int width, chtype attr);
+void dialog_clear(void);
+void print_autowrap(WINDOW * win, const char *prompt, int width, int y, int x);
+void print_button(WINDOW * win, const char *label, int y, int x, int selected);
+void print_title(WINDOW *dialog, const char *title, int width);
+void draw_box(WINDOW * win, int y, int x, int height, int width, chtype box,
+ chtype border);
+void draw_shadow(WINDOW * win, int y, int x, int height, int width);
+
+int first_alpha(const char *string, const char *exempt);
+int dialog_yesno(const char *title, const char *prompt, int height, int width);
+int dialog_msgbox(const char *title, const char *prompt, int height,
+ int width, int pause);
+
+
+typedef void (*update_text_fn)(char *buf, size_t start, size_t end, void
+ *_data);
+int dialog_textbox(const char *title, char *tbuf, int initial_height,
+ int initial_width, int *keys, int *_vscroll, int *_hscroll,
+ update_text_fn update_text, void *data);
+int dialog_menu(const char *title, const char *prompt,
+ const void *selected, int *s_scroll);
+int dialog_checklist(const char *title, const char *prompt, int height,
+ int width, int list_height);
+int dialog_inputbox(const char *title, const char *prompt, int height,
+ int width, const char *init);
+
+/*
+ * This is the base for fictitious keys, which activate
+ * the buttons.
+ *
+ * Mouse-generated keys are the following:
+ * -- the first 32 are used as numbers, in addition to '0'-'9'
+ * -- the lowercase are used to signal mouse-enter events (M_EVENT + 'o')
+ * -- uppercase chars are used to invoke the button (M_EVENT + 'O')
+ */
+#define M_EVENT (KEY_MAX+1)
diff --git a/kconf/lxdialog/inputbox.c b/kconf/lxdialog/inputbox.c
new file mode 100644
index 0000000..d58de1d
--- /dev/null
+++ b/kconf/lxdialog/inputbox.c
@@ -0,0 +1,301 @@
+/*
+ * inputbox.c -- implements the input box
+ *
+ * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
+ * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com)
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "dialog.h"
+
+char dialog_input_result[MAX_LEN + 1];
+
+/*
+ * Print the termination buttons
+ */
+static void print_buttons(WINDOW * dialog, int height, int width, int selected)
+{
+ int x = width / 2 - 11;
+ int y = height - 2;
+
+ print_button(dialog, gettext(" Ok "), y, x, selected == 0);
+ print_button(dialog, gettext(" Help "), y, x + 14, selected == 1);
+
+ wmove(dialog, y, x + 1 + 14 * selected);
+ wrefresh(dialog);
+}
+
+/*
+ * Display a dialog box for inputing a string
+ */
+int dialog_inputbox(const char *title, const char *prompt, int height, int width,
+ const char *init)
+{
+ int i, x, y, box_y, box_x, box_width;
+ int input_x = 0, key = 0, button = -1;
+ int show_x, len, pos;
+ char *instr = dialog_input_result;
+ WINDOW *dialog;
+
+ if (!init)
+ instr[0] = '\0';
+ else
+ strcpy(instr, init);
+
+do_resize:
+ if (getmaxy(stdscr) <= (height - INPUTBOX_HEIGTH_MIN))
+ return -ERRDISPLAYTOOSMALL;
+ if (getmaxx(stdscr) <= (width - INPUTBOX_WIDTH_MIN))
+ return -ERRDISPLAYTOOSMALL;
+
+ /* center dialog box on screen */
+ x = (getmaxx(stdscr) - width) / 2;
+ y = (getmaxy(stdscr) - height) / 2;
+
+ draw_shadow(stdscr, y, x, height, width);
+
+ dialog = newwin(height, width, y, x);
+ keypad(dialog, TRUE);
+
+ draw_box(dialog, 0, 0, height, width,
+ dlg.dialog.atr, dlg.border.atr);
+ wattrset(dialog, dlg.border.atr);
+ mvwaddch(dialog, height - 3, 0, ACS_LTEE);
+ for (i = 0; i < width - 2; i++)
+ waddch(dialog, ACS_HLINE);
+ wattrset(dialog, dlg.dialog.atr);
+ waddch(dialog, ACS_RTEE);
+
+ print_title(dialog, title, width);
+
+ wattrset(dialog, dlg.dialog.atr);
+ print_autowrap(dialog, prompt, width - 2, 1, 3);
+
+ /* Draw the input field box */
+ box_width = width - 6;
+ getyx(dialog, y, x);
+ box_y = y + 2;
+ box_x = (width - box_width) / 2;
+ draw_box(dialog, y + 1, box_x - 1, 3, box_width + 2,
+ dlg.dialog.atr, dlg.border.atr);
+
+ print_buttons(dialog, height, width, 0);
+
+ /* Set up the initial value */
+ wmove(dialog, box_y, box_x);
+ wattrset(dialog, dlg.inputbox.atr);
+
+ len = strlen(instr);
+ pos = len;
+
+ if (len >= box_width) {
+ show_x = len - box_width + 1;
+ input_x = box_width - 1;
+ for (i = 0; i < box_width - 1; i++)
+ waddch(dialog, instr[show_x + i]);
+ } else {
+ show_x = 0;
+ input_x = len;
+ waddstr(dialog, instr);
+ }
+
+ wmove(dialog, box_y, box_x + input_x);
+
+ wrefresh(dialog);
+
+ while (key != KEY_ESC) {
+ key = wgetch(dialog);
+
+ if (button == -1) { /* Input box selected */
+ switch (key) {
+ case TAB:
+ case KEY_UP:
+ case KEY_DOWN:
+ break;
+ case KEY_BACKSPACE:
+ case 127:
+ if (pos) {
+ wattrset(dialog, dlg.inputbox.atr);
+ if (input_x == 0) {
+ show_x--;
+ } else
+ input_x--;
+
+ if (pos < len) {
+ for (i = pos - 1; i < len; i++) {
+ instr[i] = instr[i+1];
+ }
+ }
+
+ pos--;
+ len--;
+ instr[len] = '\0';
+ wmove(dialog, box_y, box_x);
+ for (i = 0; i < box_width; i++) {
+ if (!instr[show_x + i]) {
+ waddch(dialog, ' ');
+ break;
+ }
+ waddch(dialog, instr[show_x + i]);
+ }
+ wmove(dialog, box_y, input_x + box_x);
+ wrefresh(dialog);
+ }
+ continue;
+ case KEY_LEFT:
+ if (pos > 0) {
+ if (input_x > 0) {
+ wmove(dialog, box_y, --input_x + box_x);
+ } else if (input_x == 0) {
+ show_x--;
+ wmove(dialog, box_y, box_x);
+ for (i = 0; i < box_width; i++) {
+ if (!instr[show_x + i]) {
+ waddch(dialog, ' ');
+ break;
+ }
+ waddch(dialog, instr[show_x + i]);
+ }
+ wmove(dialog, box_y, box_x);
+ }
+ pos--;
+ }
+ continue;
+ case KEY_RIGHT:
+ if (pos < len) {
+ if (input_x < box_width - 1) {
+ wmove(dialog, box_y, ++input_x + box_x);
+ } else if (input_x == box_width - 1) {
+ show_x++;
+ wmove(dialog, box_y, box_x);
+ for (i = 0; i < box_width; i++) {
+ if (!instr[show_x + i]) {
+ waddch(dialog, ' ');
+ break;
+ }
+ waddch(dialog, instr[show_x + i]);
+ }
+ wmove(dialog, box_y, input_x + box_x);
+ }
+ pos++;
+ }
+ continue;
+ default:
+ if (key < 0x100 && isprint(key)) {
+ if (len < MAX_LEN) {
+ wattrset(dialog, dlg.inputbox.atr);
+ if (pos < len) {
+ for (i = len; i > pos; i--)
+ instr[i] = instr[i-1];
+ instr[pos] = key;
+ } else {
+ instr[len] = key;
+ }
+ pos++;
+ len++;
+ instr[len] = '\0';
+
+ if (input_x == box_width - 1) {
+ show_x++;
+ } else {
+ input_x++;
+ }
+
+ wmove(dialog, box_y, box_x);
+ for (i = 0; i < box_width; i++) {
+ if (!instr[show_x + i]) {
+ waddch(dialog, ' ');
+ break;
+ }
+ waddch(dialog, instr[show_x + i]);
+ }
+ wmove(dialog, box_y, input_x + box_x);
+ wrefresh(dialog);
+ } else
+ flash(); /* Alarm user about overflow */
+ continue;
+ }
+ }
+ }
+ switch (key) {
+ case 'O':
+ case 'o':
+ delwin(dialog);
+ return 0;
+ case 'H':
+ case 'h':
+ delwin(dialog);
+ return 1;
+ case KEY_UP:
+ case KEY_LEFT:
+ switch (button) {
+ case -1:
+ button = 1; /* Indicates "Help" button is selected */
+ print_buttons(dialog, height, width, 1);
+ break;
+ case 0:
+ button = -1; /* Indicates input box is selected */
+ print_buttons(dialog, height, width, 0);
+ wmove(dialog, box_y, box_x + input_x);
+ wrefresh(dialog);
+ break;
+ case 1:
+ button = 0; /* Indicates "OK" button is selected */
+ print_buttons(dialog, height, width, 0);
+ break;
+ }
+ break;
+ case TAB:
+ case KEY_DOWN:
+ case KEY_RIGHT:
+ switch (button) {
+ case -1:
+ button = 0; /* Indicates "OK" button is selected */
+ print_buttons(dialog, height, width, 0);
+ break;
+ case 0:
+ button = 1; /* Indicates "Help" button is selected */
+ print_buttons(dialog, height, width, 1);
+ break;
+ case 1:
+ button = -1; /* Indicates input box is selected */
+ print_buttons(dialog, height, width, 0);
+ wmove(dialog, box_y, box_x + input_x);
+ wrefresh(dialog);
+ break;
+ }
+ break;
+ case ' ':
+ case '\n':
+ delwin(dialog);
+ return (button == -1 ? 0 : button);
+ case 'X':
+ case 'x':
+ key = KEY_ESC;
+ break;
+ case KEY_ESC:
+ key = on_key_esc(dialog);
+ break;
+ case KEY_RESIZE:
+ delwin(dialog);
+ on_key_resize();
+ goto do_resize;
+ }
+ }
+
+ delwin(dialog);
+ return KEY_ESC; /* ESC pressed */
+}
diff --git a/kconf/lxdialog/menubox.c b/kconf/lxdialog/menubox.c
new file mode 100644
index 0000000..11ae9ad
--- /dev/null
+++ b/kconf/lxdialog/menubox.c
@@ -0,0 +1,437 @@
+/*
+ * menubox.c -- implements the menu box
+ *
+ * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
+ * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcapw@cfw.com)
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * Changes by Clifford Wolf (god@clifford.at)
+ *
+ * [ 1998-06-13 ]
+ *
+ * *) A bugfix for the Page-Down problem
+ *
+ * *) Formerly when I used Page Down and Page Up, the cursor would be set
+ * to the first position in the menu box. Now lxdialog is a bit
+ * smarter and works more like other menu systems (just have a look at
+ * it).
+ *
+ * *) Formerly if I selected something my scrolling would be broken because
+ * lxdialog is re-invoked by the Menuconfig shell script, can't
+ * remember the last scrolling position, and just sets it so that the
+ * cursor is at the bottom of the box. Now it writes the temporary file
+ * lxdialog.scrltmp which contains this information. The file is
+ * deleted by lxdialog if the user leaves a submenu or enters a new
+ * one, but it would be nice if Menuconfig could make another "rm -f"
+ * just to be sure. Just try it out - you will recognise a difference!
+ *
+ * [ 1998-06-14 ]
+ *
+ * *) Now lxdialog is crash-safe against broken "lxdialog.scrltmp" files
+ * and menus change their size on the fly.
+ *
+ * *) If for some reason the last scrolling position is not saved by
+ * lxdialog, it sets the scrolling so that the selected item is in the
+ * middle of the menu box, not at the bottom.
+ *
+ * 02 January 1999, Michael Elizabeth Chastain (mec@shout.net)
+ * Reset 'scroll' to 0 if the value from lxdialog.scrltmp is bogus.
+ * This fixes a bug in Menuconfig where using ' ' to descend into menus
+ * would leave mis-synchronized lxdialog.scrltmp files lying around,
+ * fscanf would read in 'scroll', and eventually that value would get used.
+ */
+
+#include "dialog.h"
+
+static int menu_width, item_x;
+
+/*
+ * Print menu item
+ */
+static void do_print_item(WINDOW * win, const char *item, int line_y,
+ int selected, int hotkey)
+{
+ int j;
+ char *menu_item = malloc(menu_width + 1);
+
+ strncpy(menu_item, item, menu_width - item_x);
+ menu_item[menu_width - item_x] = '\0';
+ j = first_alpha(menu_item, "YyNnMmHh");
+
+ /* Clear 'residue' of last item */
+ wattrset(win, dlg.menubox.atr);
+ wmove(win, line_y, 0);
+#if OLD_NCURSES
+ {
+ int i;
+ for (i = 0; i < menu_width; i++)
+ waddch(win, ' ');
+ }
+#else
+ wclrtoeol(win);
+#endif
+ wattrset(win, selected ? dlg.item_selected.atr : dlg.item.atr);
+ mvwaddstr(win, line_y, item_x, menu_item);
+ if (hotkey) {
+ wattrset(win, selected ? dlg.tag_key_selected.atr
+ : dlg.tag_key.atr);
+ mvwaddch(win, line_y, item_x + j, menu_item[j]);
+ }
+ if (selected) {
+ wmove(win, line_y, item_x + 1);
+ }
+ free(menu_item);
+ wrefresh(win);
+}
+
+#define print_item(index, choice, selected) \
+do { \
+ item_set(index); \
+ do_print_item(menu, item_str(), choice, selected, !item_is_tag(':')); \
+} while (0)
+
+/*
+ * Print the scroll indicators.
+ */
+static void print_arrows(WINDOW * win, int item_no, int scroll, int y, int x,
+ int height)
+{
+ int cur_y, cur_x;
+
+ getyx(win, cur_y, cur_x);
+
+ wmove(win, y, x);
+
+ if (scroll > 0) {
+ wattrset(win, dlg.uarrow.atr);
+ waddch(win, ACS_UARROW);
+ waddstr(win, "(-)");
+ } else {
+ wattrset(win, dlg.menubox.atr);
+ waddch(win, ACS_HLINE);
+ waddch(win, ACS_HLINE);
+ waddch(win, ACS_HLINE);
+ waddch(win, ACS_HLINE);
+ }
+
+ y = y + height + 1;
+ wmove(win, y, x);
+ wrefresh(win);
+
+ if ((height < item_no) && (scroll + height < item_no)) {
+ wattrset(win, dlg.darrow.atr);
+ waddch(win, ACS_DARROW);
+ waddstr(win, "(+)");
+ } else {
+ wattrset(win, dlg.menubox_border.atr);
+ waddch(win, ACS_HLINE);
+ waddch(win, ACS_HLINE);
+ waddch(win, ACS_HLINE);
+ waddch(win, ACS_HLINE);
+ }
+
+ wmove(win, cur_y, cur_x);
+ wrefresh(win);
+}
+
+/*
+ * Display the termination buttons.
+ */
+static void print_buttons(WINDOW * win, int height, int width, int selected)
+{
+ int x = width / 2 - 28;
+ int y = height - 2;
+
+ print_button(win, gettext("Select"), y, x, selected == 0);
+ print_button(win, gettext(" Exit "), y, x + 12, selected == 1);
+ print_button(win, gettext(" Help "), y, x + 24, selected == 2);
+ print_button(win, gettext(" Save "), y, x + 36, selected == 3);
+ print_button(win, gettext(" Load "), y, x + 48, selected == 4);
+
+ wmove(win, y, x + 1 + 12 * selected);
+ wrefresh(win);
+}
+
+/* scroll up n lines (n may be negative) */
+static void do_scroll(WINDOW *win, int *scroll, int n)
+{
+ /* Scroll menu up */
+ scrollok(win, TRUE);
+ wscrl(win, n);
+ scrollok(win, FALSE);
+ *scroll = *scroll + n;
+ wrefresh(win);
+}
+
+/*
+ * Display a menu for choosing among a number of options
+ */
+int dialog_menu(const char *title, const char *prompt,
+ const void *selected, int *s_scroll)
+{
+ int i, j, x, y, box_x, box_y;
+ int height, width, menu_height;
+ int key = 0, button = 0, scroll = 0, choice = 0;
+ int first_item = 0, max_choice;
+ WINDOW *dialog, *menu;
+
+do_resize:
+ height = getmaxy(stdscr);
+ width = getmaxx(stdscr);
+ if (height < MENUBOX_HEIGTH_MIN || width < MENUBOX_WIDTH_MIN)
+ return -ERRDISPLAYTOOSMALL;
+
+ height -= 4;
+ width -= 5;
+ menu_height = height - 10;
+
+ max_choice = MIN(menu_height, item_count());
+
+ /* center dialog box on screen */
+ x = (getmaxx(stdscr) - width) / 2;
+ y = (getmaxy(stdscr) - height) / 2;
+
+ draw_shadow(stdscr, y, x, height, width);
+
+ dialog = newwin(height, width, y, x);
+ keypad(dialog, TRUE);
+
+ draw_box(dialog, 0, 0, height, width,
+ dlg.dialog.atr, dlg.border.atr);
+ wattrset(dialog, dlg.border.atr);
+ mvwaddch(dialog, height - 3, 0, ACS_LTEE);
+ for (i = 0; i < width - 2; i++)
+ waddch(dialog, ACS_HLINE);
+ wattrset(dialog, dlg.dialog.atr);
+ wbkgdset(dialog, dlg.dialog.atr & A_COLOR);
+ waddch(dialog, ACS_RTEE);
+
+ print_title(dialog, title, width);
+
+ wattrset(dialog, dlg.dialog.atr);
+ print_autowrap(dialog, prompt, width - 2, 1, 3);
+
+ menu_width = width - 6;
+ box_y = height - menu_height - 5;
+ box_x = (width - menu_width) / 2 - 1;
+
+ /* create new window for the menu */
+ menu = subwin(dialog, menu_height, menu_width,
+ y + box_y + 1, x + box_x + 1);
+ keypad(menu, TRUE);
+
+ /* draw a box around the menu items */
+ draw_box(dialog, box_y, box_x, menu_height + 2, menu_width + 2,
+ dlg.menubox_border.atr, dlg.menubox.atr);
+
+ if (menu_width >= 80)
+ item_x = (menu_width - 70) / 2;
+ else
+ item_x = 4;
+
+ /* Set choice to default item */
+ item_foreach()
+ if (selected && (selected == item_data()))
+ choice = item_n();
+ /* get the saved scroll info */
+ scroll = *s_scroll;
+ if ((scroll <= choice) && (scroll + max_choice > choice) &&
+ (scroll >= 0) && (scroll + max_choice <= item_count())) {
+ first_item = scroll;
+ choice = choice - scroll;
+ } else {
+ scroll = 0;
+ }
+ if ((choice >= max_choice)) {
+ if (choice >= item_count() - max_choice / 2)
+ scroll = first_item = item_count() - max_choice;
+ else
+ scroll = first_item = choice - max_choice / 2;
+ choice = choice - scroll;
+ }
+
+ /* Print the menu */
+ for (i = 0; i < max_choice; i++) {
+ print_item(first_item + i, i, i == choice);
+ }
+
+ wnoutrefresh(menu);
+
+ print_arrows(dialog, item_count(), scroll,
+ box_y, box_x + item_x + 1, menu_height);
+
+ print_buttons(dialog, height, width, 0);
+ wmove(menu, choice, item_x + 1);
+ wrefresh(menu);
+
+ while (key != KEY_ESC) {
+ key = wgetch(menu);
+
+ if (key < 256 && isalpha(key))
+ key = tolower(key);
+
+ if (strchr("ynmh", key))
+ i = max_choice;
+ else {
+ for (i = choice + 1; i < max_choice; i++) {
+ item_set(scroll + i);
+ j = first_alpha(item_str(), "YyNnMmHh");
+ if (key == tolower(item_str()[j]))
+ break;
+ }
+ if (i == max_choice)
+ for (i = 0; i < max_choice; i++) {
+ item_set(scroll + i);
+ j = first_alpha(item_str(), "YyNnMmHh");
+ if (key == tolower(item_str()[j]))
+ break;
+ }
+ }
+
+ if (item_count() != 0 &&
+ (i < max_choice ||
+ key == KEY_UP || key == KEY_DOWN ||
+ key == '-' || key == '+' ||
+ key == KEY_PPAGE || key == KEY_NPAGE)) {
+ /* Remove highligt of current item */
+ print_item(scroll + choice, choice, FALSE);
+
+ if (key == KEY_UP || key == '-') {
+ if (choice < 2 && scroll) {
+ /* Scroll menu down */
+ do_scroll(menu, &scroll, -1);
+
+ print_item(scroll, 0, FALSE);
+ } else
+ choice = MAX(choice - 1, 0);
+
+ } else if (key == KEY_DOWN || key == '+') {
+ print_item(scroll+choice, choice, FALSE);
+
+ if ((choice > max_choice - 3) &&
+ (scroll + max_choice < item_count())) {
+ /* Scroll menu up */
+ do_scroll(menu, &scroll, 1);
+
+ print_item(scroll+max_choice - 1,
+ max_choice - 1, FALSE);
+ } else
+ choice = MIN(choice + 1, max_choice - 1);
+
+ } else if (key == KEY_PPAGE) {
+ scrollok(menu, TRUE);
+ for (i = 0; (i < max_choice); i++) {
+ if (scroll > 0) {
+ do_scroll(menu, &scroll, -1);
+ print_item(scroll, 0, FALSE);
+ } else {
+ if (choice > 0)
+ choice--;
+ }
+ }
+
+ } else if (key == KEY_NPAGE) {
+ for (i = 0; (i < max_choice); i++) {
+ if (scroll + max_choice < item_count()) {
+ do_scroll(menu, &scroll, 1);
+ print_item(scroll+max_choice-1,
+ max_choice - 1, FALSE);
+ } else {
+ if (choice + 1 < max_choice)
+ choice++;
+ }
+ }
+ } else
+ choice = i;
+
+ print_item(scroll + choice, choice, TRUE);
+
+ print_arrows(dialog, item_count(), scroll,
+ box_y, box_x + item_x + 1, menu_height);
+
+ wnoutrefresh(dialog);
+ wrefresh(menu);
+
+ continue; /* wait for another key press */
+ }
+
+ switch (key) {
+ case KEY_LEFT:
+ case TAB:
+ case KEY_RIGHT:
+ button = ((key == KEY_LEFT ? --button : ++button) < 0)
+ ? 4 : (button > 4 ? 0 : button);
+
+ print_buttons(dialog, height, width, button);
+ wrefresh(menu);
+ break;
+ case ' ':
+ case 's':
+ case 'y':
+ case 'n':
+ case 'm':
+ case '/':
+ case 'h':
+ case '?':
+ case 'z':
+ case '\n':
+ /* save scroll info */
+ *s_scroll = scroll;
+ delwin(menu);
+ delwin(dialog);
+ item_set(scroll + choice);
+ item_set_selected(1);
+ switch (key) {
+ case 'h':
+ case '?':
+ return 2;
+ case 's':
+ case 'y':
+ return 5;
+ case 'n':
+ return 6;
+ case 'm':
+ return 7;
+ case ' ':
+ return 8;
+ case '/':
+ return 9;
+ case 'z':
+ return 10;
+ case '\n':
+ return button;
+ }
+ return 0;
+ case 'e':
+ case 'x':
+ key = KEY_ESC;
+ break;
+ case KEY_ESC:
+ key = on_key_esc(menu);
+ break;
+ case KEY_RESIZE:
+ on_key_resize();
+ delwin(menu);
+ delwin(dialog);
+ goto do_resize;
+ }
+ }
+ delwin(menu);
+ delwin(dialog);
+ return key; /* ESC pressed */
+}
diff --git a/kconf/lxdialog/textbox.c b/kconf/lxdialog/textbox.c
new file mode 100644
index 0000000..1773319
--- /dev/null
+++ b/kconf/lxdialog/textbox.c
@@ -0,0 +1,408 @@
+/*
+ * textbox.c -- implements the text box
+ *
+ * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
+ * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com)
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "dialog.h"
+
+static void back_lines(int n);
+static void print_page(WINDOW *win, int height, int width, update_text_fn
+ update_text, void *data);
+static void print_line(WINDOW *win, int row, int width);
+static char *get_line(void);
+static void print_position(WINDOW * win);
+
+static int hscroll;
+static int begin_reached, end_reached, page_length;
+static char *buf;
+static char *page;
+
+/*
+ * refresh window content
+ */
+static void refresh_text_box(WINDOW *dialog, WINDOW *box, int boxh, int boxw,
+ int cur_y, int cur_x, update_text_fn update_text,
+ void *data)
+{
+ print_page(box, boxh, boxw, update_text, data);
+ print_position(dialog);
+ wmove(dialog, cur_y, cur_x); /* Restore cursor position */
+ wrefresh(dialog);
+}
+
+
+/*
+ * Display text from a file in a dialog box.
+ *
+ * keys is a null-terminated array
+ * update_text() may not add or remove any '\n' or '\0' in tbuf
+ */
+int dialog_textbox(const char *title, char *tbuf, int initial_height,
+ int initial_width, int *keys, int *_vscroll, int *_hscroll,
+ update_text_fn update_text, void *data)
+{
+ int i, x, y, cur_x, cur_y, key = 0;
+ int height, width, boxh, boxw;
+ WINDOW *dialog, *box;
+ bool done = false;
+
+ begin_reached = 1;
+ end_reached = 0;
+ page_length = 0;
+ hscroll = 0;
+ buf = tbuf;
+ page = buf; /* page is pointer to start of page to be displayed */
+
+ if (_vscroll && *_vscroll) {
+ begin_reached = 0;
+
+ for (i = 0; i < *_vscroll; i++)
+ get_line();
+ }
+ if (_hscroll)
+ hscroll = *_hscroll;
+
+do_resize:
+ getmaxyx(stdscr, height, width);
+ if (height < TEXTBOX_HEIGTH_MIN || width < TEXTBOX_WIDTH_MIN)
+ return -ERRDISPLAYTOOSMALL;
+ if (initial_height != 0)
+ height = initial_height;
+ else
+ if (height > 4)
+ height -= 4;
+ else
+ height = 0;
+ if (initial_width != 0)
+ width = initial_width;
+ else
+ if (width > 5)
+ width -= 5;
+ else
+ width = 0;
+
+ /* center dialog box on screen */
+ x = (getmaxx(stdscr) - width) / 2;
+ y = (getmaxy(stdscr) - height) / 2;
+
+ draw_shadow(stdscr, y, x, height, width);
+
+ dialog = newwin(height, width, y, x);
+ keypad(dialog, TRUE);
+
+ /* Create window for box region, used for scrolling text */
+ boxh = height - 4;
+ boxw = width - 2;
+ box = subwin(dialog, boxh, boxw, y + 1, x + 1);
+ wattrset(box, dlg.dialog.atr);
+ wbkgdset(box, dlg.dialog.atr & A_COLOR);
+
+ keypad(box, TRUE);
+
+ /* register the new window, along with its borders */
+ draw_box(dialog, 0, 0, height, width,
+ dlg.dialog.atr, dlg.border.atr);
+
+ wattrset(dialog, dlg.border.atr);
+ mvwaddch(dialog, height - 3, 0, ACS_LTEE);
+ for (i = 0; i < width - 2; i++)
+ waddch(dialog, ACS_HLINE);
+ wattrset(dialog, dlg.dialog.atr);
+ wbkgdset(dialog, dlg.dialog.atr & A_COLOR);
+ waddch(dialog, ACS_RTEE);
+
+ print_title(dialog, title, width);
+
+ print_button(dialog, gettext(" Exit "), height - 2, width / 2 - 4, TRUE);
+ wnoutrefresh(dialog);
+ getyx(dialog, cur_y, cur_x); /* Save cursor position */
+
+ /* Print first page of text */
+ attr_clear(box, boxh, boxw, dlg.dialog.atr);
+ refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x, update_text,
+ data);
+
+ while (!done) {
+ key = wgetch(dialog);
+ switch (key) {
+ case 'E': /* Exit */
+ case 'e':
+ case 'X':
+ case 'x':
+ case 'q':
+ case '\n':
+ done = true;
+ break;
+ case 'g': /* First page */
+ case KEY_HOME:
+ if (!begin_reached) {
+ begin_reached = 1;
+ page = buf;
+ refresh_text_box(dialog, box, boxh, boxw,
+ cur_y, cur_x, update_text,
+ data);
+ }
+ break;
+ case 'G': /* Last page */
+ case KEY_END:
+
+ end_reached = 1;
+ /* point to last char in buf */
+ page = buf + strlen(buf);
+ back_lines(boxh);
+ refresh_text_box(dialog, box, boxh, boxw, cur_y,
+ cur_x, update_text, data);
+ break;
+ case 'K': /* Previous line */
+ case 'k':
+ case KEY_UP:
+ if (begin_reached)
+ break;
+
+ back_lines(page_length + 1);
+ refresh_text_box(dialog, box, boxh, boxw, cur_y,
+ cur_x, update_text, data);
+ break;
+ case 'B': /* Previous page */
+ case 'b':
+ case 'u':
+ case KEY_PPAGE:
+ if (begin_reached)
+ break;
+ back_lines(page_length + boxh);
+ refresh_text_box(dialog, box, boxh, boxw, cur_y,
+ cur_x, update_text, data);
+ break;
+ case 'J': /* Next line */
+ case 'j':
+ case KEY_DOWN:
+ if (end_reached)
+ break;
+
+ back_lines(page_length - 1);
+ refresh_text_box(dialog, box, boxh, boxw, cur_y,
+ cur_x, update_text, data);
+ break;
+ case KEY_NPAGE: /* Next page */
+ case ' ':
+ case 'd':
+ if (end_reached)
+ break;
+
+ begin_reached = 0;
+ refresh_text_box(dialog, box, boxh, boxw, cur_y,
+ cur_x, update_text, data);
+ break;
+ case '0': /* Beginning of line */
+ case 'H': /* Scroll left */
+ case 'h':
+ case KEY_LEFT:
+ if (hscroll <= 0)
+ break;
+
+ if (key == '0')
+ hscroll = 0;
+ else
+ hscroll--;
+ /* Reprint current page to scroll horizontally */
+ back_lines(page_length);
+ refresh_text_box(dialog, box, boxh, boxw, cur_y,
+ cur_x, update_text, data);
+ break;
+ case 'L': /* Scroll right */
+ case 'l':
+ case KEY_RIGHT:
+ if (hscroll >= MAX_LEN)
+ break;
+ hscroll++;
+ /* Reprint current page to scroll horizontally */
+ back_lines(page_length);
+ refresh_text_box(dialog, box, boxh, boxw, cur_y,
+ cur_x, update_text, data);
+ break;
+ case KEY_ESC:
+ if (on_key_esc(dialog) == KEY_ESC)
+ done = true;
+ break;
+ case KEY_RESIZE:
+ back_lines(height);
+ delwin(box);
+ delwin(dialog);
+ on_key_resize();
+ goto do_resize;
+ default:
+ for (i = 0; keys[i]; i++) {
+ if (key == keys[i]) {
+ done = true;
+ break;
+ }
+ }
+ }
+ }
+ delwin(box);
+ delwin(dialog);
+ if (_vscroll) {
+ const char *s;
+
+ s = buf;
+ *_vscroll = 0;
+ back_lines(page_length);
+ while (s < page && (s = strchr(s, '\n'))) {
+ (*_vscroll)++;
+ s++;
+ }
+ }
+ if (_hscroll)
+ *_hscroll = hscroll;
+ return key;
+}
+
+/*
+ * Go back 'n' lines in text. Called by dialog_textbox().
+ * 'page' will be updated to point to the desired line in 'buf'.
+ */
+static void back_lines(int n)
+{
+ int i;
+
+ begin_reached = 0;
+ /* Go back 'n' lines */
+ for (i = 0; i < n; i++) {
+ if (*page == '\0') {
+ if (end_reached) {
+ end_reached = 0;
+ continue;
+ }
+ }
+ if (page == buf) {
+ begin_reached = 1;
+ return;
+ }
+ page--;
+ do {
+ if (page == buf) {
+ begin_reached = 1;
+ return;
+ }
+ page--;
+ } while (*page != '\n');
+ page++;
+ }
+}
+
+/*
+ * Print a new page of text.
+ */
+static void print_page(WINDOW *win, int height, int width, update_text_fn
+ update_text, void *data)
+{
+ int i, passed_end = 0;
+
+ if (update_text) {
+ char *end;
+
+ for (i = 0; i < height; i++)
+ get_line();
+ end = page;
+ back_lines(height);
+ update_text(buf, page - buf, end - buf, data);
+ }
+
+ page_length = 0;
+ for (i = 0; i < height; i++) {
+ print_line(win, i, width);
+ if (!passed_end)
+ page_length++;
+ if (end_reached && !passed_end)
+ passed_end = 1;
+ }
+ wnoutrefresh(win);
+}
+
+/*
+ * Print a new line of text.
+ */
+static void print_line(WINDOW * win, int row, int width)
+{
+ char *line;
+
+ line = get_line();
+ line += MIN(strlen(line), hscroll); /* Scroll horizontally */
+ wmove(win, row, 0); /* move cursor to correct line */
+ waddch(win, ' ');
+ waddnstr(win, line, MIN(strlen(line), width - 2));
+
+ /* Clear 'residue' of previous line */
+#if OLD_NCURSES
+ {
+ int x = getcurx(win);
+ int i;
+ for (i = 0; i < width - x; i++)
+ waddch(win, ' ');
+ }
+#else
+ wclrtoeol(win);
+#endif
+}
+
+/*
+ * Return current line of text. Called by dialog_textbox() and print_line().
+ * 'page' should point to start of current line before calling, and will be
+ * updated to point to start of next line.
+ */
+static char *get_line(void)
+{
+ int i = 0;
+ static char line[MAX_LEN + 1];
+
+ end_reached = 0;
+ while (*page != '\n') {
+ if (*page == '\0') {
+ end_reached = 1;
+ break;
+ } else if (i < MAX_LEN)
+ line[i++] = *(page++);
+ else {
+ /* Truncate lines longer than MAX_LEN characters */
+ if (i == MAX_LEN)
+ line[i++] = '\0';
+ page++;
+ }
+ }
+ if (i <= MAX_LEN)
+ line[i] = '\0';
+ if (!end_reached)
+ page++; /* move past '\n' */
+
+ return line;
+}
+
+/*
+ * Print current position
+ */
+static void print_position(WINDOW * win)
+{
+ int percent;
+
+ wattrset(win, dlg.position_indicator.atr);
+ wbkgdset(win, dlg.position_indicator.atr & A_COLOR);
+ percent = (page - buf) * 100 / strlen(buf);
+ wmove(win, getmaxy(win) - 3, getmaxx(win) - 9);
+ wprintw(win, "(%3d%%)", percent);
+}
diff --git a/kconf/lxdialog/util.c b/kconf/lxdialog/util.c
new file mode 100644
index 0000000..f7abdeb
--- /dev/null
+++ b/kconf/lxdialog/util.c
@@ -0,0 +1,713 @@
+/*
+ * util.c
+ *
+ * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
+ * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com)
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdarg.h>
+
+#include "dialog.h"
+
+/* Needed in signal handler in mconf.c */
+int saved_x, saved_y;
+
+struct dialog_info dlg;
+
+static void set_mono_theme(void)
+{
+ dlg.screen.atr = A_NORMAL;
+ dlg.shadow.atr = A_NORMAL;
+ dlg.dialog.atr = A_NORMAL;
+ dlg.title.atr = A_BOLD;
+ dlg.border.atr = A_NORMAL;
+ dlg.button_active.atr = A_REVERSE;
+ dlg.button_inactive.atr = A_DIM;
+ dlg.button_key_active.atr = A_REVERSE;
+ dlg.button_key_inactive.atr = A_BOLD;
+ dlg.button_label_active.atr = A_REVERSE;
+ dlg.button_label_inactive.atr = A_NORMAL;
+ dlg.inputbox.atr = A_NORMAL;
+ dlg.inputbox_border.atr = A_NORMAL;
+ dlg.searchbox.atr = A_NORMAL;
+ dlg.searchbox_title.atr = A_BOLD;
+ dlg.searchbox_border.atr = A_NORMAL;
+ dlg.position_indicator.atr = A_BOLD;
+ dlg.menubox.atr = A_NORMAL;
+ dlg.menubox_border.atr = A_NORMAL;
+ dlg.item.atr = A_NORMAL;
+ dlg.item_selected.atr = A_REVERSE;
+ dlg.tag.atr = A_BOLD;
+ dlg.tag_selected.atr = A_REVERSE;
+ dlg.tag_key.atr = A_BOLD;
+ dlg.tag_key_selected.atr = A_REVERSE;
+ dlg.check.atr = A_BOLD;
+ dlg.check_selected.atr = A_REVERSE;
+ dlg.uarrow.atr = A_BOLD;
+ dlg.darrow.atr = A_BOLD;
+}
+
+#define DLG_COLOR(dialog, f, b, h) \
+do { \
+ dlg.dialog.fg = (f); \
+ dlg.dialog.bg = (b); \
+ dlg.dialog.hl = (h); \
+} while (0)
+
+static void set_classic_theme(void)
+{
+ DLG_COLOR(screen, COLOR_CYAN, COLOR_BLUE, true);
+ DLG_COLOR(shadow, COLOR_BLACK, COLOR_BLACK, true);
+ DLG_COLOR(dialog, COLOR_BLACK, COLOR_WHITE, false);
+ DLG_COLOR(title, COLOR_YELLOW, COLOR_WHITE, true);
+ DLG_COLOR(border, COLOR_WHITE, COLOR_WHITE, true);
+ DLG_COLOR(button_active, COLOR_WHITE, COLOR_BLUE, true);
+ DLG_COLOR(button_inactive, COLOR_BLACK, COLOR_WHITE, false);
+ DLG_COLOR(button_key_active, COLOR_WHITE, COLOR_BLUE, true);
+ DLG_COLOR(button_key_inactive, COLOR_RED, COLOR_WHITE, false);
+ DLG_COLOR(button_label_active, COLOR_YELLOW, COLOR_BLUE, true);
+ DLG_COLOR(button_label_inactive, COLOR_BLACK, COLOR_WHITE, true);
+ DLG_COLOR(inputbox, COLOR_BLACK, COLOR_WHITE, false);
+ DLG_COLOR(inputbox_border, COLOR_BLACK, COLOR_WHITE, false);
+ DLG_COLOR(searchbox, COLOR_BLACK, COLOR_WHITE, false);
+ DLG_COLOR(searchbox_title, COLOR_YELLOW, COLOR_WHITE, true);
+ DLG_COLOR(searchbox_border, COLOR_WHITE, COLOR_WHITE, true);
+ DLG_COLOR(position_indicator, COLOR_YELLOW, COLOR_WHITE, true);
+ DLG_COLOR(menubox, COLOR_BLACK, COLOR_WHITE, false);
+ DLG_COLOR(menubox_border, COLOR_WHITE, COLOR_WHITE, true);
+ DLG_COLOR(item, COLOR_BLACK, COLOR_WHITE, false);
+ DLG_COLOR(item_selected, COLOR_WHITE, COLOR_BLUE, true);
+ DLG_COLOR(tag, COLOR_YELLOW, COLOR_WHITE, true);
+ DLG_COLOR(tag_selected, COLOR_YELLOW, COLOR_BLUE, true);
+ DLG_COLOR(tag_key, COLOR_YELLOW, COLOR_WHITE, true);
+ DLG_COLOR(tag_key_selected, COLOR_YELLOW, COLOR_BLUE, true);
+ DLG_COLOR(check, COLOR_BLACK, COLOR_WHITE, false);
+ DLG_COLOR(check_selected, COLOR_WHITE, COLOR_BLUE, true);
+ DLG_COLOR(uarrow, COLOR_GREEN, COLOR_WHITE, true);
+ DLG_COLOR(darrow, COLOR_GREEN, COLOR_WHITE, true);
+}
+
+static void set_blackbg_theme(void)
+{
+ DLG_COLOR(screen, COLOR_RED, COLOR_BLACK, true);
+ DLG_COLOR(shadow, COLOR_BLACK, COLOR_BLACK, false);
+ DLG_COLOR(dialog, COLOR_WHITE, COLOR_BLACK, false);
+ DLG_COLOR(title, COLOR_RED, COLOR_BLACK, false);
+ DLG_COLOR(border, COLOR_BLACK, COLOR_BLACK, true);
+
+ DLG_COLOR(button_active, COLOR_YELLOW, COLOR_RED, false);
+ DLG_COLOR(button_inactive, COLOR_YELLOW, COLOR_BLACK, false);
+ DLG_COLOR(button_key_active, COLOR_YELLOW, COLOR_RED, true);
+ DLG_COLOR(button_key_inactive, COLOR_RED, COLOR_BLACK, false);
+ DLG_COLOR(button_label_active, COLOR_WHITE, COLOR_RED, false);
+ DLG_COLOR(button_label_inactive, COLOR_BLACK, COLOR_BLACK, true);
+
+ DLG_COLOR(inputbox, COLOR_YELLOW, COLOR_BLACK, false);
+ DLG_COLOR(inputbox_border, COLOR_YELLOW, COLOR_BLACK, false);
+
+ DLG_COLOR(searchbox, COLOR_YELLOW, COLOR_BLACK, false);
+ DLG_COLOR(searchbox_title, COLOR_YELLOW, COLOR_BLACK, true);
+ DLG_COLOR(searchbox_border, COLOR_BLACK, COLOR_BLACK, true);
+
+ DLG_COLOR(position_indicator, COLOR_RED, COLOR_BLACK, false);
+
+ DLG_COLOR(menubox, COLOR_YELLOW, COLOR_BLACK, false);
+ DLG_COLOR(menubox_border, COLOR_BLACK, COLOR_BLACK, true);
+
+ DLG_COLOR(item, COLOR_WHITE, COLOR_BLACK, false);
+ DLG_COLOR(item_selected, COLOR_WHITE, COLOR_RED, false);
+
+ DLG_COLOR(tag, COLOR_RED, COLOR_BLACK, false);
+ DLG_COLOR(tag_selected, COLOR_YELLOW, COLOR_RED, true);
+ DLG_COLOR(tag_key, COLOR_RED, COLOR_BLACK, false);
+ DLG_COLOR(tag_key_selected, COLOR_YELLOW, COLOR_RED, true);
+
+ DLG_COLOR(check, COLOR_YELLOW, COLOR_BLACK, false);
+ DLG_COLOR(check_selected, COLOR_YELLOW, COLOR_RED, true);
+
+ DLG_COLOR(uarrow, COLOR_RED, COLOR_BLACK, false);
+ DLG_COLOR(darrow, COLOR_RED, COLOR_BLACK, false);
+}
+
+static void set_bluetitle_theme(void)
+{
+ set_classic_theme();
+ DLG_COLOR(title, COLOR_BLUE, COLOR_WHITE, true);
+ DLG_COLOR(button_key_active, COLOR_YELLOW, COLOR_BLUE, true);
+ DLG_COLOR(button_label_active, COLOR_WHITE, COLOR_BLUE, true);
+ DLG_COLOR(searchbox_title, COLOR_BLUE, COLOR_WHITE, true);
+ DLG_COLOR(position_indicator, COLOR_BLUE, COLOR_WHITE, true);
+ DLG_COLOR(tag, COLOR_BLUE, COLOR_WHITE, true);
+ DLG_COLOR(tag_key, COLOR_BLUE, COLOR_WHITE, true);
+
+}
+
+/*
+ * Select color theme
+ */
+static int set_theme(const char *theme)
+{
+ int use_color = 1;
+ if (!theme)
+ set_bluetitle_theme();
+ else if (strcmp(theme, "classic") == 0)
+ set_classic_theme();
+ else if (strcmp(theme, "bluetitle") == 0)
+ set_bluetitle_theme();
+ else if (strcmp(theme, "blackbg") == 0)
+ set_blackbg_theme();
+ else if (strcmp(theme, "mono") == 0)
+ use_color = 0;
+
+ return use_color;
+}
+
+static void init_one_color(struct dialog_color *color)
+{
+ static int pair = 0;
+
+ pair++;
+ init_pair(pair, color->fg, color->bg);
+ if (color->hl)
+ color->atr = A_BOLD | COLOR_PAIR(pair);
+ else
+ color->atr = COLOR_PAIR(pair);
+}
+
+static void init_dialog_colors(void)
+{
+ init_one_color(&dlg.screen);
+ init_one_color(&dlg.shadow);
+ init_one_color(&dlg.dialog);
+ init_one_color(&dlg.title);
+ init_one_color(&dlg.border);
+ init_one_color(&dlg.button_active);
+ init_one_color(&dlg.button_inactive);
+ init_one_color(&dlg.button_key_active);
+ init_one_color(&dlg.button_key_inactive);
+ init_one_color(&dlg.button_label_active);
+ init_one_color(&dlg.button_label_inactive);
+ init_one_color(&dlg.inputbox);
+ init_one_color(&dlg.inputbox_border);
+ init_one_color(&dlg.searchbox);
+ init_one_color(&dlg.searchbox_title);
+ init_one_color(&dlg.searchbox_border);
+ init_one_color(&dlg.position_indicator);
+ init_one_color(&dlg.menubox);
+ init_one_color(&dlg.menubox_border);
+ init_one_color(&dlg.item);
+ init_one_color(&dlg.item_selected);
+ init_one_color(&dlg.tag);
+ init_one_color(&dlg.tag_selected);
+ init_one_color(&dlg.tag_key);
+ init_one_color(&dlg.tag_key_selected);
+ init_one_color(&dlg.check);
+ init_one_color(&dlg.check_selected);
+ init_one_color(&dlg.uarrow);
+ init_one_color(&dlg.darrow);
+}
+
+/*
+ * Setup for color display
+ */
+static void color_setup(const char *theme)
+{
+ int use_color;
+
+ use_color = set_theme(theme);
+ if (use_color && has_colors()) {
+ start_color();
+ init_dialog_colors();
+ } else
+ set_mono_theme();
+}
+
+/*
+ * Set window to attribute 'attr'
+ */
+void attr_clear(WINDOW * win, int height, int width, chtype attr)
+{
+ int i, j;
+
+ wattrset(win, attr);
+ for (i = 0; i < height; i++) {
+ wmove(win, i, 0);
+ for (j = 0; j < width; j++)
+ waddch(win, ' ');
+ }
+ touchwin(win);
+}
+
+void dialog_clear(void)
+{
+ int lines, columns;
+
+ lines = getmaxy(stdscr);
+ columns = getmaxx(stdscr);
+
+ attr_clear(stdscr, lines, columns, dlg.screen.atr);
+ /* Display background title if it exists ... - SLH */
+ if (dlg.backtitle != NULL) {
+ int i, len = 0, skip = 0;
+ struct subtitle_list *pos;
+
+ wattrset(stdscr, dlg.screen.atr);
+ mvwaddstr(stdscr, 0, 1, (char *)dlg.backtitle);
+
+ for (pos = dlg.subtitles; pos != NULL; pos = pos->next) {
+ /* 3 is for the arrow and spaces */
+ len += strlen(pos->text) + 3;
+ }
+
+ wmove(stdscr, 1, 1);
+ if (len > columns - 2) {
+ const char *ellipsis = "[...] ";
+ waddstr(stdscr, ellipsis);
+ skip = len - (columns - 2 - strlen(ellipsis));
+ }
+
+ for (pos = dlg.subtitles; pos != NULL; pos = pos->next) {
+ if (skip == 0)
+ waddch(stdscr, ACS_RARROW);
+ else
+ skip--;
+
+ if (skip == 0)
+ waddch(stdscr, ' ');
+ else
+ skip--;
+
+ if (skip < strlen(pos->text)) {
+ waddstr(stdscr, pos->text + skip);
+ skip = 0;
+ } else
+ skip -= strlen(pos->text);
+
+ if (skip == 0)
+ waddch(stdscr, ' ');
+ else
+ skip--;
+ }
+
+ for (i = len + 1; i < columns - 1; i++)
+ waddch(stdscr, ACS_HLINE);
+ }
+ wnoutrefresh(stdscr);
+}
+
+/*
+ * Do some initialization for dialog
+ */
+int init_dialog(const char *backtitle)
+{
+ int height, width;
+
+ initscr(); /* Init curses */
+
+ /* Get current cursor position for signal handler in mconf.c */
+ getyx(stdscr, saved_y, saved_x);
+
+ getmaxyx(stdscr, height, width);
+ if (height < WINDOW_HEIGTH_MIN || width < WINDOW_WIDTH_MIN) {
+ endwin();
+ return -ERRDISPLAYTOOSMALL;
+ }
+
+ dlg.backtitle = backtitle;
+ color_setup(getenv("MENUCONFIG_COLOR"));
+
+ keypad(stdscr, TRUE);
+ cbreak();
+ noecho();
+ dialog_clear();
+
+ return 0;
+}
+
+void set_dialog_backtitle(const char *backtitle)
+{
+ dlg.backtitle = backtitle;
+}
+
+void set_dialog_subtitles(struct subtitle_list *subtitles)
+{
+ dlg.subtitles = subtitles;
+}
+
+/*
+ * End using dialog functions.
+ */
+void end_dialog(int x, int y)
+{
+ /* move cursor back to original position */
+ move(y, x);
+ refresh();
+ endwin();
+}
+
+/* Print the title of the dialog. Center the title and truncate
+ * tile if wider than dialog (- 2 chars).
+ **/
+void print_title(WINDOW *dialog, const char *title, int width)
+{
+ if (title) {
+ int tlen = MIN(width - 2, strlen(title));
+ wattrset(dialog, dlg.title.atr);
+ mvwaddch(dialog, 0, (width - tlen) / 2 - 1, ' ');
+ mvwaddnstr(dialog, 0, (width - tlen)/2, title, tlen);
+ waddch(dialog, ' ');
+ }
+}
+
+/*
+ * Print a string of text in a window, automatically wrap around to the
+ * next line if the string is too long to fit on one line. Newline
+ * characters '\n' are propperly processed. We start on a new line
+ * if there is no room for at least 4 nonblanks following a double-space.
+ */
+void print_autowrap(WINDOW * win, const char *prompt, int width, int y, int x)
+{
+ int newl, cur_x, cur_y;
+ int prompt_len, room, wlen;
+ char tempstr[MAX_LEN + 1], *word, *sp, *sp2, *newline_separator = 0;
+
+ strcpy(tempstr, prompt);
+
+ prompt_len = strlen(tempstr);
+
+ if (prompt_len <= width - x * 2) { /* If prompt is short */
+ wmove(win, y, (width - prompt_len) / 2);
+ waddstr(win, tempstr);
+ } else {
+ cur_x = x;
+ cur_y = y;
+ newl = 1;
+ word = tempstr;
+ while (word && *word) {
+ sp = strpbrk(word, "\n ");
+ if (sp && *sp == '\n')
+ newline_separator = sp;
+
+ if (sp)
+ *sp++ = 0;
+
+ /* Wrap to next line if either the word does not fit,
+ or it is the first word of a new sentence, and it is
+ short, and the next word does not fit. */
+ room = width - cur_x;
+ wlen = strlen(word);
+ if (wlen > room ||
+ (newl && wlen < 4 && sp
+ && wlen + 1 + strlen(sp) > room
+ && (!(sp2 = strpbrk(sp, "\n "))
+ || wlen + 1 + (sp2 - sp) > room))) {
+ cur_y++;
+ cur_x = x;
+ }
+ wmove(win, cur_y, cur_x);
+ waddstr(win, word);
+ getyx(win, cur_y, cur_x);
+
+ /* Move to the next line if the word separator was a newline */
+ if (newline_separator) {
+ cur_y++;
+ cur_x = x;
+ newline_separator = 0;
+ } else
+ cur_x++;
+
+ if (sp && *sp == ' ') {
+ cur_x++; /* double space */
+ while (*++sp == ' ') ;
+ newl = 1;
+ } else
+ newl = 0;
+ word = sp;
+ }
+ }
+}
+
+/*
+ * Print a button
+ */
+void print_button(WINDOW * win, const char *label, int y, int x, int selected)
+{
+ int i, temp;
+
+ wmove(win, y, x);
+ wattrset(win, selected ? dlg.button_active.atr
+ : dlg.button_inactive.atr);
+ waddstr(win, "<");
+ temp = strspn(label, " ");
+ label += temp;
+ wattrset(win, selected ? dlg.button_label_active.atr
+ : dlg.button_label_inactive.atr);
+ for (i = 0; i < temp; i++)
+ waddch(win, ' ');
+ wattrset(win, selected ? dlg.button_key_active.atr
+ : dlg.button_key_inactive.atr);
+ waddch(win, label[0]);
+ wattrset(win, selected ? dlg.button_label_active.atr
+ : dlg.button_label_inactive.atr);
+ waddstr(win, (char *)label + 1);
+ wattrset(win, selected ? dlg.button_active.atr
+ : dlg.button_inactive.atr);
+ waddstr(win, ">");
+ wmove(win, y, x + temp + 1);
+}
+
+/*
+ * Draw a rectangular box with line drawing characters
+ */
+void
+draw_box(WINDOW * win, int y, int x, int height, int width,
+ chtype box, chtype border)
+{
+ int i, j;
+
+ wattrset(win, 0);
+ for (i = 0; i < height; i++) {
+ wmove(win, y + i, x);
+ for (j = 0; j < width; j++)
+ if (!i && !j)
+ waddch(win, border | ACS_ULCORNER);
+ else if (i == height - 1 && !j)
+ waddch(win, border | ACS_LLCORNER);
+ else if (!i && j == width - 1)
+ waddch(win, box | ACS_URCORNER);
+ else if (i == height - 1 && j == width - 1)
+ waddch(win, box | ACS_LRCORNER);
+ else if (!i)
+ waddch(win, border | ACS_HLINE);
+ else if (i == height - 1)
+ waddch(win, box | ACS_HLINE);
+ else if (!j)
+ waddch(win, border | ACS_VLINE);
+ else if (j == width - 1)
+ waddch(win, box | ACS_VLINE);
+ else
+ waddch(win, box | ' ');
+ }
+}
+
+/*
+ * Draw shadows along the right and bottom edge to give a more 3D look
+ * to the boxes
+ */
+void draw_shadow(WINDOW * win, int y, int x, int height, int width)
+{
+ int i;
+
+ if (has_colors()) { /* Whether terminal supports color? */
+ wattrset(win, dlg.shadow.atr);
+ wmove(win, y + height, x + 2);
+ for (i = 0; i < width; i++)
+ waddch(win, winch(win) & A_CHARTEXT);
+ for (i = y + 1; i < y + height + 1; i++) {
+ wmove(win, i, x + width);
+ waddch(win, winch(win) & A_CHARTEXT);
+ waddch(win, winch(win) & A_CHARTEXT);
+ }
+ wnoutrefresh(win);
+ }
+}
+
+/*
+ * Return the position of the first alphabetic character in a string.
+ */
+int first_alpha(const char *string, const char *exempt)
+{
+ int i, in_paren = 0, c;
+
+ for (i = 0; i < strlen(string); i++) {
+ c = tolower(string[i]);
+
+ if (strchr("<[(", c))
+ ++in_paren;
+ if (strchr(">])", c) && in_paren > 0)
+ --in_paren;
+
+ if ((!in_paren) && isalpha(c) && strchr(exempt, c) == 0)
+ return i;
+ }
+
+ return 0;
+}
+
+/*
+ * ncurses uses ESC to detect escaped char sequences. This resutl in
+ * a small timeout before ESC is actually delivered to the application.
+ * lxdialog suggest <ESC> <ESC> which is correctly translated to two
+ * times esc. But then we need to ignore the second esc to avoid stepping
+ * out one menu too much. Filter away all escaped key sequences since
+ * keypad(FALSE) turn off ncurses support for escape sequences - and thats
+ * needed to make notimeout() do as expected.
+ */
+int on_key_esc(WINDOW *win)
+{
+ int key;
+ int key2;
+ int key3;
+
+ nodelay(win, TRUE);
+ keypad(win, FALSE);
+ key = wgetch(win);
+ key2 = wgetch(win);
+ do {
+ key3 = wgetch(win);
+ } while (key3 != ERR);
+ nodelay(win, FALSE);
+ keypad(win, TRUE);
+ if (key == KEY_ESC && key2 == ERR)
+ return KEY_ESC;
+ else if (key != ERR && key != KEY_ESC && key2 == ERR)
+ ungetch(key);
+
+ return -1;
+}
+
+/* redraw screen in new size */
+int on_key_resize(void)
+{
+ dialog_clear();
+ return KEY_RESIZE;
+}
+
+struct dialog_list *item_cur;
+struct dialog_list item_nil;
+struct dialog_list *item_head;
+
+void item_reset(void)
+{
+ struct dialog_list *p, *next;
+
+ for (p = item_head; p; p = next) {
+ next = p->next;
+ free(p);
+ }
+ item_head = NULL;
+ item_cur = &item_nil;
+}
+
+void item_make(const char *fmt, ...)
+{
+ va_list ap;
+ struct dialog_list *p = malloc(sizeof(*p));
+
+ if (item_head)
+ item_cur->next = p;
+ else
+ item_head = p;
+ item_cur = p;
+ memset(p, 0, sizeof(*p));
+
+ va_start(ap, fmt);
+ vsnprintf(item_cur->node.str, sizeof(item_cur->node.str), fmt, ap);
+ va_end(ap);
+}
+
+void item_add_str(const char *fmt, ...)
+{
+ va_list ap;
+ size_t avail;
+
+ avail = sizeof(item_cur->node.str) - strlen(item_cur->node.str);
+
+ va_start(ap, fmt);
+ vsnprintf(item_cur->node.str + strlen(item_cur->node.str),
+ avail, fmt, ap);
+ item_cur->node.str[sizeof(item_cur->node.str) - 1] = '\0';
+ va_end(ap);
+}
+
+void item_set_tag(char tag)
+{
+ item_cur->node.tag = tag;
+}
+void item_set_data(void *ptr)
+{
+ item_cur->node.data = ptr;
+}
+
+void item_set_selected(int val)
+{
+ item_cur->node.selected = val;
+}
+
+int item_activate_selected(void)
+{
+ item_foreach()
+ if (item_is_selected())
+ return 1;
+ return 0;
+}
+
+void *item_data(void)
+{
+ return item_cur->node.data;
+}
+
+char item_tag(void)
+{
+ return item_cur->node.tag;
+}
+
+int item_count(void)
+{
+ int n = 0;
+ struct dialog_list *p;
+
+ for (p = item_head; p; p = p->next)
+ n++;
+ return n;
+}
+
+void item_set(int n)
+{
+ int i = 0;
+ item_foreach()
+ if (i++ == n)
+ return;
+}
+
+int item_n(void)
+{
+ int n = 0;
+ struct dialog_list *p;
+
+ for (p = item_head; p; p = p->next) {
+ if (p == item_cur)
+ return n;
+ n++;
+ }
+ return 0;
+}
+
+const char *item_str(void)
+{
+ return item_cur->node.str;
+}
+
+int item_is_selected(void)
+{
+ return (item_cur->node.selected != 0);
+}
+
+int item_is_tag(char tag)
+{
+ return (item_cur->node.tag == tag);
+}
diff --git a/kconf/lxdialog/yesno.c b/kconf/lxdialog/yesno.c
new file mode 100644
index 0000000..676fb2f
--- /dev/null
+++ b/kconf/lxdialog/yesno.c
@@ -0,0 +1,114 @@
+/*
+ * yesno.c -- implements the yes/no box
+ *
+ * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
+ * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com)
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "dialog.h"
+
+/*
+ * Display termination buttons
+ */
+static void print_buttons(WINDOW * dialog, int height, int width, int selected)
+{
+ int x = width / 2 - 10;
+ int y = height - 2;
+
+ print_button(dialog, gettext(" Yes "), y, x, selected == 0);
+ print_button(dialog, gettext(" No "), y, x + 13, selected == 1);
+
+ wmove(dialog, y, x + 1 + 13 * selected);
+ wrefresh(dialog);
+}
+
+/*
+ * Display a dialog box with two buttons - Yes and No
+ */
+int dialog_yesno(const char *title, const char *prompt, int height, int width)
+{
+ int i, x, y, key = 0, button = 0;
+ WINDOW *dialog;
+
+do_resize:
+ if (getmaxy(stdscr) < (height + YESNO_HEIGTH_MIN))
+ return -ERRDISPLAYTOOSMALL;
+ if (getmaxx(stdscr) < (width + YESNO_WIDTH_MIN))
+ return -ERRDISPLAYTOOSMALL;
+
+ /* center dialog box on screen */
+ x = (getmaxx(stdscr) - width) / 2;
+ y = (getmaxy(stdscr) - height) / 2;
+
+ draw_shadow(stdscr, y, x, height, width);
+
+ dialog = newwin(height, width, y, x);
+ keypad(dialog, TRUE);
+
+ draw_box(dialog, 0, 0, height, width,
+ dlg.dialog.atr, dlg.border.atr);
+ wattrset(dialog, dlg.border.atr);
+ mvwaddch(dialog, height - 3, 0, ACS_LTEE);
+ for (i = 0; i < width - 2; i++)
+ waddch(dialog, ACS_HLINE);
+ wattrset(dialog, dlg.dialog.atr);
+ waddch(dialog, ACS_RTEE);
+
+ print_title(dialog, title, width);
+
+ wattrset(dialog, dlg.dialog.atr);
+ print_autowrap(dialog, prompt, width - 2, 1, 3);
+
+ print_buttons(dialog, height, width, 0);
+
+ while (key != KEY_ESC) {
+ key = wgetch(dialog);
+ switch (key) {
+ case 'Y':
+ case 'y':
+ delwin(dialog);
+ return 0;
+ case 'N':
+ case 'n':
+ delwin(dialog);
+ return 1;
+
+ case TAB:
+ case KEY_LEFT:
+ case KEY_RIGHT:
+ button = ((key == KEY_LEFT ? --button : ++button) < 0) ? 1 : (button > 1 ? 0 : button);
+
+ print_buttons(dialog, height, width, button);
+ wrefresh(dialog);
+ break;
+ case ' ':
+ case '\n':
+ delwin(dialog);
+ return button;
+ case KEY_ESC:
+ key = on_key_esc(dialog);
+ break;
+ case KEY_RESIZE:
+ delwin(dialog);
+ on_key_resize();
+ goto do_resize;
+ }
+ }
+
+ delwin(dialog);
+ return key; /* ESC pressed */
+}
diff --git a/kconf/mconf.c b/kconf/mconf.c
new file mode 100644
index 0000000..315ce2c
--- /dev/null
+++ b/kconf/mconf.c
@@ -0,0 +1,1047 @@
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ *
+ * Introduced single menu mode (show all sub-menus in one large tree).
+ * 2002-11-06 Petr Baudis <pasky@ucw.cz>
+ *
+ * i18n, 2005, Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+#include <locale.h>
+
+#include "lkc.h"
+#include "lxdialog/dialog.h"
+
+static const char mconf_readme[] = N_(
+"Overview\n"
+"--------\n"
+"This interface lets you select features and parameters for the build.\n"
+"Features can either be built-in, modularized, or ignored. Parameters\n"
+"must be entered in as decimal or hexadecimal numbers or text.\n"
+"\n"
+"Menu items beginning with following braces represent features that\n"
+" [ ] can be built in or removed\n"
+" < > can be built in, modularized or removed\n"
+" { } can be built in or modularized (selected by other feature)\n"
+" - - are selected by other feature,\n"
+"while *, M or whitespace inside braces means to build in, build as\n"
+"a module or to exclude the feature respectively.\n"
+"\n"
+"To change any of these features, highlight it with the cursor\n"
+"keys and press <Y> to build it in, <M> to make it a module or\n"
+"<N> to remove it. You may also press the <Space Bar> to cycle\n"
+"through the available options (i.e. Y->N->M->Y).\n"
+"\n"
+"Some additional keyboard hints:\n"
+"\n"
+"Menus\n"
+"----------\n"
+"o Use the Up/Down arrow keys (cursor keys) to highlight the item you\n"
+" wish to change or the submenu you wish to select and press <Enter>.\n"
+" Submenus are designated by \"--->\", empty ones by \"----\".\n"
+"\n"
+" Shortcut: Press the option's highlighted letter (hotkey).\n"
+" Pressing a hotkey more than once will sequence\n"
+" through all visible items which use that hotkey.\n"
+"\n"
+" You may also use the <PAGE UP> and <PAGE DOWN> keys to scroll\n"
+" unseen options into view.\n"
+"\n"
+"o To exit a menu use the cursor keys to highlight the <Exit> button\n"
+" and press <ENTER>.\n"
+"\n"
+" Shortcut: Press <ESC><ESC> or <E> or <X> if there is no hotkey\n"
+" using those letters. You may press a single <ESC>, but\n"
+" there is a delayed response which you may find annoying.\n"
+"\n"
+" Also, the <TAB> and cursor keys will cycle between <Select>,\n"
+" <Exit>, <Help>, <Save>, and <Load>.\n"
+"\n"
+"o To get help with an item, use the cursor keys to highlight <Help>\n"
+" and press <ENTER>.\n"
+"\n"
+" Shortcut: Press <H> or <?>.\n"
+"\n"
+"o To toggle the display of hidden options, press <Z>.\n"
+"\n"
+"\n"
+"Radiolists (Choice lists)\n"
+"-----------\n"
+"o Use the cursor keys to select the option you wish to set and press\n"
+" <S> or the <SPACE BAR>.\n"
+"\n"
+" Shortcut: Press the first letter of the option you wish to set then\n"
+" press <S> or <SPACE BAR>.\n"
+"\n"
+"o To see available help for the item, use the cursor keys to highlight\n"
+" <Help> and Press <ENTER>.\n"
+"\n"
+" Shortcut: Press <H> or <?>.\n"
+"\n"
+" Also, the <TAB> and cursor keys will cycle between <Select> and\n"
+" <Help>\n"
+"\n"
+"\n"
+"Data Entry\n"
+"-----------\n"
+"o Enter the requested information and press <ENTER>\n"
+" If you are entering hexadecimal values, it is not necessary to\n"
+" add the '0x' prefix to the entry.\n"
+"\n"
+"o For help, use the <TAB> or cursor keys to highlight the help option\n"
+" and press <ENTER>. You can try <TAB><H> as well.\n"
+"\n"
+"\n"
+"Text Box (Help Window)\n"
+"--------\n"
+"o Use the cursor keys to scroll up/down/left/right. The VI editor\n"
+" keys h,j,k,l function here as do <u>, <d>, <SPACE BAR> and <B> for\n"
+" those who are familiar with less and lynx.\n"
+"\n"
+"o Press <E>, <X>, <q>, <Enter> or <Esc><Esc> to exit.\n"
+"\n"
+"\n"
+"Alternate Configuration Files\n"
+"-----------------------------\n"
+"Menuconfig supports the use of alternate configuration files for\n"
+"those who, for various reasons, find it necessary to switch\n"
+"between different configurations.\n"
+"\n"
+"The <Save> button will let you save the current configuration to\n"
+"a file of your choosing. Use the <Load> button to load a previously\n"
+"saved alternate configuration.\n"
+"\n"
+"Even if you don't use alternate configuration files, but you find\n"
+"during a Menuconfig session that you have completely messed up your\n"
+"settings, you may use the <Load> button to restore your previously\n"
+"saved settings from \".config\" without restarting Menuconfig.\n"
+"\n"
+"Other information\n"
+"-----------------\n"
+"If you use Menuconfig in an XTERM window, make sure you have your\n"
+"$TERM variable set to point to an xterm definition which supports\n"
+"color. Otherwise, Menuconfig will look rather bad. Menuconfig will\n"
+"not display correctly in an RXVT window because rxvt displays only one\n"
+"intensity of color, bright.\n"
+"\n"
+"Menuconfig will display larger menus on screens or xterms which are\n"
+"set to display more than the standard 25 row by 80 column geometry.\n"
+"In order for this to work, the \"stty size\" command must be able to\n"
+"display the screen's current row and column geometry. I STRONGLY\n"
+"RECOMMEND that you make sure you do NOT have the shell variables\n"
+"LINES and COLUMNS exported into your environment. Some distributions\n"
+"export those variables via /etc/profile. Some ncurses programs can\n"
+"become confused when those variables (LINES & COLUMNS) don't reflect\n"
+"the true screen size.\n"
+"\n"
+"Optional personality available\n"
+"------------------------------\n"
+"If you prefer to have all of the options listed in a single menu,\n"
+"rather than the default multimenu hierarchy, run the menuconfig with\n"
+"MENUCONFIG_MODE environment variable set to single_menu. Example:\n"
+"\n"
+"make MENUCONFIG_MODE=single_menu menuconfig\n"
+"\n"
+"<Enter> will then unroll the appropriate category, or enfold it if it\n"
+"is already unrolled.\n"
+"\n"
+"Note that this mode can eventually be a little more CPU expensive\n"
+"(especially with a larger number of unrolled categories) than the\n"
+"default mode.\n"
+"\n"
+"Different color themes available\n"
+"--------------------------------\n"
+"It is possible to select different color themes using the variable\n"
+"MENUCONFIG_COLOR. To select a theme use:\n"
+"\n"
+"make MENUCONFIG_COLOR=<theme> menuconfig\n"
+"\n"
+"Available themes are\n"
+" mono => selects colors suitable for monochrome displays\n"
+" blackbg => selects a color scheme with black background\n"
+" classic => theme with blue background. The classic look\n"
+" bluetitle => an LCD friendly version of classic. (default)\n"
+"\n"),
+menu_instructions[] = N_(
+ "Arrow keys navigate the menu. "
+ "<Enter> selects submenus ---> (or empty submenus ----). "
+ "Highlighted letters are hotkeys. "
+ "Pressing <Y> includes, <N> excludes, <M> modularizes features. "
+ "Press <Esc><Esc> to exit, <?> for Help, </> for Search. "
+ "Legend: [*] built-in [ ] excluded <M> module < > module capable"),
+radiolist_instructions[] = N_(
+ "Use the arrow keys to navigate this window or "
+ "press the hotkey of the item you wish to select "
+ "followed by the <SPACE BAR>. "
+ "Press <?> for additional information about this option."),
+inputbox_instructions_int[] = N_(
+ "Please enter a decimal value. "
+ "Fractions will not be accepted. "
+ "Use the <TAB> key to move from the input field to the buttons below it."),
+inputbox_instructions_hex[] = N_(
+ "Please enter a hexadecimal value. "
+ "Use the <TAB> key to move from the input field to the buttons below it."),
+inputbox_instructions_string[] = N_(
+ "Please enter a string value. "
+ "Use the <TAB> key to move from the input field to the buttons below it."),
+setmod_text[] = N_(
+ "This feature depends on another which has been configured as a module.\n"
+ "As a result, this feature will be built as a module."),
+load_config_text[] = N_(
+ "Enter the name of the configuration file you wish to load. "
+ "Accept the name shown to restore the configuration you "
+ "last retrieved. Leave blank to abort."),
+load_config_help[] = N_(
+ "\n"
+ "For various reasons, one may wish to keep several different\n"
+ "configurations available on a single machine.\n"
+ "\n"
+ "If you have saved a previous configuration in a file other than the\n"
+ "default one, entering its name here will allow you to modify that\n"
+ "configuration.\n"
+ "\n"
+ "If you are uncertain, then you have probably never used alternate\n"
+ "configuration files. You should therefore leave this blank to abort.\n"),
+save_config_text[] = N_(
+ "Enter a filename to which this configuration should be saved "
+ "as an alternate. Leave blank to abort."),
+save_config_help[] = N_(
+ "\n"
+ "For various reasons, one may wish to keep different configurations\n"
+ "available on a single machine.\n"
+ "\n"
+ "Entering a file name here will allow you to later retrieve, modify\n"
+ "and use the current configuration as an alternate to whatever\n"
+ "configuration options you have selected at that time.\n"
+ "\n"
+ "If you are uncertain what all this means then you should probably\n"
+ "leave this blank.\n"),
+search_help[] = N_(
+ "\n"
+ "Search for symbols and display their relations.\n"
+ "Regular expressions are allowed.\n"
+ "Example: search for \"^FOO\"\n"
+ "Result:\n"
+ "-----------------------------------------------------------------\n"
+ "Symbol: FOO [=m]\n"
+ "Type : tristate\n"
+ "Prompt: Foo bus is used to drive the bar HW\n"
+ " Location:\n"
+ " -> Bus options (PCI, PCMCIA, EISA, ISA)\n"
+ " -> PCI support (PCI [=y])\n"
+ "(1) -> PCI access mode (<choice> [=y])\n"
+ " Defined at drivers/pci/Kconfig:47\n"
+ " Depends on: X86_LOCAL_APIC && X86_IO_APIC || IA64\n"
+ " Selects: LIBCRC32\n"
+ " Selected by: BAR [=n]\n"
+ "-----------------------------------------------------------------\n"
+ "o The line 'Type:' shows the type of the configuration option for\n"
+ " this symbol (boolean, tristate, string, ...)\n"
+ "o The line 'Prompt:' shows the text used in the menu structure for\n"
+ " this symbol\n"
+ "o The 'Defined at' line tells at what file / line number the symbol\n"
+ " is defined\n"
+ "o The 'Depends on:' line tells what symbols need to be defined for\n"
+ " this symbol to be visible in the menu (selectable)\n"
+ "o The 'Location:' lines tells where in the menu structure this symbol\n"
+ " is located\n"
+ " A location followed by a [=y] indicates that this is a\n"
+ " selectable menu item - and the current value is displayed inside\n"
+ " brackets.\n"
+ " Press the key in the (#) prefix to jump directly to that\n"
+ " location. You will be returned to the current search results\n"
+ " after exiting this new menu.\n"
+ "o The 'Selects:' line tells what symbols will be automatically\n"
+ " selected if this symbol is selected (y or m)\n"
+ "o The 'Selected by' line tells what symbol has selected this symbol\n"
+ "\n"
+ "Only relevant lines are shown.\n"
+ "\n\n"
+ "Search examples:\n"
+ "Examples: USB => find all symbols containing USB\n"
+ " ^USB => find all symbols starting with USB\n"
+ " USB$ => find all symbols ending with USB\n"
+ "\n");
+
+static int indent;
+static struct menu *current_menu;
+static int child_count;
+static int single_menu_mode;
+static int show_all_options;
+static int save_and_exit;
+static int silent;
+
+static void conf(struct menu *menu, struct menu *active_menu);
+static void conf_choice(struct menu *menu);
+static void conf_string(struct menu *menu);
+static void conf_load(void);
+static void conf_save(void);
+static int show_textbox_ext(const char *title, char *text, int r, int c,
+ int *keys, int *vscroll, int *hscroll,
+ update_text_fn update_text, void *data);
+static void show_textbox(const char *title, const char *text, int r, int c);
+static void show_helptext(const char *title, const char *text);
+static void show_help(struct menu *menu);
+
+static char filename[PATH_MAX+1];
+static void set_config_filename(const char *config_filename)
+{
+ static char menu_backtitle[PATH_MAX+128];
+ int size;
+
+ size = snprintf(menu_backtitle, sizeof(menu_backtitle),
+ "%s - %s", config_filename, rootmenu.prompt->text);
+ if (size >= sizeof(menu_backtitle))
+ menu_backtitle[sizeof(menu_backtitle)-1] = '\0';
+ set_dialog_backtitle(menu_backtitle);
+
+ size = snprintf(filename, sizeof(filename), "%s", config_filename);
+ if (size >= sizeof(filename))
+ filename[sizeof(filename)-1] = '\0';
+}
+
+struct subtitle_part {
+ struct list_head entries;
+ const char *text;
+};
+static LIST_HEAD(trail);
+
+static struct subtitle_list *subtitles;
+static void set_subtitle(void)
+{
+ struct subtitle_part *sp;
+ struct subtitle_list *pos, *tmp;
+
+ for (pos = subtitles; pos != NULL; pos = tmp) {
+ tmp = pos->next;
+ free(pos);
+ }
+
+ subtitles = NULL;
+ list_for_each_entry(sp, &trail, entries) {
+ if (sp->text) {
+ if (pos) {
+ pos->next = xcalloc(1, sizeof(*pos));
+ pos = pos->next;
+ } else {
+ subtitles = pos = xcalloc(1, sizeof(*pos));
+ }
+ pos->text = sp->text;
+ }
+ }
+
+ set_dialog_subtitles(subtitles);
+}
+
+static void reset_subtitle(void)
+{
+ struct subtitle_list *pos, *tmp;
+
+ for (pos = subtitles; pos != NULL; pos = tmp) {
+ tmp = pos->next;
+ free(pos);
+ }
+ subtitles = NULL;
+ set_dialog_subtitles(subtitles);
+}
+
+struct search_data {
+ struct list_head *head;
+ struct menu **targets;
+ int *keys;
+};
+
+static void update_text(char *buf, size_t start, size_t end, void *_data)
+{
+ struct search_data *data = _data;
+ struct jump_key *pos;
+ int k = 0;
+
+ list_for_each_entry(pos, data->head, entries) {
+ if (pos->offset >= start && pos->offset < end) {
+ char header[4];
+
+ if (k < JUMP_NB) {
+ int key = '0' + (pos->index % JUMP_NB) + 1;
+
+ sprintf(header, "(%c)", key);
+ data->keys[k] = key;
+ data->targets[k] = pos->target;
+ k++;
+ } else {
+ sprintf(header, " ");
+ }
+
+ memcpy(buf + pos->offset, header, sizeof(header) - 1);
+ }
+ }
+ data->keys[k] = 0;
+}
+
+static void search_conf(void)
+{
+ struct symbol **sym_arr;
+ struct gstr res;
+ struct gstr title;
+ char *dialog_input;
+ int dres, vscroll = 0, hscroll = 0;
+ bool again;
+ struct gstr sttext;
+ struct subtitle_part stpart;
+
+ title = str_new();
+ str_printf( &title, _("Enter (sub)string or regexp to search for "
+ "(with or without \"%s\")"), CONFIG_);
+
+again:
+ dialog_clear();
+ dres = dialog_inputbox(_("Search Configuration Parameter"),
+ str_get(&title),
+ 10, 75, "");
+ switch (dres) {
+ case 0:
+ break;
+ case 1:
+ show_helptext(_("Search Configuration"), search_help);
+ goto again;
+ default:
+ str_free(&title);
+ return;
+ }
+
+ /* strip the prefix if necessary */
+ dialog_input = dialog_input_result;
+ if (strncasecmp(dialog_input_result, CONFIG_, strlen(CONFIG_)) == 0)
+ dialog_input += strlen(CONFIG_);
+
+ sttext = str_new();
+ str_printf(&sttext, "Search (%s)", dialog_input_result);
+ stpart.text = str_get(&sttext);
+ list_add_tail(&stpart.entries, &trail);
+
+ sym_arr = sym_re_search(dialog_input);
+ do {
+ LIST_HEAD(head);
+ struct menu *targets[JUMP_NB];
+ int keys[JUMP_NB + 1], i;
+ struct search_data data = {
+ .head = &head,
+ .targets = targets,
+ .keys = keys,
+ };
+ struct jump_key *pos, *tmp;
+
+ res = get_relations_str(sym_arr, &head);
+ set_subtitle();
+ dres = show_textbox_ext(_("Search Results"), (char *)
+ str_get(&res), 0, 0, keys, &vscroll,
+ &hscroll, &update_text, (void *)
+ &data);
+ again = false;
+ for (i = 0; i < JUMP_NB && keys[i]; i++)
+ if (dres == keys[i]) {
+ conf(targets[i]->parent, targets[i]);
+ again = true;
+ }
+ str_free(&res);
+ list_for_each_entry_safe(pos, tmp, &head, entries)
+ free(pos);
+ } while (again);
+ free(sym_arr);
+ str_free(&title);
+ list_del(trail.prev);
+ str_free(&sttext);
+}
+
+static void build_conf(struct menu *menu)
+{
+ struct symbol *sym;
+ struct property *prop;
+ struct menu *child;
+ int type, tmp, doint = 2;
+ tristate val;
+ char ch;
+ bool visible;
+
+ /*
+ * note: menu_is_visible() has side effect that it will
+ * recalc the value of the symbol.
+ */
+ visible = menu_is_visible(menu);
+ if (show_all_options && !menu_has_prompt(menu))
+ return;
+ else if (!show_all_options && !visible)
+ return;
+
+ sym = menu->sym;
+ prop = menu->prompt;
+ if (!sym) {
+ if (prop && menu != current_menu) {
+ const char *prompt = menu_get_prompt(menu);
+ switch (prop->type) {
+ case P_MENU:
+ child_count++;
+ prompt = _(prompt);
+ if (single_menu_mode) {
+ item_make("%s%*c%s",
+ menu->data ? "-->" : "++>",
+ indent + 1, ' ', prompt);
+ } else
+ item_make(" %*c%s %s",
+ indent + 1, ' ', prompt,
+ menu_is_empty(menu) ? "----" : "--->");
+ item_set_tag('m');
+ item_set_data(menu);
+ if (single_menu_mode && menu->data)
+ goto conf_childs;
+ return;
+ case P_COMMENT:
+ if (prompt) {
+ child_count++;
+ item_make(" %*c*** %s ***", indent + 1, ' ', _(prompt));
+ item_set_tag(':');
+ item_set_data(menu);
+ }
+ break;
+ default:
+ if (prompt) {
+ child_count++;
+ item_make("---%*c%s", indent + 1, ' ', _(prompt));
+ item_set_tag(':');
+ item_set_data(menu);
+ }
+ }
+ } else
+ doint = 0;
+ goto conf_childs;
+ }
+
+ type = sym_get_type(sym);
+ if (sym_is_choice(sym)) {
+ struct symbol *def_sym = sym_get_choice_value(sym);
+ struct menu *def_menu = NULL;
+
+ child_count++;
+ for (child = menu->list; child; child = child->next) {
+ if (menu_is_visible(child) && child->sym == def_sym)
+ def_menu = child;
+ }
+
+ val = sym_get_tristate_value(sym);
+ if (sym_is_changable(sym)) {
+ switch (type) {
+ case S_BOOLEAN:
+ item_make("[%c]", val == no ? ' ' : '*');
+ break;
+ case S_TRISTATE:
+ switch (val) {
+ case yes: ch = '*'; break;
+ case mod: ch = 'M'; break;
+ default: ch = ' '; break;
+ }
+ item_make("<%c>", ch);
+ break;
+ }
+ item_set_tag('t');
+ item_set_data(menu);
+ } else {
+ item_make(" ");
+ item_set_tag(def_menu ? 't' : ':');
+ item_set_data(menu);
+ }
+
+ item_add_str("%*c%s", indent + 1, ' ', _(menu_get_prompt(menu)));
+ if (val == yes) {
+ if (def_menu) {
+ item_add_str(" (%s)", _(menu_get_prompt(def_menu)));
+ item_add_str(" --->");
+ if (def_menu->list) {
+ indent += 2;
+ build_conf(def_menu);
+ indent -= 2;
+ }
+ }
+ return;
+ }
+ } else {
+ if (menu == current_menu) {
+ item_make("---%*c%s", indent + 1, ' ', _(menu_get_prompt(menu)));
+ item_set_tag(':');
+ item_set_data(menu);
+ goto conf_childs;
+ }
+ child_count++;
+ val = sym_get_tristate_value(sym);
+ if (sym_is_choice_value(sym) && val == yes) {
+ item_make(" ");
+ item_set_tag(':');
+ item_set_data(menu);
+ } else {
+ switch (type) {
+ case S_BOOLEAN:
+ if (sym_is_changable(sym))
+ item_make("[%c]", val == no ? ' ' : '*');
+ else
+ item_make("-%c-", val == no ? ' ' : '*');
+ item_set_tag('t');
+ item_set_data(menu);
+ break;
+ case S_TRISTATE:
+ switch (val) {
+ case yes: ch = '*'; break;
+ case mod: ch = 'M'; break;
+ default: ch = ' '; break;
+ }
+ if (sym_is_changable(sym)) {
+ if (sym->rev_dep.tri == mod)
+ item_make("{%c}", ch);
+ else
+ item_make("<%c>", ch);
+ } else
+ item_make("-%c-", ch);
+ item_set_tag('t');
+ item_set_data(menu);
+ break;
+ default:
+ tmp = 2 + strlen(sym_get_string_value(sym)); /* () = 2 */
+ item_make("(%s)", sym_get_string_value(sym));
+ tmp = indent - tmp + 4;
+ if (tmp < 0)
+ tmp = 0;
+ item_add_str("%*c%s%s", tmp, ' ', _(menu_get_prompt(menu)),
+ (sym_has_value(sym) || !sym_is_changable(sym)) ?
+ "" : _(" (NEW)"));
+ item_set_tag('s');
+ item_set_data(menu);
+ goto conf_childs;
+ }
+ }
+ item_add_str("%*c%s%s", indent + 1, ' ', _(menu_get_prompt(menu)),
+ (sym_has_value(sym) || !sym_is_changable(sym)) ?
+ "" : _(" (NEW)"));
+ if (menu->prompt->type == P_MENU) {
+ item_add_str(" %s", menu_is_empty(menu) ? "----" : "--->");
+ return;
+ }
+ }
+
+conf_childs:
+ indent += doint;
+ for (child = menu->list; child; child = child->next)
+ build_conf(child);
+ indent -= doint;
+}
+
+static void conf(struct menu *menu, struct menu *active_menu)
+{
+ struct menu *submenu;
+ const char *prompt = menu_get_prompt(menu);
+ struct subtitle_part stpart;
+ struct symbol *sym;
+ int res;
+ int s_scroll = 0;
+
+ if (menu != &rootmenu)
+ stpart.text = menu_get_prompt(menu);
+ else
+ stpart.text = NULL;
+ list_add_tail(&stpart.entries, &trail);
+
+ while (1) {
+ item_reset();
+ current_menu = menu;
+ build_conf(menu);
+ if (!child_count)
+ break;
+ set_subtitle();
+ dialog_clear();
+ res = dialog_menu(prompt ? _(prompt) : _("Main Menu"),
+ _(menu_instructions),
+ active_menu, &s_scroll);
+ if (res == 1 || res == KEY_ESC || res == -ERRDISPLAYTOOSMALL)
+ break;
+ if (item_count() != 0) {
+ if (!item_activate_selected())
+ continue;
+ if (!item_tag())
+ continue;
+ }
+ submenu = item_data();
+ active_menu = item_data();
+ if (submenu)
+ sym = submenu->sym;
+ else
+ sym = NULL;
+
+ switch (res) {
+ case 0:
+ switch (item_tag()) {
+ case 'm':
+ if (single_menu_mode)
+ submenu->data = (void *) (long) !submenu->data;
+ else
+ conf(submenu, NULL);
+ break;
+ case 't':
+ if (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes)
+ conf_choice(submenu);
+ else if (submenu->prompt->type == P_MENU)
+ conf(submenu, NULL);
+ break;
+ case 's':
+ conf_string(submenu);
+ break;
+ }
+ break;
+ case 2:
+ if (sym)
+ show_help(submenu);
+ else {
+ reset_subtitle();
+ show_helptext(_("README"), _(mconf_readme));
+ }
+ break;
+ case 3:
+ reset_subtitle();
+ conf_save();
+ break;
+ case 4:
+ reset_subtitle();
+ conf_load();
+ break;
+ case 5:
+ if (item_is_tag('t')) {
+ if (sym_set_tristate_value(sym, yes))
+ break;
+ if (sym_set_tristate_value(sym, mod))
+ show_textbox(NULL, setmod_text, 6, 74);
+ }
+ break;
+ case 6:
+ if (item_is_tag('t'))
+ sym_set_tristate_value(sym, no);
+ break;
+ case 7:
+ if (item_is_tag('t'))
+ sym_set_tristate_value(sym, mod);
+ break;
+ case 8:
+ if (item_is_tag('t'))
+ sym_toggle_tristate_value(sym);
+ else if (item_is_tag('m'))
+ conf(submenu, NULL);
+ break;
+ case 9:
+ search_conf();
+ break;
+ case 10:
+ show_all_options = !show_all_options;
+ break;
+ }
+ }
+
+ list_del(trail.prev);
+}
+
+static int show_textbox_ext(const char *title, char *text, int r, int c, int
+ *keys, int *vscroll, int *hscroll, update_text_fn
+ update_text, void *data)
+{
+ dialog_clear();
+ return dialog_textbox(title, text, r, c, keys, vscroll, hscroll,
+ update_text, data);
+}
+
+static void show_textbox(const char *title, const char *text, int r, int c)
+{
+ show_textbox_ext(title, (char *) text, r, c, (int []) {0}, NULL, NULL,
+ NULL, NULL);
+}
+
+static void show_helptext(const char *title, const char *text)
+{
+ show_textbox(title, text, 0, 0);
+}
+
+static void conf_message_callback(const char *fmt, va_list ap)
+{
+ char buf[PATH_MAX+1];
+
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ if (save_and_exit) {
+ if (!silent)
+ printf("%s", buf);
+ } else {
+ show_textbox(NULL, buf, 6, 60);
+ }
+}
+
+static void show_help(struct menu *menu)
+{
+ struct gstr help = str_new();
+
+ help.max_width = getmaxx(stdscr) - 10;
+ menu_get_ext_help(menu, &help);
+
+ show_helptext(_(menu_get_prompt(menu)), str_get(&help));
+ str_free(&help);
+}
+
+static void conf_choice(struct menu *menu)
+{
+ const char *prompt = _(menu_get_prompt(menu));
+ struct menu *child;
+ struct symbol *active;
+
+ active = sym_get_choice_value(menu->sym);
+ while (1) {
+ int res;
+ int selected;
+ item_reset();
+
+ current_menu = menu;
+ for (child = menu->list; child; child = child->next) {
+ if (!menu_is_visible(child))
+ continue;
+ if (child->sym)
+ item_make("%s", _(menu_get_prompt(child)));
+ else {
+ item_make("*** %s ***", _(menu_get_prompt(child)));
+ item_set_tag(':');
+ }
+ item_set_data(child);
+ if (child->sym == active)
+ item_set_selected(1);
+ if (child->sym == sym_get_choice_value(menu->sym))
+ item_set_tag('X');
+ }
+ dialog_clear();
+ res = dialog_checklist(prompt ? _(prompt) : _("Main Menu"),
+ _(radiolist_instructions),
+ MENUBOX_HEIGTH_MIN,
+ MENUBOX_WIDTH_MIN,
+ CHECKLIST_HEIGTH_MIN);
+ selected = item_activate_selected();
+ switch (res) {
+ case 0:
+ if (selected) {
+ child = item_data();
+ if (!child->sym)
+ break;
+
+ sym_set_tristate_value(child->sym, yes);
+ }
+ return;
+ case 1:
+ if (selected) {
+ child = item_data();
+ show_help(child);
+ active = child->sym;
+ } else
+ show_help(menu);
+ break;
+ case KEY_ESC:
+ return;
+ case -ERRDISPLAYTOOSMALL:
+ return;
+ }
+ }
+}
+
+static void conf_string(struct menu *menu)
+{
+ const char *prompt = menu_get_prompt(menu);
+
+ while (1) {
+ int res;
+ const char *heading;
+
+ switch (sym_get_type(menu->sym)) {
+ case S_INT:
+ heading = _(inputbox_instructions_int);
+ break;
+ case S_HEX:
+ heading = _(inputbox_instructions_hex);
+ break;
+ case S_STRING:
+ heading = _(inputbox_instructions_string);
+ break;
+ default:
+ heading = _("Internal mconf error!");
+ }
+ dialog_clear();
+ res = dialog_inputbox(prompt ? _(prompt) : _("Main Menu"),
+ heading, 10, 75,
+ sym_get_string_value(menu->sym));
+ switch (res) {
+ case 0:
+ if (sym_set_string_value(menu->sym, dialog_input_result))
+ return;
+ show_textbox(NULL, _("You have made an invalid entry."), 5, 43);
+ break;
+ case 1:
+ show_help(menu);
+ break;
+ case KEY_ESC:
+ return;
+ }
+ }
+}
+
+static void conf_load(void)
+{
+
+ while (1) {
+ int res;
+ dialog_clear();
+ res = dialog_inputbox(NULL, load_config_text,
+ 11, 55, filename);
+ switch(res) {
+ case 0:
+ if (!dialog_input_result[0])
+ return;
+ if (!conf_read(dialog_input_result)) {
+ set_config_filename(dialog_input_result);
+ sym_set_change_count(1);
+ return;
+ }
+ show_textbox(NULL, _("File does not exist!"), 5, 38);
+ break;
+ case 1:
+ show_helptext(_("Load Alternate Configuration"), load_config_help);
+ break;
+ case KEY_ESC:
+ return;
+ }
+ }
+}
+
+static void conf_save(void)
+{
+ while (1) {
+ int res;
+ dialog_clear();
+ res = dialog_inputbox(NULL, save_config_text,
+ 11, 55, filename);
+ switch(res) {
+ case 0:
+ if (!dialog_input_result[0])
+ return;
+ if (!conf_write(dialog_input_result)) {
+ set_config_filename(dialog_input_result);
+ return;
+ }
+ show_textbox(NULL, _("Can't create file! Probably a nonexistent directory."), 5, 60);
+ break;
+ case 1:
+ show_helptext(_("Save Alternate Configuration"), save_config_help);
+ break;
+ case KEY_ESC:
+ return;
+ }
+ }
+}
+
+static int handle_exit(void)
+{
+ int res;
+
+ save_and_exit = 1;
+ reset_subtitle();
+ dialog_clear();
+ if (conf_get_changed())
+ res = dialog_yesno(NULL,
+ _("Do you wish to save your new configuration?\n"
+ "(Press <ESC><ESC> to continue kernel configuration.)"),
+ 6, 60);
+ else
+ res = -1;
+
+ end_dialog(saved_x, saved_y);
+
+ switch (res) {
+ case 0:
+ if (conf_write(filename)) {
+ fprintf(stderr, _("\n\n"
+ "Error while writing of the configuration.\n"
+ "Your configuration changes were NOT saved."
+ "\n\n"));
+ return 1;
+ }
+ /* fall through */
+ case -1:
+ if (!silent)
+ printf(_("\n\n"
+ "*** End of the configuration.\n"
+ "*** Execute 'make' to start the build or try 'make help'."
+ "\n\n"));
+ res = 0;
+ break;
+ default:
+ if (!silent)
+ fprintf(stderr, _("\n\n"
+ "Your configuration changes were NOT saved."
+ "\n\n"));
+ if (res != KEY_ESC)
+ res = 0;
+ }
+
+ return res;
+}
+
+static void sig_handler(int signo)
+{
+ exit(handle_exit());
+}
+
+int main(int ac, char **av)
+{
+ char *mode;
+ int res;
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+
+ signal(SIGINT, sig_handler);
+
+ if (ac > 1 && strcmp(av[1], "-s") == 0) {
+ silent = 1;
+ /* Silence conf_read() until the real callback is set up */
+ conf_set_message_callback(NULL);
+ av++;
+ }
+ conf_parse(av[1]);
+ conf_read(NULL);
+
+ mode = getenv("MENUCONFIG_MODE");
+ if (mode) {
+ if (!strcasecmp(mode, "single_menu"))
+ single_menu_mode = 1;
+ }
+
+ if (init_dialog(NULL)) {
+ fprintf(stderr, N_("Your display is too small to run Menuconfig!\n"));
+ fprintf(stderr, N_("It must be at least 19 lines by 80 columns.\n"));
+ return 1;
+ }
+
+ set_config_filename(conf_get_configname());
+ conf_set_message_callback(conf_message_callback);
+ do {
+ conf(&rootmenu, NULL);
+ res = handle_exit();
+ } while (res == KEY_ESC);
+
+ return res;
+}
diff --git a/kconf/menu.c b/kconf/menu.c
new file mode 100644
index 0000000..b05cc3d
--- /dev/null
+++ b/kconf/menu.c
@@ -0,0 +1,697 @@
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lkc.h"
+
+static const char nohelp_text[] = "There is no help available for this option.";
+
+struct menu rootmenu;
+static struct menu **last_entry_ptr;
+
+struct file *file_list;
+struct file *current_file;
+
+void menu_warn(struct menu *menu, const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ fprintf(stderr, "%s:%d:warning: ", menu->file->name, menu->lineno);
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ va_end(ap);
+}
+
+static void prop_warn(struct property *prop, const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ fprintf(stderr, "%s:%d:warning: ", prop->file->name, prop->lineno);
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ va_end(ap);
+}
+
+void _menu_init(void)
+{
+ current_entry = current_menu = &rootmenu;
+ last_entry_ptr = &rootmenu.list;
+}
+
+void menu_add_entry(struct symbol *sym)
+{
+ struct menu *menu;
+
+ menu = xmalloc(sizeof(*menu));
+ memset(menu, 0, sizeof(*menu));
+ menu->sym = sym;
+ menu->parent = current_menu;
+ menu->file = current_file;
+ menu->lineno = zconf_lineno();
+
+ *last_entry_ptr = menu;
+ last_entry_ptr = &menu->next;
+ current_entry = menu;
+ if (sym)
+ menu_add_symbol(P_SYMBOL, sym, NULL);
+}
+
+void menu_end_entry(void)
+{
+}
+
+struct menu *menu_add_menu(void)
+{
+ menu_end_entry();
+ last_entry_ptr = ¤t_entry->list;
+ return current_menu = current_entry;
+}
+
+void menu_end_menu(void)
+{
+ last_entry_ptr = ¤t_menu->next;
+ current_menu = current_menu->parent;
+}
+
+static struct expr *menu_check_dep(struct expr *e)
+{
+ if (!e)
+ return e;
+
+ switch (e->type) {
+ case E_NOT:
+ e->left.expr = menu_check_dep(e->left.expr);
+ break;
+ case E_OR:
+ case E_AND:
+ e->left.expr = menu_check_dep(e->left.expr);
+ e->right.expr = menu_check_dep(e->right.expr);
+ break;
+ case E_SYMBOL:
+ /* change 'm' into 'm' && MODULES */
+ if (e->left.sym == &symbol_mod)
+ return expr_alloc_and(e, expr_alloc_symbol(modules_sym));
+ break;
+ default:
+ break;
+ }
+ return e;
+}
+
+void menu_add_dep(struct expr *dep)
+{
+ current_entry->dep = expr_alloc_and(current_entry->dep, menu_check_dep(dep));
+}
+
+void menu_set_type(int type)
+{
+ struct symbol *sym = current_entry->sym;
+
+ if (sym->type == type)
+ return;
+ if (sym->type == S_UNKNOWN) {
+ sym->type = type;
+ return;
+ }
+ menu_warn(current_entry,
+ "ignoring type redefinition of '%s' from '%s' to '%s'",
+ sym->name ? sym->name : "<choice>",
+ sym_type_name(sym->type), sym_type_name(type));
+}
+
+static struct property *menu_add_prop(enum prop_type type, char *prompt, struct expr *expr, struct expr *dep)
+{
+ struct property *prop = prop_alloc(type, current_entry->sym);
+
+ prop->menu = current_entry;
+ prop->expr = expr;
+ prop->visible.expr = menu_check_dep(dep);
+
+ if (prompt) {
+ if (isspace(*prompt)) {
+ prop_warn(prop, "leading whitespace ignored");
+ while (isspace(*prompt))
+ prompt++;
+ }
+ if (current_entry->prompt && current_entry != &rootmenu)
+ prop_warn(prop, "prompt redefined");
+
+ /* Apply all upper menus' visibilities to actual prompts. */
+ if(type == P_PROMPT) {
+ struct menu *menu = current_entry;
+
+ while ((menu = menu->parent) != NULL) {
+ struct expr *dup_expr;
+
+ if (!menu->visibility)
+ continue;
+ /*
+ * Do not add a reference to the
+ * menu's visibility expression but
+ * use a copy of it. Otherwise the
+ * expression reduction functions
+ * will modify expressions that have
+ * multiple references which can
+ * cause unwanted side effects.
+ */
+ dup_expr = expr_copy(menu->visibility);
+
+ prop->visible.expr
+ = expr_alloc_and(prop->visible.expr,
+ dup_expr);
+ }
+ }
+
+ current_entry->prompt = prop;
+ }
+ prop->text = prompt;
+
+ return prop;
+}
+
+struct property *menu_add_prompt(enum prop_type type, char *prompt, struct expr *dep)
+{
+ return menu_add_prop(type, prompt, NULL, dep);
+}
+
+void menu_add_visibility(struct expr *expr)
+{
+ current_entry->visibility = expr_alloc_and(current_entry->visibility,
+ expr);
+}
+
+void menu_add_expr(enum prop_type type, struct expr *expr, struct expr *dep)
+{
+ menu_add_prop(type, NULL, expr, dep);
+}
+
+void menu_add_symbol(enum prop_type type, struct symbol *sym, struct expr *dep)
+{
+ menu_add_prop(type, NULL, expr_alloc_symbol(sym), dep);
+}
+
+void menu_add_option(int token, char *arg)
+{
+ switch (token) {
+ case T_OPT_MODULES:
+ if (modules_sym)
+ zconf_error("symbol '%s' redefines option 'modules'"
+ " already defined by symbol '%s'",
+ current_entry->sym->name,
+ modules_sym->name
+ );
+ modules_sym = current_entry->sym;
+ break;
+ case T_OPT_DEFCONFIG_LIST:
+ if (!sym_defconfig_list)
+ sym_defconfig_list = current_entry->sym;
+ else if (sym_defconfig_list != current_entry->sym)
+ zconf_error("trying to redefine defconfig symbol");
+ break;
+ case T_OPT_ENV:
+ prop_add_env(arg);
+ break;
+ case T_OPT_ALLNOCONFIG_Y:
+ current_entry->sym->flags |= SYMBOL_ALLNOCONFIG_Y;
+ break;
+ }
+}
+
+static int menu_validate_number(struct symbol *sym, struct symbol *sym2)
+{
+ return sym2->type == S_INT || sym2->type == S_HEX ||
+ (sym2->type == S_UNKNOWN && sym_string_valid(sym, sym2->name));
+}
+
+static void sym_check_prop(struct symbol *sym)
+{
+ struct property *prop;
+ struct symbol *sym2;
+ for (prop = sym->prop; prop; prop = prop->next) {
+ switch (prop->type) {
+ case P_DEFAULT:
+ if ((sym->type == S_STRING || sym->type == S_INT || sym->type == S_HEX) &&
+ prop->expr->type != E_SYMBOL)
+ prop_warn(prop,
+ "default for config symbol '%s'"
+ " must be a single symbol", sym->name);
+ if (prop->expr->type != E_SYMBOL)
+ break;
+ sym2 = prop_get_symbol(prop);
+ if (sym->type == S_HEX || sym->type == S_INT) {
+ if (!menu_validate_number(sym, sym2))
+ prop_warn(prop,
+ "'%s': number is invalid",
+ sym->name);
+ }
+ break;
+ case P_SELECT:
+ sym2 = prop_get_symbol(prop);
+ if (sym->type != S_BOOLEAN && sym->type != S_TRISTATE)
+ prop_warn(prop,
+ "config symbol '%s' uses select, but is "
+ "not boolean or tristate", sym->name);
+ else if (sym2->type != S_UNKNOWN &&
+ sym2->type != S_BOOLEAN &&
+ sym2->type != S_TRISTATE)
+ prop_warn(prop,
+ "'%s' has wrong type. 'select' only "
+ "accept arguments of boolean and "
+ "tristate type", sym2->name);
+ break;
+ case P_RANGE:
+ if (sym->type != S_INT && sym->type != S_HEX)
+ prop_warn(prop, "range is only allowed "
+ "for int or hex symbols");
+ if (!menu_validate_number(sym, prop->expr->left.sym) ||
+ !menu_validate_number(sym, prop->expr->right.sym))
+ prop_warn(prop, "range is invalid");
+ break;
+ default:
+ ;
+ }
+ }
+}
+
+void menu_finalize(struct menu *parent)
+{
+ struct menu *menu, *last_menu;
+ struct symbol *sym;
+ struct property *prop;
+ struct expr *parentdep, *basedep, *dep, *dep2, **ep;
+
+ sym = parent->sym;
+ if (parent->list) {
+ if (sym && sym_is_choice(sym)) {
+ if (sym->type == S_UNKNOWN) {
+ /* find the first choice value to find out choice type */
+ current_entry = parent;
+ for (menu = parent->list; menu; menu = menu->next) {
+ if (menu->sym && menu->sym->type != S_UNKNOWN) {
+ menu_set_type(menu->sym->type);
+ break;
+ }
+ }
+ }
+ /* set the type of the remaining choice values */
+ for (menu = parent->list; menu; menu = menu->next) {
+ current_entry = menu;
+ if (menu->sym && menu->sym->type == S_UNKNOWN)
+ menu_set_type(sym->type);
+ }
+ parentdep = expr_alloc_symbol(sym);
+ } else if (parent->prompt)
+ parentdep = parent->prompt->visible.expr;
+ else
+ parentdep = parent->dep;
+
+ for (menu = parent->list; menu; menu = menu->next) {
+ basedep = expr_transform(menu->dep);
+ basedep = expr_alloc_and(expr_copy(parentdep), basedep);
+ basedep = expr_eliminate_dups(basedep);
+ menu->dep = basedep;
+ if (menu->sym)
+ prop = menu->sym->prop;
+ else
+ prop = menu->prompt;
+ for (; prop; prop = prop->next) {
+ if (prop->menu != menu)
+ continue;
+ dep = expr_transform(prop->visible.expr);
+ dep = expr_alloc_and(expr_copy(basedep), dep);
+ dep = expr_eliminate_dups(dep);
+ if (menu->sym && menu->sym->type != S_TRISTATE)
+ dep = expr_trans_bool(dep);
+ prop->visible.expr = dep;
+ if (prop->type == P_SELECT) {
+ struct symbol *es = prop_get_symbol(prop);
+ es->rev_dep.expr = expr_alloc_or(es->rev_dep.expr,
+ expr_alloc_and(expr_alloc_symbol(menu->sym), expr_copy(dep)));
+ }
+ }
+ }
+ for (menu = parent->list; menu; menu = menu->next)
+ menu_finalize(menu);
+ } else if (sym) {
+ basedep = parent->prompt ? parent->prompt->visible.expr : NULL;
+ basedep = expr_trans_compare(basedep, E_UNEQUAL, &symbol_no);
+ basedep = expr_eliminate_dups(expr_transform(basedep));
+ last_menu = NULL;
+ for (menu = parent->next; menu; menu = menu->next) {
+ dep = menu->prompt ? menu->prompt->visible.expr : menu->dep;
+ if (!expr_contains_symbol(dep, sym))
+ break;
+ if (expr_depends_symbol(dep, sym))
+ goto next;
+ dep = expr_trans_compare(dep, E_UNEQUAL, &symbol_no);
+ dep = expr_eliminate_dups(expr_transform(dep));
+ dep2 = expr_copy(basedep);
+ expr_eliminate_eq(&dep, &dep2);
+ expr_free(dep);
+ if (!expr_is_yes(dep2)) {
+ expr_free(dep2);
+ break;
+ }
+ expr_free(dep2);
+ next:
+ menu_finalize(menu);
+ menu->parent = parent;
+ last_menu = menu;
+ }
+ if (last_menu) {
+ parent->list = parent->next;
+ parent->next = last_menu->next;
+ last_menu->next = NULL;
+ }
+
+ sym->dir_dep.expr = expr_alloc_or(sym->dir_dep.expr, parent->dep);
+ }
+ for (menu = parent->list; menu; menu = menu->next) {
+ if (sym && sym_is_choice(sym) &&
+ menu->sym && !sym_is_choice_value(menu->sym)) {
+ current_entry = menu;
+ menu->sym->flags |= SYMBOL_CHOICEVAL;
+ if (!menu->prompt)
+ menu_warn(menu, "choice value must have a prompt");
+ for (prop = menu->sym->prop; prop; prop = prop->next) {
+ if (prop->type == P_DEFAULT)
+ prop_warn(prop, "defaults for choice "
+ "values not supported");
+ if (prop->menu == menu)
+ continue;
+ if (prop->type == P_PROMPT &&
+ prop->menu->parent->sym != sym)
+ prop_warn(prop, "choice value used outside its choice group");
+ }
+ /* Non-tristate choice values of tristate choices must
+ * depend on the choice being set to Y. The choice
+ * values' dependencies were propagated to their
+ * properties above, so the change here must be re-
+ * propagated.
+ */
+ if (sym->type == S_TRISTATE && menu->sym->type != S_TRISTATE) {
+ basedep = expr_alloc_comp(E_EQUAL, sym, &symbol_yes);
+ menu->dep = expr_alloc_and(basedep, menu->dep);
+ for (prop = menu->sym->prop; prop; prop = prop->next) {
+ if (prop->menu != menu)
+ continue;
+ prop->visible.expr = expr_alloc_and(expr_copy(basedep),
+ prop->visible.expr);
+ }
+ }
+ menu_add_symbol(P_CHOICE, sym, NULL);
+ prop = sym_get_choice_prop(sym);
+ for (ep = &prop->expr; *ep; ep = &(*ep)->left.expr)
+ ;
+ *ep = expr_alloc_one(E_LIST, NULL);
+ (*ep)->right.sym = menu->sym;
+ }
+ if (menu->list && (!menu->prompt || !menu->prompt->text)) {
+ for (last_menu = menu->list; ; last_menu = last_menu->next) {
+ last_menu->parent = parent;
+ if (!last_menu->next)
+ break;
+ }
+ last_menu->next = menu->next;
+ menu->next = menu->list;
+ menu->list = NULL;
+ }
+ }
+
+ if (sym && !(sym->flags & SYMBOL_WARNED)) {
+ if (sym->type == S_UNKNOWN)
+ menu_warn(parent, "config symbol defined without type");
+
+ if (sym_is_choice(sym) && !parent->prompt)
+ menu_warn(parent, "choice must have a prompt");
+
+ /* Check properties connected to this symbol */
+ sym_check_prop(sym);
+ sym->flags |= SYMBOL_WARNED;
+ }
+
+ if (sym && !sym_is_optional(sym) && parent->prompt) {
+ sym->rev_dep.expr = expr_alloc_or(sym->rev_dep.expr,
+ expr_alloc_and(parent->prompt->visible.expr,
+ expr_alloc_symbol(&symbol_mod)));
+ }
+}
+
+bool menu_has_prompt(struct menu *menu)
+{
+ if (!menu->prompt)
+ return false;
+ return true;
+}
+
+/*
+ * Determine if a menu is empty.
+ * A menu is considered empty if it contains no or only
+ * invisible entries.
+ */
+bool menu_is_empty(struct menu *menu)
+{
+ struct menu *child;
+
+ for (child = menu->list; child; child = child->next) {
+ if (menu_is_visible(child))
+ return(false);
+ }
+ return(true);
+}
+
+bool menu_is_visible(struct menu *menu)
+{
+ struct menu *child;
+ struct symbol *sym;
+ tristate visible;
+
+ if (!menu->prompt)
+ return false;
+
+ if (menu->visibility) {
+ if (expr_calc_value(menu->visibility) == no)
+ return no;
+ }
+
+ sym = menu->sym;
+ if (sym) {
+ sym_calc_value(sym);
+ visible = menu->prompt->visible.tri;
+ } else
+ visible = menu->prompt->visible.tri = expr_calc_value(menu->prompt->visible.expr);
+
+ if (visible != no)
+ return true;
+
+ if (!sym || sym_get_tristate_value(menu->sym) == no)
+ return false;
+
+ for (child = menu->list; child; child = child->next) {
+ if (menu_is_visible(child)) {
+ if (sym)
+ sym->flags |= SYMBOL_DEF_USER;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+const char *menu_get_prompt(struct menu *menu)
+{
+ if (menu->prompt)
+ return menu->prompt->text;
+ else if (menu->sym)
+ return menu->sym->name;
+ return NULL;
+}
+
+struct menu *menu_get_root_menu(struct menu *menu)
+{
+ return &rootmenu;
+}
+
+struct menu *menu_get_parent_menu(struct menu *menu)
+{
+ enum prop_type type;
+
+ for (; menu != &rootmenu; menu = menu->parent) {
+ type = menu->prompt ? menu->prompt->type : 0;
+ if (type == P_MENU)
+ break;
+ }
+ return menu;
+}
+
+bool menu_has_help(struct menu *menu)
+{
+ return menu->help != NULL;
+}
+
+const char *menu_get_help(struct menu *menu)
+{
+ if (menu->help)
+ return menu->help;
+ else
+ return "";
+}
+
+static void get_prompt_str(struct gstr *r, struct property *prop,
+ struct list_head *head)
+{
+ int i, j;
+ struct menu *submenu[8], *menu, *location = NULL;
+ struct jump_key *jump = NULL;
+
+ str_printf(r, _("Prompt: %s\n"), _(prop->text));
+ menu = prop->menu->parent;
+ for (i = 0; menu != &rootmenu && i < 8; menu = menu->parent) {
+ bool accessible = menu_is_visible(menu);
+
+ submenu[i++] = menu;
+ if (location == NULL && accessible)
+ location = menu;
+ }
+ if (head && location) {
+ jump = xmalloc(sizeof(struct jump_key));
+
+ if (menu_is_visible(prop->menu)) {
+ /*
+ * There is not enough room to put the hint at the
+ * beginning of the "Prompt" line. Put the hint on the
+ * last "Location" line even when it would belong on
+ * the former.
+ */
+ jump->target = prop->menu;
+ } else
+ jump->target = location;
+
+ if (list_empty(head))
+ jump->index = 0;
+ else
+ jump->index = list_entry(head->prev, struct jump_key,
+ entries)->index + 1;
+
+ list_add_tail(&jump->entries, head);
+ }
+
+ if (i > 0) {
+ str_printf(r, _(" Location:\n"));
+ for (j = 4; --i >= 0; j += 2) {
+ menu = submenu[i];
+ if (jump && menu == location)
+ jump->offset = strlen(r->s);
+ str_printf(r, "%*c-> %s", j, ' ',
+ _(menu_get_prompt(menu)));
+ if (menu->sym) {
+ str_printf(r, " (%s [=%s])", menu->sym->name ?
+ menu->sym->name : _("<choice>"),
+ sym_get_string_value(menu->sym));
+ }
+ str_append(r, "\n");
+ }
+ }
+}
+
+/*
+ * get property of type P_SYMBOL
+ */
+static struct property *get_symbol_prop(struct symbol *sym)
+{
+ struct property *prop = NULL;
+
+ for_all_properties(sym, prop, P_SYMBOL)
+ break;
+ return prop;
+}
+
+/*
+ * head is optional and may be NULL
+ */
+static void get_symbol_str(struct gstr *r, struct symbol *sym,
+ struct list_head *head)
+{
+ bool hit;
+ struct property *prop;
+
+ if (sym && sym->name) {
+ str_printf(r, "Symbol: %s [=%s]\n", sym->name,
+ sym_get_string_value(sym));
+ str_printf(r, "Type : %s\n", sym_type_name(sym->type));
+ if (sym->type == S_INT || sym->type == S_HEX) {
+ prop = sym_get_range_prop(sym);
+ if (prop) {
+ str_printf(r, "Range : ");
+ expr_gstr_print(prop->expr, r);
+ str_append(r, "\n");
+ }
+ }
+ }
+ for_all_prompts(sym, prop)
+ get_prompt_str(r, prop, head);
+
+ prop = get_symbol_prop(sym);
+ if (prop) {
+ str_printf(r, _(" Defined at %s:%d\n"), prop->menu->file->name,
+ prop->menu->lineno);
+ if (!expr_is_yes(prop->visible.expr)) {
+ str_append(r, _(" Depends on: "));
+ expr_gstr_print(prop->visible.expr, r);
+ str_append(r, "\n");
+ }
+ }
+
+ hit = false;
+ for_all_properties(sym, prop, P_SELECT) {
+ if (!hit) {
+ str_append(r, " Selects: ");
+ hit = true;
+ } else
+ str_printf(r, " && ");
+ expr_gstr_print(prop->expr, r);
+ }
+ if (hit)
+ str_append(r, "\n");
+ if (sym->rev_dep.expr) {
+ str_append(r, _(" Selected by: "));
+ expr_gstr_print(sym->rev_dep.expr, r);
+ str_append(r, "\n");
+ }
+ str_append(r, "\n\n");
+}
+
+struct gstr get_relations_str(struct symbol **sym_arr, struct list_head *head)
+{
+ struct symbol *sym;
+ struct gstr res = str_new();
+ int i;
+
+ for (i = 0; sym_arr && (sym = sym_arr[i]); i++)
+ get_symbol_str(&res, sym, head);
+ if (!i)
+ str_append(&res, _("No matches found.\n"));
+ return res;
+}
+
+
+void menu_get_ext_help(struct menu *menu, struct gstr *help)
+{
+ struct symbol *sym = menu->sym;
+ const char *help_text = nohelp_text;
+
+ if (menu_has_help(menu)) {
+ if (sym->name)
+ str_printf(help, "%s%s:\n\n", CONFIG_, sym->name);
+ help_text = menu_get_help(menu);
+ }
+ str_printf(help, "%s\n", _(help_text));
+ if (sym)
+ get_symbol_str(help, sym, NULL);
+}
diff --git a/kconf/symbol.c b/kconf/symbol.c
new file mode 100644
index 0000000..70c5ee1
--- /dev/null
+++ b/kconf/symbol.c
@@ -0,0 +1,1377 @@
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <regex.h>
+#include <sys/utsname.h>
+
+#include "lkc.h"
+
+struct symbol symbol_yes = {
+ .name = "y",
+ .curr = { "y", yes },
+ .flags = SYMBOL_CONST|SYMBOL_VALID,
+}, symbol_mod = {
+ .name = "m",
+ .curr = { "m", mod },
+ .flags = SYMBOL_CONST|SYMBOL_VALID,
+}, symbol_no = {
+ .name = "n",
+ .curr = { "n", no },
+ .flags = SYMBOL_CONST|SYMBOL_VALID,
+}, symbol_empty = {
+ .name = "",
+ .curr = { "", no },
+ .flags = SYMBOL_VALID,
+};
+
+struct symbol *sym_defconfig_list;
+struct symbol *modules_sym;
+tristate modules_val;
+
+struct expr *sym_env_list;
+
+static void sym_add_default(struct symbol *sym, const char *def)
+{
+ struct property *prop = prop_alloc(P_DEFAULT, sym);
+
+ prop->expr = expr_alloc_symbol(sym_lookup(def, SYMBOL_CONST));
+}
+
+void sym_init(void)
+{
+ struct symbol *sym;
+ struct utsname uts;
+ static bool inited = false;
+
+ if (inited)
+ return;
+ inited = true;
+
+ uname(&uts);
+
+ sym = sym_lookup("UNAME_RELEASE", 0);
+ sym->type = S_STRING;
+ sym->flags |= SYMBOL_AUTO;
+ sym_add_default(sym, uts.release);
+}
+
+enum symbol_type sym_get_type(struct symbol *sym)
+{
+ enum symbol_type type = sym->type;
+
+ if (type == S_TRISTATE) {
+ if (sym_is_choice_value(sym) && sym->visible == yes)
+ type = S_BOOLEAN;
+ else if (modules_val == no)
+ type = S_BOOLEAN;
+ }
+ return type;
+}
+
+const char *sym_type_name(enum symbol_type type)
+{
+ switch (type) {
+ case S_BOOLEAN:
+ return "boolean";
+ case S_TRISTATE:
+ return "tristate";
+ case S_INT:
+ return "integer";
+ case S_HEX:
+ return "hex";
+ case S_STRING:
+ return "string";
+ case S_UNKNOWN:
+ return "unknown";
+ case S_OTHER:
+ break;
+ }
+ return "???";
+}
+
+struct property *sym_get_choice_prop(struct symbol *sym)
+{
+ struct property *prop;
+
+ for_all_choices(sym, prop)
+ return prop;
+ return NULL;
+}
+
+struct property *sym_get_env_prop(struct symbol *sym)
+{
+ struct property *prop;
+
+ for_all_properties(sym, prop, P_ENV)
+ return prop;
+ return NULL;
+}
+
+static struct property *sym_get_default_prop(struct symbol *sym)
+{
+ struct property *prop;
+
+ for_all_defaults(sym, prop) {
+ prop->visible.tri = expr_calc_value(prop->visible.expr);
+ if (prop->visible.tri != no)
+ return prop;
+ }
+ return NULL;
+}
+
+static struct property *sym_get_range_prop(struct symbol *sym)
+{
+ struct property *prop;
+
+ for_all_properties(sym, prop, P_RANGE) {
+ prop->visible.tri = expr_calc_value(prop->visible.expr);
+ if (prop->visible.tri != no)
+ return prop;
+ }
+ return NULL;
+}
+
+static long long sym_get_range_val(struct symbol *sym, int base)
+{
+ sym_calc_value(sym);
+ switch (sym->type) {
+ case S_INT:
+ base = 10;
+ break;
+ case S_HEX:
+ base = 16;
+ break;
+ default:
+ break;
+ }
+ return strtoll(sym->curr.val, NULL, base);
+}
+
+static void sym_validate_range(struct symbol *sym)
+{
+ struct property *prop;
+ int base;
+ long long val, val2;
+ char str[64];
+
+ switch (sym->type) {
+ case S_INT:
+ base = 10;
+ break;
+ case S_HEX:
+ base = 16;
+ break;
+ default:
+ return;
+ }
+ prop = sym_get_range_prop(sym);
+ if (!prop)
+ return;
+ val = strtoll(sym->curr.val, NULL, base);
+ val2 = sym_get_range_val(prop->expr->left.sym, base);
+ if (val >= val2) {
+ val2 = sym_get_range_val(prop->expr->right.sym, base);
+ if (val <= val2)
+ return;
+ }
+ if (sym->type == S_INT)
+ sprintf(str, "%lld", val2);
+ else
+ sprintf(str, "0x%llx", val2);
+ sym->curr.val = strdup(str);
+}
+
+static void sym_set_changed(struct symbol *sym)
+{
+ struct property *prop;
+
+ sym->flags |= SYMBOL_CHANGED;
+ for (prop = sym->prop; prop; prop = prop->next) {
+ if (prop->menu)
+ prop->menu->flags |= MENU_CHANGED;
+ }
+}
+
+static void sym_set_all_changed(void)
+{
+ struct symbol *sym;
+ int i;
+
+ for_all_symbols(i, sym)
+ sym_set_changed(sym);
+}
+
+static void sym_calc_visibility(struct symbol *sym)
+{
+ struct property *prop;
+ tristate tri;
+
+ /* any prompt visible? */
+ tri = no;
+ for_all_prompts(sym, prop) {
+ prop->visible.tri = expr_calc_value(prop->visible.expr);
+ tri = EXPR_OR(tri, prop->visible.tri);
+ }
+ if (tri == mod && (sym->type != S_TRISTATE || modules_val == no))
+ tri = yes;
+ if (sym->visible != tri) {
+ sym->visible = tri;
+ sym_set_changed(sym);
+ }
+ if (sym_is_choice_value(sym))
+ return;
+ /* defaulting to "yes" if no explicit "depends on" are given */
+ tri = yes;
+ if (sym->dir_dep.expr)
+ tri = expr_calc_value(sym->dir_dep.expr);
+ if (tri == mod)
+ tri = yes;
+ if (sym->dir_dep.tri != tri) {
+ sym->dir_dep.tri = tri;
+ sym_set_changed(sym);
+ }
+ tri = no;
+ if (sym->rev_dep.expr)
+ tri = expr_calc_value(sym->rev_dep.expr);
+ if (tri == mod && sym_get_type(sym) == S_BOOLEAN)
+ tri = yes;
+ if (sym->rev_dep.tri != tri) {
+ sym->rev_dep.tri = tri;
+ sym_set_changed(sym);
+ }
+}
+
+/*
+ * Find the default symbol for a choice.
+ * First try the default values for the choice symbol
+ * Next locate the first visible choice value
+ * Return NULL if none was found
+ */
+struct symbol *sym_choice_default(struct symbol *sym)
+{
+ struct symbol *def_sym;
+ struct property *prop;
+ struct expr *e;
+
+ /* any of the defaults visible? */
+ for_all_defaults(sym, prop) {
+ prop->visible.tri = expr_calc_value(prop->visible.expr);
+ if (prop->visible.tri == no)
+ continue;
+ def_sym = prop_get_symbol(prop);
+ if (def_sym->visible != no)
+ return def_sym;
+ }
+
+ /* just get the first visible value */
+ prop = sym_get_choice_prop(sym);
+ expr_list_for_each_sym(prop->expr, e, def_sym)
+ if (def_sym->visible != no)
+ return def_sym;
+
+ /* failed to locate any defaults */
+ return NULL;
+}
+
+static struct symbol *sym_calc_choice(struct symbol *sym)
+{
+ struct symbol *def_sym;
+ struct property *prop;
+ struct expr *e;
+ int flags;
+
+ /* first calculate all choice values' visibilities */
+ flags = sym->flags;
+ prop = sym_get_choice_prop(sym);
+ expr_list_for_each_sym(prop->expr, e, def_sym) {
+ sym_calc_visibility(def_sym);
+ if (def_sym->visible != no)
+ flags &= def_sym->flags;
+ }
+
+ sym->flags &= flags | ~SYMBOL_DEF_USER;
+
+ /* is the user choice visible? */
+ def_sym = sym->def[S_DEF_USER].val;
+ if (def_sym && def_sym->visible != no)
+ return def_sym;
+
+ def_sym = sym_choice_default(sym);
+
+ if (def_sym == NULL)
+ /* no choice? reset tristate value */
+ sym->curr.tri = no;
+
+ return def_sym;
+}
+
+void sym_calc_value(struct symbol *sym)
+{
+ struct symbol_value newval, oldval;
+ struct property *prop;
+ struct expr *e;
+
+ if (!sym)
+ return;
+
+ if (sym->flags & SYMBOL_VALID)
+ return;
+
+ if (sym_is_choice_value(sym) &&
+ sym->flags & SYMBOL_NEED_SET_CHOICE_VALUES) {
+ sym->flags &= ~SYMBOL_NEED_SET_CHOICE_VALUES;
+ prop = sym_get_choice_prop(sym);
+ sym_calc_value(prop_get_symbol(prop));
+ }
+
+ sym->flags |= SYMBOL_VALID;
+
+ oldval = sym->curr;
+
+ switch (sym->type) {
+ case S_INT:
+ case S_HEX:
+ case S_STRING:
+ newval = symbol_empty.curr;
+ break;
+ case S_BOOLEAN:
+ case S_TRISTATE:
+ newval = symbol_no.curr;
+ break;
+ default:
+ sym->curr.val = sym->name;
+ sym->curr.tri = no;
+ return;
+ }
+ if (!sym_is_choice_value(sym))
+ sym->flags &= ~SYMBOL_WRITE;
+
+ sym_calc_visibility(sym);
+
+ /* set default if recursively called */
+ sym->curr = newval;
+
+ switch (sym_get_type(sym)) {
+ case S_BOOLEAN:
+ case S_TRISTATE:
+ if (sym_is_choice_value(sym) && sym->visible == yes) {
+ prop = sym_get_choice_prop(sym);
+ newval.tri = (prop_get_symbol(prop)->curr.val == sym) ? yes : no;
+ } else {
+ if (sym->visible != no) {
+ /* if the symbol is visible use the user value
+ * if available, otherwise try the default value
+ */
+ sym->flags |= SYMBOL_WRITE;
+ if (sym_has_value(sym)) {
+ newval.tri = EXPR_AND(sym->def[S_DEF_USER].tri,
+ sym->visible);
+ goto calc_newval;
+ }
+ }
+ if (sym->rev_dep.tri != no)
+ sym->flags |= SYMBOL_WRITE;
+ if (!sym_is_choice(sym)) {
+ prop = sym_get_default_prop(sym);
+ if (prop) {
+ sym->flags |= SYMBOL_WRITE;
+ newval.tri = EXPR_AND(expr_calc_value(prop->expr),
+ prop->visible.tri);
+ }
+ }
+ calc_newval:
+ if (sym->dir_dep.tri == no && sym->rev_dep.tri != no) {
+ struct expr *e;
+ e = expr_simplify_unmet_dep(sym->rev_dep.expr,
+ sym->dir_dep.expr);
+ fprintf(stderr, "warning: (");
+ expr_fprint(e, stderr);
+ fprintf(stderr, ") selects %s which has unmet direct dependencies (",
+ sym->name);
+ expr_fprint(sym->dir_dep.expr, stderr);
+ fprintf(stderr, ")\n");
+ expr_free(e);
+ }
+ newval.tri = EXPR_OR(newval.tri, sym->rev_dep.tri);
+ }
+ if (newval.tri == mod && sym_get_type(sym) == S_BOOLEAN)
+ newval.tri = yes;
+ break;
+ case S_STRING:
+ case S_HEX:
+ case S_INT:
+ if (sym->visible != no) {
+ sym->flags |= SYMBOL_WRITE;
+ if (sym_has_value(sym)) {
+ newval.val = sym->def[S_DEF_USER].val;
+ break;
+ }
+ }
+ prop = sym_get_default_prop(sym);
+ if (prop) {
+ struct symbol *ds = prop_get_symbol(prop);
+ if (ds) {
+ sym->flags |= SYMBOL_WRITE;
+ sym_calc_value(ds);
+ newval.val = ds->curr.val;
+ }
+ }
+ break;
+ default:
+ ;
+ }
+
+ sym->curr = newval;
+ if (sym_is_choice(sym) && newval.tri == yes)
+ sym->curr.val = sym_calc_choice(sym);
+ sym_validate_range(sym);
+
+ if (memcmp(&oldval, &sym->curr, sizeof(oldval))) {
+ sym_set_changed(sym);
+ if (modules_sym == sym) {
+ sym_set_all_changed();
+ modules_val = modules_sym->curr.tri;
+ }
+ }
+
+ if (sym_is_choice(sym)) {
+ struct symbol *choice_sym;
+
+ prop = sym_get_choice_prop(sym);
+ expr_list_for_each_sym(prop->expr, e, choice_sym) {
+ if ((sym->flags & SYMBOL_WRITE) &&
+ choice_sym->visible != no)
+ choice_sym->flags |= SYMBOL_WRITE;
+ if (sym->flags & SYMBOL_CHANGED)
+ sym_set_changed(choice_sym);
+ }
+ }
+
+ if (sym->flags & SYMBOL_AUTO)
+ sym->flags &= ~SYMBOL_WRITE;
+
+ if (sym->flags & SYMBOL_NEED_SET_CHOICE_VALUES)
+ set_all_choice_values(sym);
+}
+
+void sym_clear_all_valid(void)
+{
+ struct symbol *sym;
+ int i;
+
+ for_all_symbols(i, sym)
+ sym->flags &= ~SYMBOL_VALID;
+ sym_add_change_count(1);
+ if (modules_sym)
+ sym_calc_value(modules_sym);
+}
+
+bool sym_tristate_within_range(struct symbol *sym, tristate val)
+{
+ int type = sym_get_type(sym);
+
+ if (sym->visible == no)
+ return false;
+
+ if (type != S_BOOLEAN && type != S_TRISTATE)
+ return false;
+
+ if (type == S_BOOLEAN && val == mod)
+ return false;
+ if (sym->visible <= sym->rev_dep.tri)
+ return false;
+ if (sym_is_choice_value(sym) && sym->visible == yes)
+ return val == yes;
+ return val >= sym->rev_dep.tri && val <= sym->visible;
+}
+
+bool sym_set_tristate_value(struct symbol *sym, tristate val)
+{
+ tristate oldval = sym_get_tristate_value(sym);
+
+ if (oldval != val && !sym_tristate_within_range(sym, val))
+ return false;
+
+ if (!(sym->flags & SYMBOL_DEF_USER)) {
+ sym->flags |= SYMBOL_DEF_USER;
+ sym_set_changed(sym);
+ }
+ /*
+ * setting a choice value also resets the new flag of the choice
+ * symbol and all other choice values.
+ */
+ if (sym_is_choice_value(sym) && val == yes) {
+ struct symbol *cs = prop_get_symbol(sym_get_choice_prop(sym));
+ struct property *prop;
+ struct expr *e;
+
+ cs->def[S_DEF_USER].val = sym;
+ cs->flags |= SYMBOL_DEF_USER;
+ prop = sym_get_choice_prop(cs);
+ for (e = prop->expr; e; e = e->left.expr) {
+ if (e->right.sym->visible != no)
+ e->right.sym->flags |= SYMBOL_DEF_USER;
+ }
+ }
+
+ sym->def[S_DEF_USER].tri = val;
+ if (oldval != val)
+ sym_clear_all_valid();
+
+ return true;
+}
+
+tristate sym_toggle_tristate_value(struct symbol *sym)
+{
+ tristate oldval, newval;
+
+ oldval = newval = sym_get_tristate_value(sym);
+ do {
+ switch (newval) {
+ case no:
+ newval = mod;
+ break;
+ case mod:
+ newval = yes;
+ break;
+ case yes:
+ newval = no;
+ break;
+ }
+ if (sym_set_tristate_value(sym, newval))
+ break;
+ } while (oldval != newval);
+ return newval;
+}
+
+bool sym_string_valid(struct symbol *sym, const char *str)
+{
+ signed char ch;
+
+ switch (sym->type) {
+ case S_STRING:
+ return true;
+ case S_INT:
+ ch = *str++;
+ if (ch == '-')
+ ch = *str++;
+ if (!isdigit(ch))
+ return false;
+ if (ch == '0' && *str != 0)
+ return false;
+ while ((ch = *str++)) {
+ if (!isdigit(ch))
+ return false;
+ }
+ return true;
+ case S_HEX:
+ if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X'))
+ str += 2;
+ ch = *str++;
+ do {
+ if (!isxdigit(ch))
+ return false;
+ } while ((ch = *str++));
+ return true;
+ case S_BOOLEAN:
+ case S_TRISTATE:
+ switch (str[0]) {
+ case 'y': case 'Y':
+ case 'm': case 'M':
+ case 'n': case 'N':
+ return true;
+ }
+ return false;
+ default:
+ return false;
+ }
+}
+
+bool sym_string_within_range(struct symbol *sym, const char *str)
+{
+ struct property *prop;
+ long long val;
+
+ switch (sym->type) {
+ case S_STRING:
+ return sym_string_valid(sym, str);
+ case S_INT:
+ if (!sym_string_valid(sym, str))
+ return false;
+ prop = sym_get_range_prop(sym);
+ if (!prop)
+ return true;
+ val = strtoll(str, NULL, 10);
+ return val >= sym_get_range_val(prop->expr->left.sym, 10) &&
+ val <= sym_get_range_val(prop->expr->right.sym, 10);
+ case S_HEX:
+ if (!sym_string_valid(sym, str))
+ return false;
+ prop = sym_get_range_prop(sym);
+ if (!prop)
+ return true;
+ val = strtoll(str, NULL, 16);
+ return val >= sym_get_range_val(prop->expr->left.sym, 16) &&
+ val <= sym_get_range_val(prop->expr->right.sym, 16);
+ case S_BOOLEAN:
+ case S_TRISTATE:
+ switch (str[0]) {
+ case 'y': case 'Y':
+ return sym_tristate_within_range(sym, yes);
+ case 'm': case 'M':
+ return sym_tristate_within_range(sym, mod);
+ case 'n': case 'N':
+ return sym_tristate_within_range(sym, no);
+ }
+ return false;
+ default:
+ return false;
+ }
+}
+
+bool sym_set_string_value(struct symbol *sym, const char *newval)
+{
+ const char *oldval;
+ char *val;
+ int size;
+
+ switch (sym->type) {
+ case S_BOOLEAN:
+ case S_TRISTATE:
+ switch (newval[0]) {
+ case 'y': case 'Y':
+ return sym_set_tristate_value(sym, yes);
+ case 'm': case 'M':
+ return sym_set_tristate_value(sym, mod);
+ case 'n': case 'N':
+ return sym_set_tristate_value(sym, no);
+ }
+ return false;
+ default:
+ ;
+ }
+
+ if (!sym_string_within_range(sym, newval))
+ return false;
+
+ if (!(sym->flags & SYMBOL_DEF_USER)) {
+ sym->flags |= SYMBOL_DEF_USER;
+ sym_set_changed(sym);
+ }
+
+ oldval = sym->def[S_DEF_USER].val;
+ size = strlen(newval) + 1;
+ if (sym->type == S_HEX && (newval[0] != '0' || (newval[1] != 'x' && newval[1] != 'X'))) {
+ size += 2;
+ sym->def[S_DEF_USER].val = val = xmalloc(size);
+ *val++ = '0';
+ *val++ = 'x';
+ } else if (!oldval || strcmp(oldval, newval))
+ sym->def[S_DEF_USER].val = val = xmalloc(size);
+ else
+ return true;
+
+ strcpy(val, newval);
+ free((void *)oldval);
+ sym_clear_all_valid();
+
+ return true;
+}
+
+/*
+ * Find the default value associated to a symbol.
+ * For tristate symbol handle the modules=n case
+ * in which case "m" becomes "y".
+ * If the symbol does not have any default then fallback
+ * to the fixed default values.
+ */
+const char *sym_get_string_default(struct symbol *sym)
+{
+ struct property *prop;
+ struct symbol *ds;
+ const char *str;
+ tristate val;
+
+ sym_calc_visibility(sym);
+ sym_calc_value(modules_sym);
+ val = symbol_no.curr.tri;
+ str = symbol_empty.curr.val;
+
+ /* If symbol has a default value look it up */
+ prop = sym_get_default_prop(sym);
+ if (prop != NULL) {
+ switch (sym->type) {
+ case S_BOOLEAN:
+ case S_TRISTATE:
+ /* The visibility may limit the value from yes => mod */
+ val = EXPR_AND(expr_calc_value(prop->expr), prop->visible.tri);
+ break;
+ default:
+ /*
+ * The following fails to handle the situation
+ * where a default value is further limited by
+ * the valid range.
+ */
+ ds = prop_get_symbol(prop);
+ if (ds != NULL) {
+ sym_calc_value(ds);
+ str = (const char *)ds->curr.val;
+ }
+ }
+ }
+
+ /* Handle select statements */
+ val = EXPR_OR(val, sym->rev_dep.tri);
+
+ /* transpose mod to yes if modules are not enabled */
+ if (val == mod)
+ if (!sym_is_choice_value(sym) && modules_sym->curr.tri == no)
+ val = yes;
+
+ /* transpose mod to yes if type is bool */
+ if (sym->type == S_BOOLEAN && val == mod)
+ val = yes;
+
+ switch (sym->type) {
+ case S_BOOLEAN:
+ case S_TRISTATE:
+ switch (val) {
+ case no: return "n";
+ case mod: return "m";
+ case yes: return "y";
+ }
+ case S_INT:
+ case S_HEX:
+ return str;
+ case S_STRING:
+ return str;
+ case S_OTHER:
+ case S_UNKNOWN:
+ break;
+ }
+ return "";
+}
+
+const char *sym_get_string_value(struct symbol *sym)
+{
+ tristate val;
+
+ switch (sym->type) {
+ case S_BOOLEAN:
+ case S_TRISTATE:
+ val = sym_get_tristate_value(sym);
+ switch (val) {
+ case no:
+ return "n";
+ case mod:
+ sym_calc_value(modules_sym);
+ return (modules_sym->curr.tri == no) ? "n" : "m";
+ case yes:
+ return "y";
+ }
+ break;
+ default:
+ ;
+ }
+ return (const char *)sym->curr.val;
+}
+
+bool sym_is_changable(struct symbol *sym)
+{
+ return sym->visible > sym->rev_dep.tri;
+}
+
+static unsigned strhash(const char *s)
+{
+ /* fnv32 hash */
+ unsigned hash = 2166136261U;
+ for (; *s; s++)
+ hash = (hash ^ *s) * 0x01000193;
+ return hash;
+}
+
+struct symbol *sym_lookup(const char *name, int flags)
+{
+ struct symbol *symbol;
+ char *new_name;
+ int hash;
+
+ if (name) {
+ if (name[0] && !name[1]) {
+ switch (name[0]) {
+ case 'y': return &symbol_yes;
+ case 'm': return &symbol_mod;
+ case 'n': return &symbol_no;
+ }
+ }
+ hash = strhash(name) % SYMBOL_HASHSIZE;
+
+ for (symbol = symbol_hash[hash]; symbol; symbol = symbol->next) {
+ if (symbol->name &&
+ !strcmp(symbol->name, name) &&
+ (flags ? symbol->flags & flags
+ : !(symbol->flags & (SYMBOL_CONST|SYMBOL_CHOICE))))
+ return symbol;
+ }
+ new_name = strdup(name);
+ } else {
+ new_name = NULL;
+ hash = 0;
+ }
+
+ symbol = xmalloc(sizeof(*symbol));
+ memset(symbol, 0, sizeof(*symbol));
+ symbol->name = new_name;
+ symbol->type = S_UNKNOWN;
+ symbol->flags |= flags;
+
+ symbol->next = symbol_hash[hash];
+ symbol_hash[hash] = symbol;
+
+ return symbol;
+}
+
+struct symbol *sym_find(const char *name)
+{
+ struct symbol *symbol = NULL;
+ int hash = 0;
+
+ if (!name)
+ return NULL;
+
+ if (name[0] && !name[1]) {
+ switch (name[0]) {
+ case 'y': return &symbol_yes;
+ case 'm': return &symbol_mod;
+ case 'n': return &symbol_no;
+ }
+ }
+ hash = strhash(name) % SYMBOL_HASHSIZE;
+
+ for (symbol = symbol_hash[hash]; symbol; symbol = symbol->next) {
+ if (symbol->name &&
+ !strcmp(symbol->name, name) &&
+ !(symbol->flags & SYMBOL_CONST))
+ break;
+ }
+
+ return symbol;
+}
+
+/*
+ * Expand symbol's names embedded in the string given in argument. Symbols'
+ * name to be expanded shall be prefixed by a '$'. Unknown symbol expands to
+ * the empty string.
+ */
+const char *sym_expand_string_value(const char *in)
+{
+ const char *src;
+ char *res;
+ size_t reslen;
+
+ reslen = strlen(in) + 1;
+ res = xmalloc(reslen);
+ res[0] = '\0';
+
+ while ((src = strchr(in, '$'))) {
+ char *p, name[SYMBOL_MAXLENGTH];
+ const char *symval = "";
+ struct symbol *sym;
+ size_t newlen;
+
+ strncat(res, in, src - in);
+ src++;
+
+ p = name;
+ while (isalnum(*src) || *src == '_')
+ *p++ = *src++;
+ *p = '\0';
+
+ sym = sym_find(name);
+ if (sym != NULL) {
+ sym_calc_value(sym);
+ symval = sym_get_string_value(sym);
+ }
+
+ newlen = strlen(res) + strlen(symval) + strlen(src) + 1;
+ if (newlen > reslen) {
+ reslen = newlen;
+ res = realloc(res, reslen);
+ }
+
+ strcat(res, symval);
+ in = src;
+ }
+ strcat(res, in);
+
+ return res;
+}
+
+const char *sym_escape_string_value(const char *in)
+{
+ const char *p;
+ size_t reslen;
+ char *res;
+ size_t l;
+
+ reslen = strlen(in) + strlen("\"\"") + 1;
+
+ p = in;
+ for (;;) {
+ l = strcspn(p, "\"\\");
+ p += l;
+
+ if (p[0] == '\0')
+ break;
+
+ reslen++;
+ p++;
+ }
+
+ res = xmalloc(reslen);
+ res[0] = '\0';
+
+ strcat(res, "\"");
+
+ p = in;
+ for (;;) {
+ l = strcspn(p, "\"\\");
+ strncat(res, p, l);
+ p += l;
+
+ if (p[0] == '\0')
+ break;
+
+ strcat(res, "\\");
+ strncat(res, p++, 1);
+ }
+
+ strcat(res, "\"");
+ return res;
+}
+
+struct sym_match {
+ struct symbol *sym;
+ off_t so, eo;
+};
+
+/* Compare matched symbols as thus:
+ * - first, symbols that match exactly
+ * - then, alphabetical sort
+ */
+static int sym_rel_comp(const void *sym1, const void *sym2)
+{
+ const struct sym_match *s1 = sym1;
+ const struct sym_match *s2 = sym2;
+ int exact1, exact2;
+
+ /* Exact match:
+ * - if matched length on symbol s1 is the length of that symbol,
+ * then this symbol should come first;
+ * - if matched length on symbol s2 is the length of that symbol,
+ * then this symbol should come first.
+ * Note: since the search can be a regexp, both symbols may match
+ * exactly; if this is the case, we can't decide which comes first,
+ * and we fallback to sorting alphabetically.
+ */
+ exact1 = (s1->eo - s1->so) == strlen(s1->sym->name);
+ exact2 = (s2->eo - s2->so) == strlen(s2->sym->name);
+ if (exact1 && !exact2)
+ return -1;
+ if (!exact1 && exact2)
+ return 1;
+
+ /* As a fallback, sort symbols alphabetically */
+ return strcmp(s1->sym->name, s2->sym->name);
+}
+
+struct symbol **sym_re_search(const char *pattern)
+{
+ struct symbol *sym, **sym_arr = NULL;
+ struct sym_match *sym_match_arr = NULL;
+ int i, cnt, size;
+ regex_t re;
+ regmatch_t match[1];
+
+ cnt = size = 0;
+ /* Skip if empty */
+ if (strlen(pattern) == 0)
+ return NULL;
+ if (regcomp(&re, pattern, REG_EXTENDED|REG_ICASE))
+ return NULL;
+
+ for_all_symbols(i, sym) {
+ if (sym->flags & SYMBOL_CONST || !sym->name)
+ continue;
+ if (regexec(&re, sym->name, 1, match, 0))
+ continue;
+ if (cnt >= size) {
+ void *tmp;
+ size += 16;
+ tmp = realloc(sym_match_arr, size * sizeof(struct sym_match));
+ if (!tmp)
+ goto sym_re_search_free;
+ sym_match_arr = tmp;
+ }
+ sym_calc_value(sym);
+ /* As regexec returned 0, we know we have a match, so
+ * we can use match[0].rm_[se]o without further checks
+ */
+ sym_match_arr[cnt].so = match[0].rm_so;
+ sym_match_arr[cnt].eo = match[0].rm_eo;
+ sym_match_arr[cnt++].sym = sym;
+ }
+ if (sym_match_arr) {
+ qsort(sym_match_arr, cnt, sizeof(struct sym_match), sym_rel_comp);
+ sym_arr = malloc((cnt+1) * sizeof(struct symbol));
+ if (!sym_arr)
+ goto sym_re_search_free;
+ for (i = 0; i < cnt; i++)
+ sym_arr[i] = sym_match_arr[i].sym;
+ sym_arr[cnt] = NULL;
+ }
+sym_re_search_free:
+ /* sym_match_arr can be NULL if no match, but free(NULL) is OK */
+ free(sym_match_arr);
+ regfree(&re);
+
+ return sym_arr;
+}
+
+/*
+ * When we check for recursive dependencies we use a stack to save
+ * current state so we can print out relevant info to user.
+ * The entries are located on the call stack so no need to free memory.
+ * Note insert() remove() must always match to properly clear the stack.
+ */
+static struct dep_stack {
+ struct dep_stack *prev, *next;
+ struct symbol *sym;
+ struct property *prop;
+ struct expr *expr;
+} *check_top;
+
+static void dep_stack_insert(struct dep_stack *stack, struct symbol *sym)
+{
+ memset(stack, 0, sizeof(*stack));
+ if (check_top)
+ check_top->next = stack;
+ stack->prev = check_top;
+ stack->sym = sym;
+ check_top = stack;
+}
+
+static void dep_stack_remove(void)
+{
+ check_top = check_top->prev;
+ if (check_top)
+ check_top->next = NULL;
+}
+
+/*
+ * Called when we have detected a recursive dependency.
+ * check_top point to the top of the stact so we use
+ * the ->prev pointer to locate the bottom of the stack.
+ */
+static void sym_check_print_recursive(struct symbol *last_sym)
+{
+ struct dep_stack *stack;
+ struct symbol *sym, *next_sym;
+ struct menu *menu = NULL;
+ struct property *prop;
+ struct dep_stack cv_stack;
+
+ if (sym_is_choice_value(last_sym)) {
+ dep_stack_insert(&cv_stack, last_sym);
+ last_sym = prop_get_symbol(sym_get_choice_prop(last_sym));
+ }
+
+ for (stack = check_top; stack != NULL; stack = stack->prev)
+ if (stack->sym == last_sym)
+ break;
+ if (!stack) {
+ fprintf(stderr, "unexpected recursive dependency error\n");
+ return;
+ }
+
+ for (; stack; stack = stack->next) {
+ sym = stack->sym;
+ next_sym = stack->next ? stack->next->sym : last_sym;
+ prop = stack->prop;
+ if (prop == NULL)
+ prop = stack->sym->prop;
+
+ /* for choice values find the menu entry (used below) */
+ if (sym_is_choice(sym) || sym_is_choice_value(sym)) {
+ for (prop = sym->prop; prop; prop = prop->next) {
+ menu = prop->menu;
+ if (prop->menu)
+ break;
+ }
+ }
+ if (stack->sym == last_sym)
+ fprintf(stderr, "%s:%d:error: recursive dependency detected!\n",
+ prop->file->name, prop->lineno);
+ if (stack->expr) {
+ fprintf(stderr, "%s:%d:\tsymbol %s %s value contains %s\n",
+ prop->file->name, prop->lineno,
+ sym->name ? sym->name : "<choice>",
+ prop_get_type_name(prop->type),
+ next_sym->name ? next_sym->name : "<choice>");
+ } else if (stack->prop) {
+ fprintf(stderr, "%s:%d:\tsymbol %s depends on %s\n",
+ prop->file->name, prop->lineno,
+ sym->name ? sym->name : "<choice>",
+ next_sym->name ? next_sym->name : "<choice>");
+ } else if (sym_is_choice(sym)) {
+ fprintf(stderr, "%s:%d:\tchoice %s contains symbol %s\n",
+ menu->file->name, menu->lineno,
+ sym->name ? sym->name : "<choice>",
+ next_sym->name ? next_sym->name : "<choice>");
+ } else if (sym_is_choice_value(sym)) {
+ fprintf(stderr, "%s:%d:\tsymbol %s is part of choice %s\n",
+ menu->file->name, menu->lineno,
+ sym->name ? sym->name : "<choice>",
+ next_sym->name ? next_sym->name : "<choice>");
+ } else {
+ fprintf(stderr, "%s:%d:\tsymbol %s is selected by %s\n",
+ prop->file->name, prop->lineno,
+ sym->name ? sym->name : "<choice>",
+ next_sym->name ? next_sym->name : "<choice>");
+ }
+ }
+
+ if (check_top == &cv_stack)
+ dep_stack_remove();
+}
+
+static struct symbol *sym_check_expr_deps(struct expr *e)
+{
+ struct symbol *sym;
+
+ if (!e)
+ return NULL;
+ switch (e->type) {
+ case E_OR:
+ case E_AND:
+ sym = sym_check_expr_deps(e->left.expr);
+ if (sym)
+ return sym;
+ return sym_check_expr_deps(e->right.expr);
+ case E_NOT:
+ return sym_check_expr_deps(e->left.expr);
+ case E_EQUAL:
+ case E_GEQ:
+ case E_GTH:
+ case E_LEQ:
+ case E_LTH:
+ case E_UNEQUAL:
+ sym = sym_check_deps(e->left.sym);
+ if (sym)
+ return sym;
+ return sym_check_deps(e->right.sym);
+ case E_SYMBOL:
+ return sym_check_deps(e->left.sym);
+ default:
+ break;
+ }
+ printf("Oops! How to check %d?\n", e->type);
+ return NULL;
+}
+
+/* return NULL when dependencies are OK */
+static struct symbol *sym_check_sym_deps(struct symbol *sym)
+{
+ struct symbol *sym2;
+ struct property *prop;
+ struct dep_stack stack;
+
+ dep_stack_insert(&stack, sym);
+
+ sym2 = sym_check_expr_deps(sym->rev_dep.expr);
+ if (sym2)
+ goto out;
+
+ for (prop = sym->prop; prop; prop = prop->next) {
+ if (prop->type == P_CHOICE || prop->type == P_SELECT)
+ continue;
+ stack.prop = prop;
+ sym2 = sym_check_expr_deps(prop->visible.expr);
+ if (sym2)
+ break;
+ if (prop->type != P_DEFAULT || sym_is_choice(sym))
+ continue;
+ stack.expr = prop->expr;
+ sym2 = sym_check_expr_deps(prop->expr);
+ if (sym2)
+ break;
+ stack.expr = NULL;
+ }
+
+out:
+ dep_stack_remove();
+
+ return sym2;
+}
+
+static struct symbol *sym_check_choice_deps(struct symbol *choice)
+{
+ struct symbol *sym, *sym2;
+ struct property *prop;
+ struct expr *e;
+ struct dep_stack stack;
+
+ dep_stack_insert(&stack, choice);
+
+ prop = sym_get_choice_prop(choice);
+ expr_list_for_each_sym(prop->expr, e, sym)
+ sym->flags |= (SYMBOL_CHECK | SYMBOL_CHECKED);
+
+ choice->flags |= (SYMBOL_CHECK | SYMBOL_CHECKED);
+ sym2 = sym_check_sym_deps(choice);
+ choice->flags &= ~SYMBOL_CHECK;
+ if (sym2)
+ goto out;
+
+ expr_list_for_each_sym(prop->expr, e, sym) {
+ sym2 = sym_check_sym_deps(sym);
+ if (sym2)
+ break;
+ }
+out:
+ expr_list_for_each_sym(prop->expr, e, sym)
+ sym->flags &= ~SYMBOL_CHECK;
+
+ if (sym2 && sym_is_choice_value(sym2) &&
+ prop_get_symbol(sym_get_choice_prop(sym2)) == choice)
+ sym2 = choice;
+
+ dep_stack_remove();
+
+ return sym2;
+}
+
+struct symbol *sym_check_deps(struct symbol *sym)
+{
+ struct symbol *sym2;
+ struct property *prop;
+
+ if (sym->flags & SYMBOL_CHECK) {
+ sym_check_print_recursive(sym);
+ return sym;
+ }
+ if (sym->flags & SYMBOL_CHECKED)
+ return NULL;
+
+ if (sym_is_choice_value(sym)) {
+ struct dep_stack stack;
+
+ /* for choice groups start the check with main choice symbol */
+ dep_stack_insert(&stack, sym);
+ prop = sym_get_choice_prop(sym);
+ sym2 = sym_check_deps(prop_get_symbol(prop));
+ dep_stack_remove();
+ } else if (sym_is_choice(sym)) {
+ sym2 = sym_check_choice_deps(sym);
+ } else {
+ sym->flags |= (SYMBOL_CHECK | SYMBOL_CHECKED);
+ sym2 = sym_check_sym_deps(sym);
+ sym->flags &= ~SYMBOL_CHECK;
+ }
+
+ if (sym2 && sym2 == sym)
+ sym2 = NULL;
+
+ return sym2;
+}
+
+struct property *prop_alloc(enum prop_type type, struct symbol *sym)
+{
+ struct property *prop;
+ struct property **propp;
+
+ prop = xmalloc(sizeof(*prop));
+ memset(prop, 0, sizeof(*prop));
+ prop->type = type;
+ prop->sym = sym;
+ prop->file = current_file;
+ prop->lineno = zconf_lineno();
+
+ /* append property to the prop list of symbol */
+ if (sym) {
+ for (propp = &sym->prop; *propp; propp = &(*propp)->next)
+ ;
+ *propp = prop;
+ }
+
+ return prop;
+}
+
+struct symbol *prop_get_symbol(struct property *prop)
+{
+ if (prop->expr && (prop->expr->type == E_SYMBOL ||
+ prop->expr->type == E_LIST))
+ return prop->expr->left.sym;
+ return NULL;
+}
+
+const char *prop_get_type_name(enum prop_type type)
+{
+ switch (type) {
+ case P_PROMPT:
+ return "prompt";
+ case P_ENV:
+ return "env";
+ case P_COMMENT:
+ return "comment";
+ case P_MENU:
+ return "menu";
+ case P_DEFAULT:
+ return "default";
+ case P_CHOICE:
+ return "choice";
+ case P_SELECT:
+ return "select";
+ case P_RANGE:
+ return "range";
+ case P_SYMBOL:
+ return "symbol";
+ case P_UNKNOWN:
+ break;
+ }
+ return "unknown";
+}
+
+static void prop_add_env(const char *env)
+{
+ struct symbol *sym, *sym2;
+ struct property *prop;
+ char *p;
+
+ sym = current_entry->sym;
+ sym->flags |= SYMBOL_AUTO;
+ for_all_properties(sym, prop, P_ENV) {
+ sym2 = prop_get_symbol(prop);
+ if (strcmp(sym2->name, env))
+ menu_warn(current_entry, "redefining environment symbol from %s",
+ sym2->name);
+ return;
+ }
+
+ prop = prop_alloc(P_ENV, sym);
+ prop->expr = expr_alloc_symbol(sym_lookup(env, SYMBOL_CONST));
+
+ sym_env_list = expr_alloc_one(E_LIST, sym_env_list);
+ sym_env_list->right.sym = sym;
+
+ p = getenv(env);
+ if (p)
+ sym_add_default(sym, p);
+ else
+ menu_warn(current_entry, "environment variable %s undefined", env);
+}
diff --git a/kconf/util.c b/kconf/util.c
new file mode 100644
index 0000000..0e76042
--- /dev/null
+++ b/kconf/util.c
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2002-2005 Roman Zippel <zippel@linux-m68k.org>
+ * Copyright (C) 2002-2005 Sam Ravnborg <sam@ravnborg.org>
+ *
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include "lkc.h"
+
+/* file already present in list? If not add it */
+struct file *file_lookup(const char *name)
+{
+ struct file *file;
+ const char *file_name = sym_expand_string_value(name);
+
+ for (file = file_list; file; file = file->next) {
+ if (!strcmp(name, file->name)) {
+ free((void *)file_name);
+ return file;
+ }
+ }
+
+ file = xmalloc(sizeof(*file));
+ memset(file, 0, sizeof(*file));
+ file->name = file_name;
+ file->next = file_list;
+ file_list = file;
+ return file;
+}
+
+/* write a dependency file as used by kbuild to track dependencies */
+int file_write_dep(const char *name)
+{
+ struct symbol *sym, *env_sym;
+ struct expr *e;
+ struct file *file;
+ FILE *out;
+
+ if (!name)
+ name = ".kconfig.d";
+ out = fopen("..config.tmp", "w");
+ if (!out)
+ return 1;
+ fprintf(out, "deps_config := \\\n");
+ for (file = file_list; file; file = file->next) {
+ if (file->next)
+ fprintf(out, "\t%s \\\n", file->name);
+ else
+ fprintf(out, "\t%s\n", file->name);
+ }
+ fprintf(out, "\n%s: \\\n"
+ "\t$(deps_config)\n\n", conf_get_autoconfig_name());
+
+ expr_list_for_each_sym(sym_env_list, e, sym) {
+ struct property *prop;
+ const char *value;
+
+ prop = sym_get_env_prop(sym);
+ env_sym = prop_get_symbol(prop);
+ if (!env_sym)
+ continue;
+ value = getenv(env_sym->name);
+ if (!value)
+ value = "";
+ fprintf(out, "ifneq \"$(%s)\" \"%s\"\n", env_sym->name, value);
+ fprintf(out, "%s: FORCE\n", conf_get_autoconfig_name());
+ fprintf(out, "endif\n");
+ }
+
+ fprintf(out, "\n$(deps_config): ;\n");
+ fclose(out);
+ rename("..config.tmp", name);
+ return 0;
+}
+
+
+/* Allocate initial growable string */
+struct gstr str_new(void)
+{
+ struct gstr gs;
+ gs.s = xmalloc(sizeof(char) * 64);
+ gs.len = 64;
+ gs.max_width = 0;
+ strcpy(gs.s, "\0");
+ return gs;
+}
+
+/* Free storage for growable string */
+void str_free(struct gstr *gs)
+{
+ if (gs->s)
+ free(gs->s);
+ gs->s = NULL;
+ gs->len = 0;
+}
+
+/* Append to growable string */
+void str_append(struct gstr *gs, const char *s)
+{
+ size_t l;
+ if (s) {
+ l = strlen(gs->s) + strlen(s) + 1;
+ if (l > gs->len) {
+ gs->s = realloc(gs->s, l);
+ gs->len = l;
+ }
+ strcat(gs->s, s);
+ }
+}
+
+/* Append printf formatted string to growable string */
+void str_printf(struct gstr *gs, const char *fmt, ...)
+{
+ va_list ap;
+ char s[10000]; /* big enough... */
+ va_start(ap, fmt);
+ vsnprintf(s, sizeof(s), fmt, ap);
+ str_append(gs, s);
+ va_end(ap);
+}
+
+/* Retrieve value of growable string */
+const char *str_get(struct gstr *gs)
+{
+ return gs->s;
+}
+
+void *xmalloc(size_t size)
+{
+ void *p = malloc(size);
+ if (p)
+ return p;
+ fprintf(stderr, "Out of memory.\n");
+ exit(1);
+}
+
+void *xcalloc(size_t nmemb, size_t size)
+{
+ void *p = calloc(nmemb, size);
+ if (p)
+ return p;
+ fprintf(stderr, "Out of memory.\n");
+ exit(1);
+}
diff --git a/kconf/zconf.hash.c b/kconf/zconf.hash.c
new file mode 100644
index 0000000..c77a8ef
--- /dev/null
+++ b/kconf/zconf.hash.c
@@ -0,0 +1,289 @@
+/* ANSI-C code produced by gperf version 3.0.4 */
+/* Command-line: gperf -t --output-file scripts/kconfig/zconf.hash.c_shipped -a -C -E -g -k '1,3,$' -p -t scripts/kconfig/zconf.gperf */
+
+#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \
+ && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \
+ && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \
+ && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \
+ && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \
+ && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \
+ && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \
+ && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \
+ && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \
+ && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \
+ && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \
+ && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \
+ && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \
+ && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \
+ && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \
+ && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \
+ && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \
+ && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \
+ && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \
+ && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \
+ && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \
+ && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \
+ && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126))
+/* The character set is not based on ISO-646. */
+#error "gperf generated tables don't work with this execution character set. Please report a bug to <bug-gnu-gperf@gnu.org>."
+#endif
+
+#line 10 "scripts/kconfig/zconf.gperf"
+struct kconf_id;
+
+static const struct kconf_id *kconf_id_lookup(register const char *str, register unsigned int len);
+/* maximum key range = 71, duplicates = 0 */
+
+#ifdef __GNUC__
+__inline
+#else
+#ifdef __cplusplus
+inline
+#endif
+#endif
+static unsigned int
+kconf_id_hash (register const char *str, register unsigned int len)
+{
+ static const unsigned char asso_values[] =
+ {
+ 73, 73, 73, 73, 73, 73, 73, 73, 73, 73,
+ 73, 73, 73, 73, 73, 73, 73, 73, 73, 73,
+ 73, 73, 73, 73, 73, 73, 73, 73, 73, 73,
+ 73, 73, 73, 73, 73, 73, 73, 73, 73, 73,
+ 73, 73, 73, 73, 73, 73, 73, 73, 73, 73,
+ 73, 73, 73, 73, 73, 73, 73, 73, 73, 73,
+ 73, 73, 73, 73, 73, 73, 73, 73, 73, 73,
+ 73, 73, 73, 73, 73, 73, 73, 73, 73, 73,
+ 73, 73, 73, 73, 73, 73, 73, 73, 73, 73,
+ 73, 73, 73, 73, 73, 73, 73, 5, 25, 25,
+ 0, 0, 0, 5, 0, 0, 73, 73, 5, 0,
+ 10, 5, 45, 73, 20, 20, 0, 15, 15, 73,
+ 20, 5, 73, 73, 73, 73, 73, 73, 73, 73,
+ 73, 73, 73, 73, 73, 73, 73, 73, 73, 73,
+ 73, 73, 73, 73, 73, 73, 73, 73, 73, 73,
+ 73, 73, 73, 73, 73, 73, 73, 73, 73, 73,
+ 73, 73, 73, 73, 73, 73, 73, 73, 73, 73,
+ 73, 73, 73, 73, 73, 73, 73, 73, 73, 73,
+ 73, 73, 73, 73, 73, 73, 73, 73, 73, 73,
+ 73, 73, 73, 73, 73, 73, 73, 73, 73, 73,
+ 73, 73, 73, 73, 73, 73, 73, 73, 73, 73,
+ 73, 73, 73, 73, 73, 73, 73, 73, 73, 73,
+ 73, 73, 73, 73, 73, 73, 73, 73, 73, 73,
+ 73, 73, 73, 73, 73, 73, 73, 73, 73, 73,
+ 73, 73, 73, 73, 73, 73, 73, 73, 73, 73,
+ 73, 73, 73, 73, 73, 73
+ };
+ register int hval = len;
+
+ switch (hval)
+ {
+ default:
+ hval += asso_values[(unsigned char)str[2]];
+ /*FALLTHROUGH*/
+ case 2:
+ case 1:
+ hval += asso_values[(unsigned char)str[0]];
+ break;
+ }
+ return hval + asso_values[(unsigned char)str[len - 1]];
+}
+
+struct kconf_id_strings_t
+ {
+ char kconf_id_strings_str2[sizeof("if")];
+ char kconf_id_strings_str3[sizeof("int")];
+ char kconf_id_strings_str5[sizeof("endif")];
+ char kconf_id_strings_str7[sizeof("default")];
+ char kconf_id_strings_str8[sizeof("tristate")];
+ char kconf_id_strings_str9[sizeof("endchoice")];
+ char kconf_id_strings_str12[sizeof("def_tristate")];
+ char kconf_id_strings_str13[sizeof("def_bool")];
+ char kconf_id_strings_str14[sizeof("defconfig_list")];
+ char kconf_id_strings_str17[sizeof("on")];
+ char kconf_id_strings_str18[sizeof("optional")];
+ char kconf_id_strings_str21[sizeof("option")];
+ char kconf_id_strings_str22[sizeof("endmenu")];
+ char kconf_id_strings_str23[sizeof("mainmenu")];
+ char kconf_id_strings_str25[sizeof("menuconfig")];
+ char kconf_id_strings_str27[sizeof("modules")];
+ char kconf_id_strings_str28[sizeof("allnoconfig_y")];
+ char kconf_id_strings_str29[sizeof("menu")];
+ char kconf_id_strings_str31[sizeof("select")];
+ char kconf_id_strings_str32[sizeof("comment")];
+ char kconf_id_strings_str33[sizeof("env")];
+ char kconf_id_strings_str35[sizeof("range")];
+ char kconf_id_strings_str36[sizeof("choice")];
+ char kconf_id_strings_str39[sizeof("bool")];
+ char kconf_id_strings_str41[sizeof("source")];
+ char kconf_id_strings_str42[sizeof("visible")];
+ char kconf_id_strings_str43[sizeof("hex")];
+ char kconf_id_strings_str46[sizeof("config")];
+ char kconf_id_strings_str47[sizeof("boolean")];
+ char kconf_id_strings_str51[sizeof("string")];
+ char kconf_id_strings_str54[sizeof("help")];
+ char kconf_id_strings_str56[sizeof("prompt")];
+ char kconf_id_strings_str72[sizeof("depends")];
+ };
+static const struct kconf_id_strings_t kconf_id_strings_contents =
+ {
+ "if",
+ "int",
+ "endif",
+ "default",
+ "tristate",
+ "endchoice",
+ "def_tristate",
+ "def_bool",
+ "defconfig_list",
+ "on",
+ "optional",
+ "option",
+ "endmenu",
+ "mainmenu",
+ "menuconfig",
+ "modules",
+ "allnoconfig_y",
+ "menu",
+ "select",
+ "comment",
+ "env",
+ "range",
+ "choice",
+ "bool",
+ "source",
+ "visible",
+ "hex",
+ "config",
+ "boolean",
+ "string",
+ "help",
+ "prompt",
+ "depends"
+ };
+#define kconf_id_strings ((const char *) &kconf_id_strings_contents)
+#ifdef __GNUC__
+__inline
+#if defined __GNUC_STDC_INLINE__ || defined __GNUC_GNU_INLINE__
+__attribute__ ((__gnu_inline__))
+#endif
+#endif
+const struct kconf_id *
+kconf_id_lookup (register const char *str, register unsigned int len)
+{
+ enum
+ {
+ TOTAL_KEYWORDS = 33,
+ MIN_WORD_LENGTH = 2,
+ MAX_WORD_LENGTH = 14,
+ MIN_HASH_VALUE = 2,
+ MAX_HASH_VALUE = 72
+ };
+
+ static const struct kconf_id wordlist[] =
+ {
+ {-1}, {-1},
+#line 25 "scripts/kconfig/zconf.gperf"
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str2, T_IF, TF_COMMAND|TF_PARAM},
+#line 36 "scripts/kconfig/zconf.gperf"
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str3, T_TYPE, TF_COMMAND, S_INT},
+ {-1},
+#line 26 "scripts/kconfig/zconf.gperf"
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str5, T_ENDIF, TF_COMMAND},
+ {-1},
+#line 29 "scripts/kconfig/zconf.gperf"
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str7, T_DEFAULT, TF_COMMAND, S_UNKNOWN},
+#line 31 "scripts/kconfig/zconf.gperf"
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str8, T_TYPE, TF_COMMAND, S_TRISTATE},
+#line 20 "scripts/kconfig/zconf.gperf"
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str9, T_ENDCHOICE, TF_COMMAND},
+ {-1}, {-1},
+#line 32 "scripts/kconfig/zconf.gperf"
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str12, T_DEFAULT, TF_COMMAND, S_TRISTATE},
+#line 35 "scripts/kconfig/zconf.gperf"
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str13, T_DEFAULT, TF_COMMAND, S_BOOLEAN},
+#line 45 "scripts/kconfig/zconf.gperf"
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str14, T_OPT_DEFCONFIG_LIST,TF_OPTION},
+ {-1}, {-1},
+#line 43 "scripts/kconfig/zconf.gperf"
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str17, T_ON, TF_PARAM},
+#line 28 "scripts/kconfig/zconf.gperf"
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str18, T_OPTIONAL, TF_COMMAND},
+ {-1}, {-1},
+#line 42 "scripts/kconfig/zconf.gperf"
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str21, T_OPTION, TF_COMMAND},
+#line 17 "scripts/kconfig/zconf.gperf"
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str22, T_ENDMENU, TF_COMMAND},
+#line 15 "scripts/kconfig/zconf.gperf"
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str23, T_MAINMENU, TF_COMMAND},
+ {-1},
+#line 23 "scripts/kconfig/zconf.gperf"
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str25, T_MENUCONFIG, TF_COMMAND},
+ {-1},
+#line 44 "scripts/kconfig/zconf.gperf"
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str27, T_OPT_MODULES, TF_OPTION},
+#line 47 "scripts/kconfig/zconf.gperf"
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str28, T_OPT_ALLNOCONFIG_Y,TF_OPTION},
+#line 16 "scripts/kconfig/zconf.gperf"
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str29, T_MENU, TF_COMMAND},
+ {-1},
+#line 39 "scripts/kconfig/zconf.gperf"
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str31, T_SELECT, TF_COMMAND},
+#line 21 "scripts/kconfig/zconf.gperf"
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str32, T_COMMENT, TF_COMMAND},
+#line 46 "scripts/kconfig/zconf.gperf"
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str33, T_OPT_ENV, TF_OPTION},
+ {-1},
+#line 40 "scripts/kconfig/zconf.gperf"
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str35, T_RANGE, TF_COMMAND},
+#line 19 "scripts/kconfig/zconf.gperf"
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str36, T_CHOICE, TF_COMMAND},
+ {-1}, {-1},
+#line 33 "scripts/kconfig/zconf.gperf"
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str39, T_TYPE, TF_COMMAND, S_BOOLEAN},
+ {-1},
+#line 18 "scripts/kconfig/zconf.gperf"
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str41, T_SOURCE, TF_COMMAND},
+#line 41 "scripts/kconfig/zconf.gperf"
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str42, T_VISIBLE, TF_COMMAND},
+#line 37 "scripts/kconfig/zconf.gperf"
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str43, T_TYPE, TF_COMMAND, S_HEX},
+ {-1}, {-1},
+#line 22 "scripts/kconfig/zconf.gperf"
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str46, T_CONFIG, TF_COMMAND},
+#line 34 "scripts/kconfig/zconf.gperf"
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str47, T_TYPE, TF_COMMAND, S_BOOLEAN},
+ {-1}, {-1}, {-1},
+#line 38 "scripts/kconfig/zconf.gperf"
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str51, T_TYPE, TF_COMMAND, S_STRING},
+ {-1}, {-1},
+#line 24 "scripts/kconfig/zconf.gperf"
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str54, T_HELP, TF_COMMAND},
+ {-1},
+#line 30 "scripts/kconfig/zconf.gperf"
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str56, T_PROMPT, TF_COMMAND},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+#line 27 "scripts/kconfig/zconf.gperf"
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str72, T_DEPENDS, TF_COMMAND}
+ };
+
+ if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
+ {
+ register int key = kconf_id_hash (str, len);
+
+ if (key <= MAX_HASH_VALUE && key >= 0)
+ {
+ register int o = wordlist[key].name;
+ if (o >= 0)
+ {
+ register const char *s = o + kconf_id_strings;
+
+ if (*str == *s && !strncmp (str + 1, s + 1, len - 1) && s[len] == '\0')
+ return &wordlist[key];
+ }
+ }
+ }
+ return 0;
+}
+#line 48 "scripts/kconfig/zconf.gperf"
+
diff --git a/kconf/zconf.lex.c b/kconf/zconf.lex.c
new file mode 100644
index 0000000..3633b4c
--- /dev/null
+++ b/kconf/zconf.lex.c
@@ -0,0 +1,2476 @@
+
+#line 3 "scripts/kconfig/zconf.lex.c_shipped"
+
+#define YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+#define yy_create_buffer zconf_create_buffer
+#define yy_delete_buffer zconf_delete_buffer
+#define yy_flex_debug zconf_flex_debug
+#define yy_init_buffer zconf_init_buffer
+#define yy_flush_buffer zconf_flush_buffer
+#define yy_load_buffer_state zconf_load_buffer_state
+#define yy_switch_to_buffer zconf_switch_to_buffer
+#define yyin zconfin
+#define yyleng zconfleng
+#define yylex zconflex
+#define yylineno zconflineno
+#define yyout zconfout
+#define yyrestart zconfrestart
+#define yytext zconftext
+#define yywrap zconfwrap
+#define yyalloc zconfalloc
+#define yyrealloc zconfrealloc
+#define yyfree zconffree
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+#define YY_FLEX_SUBMINOR_VERSION 35
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+/* First, we deal with platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+
+/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
+ * if you want the limit (max/min) macros for int types.
+ */
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS 1
+#endif
+
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t;
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+#endif /* ! C99 */
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX (4294967295U)
+#endif
+
+#endif /* ! FLEXINT_H */
+
+#ifdef __cplusplus
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else /* ! __cplusplus */
+
+/* C99 requires __STDC__ to be defined as 1. */
+#if defined (__STDC__)
+
+#define YY_USE_CONST
+
+#endif /* defined (__STDC__) */
+#endif /* ! __cplusplus */
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an unsigned
+ * integer for use as an array index. If the signed char is negative,
+ * we want to instead treat it as an 8-bit unsigned char, hence the
+ * double cast.
+ */
+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
+
+/* Enter a start condition. This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN (yy_start) = 1 + 2 *
+
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state. The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START (((yy_start) - 1) / 2)
+#define YYSTATE YY_START
+
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE zconfrestart(zconfin )
+
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#define YY_BUF_SIZE 16384
+#endif
+
+/* The state buf must be large enough to hold one state per character in the main buffer.
+ */
+#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type))
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+extern int zconfleng;
+
+extern FILE *zconfin, *zconfout;
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+ #define YY_LESS_LINENO(n)
+
+/* Return all but the first "n" matched characters back to the input stream. */
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up zconftext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ *yy_cp = (yy_hold_char); \
+ YY_RESTORE_YY_MORE_OFFSET \
+ (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \
+ YY_DO_BEFORE_ACTION; /* set up zconftext again */ \
+ } \
+ while ( 0 )
+
+#define unput(c) yyunput( c, (yytext_ptr) )
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef size_t yy_size_t;
+#endif
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+ {
+ FILE *yy_input_file;
+
+ char *yy_ch_buf; /* input buffer */
+ char *yy_buf_pos; /* current position in input buffer */
+
+ /* Size of input buffer in bytes, not including room for EOB
+ * characters.
+ */
+ yy_size_t yy_buf_size;
+
+ /* Number of characters read into yy_ch_buf, not including EOB
+ * characters.
+ */
+ int yy_n_chars;
+
+ /* Whether we "own" the buffer - i.e., we know we created it,
+ * and can realloc() it to grow it, and should free() it to
+ * delete it.
+ */
+ int yy_is_our_buffer;
+
+ /* Whether this is an "interactive" input source; if so, and
+ * if we're using stdio for input, then we want to use getc()
+ * instead of fread(), to make sure we stop fetching input after
+ * each newline.
+ */
+ int yy_is_interactive;
+
+ /* Whether we're considered to be at the beginning of a line.
+ * If so, '^' rules will be active on the next match, otherwise
+ * not.
+ */
+ int yy_at_bol;
+
+ int yy_bs_lineno; /**< The line count. */
+ int yy_bs_column; /**< The column count. */
+
+ /* Whether to try to fill the input buffer when we reach the
+ * end of it.
+ */
+ int yy_fill_buffer;
+
+ int yy_buffer_status;
+
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+ /* When an EOF's been seen but there's still some text to process
+ * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+ * shouldn't try reading from the input source any more. We might
+ * still have a bunch of tokens to match, though, because of
+ * possible backing-up.
+ *
+ * When we actually see the EOF, we change the status to "new"
+ * (via zconfrestart()), so that the user can continue scanning by
+ * just pointing zconfin at a new input file.
+ */
+#define YY_BUFFER_EOF_PENDING 2
+
+ };
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+/* Stack of input buffers. */
+static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */
+static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */
+static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ *
+ * Returns the top of the stack, or NULL.
+ */
+#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \
+ ? (yy_buffer_stack)[(yy_buffer_stack_top)] \
+ : NULL)
+
+/* Same as previous macro, but useful when we know that the buffer stack is not
+ * NULL or when we need an lvalue. For internal use only.
+ */
+#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)]
+
+/* yy_hold_char holds the character lost when zconftext is formed. */
+static char yy_hold_char;
+static int yy_n_chars; /* number of characters read into yy_ch_buf */
+int zconfleng;
+
+/* Points to current character in buffer. */
+static char *yy_c_buf_p = (char *) 0;
+static int yy_init = 0; /* whether we need to initialize */
+static int yy_start = 0; /* start state number */
+
+/* Flag which is used to allow zconfwrap()'s to do buffer switches
+ * instead of setting up a fresh zconfin. A bit of a hack ...
+ */
+static int yy_did_buffer_switch_on_eof;
+
+void zconfrestart (FILE *input_file );
+void zconf_switch_to_buffer (YY_BUFFER_STATE new_buffer );
+YY_BUFFER_STATE zconf_create_buffer (FILE *file,int size );
+void zconf_delete_buffer (YY_BUFFER_STATE b );
+void zconf_flush_buffer (YY_BUFFER_STATE b );
+void zconfpush_buffer_state (YY_BUFFER_STATE new_buffer );
+void zconfpop_buffer_state (void );
+
+static void zconfensure_buffer_stack (void );
+static void zconf_load_buffer_state (void );
+static void zconf_init_buffer (YY_BUFFER_STATE b,FILE *file );
+
+#define YY_FLUSH_BUFFER zconf_flush_buffer(YY_CURRENT_BUFFER )
+
+YY_BUFFER_STATE zconf_scan_buffer (char *base,yy_size_t size );
+YY_BUFFER_STATE zconf_scan_string (yyconst char *yy_str );
+YY_BUFFER_STATE zconf_scan_bytes (yyconst char *bytes,int len );
+
+void *zconfalloc (yy_size_t );
+void *zconfrealloc (void *,yy_size_t );
+void zconffree (void * );
+
+#define yy_new_buffer zconf_create_buffer
+
+#define yy_set_interactive(is_interactive) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){ \
+ zconfensure_buffer_stack (); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ zconf_create_buffer(zconfin,YY_BUF_SIZE ); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
+ }
+
+#define yy_set_bol(at_bol) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){\
+ zconfensure_buffer_stack (); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ zconf_create_buffer(zconfin,YY_BUF_SIZE ); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
+ }
+
+#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
+
+/* Begin user sect3 */
+
+#define zconfwrap(n) 1
+#define YY_SKIP_YYWRAP
+
+typedef unsigned char YY_CHAR;
+
+FILE *zconfin = (FILE *) 0, *zconfout = (FILE *) 0;
+
+typedef int yy_state_type;
+
+extern int zconflineno;
+
+int zconflineno = 1;
+
+extern char *zconftext;
+#define yytext_ptr zconftext
+static yyconst flex_int16_t yy_nxt[][19] =
+ {
+ {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0
+ },
+
+ {
+ 11, 12, 13, 14, 12, 12, 15, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12, 12
+ },
+
+ {
+ 11, 12, 13, 14, 12, 12, 15, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12, 12
+ },
+
+ {
+ 11, 16, 16, 17, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 18, 16, 16, 16, 16, 16
+ },
+
+ {
+ 11, 16, 16, 17, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 18, 16, 16, 16, 16, 16
+
+ },
+
+ {
+ 11, 19, 20, 21, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19
+ },
+
+ {
+ 11, 19, 20, 21, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19
+ },
+
+ {
+ 11, 22, 22, 23, 22, 24, 22, 22, 24, 22,
+ 22, 22, 22, 22, 22, 22, 22, 25, 22
+ },
+
+ {
+ 11, 22, 22, 23, 22, 24, 22, 22, 24, 22,
+ 22, 22, 22, 22, 22, 22, 22, 25, 22
+ },
+
+ {
+ 11, 26, 27, 28, 29, 30, 31, 32, 30, 33,
+ 34, 35, 36, 36, 37, 38, 39, 40, 41
+
+ },
+
+ {
+ 11, 26, 27, 28, 29, 30, 31, 32, 30, 33,
+ 34, 35, 36, 36, 37, 38, 39, 40, 41
+ },
+
+ {
+ -11, -11, -11, -11, -11, -11, -11, -11, -11, -11,
+ -11, -11, -11, -11, -11, -11, -11, -11, -11
+ },
+
+ {
+ 11, -12, -12, -12, -12, -12, -12, -12, -12, -12,
+ -12, -12, -12, -12, -12, -12, -12, -12, -12
+ },
+
+ {
+ 11, -13, 42, 43, -13, -13, 44, -13, -13, -13,
+ -13, -13, -13, -13, -13, -13, -13, -13, -13
+ },
+
+ {
+ 11, -14, -14, -14, -14, -14, -14, -14, -14, -14,
+ -14, -14, -14, -14, -14, -14, -14, -14, -14
+
+ },
+
+ {
+ 11, 45, 45, 46, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45
+ },
+
+ {
+ 11, -16, -16, -16, -16, -16, -16, -16, -16, -16,
+ -16, -16, -16, -16, -16, -16, -16, -16, -16
+ },
+
+ {
+ 11, -17, -17, -17, -17, -17, -17, -17, -17, -17,
+ -17, -17, -17, -17, -17, -17, -17, -17, -17
+ },
+
+ {
+ 11, -18, -18, -18, -18, -18, -18, -18, -18, -18,
+ -18, -18, -18, 47, -18, -18, -18, -18, -18
+ },
+
+ {
+ 11, 48, 48, -19, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48, 48
+
+ },
+
+ {
+ 11, -20, 49, 50, -20, -20, -20, -20, -20, -20,
+ -20, -20, -20, -20, -20, -20, -20, -20, -20
+ },
+
+ {
+ 11, 51, -21, -21, 51, 51, 51, 51, 51, 51,
+ 51, 51, 51, 51, 51, 51, 51, 51, 51
+ },
+
+ {
+ 11, 52, 52, 53, 52, -22, 52, 52, -22, 52,
+ 52, 52, 52, 52, 52, 52, 52, -22, 52
+ },
+
+ {
+ 11, -23, -23, -23, -23, -23, -23, -23, -23, -23,
+ -23, -23, -23, -23, -23, -23, -23, -23, -23
+ },
+
+ {
+ 11, -24, -24, -24, -24, -24, -24, -24, -24, -24,
+ -24, -24, -24, -24, -24, -24, -24, -24, -24
+
+ },
+
+ {
+ 11, 54, 54, 55, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54
+ },
+
+ {
+ 11, -26, -26, -26, -26, -26, -26, -26, -26, -26,
+ -26, -26, -26, -26, -26, -26, -26, -26, -26
+ },
+
+ {
+ 11, -27, 56, -27, -27, -27, -27, -27, -27, -27,
+ -27, -27, -27, -27, -27, -27, -27, -27, -27
+ },
+
+ {
+ 11, -28, -28, -28, -28, -28, -28, -28, -28, -28,
+ -28, -28, -28, -28, -28, -28, -28, -28, -28
+ },
+
+ {
+ 11, -29, -29, -29, -29, -29, -29, -29, -29, -29,
+ -29, -29, -29, -29, -29, 57, -29, -29, -29
+
+ },
+
+ {
+ 11, -30, -30, -30, -30, -30, -30, -30, -30, -30,
+ -30, -30, -30, -30, -30, -30, -30, -30, -30
+ },
+
+ {
+ 11, 58, 58, -31, 58, 58, 58, 58, 58, 58,
+ 58, 58, 58, 58, 58, 58, 58, 58, 58
+ },
+
+ {
+ 11, -32, -32, -32, -32, -32, -32, 59, -32, -32,
+ -32, -32, -32, -32, -32, -32, -32, -32, -32
+ },
+
+ {
+ 11, -33, -33, -33, -33, -33, -33, -33, -33, -33,
+ -33, -33, -33, -33, -33, -33, -33, -33, -33
+ },
+
+ {
+ 11, -34, -34, -34, -34, -34, -34, -34, -34, -34,
+ -34, -34, -34, -34, -34, -34, -34, -34, -34
+
+ },
+
+ {
+ 11, -35, -35, -35, -35, -35, -35, -35, -35, -35,
+ -35, 60, 61, 61, -35, -35, -35, -35, -35
+ },
+
+ {
+ 11, -36, -36, -36, -36, -36, -36, -36, -36, -36,
+ -36, 61, 61, 61, -36, -36, -36, -36, -36
+ },
+
+ {
+ 11, -37, -37, -37, -37, -37, -37, -37, -37, -37,
+ -37, -37, -37, -37, -37, 62, -37, -37, -37
+ },
+
+ {
+ 11, -38, -38, -38, -38, -38, -38, -38, -38, -38,
+ -38, -38, -38, -38, -38, -38, -38, -38, -38
+ },
+
+ {
+ 11, -39, -39, -39, -39, -39, -39, -39, -39, -39,
+ -39, -39, -39, -39, -39, 63, -39, -39, -39
+
+ },
+
+ {
+ 11, -40, -40, 64, -40, -40, -40, -40, -40, -40,
+ -40, -40, -40, -40, -40, -40, -40, -40, -40
+ },
+
+ {
+ 11, -41, -41, -41, -41, -41, -41, -41, -41, -41,
+ -41, -41, -41, -41, -41, -41, -41, -41, 65
+ },
+
+ {
+ 11, -42, 42, 43, -42, -42, 44, -42, -42, -42,
+ -42, -42, -42, -42, -42, -42, -42, -42, -42
+ },
+
+ {
+ 11, -43, -43, -43, -43, -43, -43, -43, -43, -43,
+ -43, -43, -43, -43, -43, -43, -43, -43, -43
+ },
+
+ {
+ 11, 45, 45, 46, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45
+
+ },
+
+ {
+ 11, 45, 45, 46, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45
+ },
+
+ {
+ 11, -46, -46, -46, -46, -46, -46, -46, -46, -46,
+ -46, -46, -46, -46, -46, -46, -46, -46, -46
+ },
+
+ {
+ 11, -47, -47, -47, -47, -47, -47, -47, -47, -47,
+ -47, -47, -47, 47, -47, -47, -47, -47, -47
+ },
+
+ {
+ 11, 48, 48, -48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48, 48
+ },
+
+ {
+ 11, -49, 49, 50, -49, -49, -49, -49, -49, -49,
+ -49, -49, -49, -49, -49, -49, -49, -49, -49
+
+ },
+
+ {
+ 11, 51, -50, -50, 51, 51, 51, 51, 51, 51,
+ 51, 51, 51, 51, 51, 51, 51, 51, 51
+ },
+
+ {
+ 11, -51, -51, -51, -51, -51, -51, -51, -51, -51,
+ -51, -51, -51, -51, -51, -51, -51, -51, -51
+ },
+
+ {
+ 11, 52, 52, 53, 52, -52, 52, 52, -52, 52,
+ 52, 52, 52, 52, 52, 52, 52, -52, 52
+ },
+
+ {
+ 11, -53, -53, -53, -53, -53, -53, -53, -53, -53,
+ -53, -53, -53, -53, -53, -53, -53, -53, -53
+ },
+
+ {
+ 11, -54, -54, 55, -54, -54, -54, -54, -54, -54,
+ -54, -54, -54, -54, -54, -54, -54, -54, -54
+
+ },
+
+ {
+ 11, -55, -55, -55, -55, -55, -55, -55, -55, -55,
+ -55, -55, -55, -55, -55, -55, -55, -55, -55
+ },
+
+ {
+ 11, -56, 56, -56, -56, -56, -56, -56, -56, -56,
+ -56, -56, -56, -56, -56, -56, -56, -56, -56
+ },
+
+ {
+ 11, -57, -57, -57, -57, -57, -57, -57, -57, -57,
+ -57, -57, -57, -57, -57, -57, -57, -57, -57
+ },
+
+ {
+ 11, 58, 58, -58, 58, 58, 58, 58, 58, 58,
+ 58, 58, 58, 58, 58, 58, 58, 58, 58
+ },
+
+ {
+ 11, -59, -59, -59, -59, -59, -59, -59, -59, -59,
+ -59, -59, -59, -59, -59, -59, -59, -59, -59
+
+ },
+
+ {
+ 11, -60, -60, -60, -60, -60, -60, -60, -60, -60,
+ -60, 66, 61, 61, -60, -60, -60, -60, -60
+ },
+
+ {
+ 11, -61, -61, -61, -61, -61, -61, -61, -61, -61,
+ -61, 61, 61, 61, -61, -61, -61, -61, -61
+ },
+
+ {
+ 11, -62, -62, -62, -62, -62, -62, -62, -62, -62,
+ -62, -62, -62, -62, -62, -62, -62, -62, -62
+ },
+
+ {
+ 11, -63, -63, -63, -63, -63, -63, -63, -63, -63,
+ -63, -63, -63, -63, -63, -63, -63, -63, -63
+ },
+
+ {
+ 11, -64, -64, -64, -64, -64, -64, -64, -64, -64,
+ -64, -64, -64, -64, -64, -64, -64, -64, -64
+
+ },
+
+ {
+ 11, -65, -65, -65, -65, -65, -65, -65, -65, -65,
+ -65, -65, -65, -65, -65, -65, -65, -65, -65
+ },
+
+ {
+ 11, -66, -66, -66, -66, -66, -66, -66, -66, -66,
+ -66, 61, 61, 61, -66, -66, -66, -66, -66
+ },
+
+ } ;
+
+static yy_state_type yy_get_previous_state (void );
+static yy_state_type yy_try_NUL_trans (yy_state_type current_state );
+static int yy_get_next_buffer (void );
+static void yy_fatal_error (yyconst char msg[] );
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up zconftext.
+ */
+#define YY_DO_BEFORE_ACTION \
+ (yytext_ptr) = yy_bp; \
+ zconfleng = (size_t) (yy_cp - yy_bp); \
+ (yy_hold_char) = *yy_cp; \
+ *yy_cp = '\0'; \
+ (yy_c_buf_p) = yy_cp;
+
+#define YY_NUM_RULES 38
+#define YY_END_OF_BUFFER 39
+/* This struct is not used in this scanner,
+ but its presence is necessary. */
+struct yy_trans_info
+ {
+ flex_int32_t yy_verify;
+ flex_int32_t yy_nxt;
+ };
+static yyconst flex_int16_t yy_accept[67] =
+ { 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 39, 5, 4, 2, 3, 7, 8, 6, 37, 34,
+ 36, 29, 33, 32, 31, 27, 26, 21, 13, 20,
+ 24, 27, 11, 12, 23, 23, 18, 14, 19, 27,
+ 27, 4, 2, 3, 3, 1, 6, 37, 34, 36,
+ 35, 29, 28, 31, 30, 26, 15, 24, 9, 23,
+ 23, 16, 17, 25, 10, 22
+ } ;
+
+static yyconst flex_int32_t yy_ec[256] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 2, 3,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 2, 4, 5, 6, 1, 1, 7, 8, 9,
+ 10, 1, 1, 1, 11, 12, 12, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 1, 1, 14,
+ 15, 16, 1, 1, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 1, 17, 1, 1, 13, 1, 13, 13, 13, 13,
+
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 1, 18, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1
+ } ;
+
+extern int zconf_flex_debug;
+int zconf_flex_debug = 0;
+
+/* The intent behind this definition is that it'll catch
+ * any uses of REJECT which flex missed.
+ */
+#define REJECT reject_used_but_not_detected
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+#define YY_RESTORE_YY_MORE_OFFSET
+char *zconftext;
+#define YY_NO_INPUT 1
+
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "lkc.h"
+
+#define START_STRSIZE 16
+
+static struct {
+ struct file *file;
+ int lineno;
+} current_pos;
+
+static char *text;
+static int text_size, text_asize;
+
+struct buffer {
+ struct buffer *parent;
+ YY_BUFFER_STATE state;
+};
+
+struct buffer *current_buf;
+
+static int last_ts, first_ts;
+
+static void zconf_endhelp(void);
+static void zconf_endfile(void);
+
+static void new_string(void)
+{
+ text = xmalloc(START_STRSIZE);
+ text_asize = START_STRSIZE;
+ text_size = 0;
+ *text = 0;
+}
+
+static void append_string(const char *str, int size)
+{
+ int new_size = text_size + size + 1;
+ if (new_size > text_asize) {
+ new_size += START_STRSIZE - 1;
+ new_size &= -START_STRSIZE;
+ text = realloc(text, new_size);
+ text_asize = new_size;
+ }
+ memcpy(text + text_size, str, size);
+ text_size += size;
+ text[text_size] = 0;
+}
+
+static void alloc_string(const char *str, int size)
+{
+ text = xmalloc(size + 1);
+ memcpy(text, str, size);
+ text[size] = 0;
+}
+
+#define INITIAL 0
+#define COMMAND 1
+#define HELP 2
+#define STRING 3
+#define PARAM 4
+
+#ifndef YY_NO_UNISTD_H
+/* Special case for "unistd.h", since it is non-ANSI. We include it way
+ * down here because we want the user's section 1 to have been scanned first.
+ * The user has a chance to override it with an option.
+ */
+#include <unistd.h>
+#endif
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+static int yy_init_globals (void );
+
+/* Accessor methods to globals.
+ These are made visible to non-reentrant scanners for convenience. */
+
+int zconflex_destroy (void );
+
+int zconfget_debug (void );
+
+void zconfset_debug (int debug_flag );
+
+YY_EXTRA_TYPE zconfget_extra (void );
+
+void zconfset_extra (YY_EXTRA_TYPE user_defined );
+
+FILE *zconfget_in (void );
+
+void zconfset_in (FILE * in_str );
+
+FILE *zconfget_out (void );
+
+void zconfset_out (FILE * out_str );
+
+int zconfget_leng (void );
+
+char *zconfget_text (void );
+
+int zconfget_lineno (void );
+
+void zconfset_lineno (int line_number );
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int zconfwrap (void );
+#else
+extern int zconfwrap (void );
+#endif
+#endif
+
+ static void yyunput (int c,char *buf_ptr );
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char *,yyconst char *,int );
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * );
+#endif
+
+#ifndef YY_NO_INPUT
+
+#ifdef __cplusplus
+static int yyinput (void );
+#else
+static int input (void );
+#endif
+
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 8192
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO fwrite( zconftext, zconfleng, 1, zconfout )
+#endif
+
+/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+ errno=0; \
+ while ( (result = read( fileno(zconfin), (char *) buf, max_size )) < 0 ) \
+ { \
+ if( errno != EINTR) \
+ { \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ break; \
+ } \
+ errno=0; \
+ clearerr(zconfin); \
+ }\
+\
+
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg )
+#endif
+
+/* end tables serialization structures and prototypes */
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+extern int zconflex (void);
+
+#define YY_DECL int zconflex (void)
+#endif /* !YY_DECL */
+
+/* Code executed at the beginning of each rule, after zconftext and zconfleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK break;
+#endif
+
+#define YY_RULE_SETUP \
+ YY_USER_ACTION
+
+/** The main scanner function which does all the work.
+ */
+YY_DECL
+{
+ register yy_state_type yy_current_state;
+ register char *yy_cp, *yy_bp;
+ register int yy_act;
+
+ int str = 0;
+ int ts, i;
+
+ if ( !(yy_init) )
+ {
+ (yy_init) = 1;
+
+#ifdef YY_USER_INIT
+ YY_USER_INIT;
+#endif
+
+ if ( ! (yy_start) )
+ (yy_start) = 1; /* first start state */
+
+ if ( ! zconfin )
+ zconfin = stdin;
+
+ if ( ! zconfout )
+ zconfout = stdout;
+
+ if ( ! YY_CURRENT_BUFFER ) {
+ zconfensure_buffer_stack ();
+ YY_CURRENT_BUFFER_LVALUE =
+ zconf_create_buffer(zconfin,YY_BUF_SIZE );
+ }
+
+ zconf_load_buffer_state( );
+ }
+
+ while ( 1 ) /* loops until end-of-file is reached */
+ {
+ yy_cp = (yy_c_buf_p);
+
+ /* Support of zconftext. */
+ *yy_cp = (yy_hold_char);
+
+ /* yy_bp points to the position in yy_ch_buf of the start of
+ * the current run.
+ */
+ yy_bp = yy_cp;
+
+ yy_current_state = (yy_start);
+yy_match:
+ while ( (yy_current_state = yy_nxt[yy_current_state][ yy_ec[YY_SC_TO_UI(*yy_cp)] ]) > 0 )
+ ++yy_cp;
+
+ yy_current_state = -yy_current_state;
+
+yy_find_action:
+ yy_act = yy_accept[yy_current_state];
+
+ YY_DO_BEFORE_ACTION;
+
+do_action: /* This label is used only to access EOF actions. */
+
+ switch ( yy_act )
+ { /* beginning of action switch */
+case 1:
+/* rule 1 can match eol */
+case 2:
+/* rule 2 can match eol */
+YY_RULE_SETUP
+{
+ current_file->lineno++;
+ return T_EOL;
+}
+ YY_BREAK
+case 3:
+YY_RULE_SETUP
+
+ YY_BREAK
+case 4:
+YY_RULE_SETUP
+{
+ BEGIN(COMMAND);
+}
+ YY_BREAK
+case 5:
+YY_RULE_SETUP
+{
+ unput(zconftext[0]);
+ BEGIN(COMMAND);
+}
+ YY_BREAK
+
+case 6:
+YY_RULE_SETUP
+{
+ const struct kconf_id *id = kconf_id_lookup(zconftext, zconfleng);
+ BEGIN(PARAM);
+ current_pos.file = current_file;
+ current_pos.lineno = current_file->lineno;
+ if (id && id->flags & TF_COMMAND) {
+ zconflval.id = id;
+ return id->token;
+ }
+ alloc_string(zconftext, zconfleng);
+ zconflval.string = text;
+ return T_WORD;
+ }
+ YY_BREAK
+case 7:
+YY_RULE_SETUP
+
+ YY_BREAK
+case 8:
+/* rule 8 can match eol */
+YY_RULE_SETUP
+{
+ BEGIN(INITIAL);
+ current_file->lineno++;
+ return T_EOL;
+ }
+ YY_BREAK
+
+case 9:
+YY_RULE_SETUP
+return T_AND;
+ YY_BREAK
+case 10:
+YY_RULE_SETUP
+return T_OR;
+ YY_BREAK
+case 11:
+YY_RULE_SETUP
+return T_OPEN_PAREN;
+ YY_BREAK
+case 12:
+YY_RULE_SETUP
+return T_CLOSE_PAREN;
+ YY_BREAK
+case 13:
+YY_RULE_SETUP
+return T_NOT;
+ YY_BREAK
+case 14:
+YY_RULE_SETUP
+return T_EQUAL;
+ YY_BREAK
+case 15:
+YY_RULE_SETUP
+return T_UNEQUAL;
+ YY_BREAK
+case 16:
+YY_RULE_SETUP
+return T_LESS_EQUAL;
+ YY_BREAK
+case 17:
+YY_RULE_SETUP
+return T_GREATER_EQUAL;
+ YY_BREAK
+case 18:
+YY_RULE_SETUP
+return T_LESS;
+ YY_BREAK
+case 19:
+YY_RULE_SETUP
+return T_GREATER;
+ YY_BREAK
+case 20:
+YY_RULE_SETUP
+{
+ str = zconftext[0];
+ new_string();
+ BEGIN(STRING);
+ }
+ YY_BREAK
+case 21:
+/* rule 21 can match eol */
+YY_RULE_SETUP
+BEGIN(INITIAL); current_file->lineno++; return T_EOL;
+ YY_BREAK
+case 22:
+YY_RULE_SETUP
+/* ignore */
+ YY_BREAK
+case 23:
+YY_RULE_SETUP
+{
+ const struct kconf_id *id = kconf_id_lookup(zconftext, zconfleng);
+ if (id && id->flags & TF_PARAM) {
+ zconflval.id = id;
+ return id->token;
+ }
+ alloc_string(zconftext, zconfleng);
+ zconflval.string = text;
+ return T_WORD;
+ }
+ YY_BREAK
+case 24:
+YY_RULE_SETUP
+/* comment */
+ YY_BREAK
+case 25:
+/* rule 25 can match eol */
+YY_RULE_SETUP
+current_file->lineno++;
+ YY_BREAK
+case 26:
+YY_RULE_SETUP
+
+ YY_BREAK
+case 27:
+YY_RULE_SETUP
+{
+ fprintf(stderr,
+ "%s:%d:warning: ignoring unsupported character '%c'\n",
+ zconf_curname(), zconf_lineno(), *zconftext);
+ }
+ YY_BREAK
+case YY_STATE_EOF(PARAM):
+{
+ BEGIN(INITIAL);
+ }
+ YY_BREAK
+
+case 28:
+/* rule 28 can match eol */
+*yy_cp = (yy_hold_char); /* undo effects of setting up zconftext */
+(yy_c_buf_p) = yy_cp -= 1;
+YY_DO_BEFORE_ACTION; /* set up zconftext again */
+YY_RULE_SETUP
+{
+ append_string(zconftext, zconfleng);
+ zconflval.string = text;
+ return T_WORD_QUOTE;
+ }
+ YY_BREAK
+case 29:
+YY_RULE_SETUP
+{
+ append_string(zconftext, zconfleng);
+ }
+ YY_BREAK
+case 30:
+/* rule 30 can match eol */
+*yy_cp = (yy_hold_char); /* undo effects of setting up zconftext */
+(yy_c_buf_p) = yy_cp -= 1;
+YY_DO_BEFORE_ACTION; /* set up zconftext again */
+YY_RULE_SETUP
+{
+ append_string(zconftext + 1, zconfleng - 1);
+ zconflval.string = text;
+ return T_WORD_QUOTE;
+ }
+ YY_BREAK
+case 31:
+YY_RULE_SETUP
+{
+ append_string(zconftext + 1, zconfleng - 1);
+ }
+ YY_BREAK
+case 32:
+YY_RULE_SETUP
+{
+ if (str == zconftext[0]) {
+ BEGIN(PARAM);
+ zconflval.string = text;
+ return T_WORD_QUOTE;
+ } else
+ append_string(zconftext, 1);
+ }
+ YY_BREAK
+case 33:
+/* rule 33 can match eol */
+YY_RULE_SETUP
+{
+ printf("%s:%d:warning: multi-line strings not supported\n", zconf_curname(), zconf_lineno());
+ current_file->lineno++;
+ BEGIN(INITIAL);
+ return T_EOL;
+ }
+ YY_BREAK
+case YY_STATE_EOF(STRING):
+{
+ BEGIN(INITIAL);
+ }
+ YY_BREAK
+
+case 34:
+YY_RULE_SETUP
+{
+ ts = 0;
+ for (i = 0; i < zconfleng; i++) {
+ if (zconftext[i] == '\t')
+ ts = (ts & ~7) + 8;
+ else
+ ts++;
+ }
+ last_ts = ts;
+ if (first_ts) {
+ if (ts < first_ts) {
+ zconf_endhelp();
+ return T_HELPTEXT;
+ }
+ ts -= first_ts;
+ while (ts > 8) {
+ append_string(" ", 8);
+ ts -= 8;
+ }
+ append_string(" ", ts);
+ }
+ }
+ YY_BREAK
+case 35:
+/* rule 35 can match eol */
+*yy_cp = (yy_hold_char); /* undo effects of setting up zconftext */
+(yy_c_buf_p) = yy_cp -= 1;
+YY_DO_BEFORE_ACTION; /* set up zconftext again */
+YY_RULE_SETUP
+{
+ current_file->lineno++;
+ zconf_endhelp();
+ return T_HELPTEXT;
+ }
+ YY_BREAK
+case 36:
+/* rule 36 can match eol */
+YY_RULE_SETUP
+{
+ current_file->lineno++;
+ append_string("\n", 1);
+ }
+ YY_BREAK
+case 37:
+YY_RULE_SETUP
+{
+ while (zconfleng) {
+ if ((zconftext[zconfleng-1] != ' ') && (zconftext[zconfleng-1] != '\t'))
+ break;
+ zconfleng--;
+ }
+ append_string(zconftext, zconfleng);
+ if (!first_ts)
+ first_ts = last_ts;
+ }
+ YY_BREAK
+case YY_STATE_EOF(HELP):
+{
+ zconf_endhelp();
+ return T_HELPTEXT;
+ }
+ YY_BREAK
+
+case YY_STATE_EOF(INITIAL):
+case YY_STATE_EOF(COMMAND):
+{
+ if (current_file) {
+ zconf_endfile();
+ return T_EOL;
+ }
+ fclose(zconfin);
+ yyterminate();
+}
+ YY_BREAK
+case 38:
+YY_RULE_SETUP
+YY_FATAL_ERROR( "flex scanner jammed" );
+ YY_BREAK
+
+ case YY_END_OF_BUFFER:
+ {
+ /* Amount of text matched not including the EOB char. */
+ int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1;
+
+ /* Undo the effects of YY_DO_BEFORE_ACTION. */
+ *yy_cp = (yy_hold_char);
+ YY_RESTORE_YY_MORE_OFFSET
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )
+ {
+ /* We're scanning a new file or input source. It's
+ * possible that this happened because the user
+ * just pointed zconfin at a new source and called
+ * zconflex(). If so, then we have to assure
+ * consistency between YY_CURRENT_BUFFER and our
+ * globals. Here is the right place to do so, because
+ * this is the first action (other than possibly a
+ * back-up) that will match for the new input source.
+ */
+ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ YY_CURRENT_BUFFER_LVALUE->yy_input_file = zconfin;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;
+ }
+
+ /* Note that here we test for yy_c_buf_p "<=" to the position
+ * of the first EOB in the buffer, since yy_c_buf_p will
+ * already have been incremented past the NUL character
+ * (since all states make transitions on EOB to the
+ * end-of-buffer state). Contrast this with the test
+ * in input().
+ */
+ if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+ { /* This was really a NUL. */
+ yy_state_type yy_next_state;
+
+ (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( );
+
+ /* Okay, we're now positioned to make the NUL
+ * transition. We couldn't have
+ * yy_get_previous_state() go ahead and do it
+ * for us because it doesn't know how to deal
+ * with the possibility of jamming (and we don't
+ * want to build jamming into it because then it
+ * will run more slowly).
+ */
+
+ yy_next_state = yy_try_NUL_trans( yy_current_state );
+
+ yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+
+ if ( yy_next_state )
+ {
+ /* Consume the NUL. */
+ yy_cp = ++(yy_c_buf_p);
+ yy_current_state = yy_next_state;
+ goto yy_match;
+ }
+
+ else
+ {
+ yy_cp = (yy_c_buf_p);
+ goto yy_find_action;
+ }
+ }
+
+ else switch ( yy_get_next_buffer( ) )
+ {
+ case EOB_ACT_END_OF_FILE:
+ {
+ (yy_did_buffer_switch_on_eof) = 0;
+
+ if ( zconfwrap( ) )
+ {
+ /* Note: because we've taken care in
+ * yy_get_next_buffer() to have set up
+ * zconftext, we can now set up
+ * yy_c_buf_p so that if some total
+ * hoser (like flex itself) wants to
+ * call the scanner after we return the
+ * YY_NULL, it'll still work - another
+ * YY_NULL will get returned.
+ */
+ (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ;
+
+ yy_act = YY_STATE_EOF(YY_START);
+ goto do_action;
+ }
+
+ else
+ {
+ if ( ! (yy_did_buffer_switch_on_eof) )
+ YY_NEW_FILE;
+ }
+ break;
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ (yy_c_buf_p) =
+ (yytext_ptr) + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( );
+
+ yy_cp = (yy_c_buf_p);
+ yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+ goto yy_match;
+
+ case EOB_ACT_LAST_MATCH:
+ (yy_c_buf_p) =
+ &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)];
+
+ yy_current_state = yy_get_previous_state( );
+
+ yy_cp = (yy_c_buf_p);
+ yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+ goto yy_find_action;
+ }
+ break;
+ }
+
+ default:
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--no action found" );
+ } /* end of action switch */
+ } /* end of scanning one token */
+} /* end of zconflex */
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ * EOB_ACT_LAST_MATCH -
+ * EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ * EOB_ACT_END_OF_FILE - end of file
+ */
+static int yy_get_next_buffer (void)
+{
+ register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
+ register char *source = (yytext_ptr);
+ register int number_to_move, i;
+ int ret_val;
+
+ if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] )
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--end of buffer missed" );
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )
+ { /* Don't try to fill the buffer, so this is an EOF. */
+ if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 )
+ {
+ /* We matched a single character, the EOB, so
+ * treat this as a final EOF.
+ */
+ return EOB_ACT_END_OF_FILE;
+ }
+
+ else
+ {
+ /* We matched some text prior to the EOB, first
+ * process it.
+ */
+ return EOB_ACT_LAST_MATCH;
+ }
+ }
+
+ /* Try to read more data. */
+
+ /* First move last chars to start of buffer. */
+ number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1;
+
+ for ( i = 0; i < number_to_move; ++i )
+ *(dest++) = *(source++);
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+ /* don't do the read, it's not guaranteed to return an EOF,
+ * just force an EOF
+ */
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0;
+
+ else
+ {
+ int num_to_read =
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
+
+ while ( num_to_read <= 0 )
+ { /* Not enough room in the buffer - grow it. */
+
+ /* just a shorter name for the current buffer */
+ YY_BUFFER_STATE b = YY_CURRENT_BUFFER;
+
+ int yy_c_buf_p_offset =
+ (int) ((yy_c_buf_p) - b->yy_ch_buf);
+
+ if ( b->yy_is_our_buffer )
+ {
+ int new_size = b->yy_buf_size * 2;
+
+ if ( new_size <= 0 )
+ b->yy_buf_size += b->yy_buf_size / 8;
+ else
+ b->yy_buf_size *= 2;
+
+ b->yy_ch_buf = (char *)
+ /* Include room in for 2 EOB chars. */
+ zconfrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 );
+ }
+ else
+ /* Can't grow it, we don't own it. */
+ b->yy_ch_buf = 0;
+
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR(
+ "fatal error - scanner input buffer overflow" );
+
+ (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+ num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
+ number_to_move - 1;
+
+ }
+
+ if ( num_to_read > YY_READ_BUF_SIZE )
+ num_to_read = YY_READ_BUF_SIZE;
+
+ /* Read in more data. */
+ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
+ (yy_n_chars), (size_t) num_to_read );
+
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+ }
+
+ if ( (yy_n_chars) == 0 )
+ {
+ if ( number_to_move == YY_MORE_ADJ )
+ {
+ ret_val = EOB_ACT_END_OF_FILE;
+ zconfrestart(zconfin );
+ }
+
+ else
+ {
+ ret_val = EOB_ACT_LAST_MATCH;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
+ YY_BUFFER_EOF_PENDING;
+ }
+ }
+
+ else
+ ret_val = EOB_ACT_CONTINUE_SCAN;
+
+ if ((yy_size_t) ((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
+ /* Extend the array by 50%, plus the number we really need. */
+ yy_size_t new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1);
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) zconfrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size );
+ if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" );
+ }
+
+ (yy_n_chars) += number_to_move;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR;
+
+ (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];
+
+ return ret_val;
+}
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+ static yy_state_type yy_get_previous_state (void)
+{
+ register yy_state_type yy_current_state;
+ register char *yy_cp;
+
+ yy_current_state = (yy_start);
+
+ for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp )
+ {
+ yy_current_state = yy_nxt[yy_current_state][(*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1)];
+ }
+
+ return yy_current_state;
+}
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ * next_state = yy_try_NUL_trans( current_state );
+ */
+ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state )
+{
+ register int yy_is_jam;
+
+ yy_current_state = yy_nxt[yy_current_state][1];
+ yy_is_jam = (yy_current_state <= 0);
+
+ return yy_is_jam ? 0 : yy_current_state;
+}
+
+ static void yyunput (int c, register char * yy_bp )
+{
+ register char *yy_cp;
+
+ yy_cp = (yy_c_buf_p);
+
+ /* undo effects of setting up zconftext */
+ *yy_cp = (yy_hold_char);
+
+ if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+ { /* need to shift things up to make room */
+ /* +2 for EOB chars. */
+ register int number_to_move = (yy_n_chars) + 2;
+ register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2];
+ register char *source =
+ &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move];
+
+ while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+ *--dest = *--source;
+
+ yy_cp += (int) (dest - source);
+ yy_bp += (int) (dest - source);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars =
+ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_buf_size;
+
+ if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+ YY_FATAL_ERROR( "flex scanner push-back overflow" );
+ }
+
+ *--yy_cp = (char) c;
+
+ (yytext_ptr) = yy_bp;
+ (yy_hold_char) = *yy_cp;
+ (yy_c_buf_p) = yy_cp;
+}
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+ static int yyinput (void)
+#else
+ static int input (void)
+#endif
+
+{
+ int c;
+
+ *(yy_c_buf_p) = (yy_hold_char);
+
+ if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR )
+ {
+ /* yy_c_buf_p now points to the character we want to return.
+ * If this occurs *before* the EOB characters, then it's a
+ * valid NUL; if not, then we've hit the end of the buffer.
+ */
+ if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+ /* This was really a NUL. */
+ *(yy_c_buf_p) = '\0';
+
+ else
+ { /* need more input */
+ int offset = (yy_c_buf_p) - (yytext_ptr);
+ ++(yy_c_buf_p);
+
+ switch ( yy_get_next_buffer( ) )
+ {
+ case EOB_ACT_LAST_MATCH:
+ /* This happens because yy_g_n_b()
+ * sees that we've accumulated a
+ * token and flags that we need to
+ * try matching the token before
+ * proceeding. But for input(),
+ * there's no matching to consider.
+ * So convert the EOB_ACT_LAST_MATCH
+ * to EOB_ACT_END_OF_FILE.
+ */
+
+ /* Reset buffer status. */
+ zconfrestart(zconfin );
+
+ /*FALLTHROUGH*/
+
+ case EOB_ACT_END_OF_FILE:
+ {
+ if ( zconfwrap( ) )
+ return EOF;
+
+ if ( ! (yy_did_buffer_switch_on_eof) )
+ YY_NEW_FILE;
+#ifdef __cplusplus
+ return yyinput();
+#else
+ return input();
+#endif
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ (yy_c_buf_p) = (yytext_ptr) + offset;
+ break;
+ }
+ }
+ }
+
+ c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */
+ *(yy_c_buf_p) = '\0'; /* preserve zconftext */
+ (yy_hold_char) = *++(yy_c_buf_p);
+
+ return c;
+}
+#endif /* ifndef YY_NO_INPUT */
+
+/** Immediately switch to a different input stream.
+ * @param input_file A readable stream.
+ *
+ * @note This function does not reset the start condition to @c INITIAL .
+ */
+ void zconfrestart (FILE * input_file )
+{
+
+ if ( ! YY_CURRENT_BUFFER ){
+ zconfensure_buffer_stack ();
+ YY_CURRENT_BUFFER_LVALUE =
+ zconf_create_buffer(zconfin,YY_BUF_SIZE );
+ }
+
+ zconf_init_buffer(YY_CURRENT_BUFFER,input_file );
+ zconf_load_buffer_state( );
+}
+
+/** Switch to a different input buffer.
+ * @param new_buffer The new input buffer.
+ *
+ */
+ void zconf_switch_to_buffer (YY_BUFFER_STATE new_buffer )
+{
+
+ /* TODO. We should be able to replace this entire function body
+ * with
+ * zconfpop_buffer_state();
+ * zconfpush_buffer_state(new_buffer);
+ */
+ zconfensure_buffer_stack ();
+ if ( YY_CURRENT_BUFFER == new_buffer )
+ return;
+
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *(yy_c_buf_p) = (yy_hold_char);
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+ }
+
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+ zconf_load_buffer_state( );
+
+ /* We don't actually know whether we did this switch during
+ * EOF (zconfwrap()) processing, but the only time this flag
+ * is looked at is after zconfwrap() is called, so it's safe
+ * to go ahead and always set it.
+ */
+ (yy_did_buffer_switch_on_eof) = 1;
+}
+
+static void zconf_load_buffer_state (void)
+{
+ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
+ zconfin = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
+ (yy_hold_char) = *(yy_c_buf_p);
+}
+
+/** Allocate and initialize an input buffer state.
+ * @param file A readable stream.
+ * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
+ *
+ * @return the allocated buffer state.
+ */
+ YY_BUFFER_STATE zconf_create_buffer (FILE * file, int size )
+{
+ YY_BUFFER_STATE b;
+
+ b = (YY_BUFFER_STATE) zconfalloc(sizeof( struct yy_buffer_state ) );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in zconf_create_buffer()" );
+
+ b->yy_buf_size = size;
+
+ /* yy_ch_buf has to be 2 characters longer than the size given because
+ * we need to put in 2 end-of-buffer characters.
+ */
+ b->yy_ch_buf = (char *) zconfalloc(b->yy_buf_size + 2 );
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in zconf_create_buffer()" );
+
+ b->yy_is_our_buffer = 1;
+
+ zconf_init_buffer(b,file );
+
+ return b;
+}
+
+/** Destroy the buffer.
+ * @param b a buffer created with zconf_create_buffer()
+ *
+ */
+ void zconf_delete_buffer (YY_BUFFER_STATE b )
+{
+
+ if ( ! b )
+ return;
+
+ if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */
+ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;
+
+ if ( b->yy_is_our_buffer )
+ zconffree((void *) b->yy_ch_buf );
+
+ zconffree((void *) b );
+}
+
+/* Initializes or reinitializes a buffer.
+ * This function is sometimes called more than once on the same buffer,
+ * such as during a zconfrestart() or at EOF.
+ */
+ static void zconf_init_buffer (YY_BUFFER_STATE b, FILE * file )
+
+{
+ int oerrno = errno;
+
+ zconf_flush_buffer(b );
+
+ b->yy_input_file = file;
+ b->yy_fill_buffer = 1;
+
+ /* If b is the current buffer, then zconf_init_buffer was _probably_
+ * called from zconfrestart() or through yy_get_next_buffer.
+ * In that case, we don't want to reset the lineno or column.
+ */
+ if (b != YY_CURRENT_BUFFER){
+ b->yy_bs_lineno = 1;
+ b->yy_bs_column = 0;
+ }
+
+ b->yy_is_interactive = 0;
+
+ errno = oerrno;
+}
+
+/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
+ * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
+ *
+ */
+ void zconf_flush_buffer (YY_BUFFER_STATE b )
+{
+ if ( ! b )
+ return;
+
+ b->yy_n_chars = 0;
+
+ /* We always need two end-of-buffer characters. The first causes
+ * a transition to the end-of-buffer state. The second causes
+ * a jam in that state.
+ */
+ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+ b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+ b->yy_buf_pos = &b->yy_ch_buf[0];
+
+ b->yy_at_bol = 1;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ if ( b == YY_CURRENT_BUFFER )
+ zconf_load_buffer_state( );
+}
+
+/** Pushes the new state onto the stack. The new state becomes
+ * the current state. This function will allocate the stack
+ * if necessary.
+ * @param new_buffer The new state.
+ *
+ */
+void zconfpush_buffer_state (YY_BUFFER_STATE new_buffer )
+{
+ if (new_buffer == NULL)
+ return;
+
+ zconfensure_buffer_stack();
+
+ /* This block is copied from zconf_switch_to_buffer. */
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *(yy_c_buf_p) = (yy_hold_char);
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+ }
+
+ /* Only push if top exists. Otherwise, replace top. */
+ if (YY_CURRENT_BUFFER)
+ (yy_buffer_stack_top)++;
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+
+ /* copied from zconf_switch_to_buffer. */
+ zconf_load_buffer_state( );
+ (yy_did_buffer_switch_on_eof) = 1;
+}
+
+/** Removes and deletes the top of the stack, if present.
+ * The next element becomes the new top.
+ *
+ */
+void zconfpop_buffer_state (void)
+{
+ if (!YY_CURRENT_BUFFER)
+ return;
+
+ zconf_delete_buffer(YY_CURRENT_BUFFER );
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ if ((yy_buffer_stack_top) > 0)
+ --(yy_buffer_stack_top);
+
+ if (YY_CURRENT_BUFFER) {
+ zconf_load_buffer_state( );
+ (yy_did_buffer_switch_on_eof) = 1;
+ }
+}
+
+/* Allocates the stack if it does not exist.
+ * Guarantees space for at least one push.
+ */
+static void zconfensure_buffer_stack (void)
+{
+ int num_to_alloc;
+
+ if (!(yy_buffer_stack)) {
+
+ /* First allocation is just for 2 elements, since we don't know if this
+ * scanner will even need a stack. We use 2 instead of 1 to avoid an
+ * immediate realloc on the next call.
+ */
+ num_to_alloc = 1;
+ (yy_buffer_stack) = (struct yy_buffer_state**)zconfalloc
+ (num_to_alloc * sizeof(struct yy_buffer_state*)
+ );
+ if ( ! (yy_buffer_stack) )
+ YY_FATAL_ERROR( "out of dynamic memory in zconfensure_buffer_stack()" );
+
+ memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*));
+
+ (yy_buffer_stack_max) = num_to_alloc;
+ (yy_buffer_stack_top) = 0;
+ return;
+ }
+
+ if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){
+
+ /* Increase the buffer to prepare for a possible push. */
+ int grow_size = 8 /* arbitrary grow size */;
+
+ num_to_alloc = (yy_buffer_stack_max) + grow_size;
+ (yy_buffer_stack) = (struct yy_buffer_state**)zconfrealloc
+ ((yy_buffer_stack),
+ num_to_alloc * sizeof(struct yy_buffer_state*)
+ );
+ if ( ! (yy_buffer_stack) )
+ YY_FATAL_ERROR( "out of dynamic memory in zconfensure_buffer_stack()" );
+
+ /* zero only the new slots.*/
+ memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*));
+ (yy_buffer_stack_max) = num_to_alloc;
+ }
+}
+
+/** Setup the input buffer state to scan directly from a user-specified character buffer.
+ * @param base the character buffer
+ * @param size the size in bytes of the character buffer
+ *
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE zconf_scan_buffer (char * base, yy_size_t size )
+{
+ YY_BUFFER_STATE b;
+
+ if ( size < 2 ||
+ base[size-2] != YY_END_OF_BUFFER_CHAR ||
+ base[size-1] != YY_END_OF_BUFFER_CHAR )
+ /* They forgot to leave room for the EOB's. */
+ return 0;
+
+ b = (YY_BUFFER_STATE) zconfalloc(sizeof( struct yy_buffer_state ) );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in zconf_scan_buffer()" );
+
+ b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */
+ b->yy_buf_pos = b->yy_ch_buf = base;
+ b->yy_is_our_buffer = 0;
+ b->yy_input_file = 0;
+ b->yy_n_chars = b->yy_buf_size;
+ b->yy_is_interactive = 0;
+ b->yy_at_bol = 1;
+ b->yy_fill_buffer = 0;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ zconf_switch_to_buffer(b );
+
+ return b;
+}
+
+/** Setup the input buffer state to scan a string. The next call to zconflex() will
+ * scan from a @e copy of @a str.
+ * @param yystr a NUL-terminated string to scan
+ *
+ * @return the newly allocated buffer state object.
+ * @note If you want to scan bytes that may contain NUL values, then use
+ * zconf_scan_bytes() instead.
+ */
+YY_BUFFER_STATE zconf_scan_string (yyconst char * yystr )
+{
+
+ return zconf_scan_bytes(yystr,strlen(yystr) );
+}
+
+/** Setup the input buffer state to scan the given bytes. The next call to zconflex() will
+ * scan from a @e copy of @a bytes.
+ * @param bytes the byte buffer to scan
+ * @param len the number of bytes in the buffer pointed to by @a bytes.
+ *
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE zconf_scan_bytes (yyconst char * yybytes, int _yybytes_len )
+{
+ YY_BUFFER_STATE b;
+ char *buf;
+ yy_size_t n;
+ int i;
+
+ /* Get memory for full buffer, including space for trailing EOB's. */
+ n = _yybytes_len + 2;
+ buf = (char *) zconfalloc(n );
+ if ( ! buf )
+ YY_FATAL_ERROR( "out of dynamic memory in zconf_scan_bytes()" );
+
+ for ( i = 0; i < _yybytes_len; ++i )
+ buf[i] = yybytes[i];
+
+ buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR;
+
+ b = zconf_scan_buffer(buf,n );
+ if ( ! b )
+ YY_FATAL_ERROR( "bad buffer in zconf_scan_bytes()" );
+
+ /* It's okay to grow etc. this buffer, and we should throw it
+ * away when we're done.
+ */
+ b->yy_is_our_buffer = 1;
+
+ return b;
+}
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+static void yy_fatal_error (yyconst char* msg )
+{
+ (void) fprintf( stderr, "%s\n", msg );
+ exit( YY_EXIT_FAILURE );
+}
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up zconftext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ zconftext[zconfleng] = (yy_hold_char); \
+ (yy_c_buf_p) = zconftext + yyless_macro_arg; \
+ (yy_hold_char) = *(yy_c_buf_p); \
+ *(yy_c_buf_p) = '\0'; \
+ zconfleng = yyless_macro_arg; \
+ } \
+ while ( 0 )
+
+/* Accessor methods (get/set functions) to struct members. */
+
+/** Get the current line number.
+ *
+ */
+int zconfget_lineno (void)
+{
+
+ return zconflineno;
+}
+
+/** Get the input stream.
+ *
+ */
+FILE *zconfget_in (void)
+{
+ return zconfin;
+}
+
+/** Get the output stream.
+ *
+ */
+FILE *zconfget_out (void)
+{
+ return zconfout;
+}
+
+/** Get the length of the current token.
+ *
+ */
+int zconfget_leng (void)
+{
+ return zconfleng;
+}
+
+/** Get the current token.
+ *
+ */
+
+char *zconfget_text (void)
+{
+ return zconftext;
+}
+
+/** Set the current line number.
+ * @param line_number
+ *
+ */
+void zconfset_lineno (int line_number )
+{
+
+ zconflineno = line_number;
+}
+
+/** Set the input stream. This does not discard the current
+ * input buffer.
+ * @param in_str A readable stream.
+ *
+ * @see zconf_switch_to_buffer
+ */
+void zconfset_in (FILE * in_str )
+{
+ zconfin = in_str ;
+}
+
+void zconfset_out (FILE * out_str )
+{
+ zconfout = out_str ;
+}
+
+int zconfget_debug (void)
+{
+ return zconf_flex_debug;
+}
+
+void zconfset_debug (int bdebug )
+{
+ zconf_flex_debug = bdebug ;
+}
+
+static int yy_init_globals (void)
+{
+ /* Initialization is the same as for the non-reentrant scanner.
+ * This function is called from zconflex_destroy(), so don't allocate here.
+ */
+
+ (yy_buffer_stack) = 0;
+ (yy_buffer_stack_top) = 0;
+ (yy_buffer_stack_max) = 0;
+ (yy_c_buf_p) = (char *) 0;
+ (yy_init) = 0;
+ (yy_start) = 0;
+
+/* Defined in main.c */
+#ifdef YY_STDINIT
+ zconfin = stdin;
+ zconfout = stdout;
+#else
+ zconfin = (FILE *) 0;
+ zconfout = (FILE *) 0;
+#endif
+
+ /* For future reference: Set errno on error, since we are called by
+ * zconflex_init()
+ */
+ return 0;
+}
+
+/* zconflex_destroy is for both reentrant and non-reentrant scanners. */
+int zconflex_destroy (void)
+{
+
+ /* Pop the buffer stack, destroying each element. */
+ while(YY_CURRENT_BUFFER){
+ zconf_delete_buffer(YY_CURRENT_BUFFER );
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ zconfpop_buffer_state();
+ }
+
+ /* Destroy the stack itself. */
+ zconffree((yy_buffer_stack) );
+ (yy_buffer_stack) = NULL;
+
+ /* Reset the globals. This is important in a non-reentrant scanner so the next time
+ * zconflex() is called, initialization will occur. */
+ yy_init_globals( );
+
+ return 0;
+}
+
+/*
+ * Internal utility routines.
+ */
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char* s1, yyconst char * s2, int n )
+{
+ register int i;
+ for ( i = 0; i < n; ++i )
+ s1[i] = s2[i];
+}
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * s )
+{
+ register int n;
+ for ( n = 0; s[n]; ++n )
+ ;
+
+ return n;
+}
+#endif
+
+void *zconfalloc (yy_size_t size )
+{
+ return (void *) malloc( size );
+}
+
+void *zconfrealloc (void * ptr, yy_size_t size )
+{
+ /* The cast to (char *) in the following accommodates both
+ * implementations that use char* generic pointers, and those
+ * that use void* generic pointers. It works with the latter
+ * because both ANSI C and C++ allow castless assignment from
+ * any pointer type to void*, and deal with argument conversions
+ * as though doing an assignment.
+ */
+ return (void *) realloc( (char *) ptr, size );
+}
+
+void zconffree (void * ptr )
+{
+ free( (char *) ptr ); /* see zconfrealloc() for (char *) cast */
+}
+
+#define YYTABLES_NAME "yytables"
+
+void zconf_starthelp(void)
+{
+ new_string();
+ last_ts = first_ts = 0;
+ BEGIN(HELP);
+}
+
+static void zconf_endhelp(void)
+{
+ zconflval.string = text;
+ BEGIN(INITIAL);
+}
+
+/*
+ * Try to open specified file with following names:
+ * ./name
+ * $(backport_srctree)/name
+ * The latter is used when srctree is separate from objtree
+ * when compiling the kernel.
+ * Return NULL if file is not found.
+ */
+FILE *zconf_fopen(const char *name)
+{
+ char *env, fullname[PATH_MAX+1];
+ FILE *f;
+
+ f = fopen(name, "r");
+ if (!f && name != NULL && name[0] != '/') {
+ env = getenv(SRCTREE);
+ if (env) {
+ sprintf(fullname, "%s/%s", env, name);
+ f = fopen(fullname, "r");
+ }
+ }
+ return f;
+}
+
+void zconf_initscan(const char *name)
+{
+ zconfin = zconf_fopen(name);
+ if (!zconfin) {
+ printf("can't find file %s\n", name);
+ exit(1);
+ }
+
+ current_buf = xmalloc(sizeof(*current_buf));
+ memset(current_buf, 0, sizeof(*current_buf));
+
+ current_file = file_lookup(name);
+ current_file->lineno = 1;
+}
+
+void zconf_nextfile(const char *name)
+{
+ struct file *iter;
+ struct file *file = file_lookup(name);
+ struct buffer *buf = xmalloc(sizeof(*buf));
+ memset(buf, 0, sizeof(*buf));
+
+ current_buf->state = YY_CURRENT_BUFFER;
+ zconfin = zconf_fopen(file->name);
+ if (!zconfin) {
+ printf("%s:%d: can't open file \"%s\"\n",
+ zconf_curname(), zconf_lineno(), file->name);
+ exit(1);
+ }
+ zconf_switch_to_buffer(zconf_create_buffer(zconfin,YY_BUF_SIZE));
+ buf->parent = current_buf;
+ current_buf = buf;
+
+ for (iter = current_file->parent; iter; iter = iter->parent ) {
+ if (!strcmp(current_file->name,iter->name) ) {
+ printf("%s:%d: recursive inclusion detected. "
+ "Inclusion path:\n current file : '%s'\n",
+ zconf_curname(), zconf_lineno(),
+ zconf_curname());
+ iter = current_file->parent;
+ while (iter && \
+ strcmp(iter->name,current_file->name)) {
+ printf(" included from: '%s:%d'\n",
+ iter->name, iter->lineno-1);
+ iter = iter->parent;
+ }
+ if (iter)
+ printf(" included from: '%s:%d'\n",
+ iter->name, iter->lineno+1);
+ exit(1);
+ }
+ }
+ file->lineno = 1;
+ file->parent = current_file;
+ current_file = file;
+}
+
+static void zconf_endfile(void)
+{
+ struct buffer *parent;
+
+ current_file = current_file->parent;
+
+ parent = current_buf->parent;
+ if (parent) {
+ fclose(zconfin);
+ zconf_delete_buffer(YY_CURRENT_BUFFER);
+ zconf_switch_to_buffer(parent->state);
+ }
+ free(current_buf);
+ current_buf = parent;
+}
+
+int zconf_lineno(void)
+{
+ return current_pos.lineno;
+}
+
+const char *zconf_curname(void)
+{
+ return current_pos.file ? current_pos.file->name : "<none>";
+}
+
diff --git a/kconf/zconf.tab.c b/kconf/zconf.tab.c
new file mode 100644
index 0000000..7a4d658
--- /dev/null
+++ b/kconf/zconf.tab.c
@@ -0,0 +1,2580 @@
+/* A Bison parser, made by GNU Bison 2.5.1. */
+
+/* Bison implementation for Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989-1990, 2000-2012 Free Software Foundation, Inc.
+
+ 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 3 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, see <http://www.gnu.org/licenses/>. */
+
+/* As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ special exception, which will cause the skeleton and the resulting
+ Bison output files to be licensed under the GNU General Public
+ License without this special exception.
+
+ This special exception was added by the Free Software Foundation in
+ version 2.2 of Bison. */
+
+/* C LALR(1) parser skeleton written by Richard Stallman, by
+ simplifying the original so-called "semantic" parser. */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+ infringing on user name space. This should be done even for local
+ variables, as they might otherwise be expanded by user macros.
+ There are some unavoidable exceptions within include files to
+ define necessary library symbols; they are noted "INFRINGES ON
+ USER NAME SPACE" below. */
+
+/* Identify Bison output. */
+#define YYBISON 1
+
+/* Bison version. */
+#define YYBISON_VERSION "2.5.1"
+
+/* Skeleton name. */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers. */
+#define YYPURE 0
+
+/* Push parsers. */
+#define YYPUSH 0
+
+/* Pull parsers. */
+#define YYPULL 1
+
+/* Using locations. */
+#define YYLSP_NEEDED 0
+
+/* Substitute the variable and function names. */
+#define yyparse zconfparse
+#define yylex zconflex
+#define yyerror zconferror
+#define yylval zconflval
+#define yychar zconfchar
+#define yydebug zconfdebug
+#define yynerrs zconfnerrs
+
+
+/* Copy the first part of user declarations. */
+
+
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+
+#include "lkc.h"
+
+#define printd(mask, fmt...) if (cdebug & (mask)) printf(fmt)
+
+#define PRINTD 0x0001
+#define DEBUG_PARSE 0x0002
+
+int cdebug = PRINTD;
+
+extern int zconflex(void);
+static void zconfprint(const char *err, ...);
+static void zconf_error(const char *err, ...);
+static void zconferror(const char *err);
+static bool zconf_endtoken(const struct kconf_id *id, int starttoken, int endtoken);
+
+struct symbol *symbol_hash[SYMBOL_HASHSIZE];
+
+static struct menu *current_menu, *current_entry;
+
+
+
+
+# ifndef YY_NULL
+# if defined __cplusplus && 201103L <= __cplusplus
+# define YY_NULL nullptr
+# else
+# define YY_NULL 0
+# endif
+# endif
+
+/* Enabling traces. */
+#ifndef YYDEBUG
+# define YYDEBUG 1
+#endif
+
+/* Enabling verbose error messages. */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 0
+#endif
+
+/* Enabling the token table. */
+#ifndef YYTOKEN_TABLE
+# define YYTOKEN_TABLE 0
+#endif
+
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ T_MAINMENU = 258,
+ T_MENU = 259,
+ T_ENDMENU = 260,
+ T_SOURCE = 261,
+ T_CHOICE = 262,
+ T_ENDCHOICE = 263,
+ T_COMMENT = 264,
+ T_CONFIG = 265,
+ T_MENUCONFIG = 266,
+ T_HELP = 267,
+ T_HELPTEXT = 268,
+ T_IF = 269,
+ T_ENDIF = 270,
+ T_DEPENDS = 271,
+ T_OPTIONAL = 272,
+ T_PROMPT = 273,
+ T_TYPE = 274,
+ T_DEFAULT = 275,
+ T_SELECT = 276,
+ T_RANGE = 277,
+ T_VISIBLE = 278,
+ T_OPTION = 279,
+ T_ON = 280,
+ T_WORD = 281,
+ T_WORD_QUOTE = 282,
+ T_UNEQUAL = 283,
+ T_LESS = 284,
+ T_LESS_EQUAL = 285,
+ T_GREATER = 286,
+ T_GREATER_EQUAL = 287,
+ T_CLOSE_PAREN = 288,
+ T_OPEN_PAREN = 289,
+ T_EOL = 290,
+ T_OR = 291,
+ T_AND = 292,
+ T_EQUAL = 293,
+ T_NOT = 294
+ };
+#endif
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+{
+
+
+ char *string;
+ struct file *file;
+ struct symbol *symbol;
+ struct expr *expr;
+ struct menu *menu;
+ const struct kconf_id *id;
+
+
+
+} YYSTYPE;
+# define YYSTYPE_IS_TRIVIAL 1
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+
+/* Copy the second part of user declarations. */
+
+
+/* Include zconf.hash.c here so it can see the token constants. */
+#include "zconf.hash.c"
+
+
+
+#ifdef short
+# undef short
+#endif
+
+#ifdef YYTYPE_UINT8
+typedef YYTYPE_UINT8 yytype_uint8;
+#else
+typedef unsigned char yytype_uint8;
+#endif
+
+#ifdef YYTYPE_INT8
+typedef YYTYPE_INT8 yytype_int8;
+#elif (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+typedef signed char yytype_int8;
+#else
+typedef short int yytype_int8;
+#endif
+
+#ifdef YYTYPE_UINT16
+typedef YYTYPE_UINT16 yytype_uint16;
+#else
+typedef unsigned short int yytype_uint16;
+#endif
+
+#ifdef YYTYPE_INT16
+typedef YYTYPE_INT16 yytype_int16;
+#else
+typedef short int yytype_int16;
+#endif
+
+#ifndef YYSIZE_T
+# ifdef __SIZE_TYPE__
+# define YYSIZE_T __SIZE_TYPE__
+# elif defined size_t
+# define YYSIZE_T size_t
+# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+# include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+# define YYSIZE_T size_t
+# else
+# define YYSIZE_T unsigned int
+# endif
+#endif
+
+#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
+
+#ifndef YY_
+# if defined YYENABLE_NLS && YYENABLE_NLS
+# if ENABLE_NLS
+# include <libintl.h> /* INFRINGES ON USER NAME SPACE */
+# define YY_(msgid) dgettext ("bison-runtime", msgid)
+# endif
+# endif
+# ifndef YY_
+# define YY_(msgid) msgid
+# endif
+#endif
+
+/* Suppress unused-variable warnings by "using" E. */
+#if ! defined lint || defined __GNUC__
+# define YYUSE(e) ((void) (e))
+#else
+# define YYUSE(e) /* empty */
+#endif
+
+/* Identity function, used to suppress warnings about constant conditions. */
+#ifndef lint
+# define YYID(n) (n)
+#else
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static int
+YYID (int yyi)
+#else
+static int
+YYID (yyi)
+ int yyi;
+#endif
+{
+ return yyi;
+}
+#endif
+
+#if ! defined yyoverflow || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols. */
+
+# ifdef YYSTACK_USE_ALLOCA
+# if YYSTACK_USE_ALLOCA
+# ifdef __GNUC__
+# define YYSTACK_ALLOC __builtin_alloca
+# elif defined __BUILTIN_VA_ARG_INCR
+# include <alloca.h> /* INFRINGES ON USER NAME SPACE */
+# elif defined _AIX
+# define YYSTACK_ALLOC __alloca
+# elif defined _MSC_VER
+# include <malloc.h> /* INFRINGES ON USER NAME SPACE */
+# define alloca _alloca
+# else
+# define YYSTACK_ALLOC alloca
+# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+ /* Use EXIT_SUCCESS as a witness for stdlib.h. */
+# ifndef EXIT_SUCCESS
+# define EXIT_SUCCESS 0
+# endif
+# endif
+# endif
+# endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+ /* Pacify GCC's `empty if-body' warning. */
+# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0))
+# ifndef YYSTACK_ALLOC_MAXIMUM
+ /* The OS might guarantee only one guard page at the bottom of the stack,
+ and a page size can be as small as 4096 bytes. So we cannot safely
+ invoke alloca (N) if N exceeds 4096. Use a slightly smaller number
+ to allow for a few compiler-allocated temporary stack slots. */
+# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
+# endif
+# else
+# define YYSTACK_ALLOC YYMALLOC
+# define YYSTACK_FREE YYFREE
+# ifndef YYSTACK_ALLOC_MAXIMUM
+# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
+# endif
+# if (defined __cplusplus && ! defined EXIT_SUCCESS \
+ && ! ((defined YYMALLOC || defined malloc) \
+ && (defined YYFREE || defined free)))
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# ifndef EXIT_SUCCESS
+# define EXIT_SUCCESS 0
+# endif
+# endif
+# ifndef YYMALLOC
+# define YYMALLOC malloc
+# if ! defined malloc && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# ifndef YYFREE
+# define YYFREE free
+# if ! defined free && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+void free (void *); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# endif
+#endif /* ! defined yyoverflow || YYERROR_VERBOSE */
+
+
+#if (! defined yyoverflow \
+ && (! defined __cplusplus \
+ || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member. */
+union yyalloc
+{
+ yytype_int16 yyss_alloc;
+ YYSTYPE yyvs_alloc;
+};
+
+/* The size of the maximum gap between one aligned stack and the next. */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+ N elements. */
+# define YYSTACK_BYTES(N) \
+ ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \
+ + YYSTACK_GAP_MAXIMUM)
+
+# define YYCOPY_NEEDED 1
+
+/* Relocate STACK from its old location to the new one. The
+ local variables YYSIZE and YYSTACKSIZE give the old and new number of
+ elements in the stack, and YYPTR gives the new location of the
+ stack. Advance YYPTR to a properly aligned location for the next
+ stack. */
+# define YYSTACK_RELOCATE(Stack_alloc, Stack) \
+ do \
+ { \
+ YYSIZE_T yynewbytes; \
+ YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \
+ Stack = &yyptr->Stack_alloc; \
+ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+ yyptr += yynewbytes / sizeof (*yyptr); \
+ } \
+ while (YYID (0))
+
+#endif
+
+#if defined YYCOPY_NEEDED && YYCOPY_NEEDED
+/* Copy COUNT objects from SRC to DST. The source and destination do
+ not overlap. */
+# ifndef YYCOPY
+# if defined __GNUC__ && 1 < __GNUC__
+# define YYCOPY(Dst, Src, Count) \
+ __builtin_memcpy (Dst, Src, (Count) * sizeof (*(Src)))
+# else
+# define YYCOPY(Dst, Src, Count) \
+ do \
+ { \
+ YYSIZE_T yyi; \
+ for (yyi = 0; yyi < (Count); yyi++) \
+ (Dst)[yyi] = (Src)[yyi]; \
+ } \
+ while (YYID (0))
+# endif
+# endif
+#endif /* !YYCOPY_NEEDED */
+
+/* YYFINAL -- State number of the termination state. */
+#define YYFINAL 11
+/* YYLAST -- Last index in YYTABLE. */
+#define YYLAST 298
+
+/* YYNTOKENS -- Number of terminals. */
+#define YYNTOKENS 40
+/* YYNNTS -- Number of nonterminals. */
+#define YYNNTS 50
+/* YYNRULES -- Number of rules. */
+#define YYNRULES 122
+/* YYNRULES -- Number of states. */
+#define YYNSTATES 199
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */
+#define YYUNDEFTOK 2
+#define YYMAXUTOK 294
+
+#define YYTRANSLATE(YYX) \
+ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */
+static const yytype_uint8 yytranslate[] =
+{
+ 0, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 26, 27, 28, 29, 30, 31, 32, 33, 34,
+ 35, 36, 37, 38, 39
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+ YYRHS. */
+static const yytype_uint16 yyprhs[] =
+{
+ 0, 0, 3, 6, 8, 11, 13, 14, 17, 20,
+ 23, 26, 31, 36, 40, 42, 44, 46, 48, 50,
+ 52, 54, 56, 58, 60, 62, 64, 66, 68, 72,
+ 75, 79, 82, 86, 89, 90, 93, 96, 99, 102,
+ 105, 108, 112, 117, 122, 127, 133, 137, 138, 142,
+ 143, 146, 150, 153, 155, 159, 160, 163, 166, 169,
+ 172, 175, 180, 184, 187, 192, 193, 196, 200, 202,
+ 206, 207, 210, 213, 216, 220, 224, 228, 230, 234,
+ 235, 238, 241, 244, 248, 252, 255, 258, 261, 262,
+ 265, 268, 271, 276, 277, 280, 283, 286, 287, 290,
+ 292, 294, 297, 300, 303, 305, 308, 309, 312, 314,
+ 318, 322, 326, 330, 334, 338, 342, 345, 349, 353,
+ 355, 357, 358
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS. */
+static const yytype_int8 yyrhs[] =
+{
+ 41, 0, -1, 85, 42, -1, 42, -1, 67, 43,
+ -1, 43, -1, -1, 43, 45, -1, 43, 59, -1,
+ 43, 71, -1, 43, 84, -1, 43, 26, 1, 35,
+ -1, 43, 44, 1, 35, -1, 43, 1, 35, -1,
+ 16, -1, 18, -1, 19, -1, 21, -1, 17, -1,
+ 22, -1, 20, -1, 23, -1, 35, -1, 65, -1,
+ 75, -1, 48, -1, 50, -1, 73, -1, 26, 1,
+ 35, -1, 1, 35, -1, 10, 26, 35, -1, 47,
+ 51, -1, 11, 26, 35, -1, 49, 51, -1, -1,
+ 51, 52, -1, 51, 53, -1, 51, 79, -1, 51,
+ 77, -1, 51, 46, -1, 51, 35, -1, 19, 82,
+ 35, -1, 18, 83, 86, 35, -1, 20, 87, 86,
+ 35, -1, 21, 26, 86, 35, -1, 22, 88, 88,
+ 86, 35, -1, 24, 54, 35, -1, -1, 54, 26,
+ 55, -1, -1, 38, 83, -1, 7, 89, 35, -1,
+ 56, 60, -1, 84, -1, 57, 62, 58, -1, -1,
+ 60, 61, -1, 60, 79, -1, 60, 77, -1, 60,
+ 35, -1, 60, 46, -1, 18, 83, 86, 35, -1,
+ 19, 82, 35, -1, 17, 35, -1, 20, 26, 86,
+ 35, -1, -1, 62, 45, -1, 14, 87, 85, -1,
+ 84, -1, 63, 66, 64, -1, -1, 66, 45, -1,
+ 66, 71, -1, 66, 59, -1, 3, 83, 85, -1,
+ 4, 83, 35, -1, 68, 80, 78, -1, 84, -1,
+ 69, 72, 70, -1, -1, 72, 45, -1, 72, 71,
+ -1, 72, 59, -1, 6, 83, 35, -1, 9, 83,
+ 35, -1, 74, 78, -1, 12, 35, -1, 76, 13,
+ -1, -1, 78, 79, -1, 78, 35, -1, 78, 46,
+ -1, 16, 25, 87, 35, -1, -1, 80, 81, -1,
+ 80, 35, -1, 23, 86, -1, -1, 83, 86, -1,
+ 26, -1, 27, -1, 5, 35, -1, 8, 35, -1,
+ 15, 35, -1, 35, -1, 85, 35, -1, -1, 14,
+ 87, -1, 88, -1, 88, 29, 88, -1, 88, 30,
+ 88, -1, 88, 31, 88, -1, 88, 32, 88, -1,
+ 88, 38, 88, -1, 88, 28, 88, -1, 34, 87,
+ 33, -1, 39, 87, -1, 87, 36, 87, -1, 87,
+ 37, 87, -1, 26, -1, 27, -1, -1, 26, -1
+};
+
+/* YYRLINE[YYN] -- source line where rule number YYN was defined. */
+static const yytype_uint16 yyrline[] =
+{
+ 0, 108, 108, 108, 110, 110, 112, 114, 115, 116,
+ 117, 118, 119, 123, 127, 127, 127, 127, 127, 127,
+ 127, 127, 131, 132, 133, 134, 135, 136, 140, 141,
+ 147, 155, 161, 169, 179, 181, 182, 183, 184, 185,
+ 186, 189, 197, 203, 213, 219, 225, 228, 230, 241,
+ 242, 247, 256, 261, 269, 272, 274, 275, 276, 277,
+ 278, 281, 287, 298, 304, 314, 316, 321, 329, 337,
+ 340, 342, 343, 344, 349, 356, 363, 368, 376, 379,
+ 381, 382, 383, 386, 394, 401, 408, 414, 421, 423,
+ 424, 425, 428, 436, 438, 439, 442, 449, 451, 456,
+ 457, 460, 461, 462, 466, 467, 470, 471, 474, 475,
+ 476, 477, 478, 479, 480, 481, 482, 483, 484, 487,
+ 488, 491, 492
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE
+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+ First, the terminals, then, starting at YYNTOKENS, nonterminals. */
+static const char *const yytname[] =
+{
+ "$end", "error", "$undefined", "T_MAINMENU", "T_MENU", "T_ENDMENU",
+ "T_SOURCE", "T_CHOICE", "T_ENDCHOICE", "T_COMMENT", "T_CONFIG",
+ "T_MENUCONFIG", "T_HELP", "T_HELPTEXT", "T_IF", "T_ENDIF", "T_DEPENDS",
+ "T_OPTIONAL", "T_PROMPT", "T_TYPE", "T_DEFAULT", "T_SELECT", "T_RANGE",
+ "T_VISIBLE", "T_OPTION", "T_ON", "T_WORD", "T_WORD_QUOTE", "T_UNEQUAL",
+ "T_LESS", "T_LESS_EQUAL", "T_GREATER", "T_GREATER_EQUAL",
+ "T_CLOSE_PAREN", "T_OPEN_PAREN", "T_EOL", "T_OR", "T_AND", "T_EQUAL",
+ "T_NOT", "$accept", "input", "start", "stmt_list", "option_name",
+ "common_stmt", "option_error", "config_entry_start", "config_stmt",
+ "menuconfig_entry_start", "menuconfig_stmt", "config_option_list",
+ "config_option", "symbol_option", "symbol_option_list",
+ "symbol_option_arg", "choice", "choice_entry", "choice_end",
+ "choice_stmt", "choice_option_list", "choice_option", "choice_block",
+ "if_entry", "if_end", "if_stmt", "if_block", "mainmenu_stmt", "menu",
+ "menu_entry", "menu_end", "menu_stmt", "menu_block", "source_stmt",
+ "comment", "comment_stmt", "help_start", "help", "depends_list",
+ "depends", "visibility_list", "visible", "prompt_stmt_opt", "prompt",
+ "end", "nl", "if_expr", "expr", "symbol", "word_opt", YY_NULL
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+ token YYLEX-NUM. */
+static const yytype_uint16 yytoknum[] =
+{
+ 0, 256, 257, 258, 259, 260, 261, 262, 263, 264,
+ 265, 266, 267, 268, 269, 270, 271, 272, 273, 274,
+ 275, 276, 277, 278, 279, 280, 281, 282, 283, 284,
+ 285, 286, 287, 288, 289, 290, 291, 292, 293, 294
+};
+# endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */
+static const yytype_uint8 yyr1[] =
+{
+ 0, 40, 41, 41, 42, 42, 43, 43, 43, 43,
+ 43, 43, 43, 43, 44, 44, 44, 44, 44, 44,
+ 44, 44, 45, 45, 45, 45, 45, 45, 46, 46,
+ 47, 48, 49, 50, 51, 51, 51, 51, 51, 51,
+ 51, 52, 52, 52, 52, 52, 53, 54, 54, 55,
+ 55, 56, 57, 58, 59, 60, 60, 60, 60, 60,
+ 60, 61, 61, 61, 61, 62, 62, 63, 64, 65,
+ 66, 66, 66, 66, 67, 68, 69, 70, 71, 72,
+ 72, 72, 72, 73, 74, 75, 76, 77, 78, 78,
+ 78, 78, 79, 80, 80, 80, 81, 82, 82, 83,
+ 83, 84, 84, 84, 85, 85, 86, 86, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87, 87, 88,
+ 88, 89, 89
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */
+static const yytype_uint8 yyr2[] =
+{
+ 0, 2, 2, 1, 2, 1, 0, 2, 2, 2,
+ 2, 4, 4, 3, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 3, 2,
+ 3, 2, 3, 2, 0, 2, 2, 2, 2, 2,
+ 2, 3, 4, 4, 4, 5, 3, 0, 3, 0,
+ 2, 3, 2, 1, 3, 0, 2, 2, 2, 2,
+ 2, 4, 3, 2, 4, 0, 2, 3, 1, 3,
+ 0, 2, 2, 2, 3, 3, 3, 1, 3, 0,
+ 2, 2, 2, 3, 3, 2, 2, 2, 0, 2,
+ 2, 2, 4, 0, 2, 2, 2, 0, 2, 1,
+ 1, 2, 2, 2, 1, 2, 0, 2, 1, 3,
+ 3, 3, 3, 3, 3, 3, 2, 3, 3, 1,
+ 1, 0, 1
+};
+
+/* YYDEFACT[STATE-NAME] -- Default reduction number in state STATE-NUM.
+ Performed when YYTABLE doesn't specify something else to do. Zero
+ means the default is an error. */
+static const yytype_uint8 yydefact[] =
+{
+ 6, 0, 104, 0, 3, 0, 6, 6, 99, 100,
+ 0, 1, 0, 0, 0, 0, 121, 0, 0, 0,
+ 0, 0, 0, 14, 18, 15, 16, 20, 17, 19,
+ 21, 0, 22, 0, 7, 34, 25, 34, 26, 55,
+ 65, 8, 70, 23, 93, 79, 9, 27, 88, 24,
+ 10, 0, 105, 2, 74, 13, 0, 101, 0, 122,
+ 0, 102, 0, 0, 0, 119, 120, 0, 0, 0,
+ 108, 103, 0, 0, 0, 0, 0, 0, 0, 88,
+ 0, 0, 75, 83, 51, 84, 30, 32, 0, 116,
+ 0, 0, 67, 0, 0, 0, 0, 0, 0, 11,
+ 12, 0, 0, 0, 0, 97, 0, 0, 0, 47,
+ 0, 40, 39, 35, 36, 0, 38, 37, 0, 0,
+ 97, 0, 59, 60, 56, 58, 57, 66, 54, 53,
+ 71, 73, 69, 72, 68, 106, 95, 0, 94, 80,
+ 82, 78, 81, 77, 90, 91, 89, 115, 117, 118,
+ 114, 109, 110, 111, 112, 113, 29, 86, 0, 106,
+ 0, 106, 106, 106, 0, 0, 0, 87, 63, 106,
+ 0, 106, 0, 96, 0, 0, 41, 98, 0, 0,
+ 106, 49, 46, 28, 0, 62, 0, 107, 92, 42,
+ 43, 44, 0, 0, 48, 61, 64, 45, 50
+};
+
+/* YYDEFGOTO[NTERM-NUM]. */
+static const yytype_int16 yydefgoto[] =
+{
+ -1, 3, 4, 5, 33, 34, 112, 35, 36, 37,
+ 38, 74, 113, 114, 165, 194, 39, 40, 128, 41,
+ 76, 124, 77, 42, 132, 43, 78, 6, 44, 45,
+ 141, 46, 80, 47, 48, 49, 115, 116, 81, 117,
+ 79, 138, 160, 161, 50, 7, 173, 69, 70, 60
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+ STATE-NUM. */
+#define YYPACT_NINF -91
+static const yytype_int16 yypact[] =
+{
+ 19, 37, -91, 13, -91, 79, -91, 20, -91, -91,
+ -16, -91, 21, 37, 25, 37, 41, 36, 37, 78,
+ 83, 31, 56, -91, -91, -91, -91, -91, -91, -91,
+ -91, 116, -91, 127, -91, -91, -91, -91, -91, -91,
+ -91, -91, -91, -91, -91, -91, -91, -91, -91, -91,
+ -91, 147, -91, -91, 105, -91, 109, -91, 111, -91,
+ 114, -91, 136, 137, 142, -91, -91, 31, 31, 76,
+ 254, -91, 143, 146, 27, 115, 207, 258, 243, -14,
+ 243, 179, -91, -91, -91, -91, -91, -91, -7, -91,
+ 31, 31, 105, 51, 51, 51, 51, 51, 51, -91,
+ -91, 156, 168, 181, 37, 37, 31, 178, 51, -91,
+ 206, -91, -91, -91, -91, 196, -91, -91, 175, 37,
+ 37, 185, -91, -91, -91, -91, -91, -91, -91, -91,
+ -91, -91, -91, -91, -91, 214, -91, 230, -91, -91,
+ -91, -91, -91, -91, -91, -91, -91, -91, 183, -91,
+ -91, -91, -91, -91, -91, -91, -91, -91, 31, 214,
+ 194, 214, 45, 214, 51, 26, 195, -91, -91, 214,
+ 197, 214, 31, -91, 139, 208, -91, -91, 220, 224,
+ 214, 222, -91, -91, 226, -91, 227, 123, -91, -91,
+ -91, -91, 235, 37, -91, -91, -91, -91, -91
+};
+
+/* YYPGOTO[NTERM-NUM]. */
+static const yytype_int16 yypgoto[] =
+{
+ -91, -91, 264, 268, -91, 30, -65, -91, -91, -91,
+ -91, 238, -91, -91, -91, -91, -91, -91, -91, -12,
+ -91, -91, -91, -91, -91, -91, -91, -91, -91, -91,
+ -91, -5, -91, -91, -91, -91, -91, 200, 209, -61,
+ -91, -91, 170, -1, 65, 0, 118, -66, -90, -91
+};
+
+/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If
+ positive, shift that token. If negative, reduce the rule which
+ number is the opposite. If YYTABLE_NINF, syntax error. */
+#define YYTABLE_NINF -86
+static const yytype_int16 yytable[] =
+{
+ 10, 88, 89, 150, 151, 152, 153, 154, 155, 135,
+ 54, 123, 56, 11, 58, 126, 145, 62, 164, 2,
+ 146, 136, 1, 1, 148, 149, 147, -31, 101, 90,
+ 91, -31, -31, -31, -31, -31, -31, -31, -31, 102,
+ 162, -31, -31, 103, -31, 104, 105, 106, 107, 108,
+ -31, 109, 181, 110, 2, 52, 55, 65, 66, 172,
+ 57, 182, 111, 8, 9, 67, 131, 59, 140, 92,
+ 68, 61, 145, 133, 180, 142, 146, 65, 66, -5,
+ 12, 90, 91, 13, 14, 15, 16, 17, 18, 19,
+ 20, 71, 174, 21, 22, 23, 24, 25, 26, 27,
+ 28, 29, 30, 159, 63, 31, 187, 127, 130, 64,
+ 139, 2, 90, 91, 32, -33, 101, 72, 169, -33,
+ -33, -33, -33, -33, -33, -33, -33, 102, 73, -33,
+ -33, 103, -33, 104, 105, 106, 107, 108, -33, 109,
+ 52, 110, 129, 134, 82, 143, 83, -4, 12, 84,
+ 111, 13, 14, 15, 16, 17, 18, 19, 20, 90,
+ 91, 21, 22, 23, 24, 25, 26, 27, 28, 29,
+ 30, 85, 86, 31, 188, 90, 91, 87, 99, -85,
+ 101, 100, 32, -85, -85, -85, -85, -85, -85, -85,
+ -85, 156, 198, -85, -85, 103, -85, -85, -85, -85,
+ -85, -85, -85, 157, 163, 110, 158, 166, 101, 167,
+ 168, 171, -52, -52, 144, -52, -52, -52, -52, 102,
+ 91, -52, -52, 103, 118, 119, 120, 121, 172, 176,
+ 183, 101, 185, 110, -76, -76, -76, -76, -76, -76,
+ -76, -76, 122, 189, -76, -76, 103, 13, 14, 15,
+ 16, 17, 18, 19, 20, 190, 110, 21, 22, 191,
+ 193, 195, 196, 14, 15, 144, 17, 18, 19, 20,
+ 197, 53, 21, 22, 51, 75, 125, 175, 32, 177,
+ 178, 179, 93, 94, 95, 96, 97, 184, 137, 186,
+ 170, 0, 98, 32, 0, 0, 0, 0, 192
+};
+
+#define yypact_value_is_default(yystate) \
+ ((yystate) == (-91))
+
+#define yytable_value_is_error(yytable_value) \
+ YYID (0)
+
+static const yytype_int16 yycheck[] =
+{
+ 1, 67, 68, 93, 94, 95, 96, 97, 98, 23,
+ 10, 76, 13, 0, 15, 76, 81, 18, 108, 35,
+ 81, 35, 3, 3, 90, 91, 33, 0, 1, 36,
+ 37, 4, 5, 6, 7, 8, 9, 10, 11, 12,
+ 106, 14, 15, 16, 17, 18, 19, 20, 21, 22,
+ 23, 24, 26, 26, 35, 35, 35, 26, 27, 14,
+ 35, 35, 35, 26, 27, 34, 78, 26, 80, 69,
+ 39, 35, 137, 78, 164, 80, 137, 26, 27, 0,
+ 1, 36, 37, 4, 5, 6, 7, 8, 9, 10,
+ 11, 35, 158, 14, 15, 16, 17, 18, 19, 20,
+ 21, 22, 23, 104, 26, 26, 172, 77, 78, 26,
+ 80, 35, 36, 37, 35, 0, 1, 1, 119, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 1, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 35, 26, 77, 78, 35, 80, 35, 0, 1, 35,
+ 35, 4, 5, 6, 7, 8, 9, 10, 11, 36,
+ 37, 14, 15, 16, 17, 18, 19, 20, 21, 22,
+ 23, 35, 35, 26, 35, 36, 37, 35, 35, 0,
+ 1, 35, 35, 4, 5, 6, 7, 8, 9, 10,
+ 11, 35, 193, 14, 15, 16, 17, 18, 19, 20,
+ 21, 22, 23, 35, 26, 26, 25, 1, 1, 13,
+ 35, 26, 5, 6, 35, 8, 9, 10, 11, 12,
+ 37, 14, 15, 16, 17, 18, 19, 20, 14, 35,
+ 35, 1, 35, 26, 4, 5, 6, 7, 8, 9,
+ 10, 11, 35, 35, 14, 15, 16, 4, 5, 6,
+ 7, 8, 9, 10, 11, 35, 26, 14, 15, 35,
+ 38, 35, 35, 5, 6, 35, 8, 9, 10, 11,
+ 35, 7, 14, 15, 6, 37, 76, 159, 35, 161,
+ 162, 163, 28, 29, 30, 31, 32, 169, 79, 171,
+ 120, -1, 38, 35, -1, -1, -1, -1, 180
+};
+
+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+ symbol of state STATE-NUM. */
+static const yytype_uint8 yystos[] =
+{
+ 0, 3, 35, 41, 42, 43, 67, 85, 26, 27,
+ 83, 0, 1, 4, 5, 6, 7, 8, 9, 10,
+ 11, 14, 15, 16, 17, 18, 19, 20, 21, 22,
+ 23, 26, 35, 44, 45, 47, 48, 49, 50, 56,
+ 57, 59, 63, 65, 68, 69, 71, 73, 74, 75,
+ 84, 43, 35, 42, 85, 35, 83, 35, 83, 26,
+ 89, 35, 83, 26, 26, 26, 27, 34, 39, 87,
+ 88, 35, 1, 1, 51, 51, 60, 62, 66, 80,
+ 72, 78, 35, 35, 35, 35, 35, 35, 87, 87,
+ 36, 37, 85, 28, 29, 30, 31, 32, 38, 35,
+ 35, 1, 12, 16, 18, 19, 20, 21, 22, 24,
+ 26, 35, 46, 52, 53, 76, 77, 79, 17, 18,
+ 19, 20, 35, 46, 61, 77, 79, 45, 58, 84,
+ 45, 59, 64, 71, 84, 23, 35, 78, 81, 45,
+ 59, 70, 71, 84, 35, 46, 79, 33, 87, 87,
+ 88, 88, 88, 88, 88, 88, 35, 35, 25, 83,
+ 82, 83, 87, 26, 88, 54, 1, 13, 35, 83,
+ 82, 26, 14, 86, 87, 86, 35, 86, 86, 86,
+ 88, 26, 35, 35, 86, 35, 86, 87, 35, 35,
+ 35, 35, 86, 38, 55, 35, 35, 35, 83
+};
+
+#define yyerrok (yyerrstatus = 0)
+#define yyclearin (yychar = YYEMPTY)
+#define YYEMPTY (-2)
+#define YYEOF 0
+
+#define YYACCEPT goto yyacceptlab
+#define YYABORT goto yyabortlab
+#define YYERROR goto yyerrorlab
+
+
+/* Like YYERROR except do call yyerror. This remains here temporarily
+ to ease the transition to the new meaning of YYERROR, for GCC.
+ Once GCC version 2 has supplanted version 1, this can go. However,
+ YYFAIL appears to be in use. Nevertheless, it is formally deprecated
+ in Bison 2.4.2's NEWS entry, where a plan to phase it out is
+ discussed. */
+
+#define YYFAIL goto yyerrlab
+#if defined YYFAIL
+ /* This is here to suppress warnings from the GCC cpp's
+ -Wunused-macros. Normally we don't worry about that warning, but
+ some users do, and we want to make it easy for users to remove
+ YYFAIL uses, which will produce warnings from Bison 2.5. */
+#endif
+
+#define YYRECOVERING() (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value) \
+do \
+ if (yychar == YYEMPTY) \
+ { \
+ yychar = (Token); \
+ yylval = (Value); \
+ YYPOPSTACK (yylen); \
+ yystate = *yyssp; \
+ goto yybackup; \
+ } \
+ else \
+ { \
+ yyerror (YY_("syntax error: cannot back up")); \
+ YYERROR; \
+ } \
+while (YYID (0))
+
+
+#define YYTERROR 1
+#define YYERRCODE 256
+
+
+/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N].
+ If N is 0, then set CURRENT to the empty location which ends
+ the previous symbol: RHS[0] (always defined). */
+
+#define YYRHSLOC(Rhs, K) ((Rhs)[K])
+#ifndef YYLLOC_DEFAULT
+# define YYLLOC_DEFAULT(Current, Rhs, N) \
+ do \
+ if (YYID (N)) \
+ { \
+ (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \
+ (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \
+ (Current).last_line = YYRHSLOC (Rhs, N).last_line; \
+ (Current).last_column = YYRHSLOC (Rhs, N).last_column; \
+ } \
+ else \
+ { \
+ (Current).first_line = (Current).last_line = \
+ YYRHSLOC (Rhs, 0).last_line; \
+ (Current).first_column = (Current).last_column = \
+ YYRHSLOC (Rhs, 0).last_column; \
+ } \
+ while (YYID (0))
+#endif
+
+
+/* This macro is provided for backward compatibility. */
+
+#ifndef YY_LOCATION_PRINT
+# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+#endif
+
+
+/* YYLEX -- calling `yylex' with the right arguments. */
+
+#ifdef YYLEX_PARAM
+# define YYLEX yylex (YYLEX_PARAM)
+#else
+# define YYLEX yylex ()
+#endif
+
+/* Enable debugging if requested. */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+# include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+# define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args) \
+do { \
+ if (yydebug) \
+ YYFPRINTF Args; \
+} while (YYID (0))
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \
+do { \
+ if (yydebug) \
+ { \
+ YYFPRINTF (stderr, "%s ", Title); \
+ yy_symbol_print (stderr, \
+ Type, Value); \
+ YYFPRINTF (stderr, "\n"); \
+ } \
+} while (YYID (0))
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+#else
+static void
+yy_symbol_value_print (yyoutput, yytype, yyvaluep)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE const * const yyvaluep;
+#endif
+{
+ FILE *yyo = yyoutput;
+ YYUSE (yyo);
+ if (!yyvaluep)
+ return;
+# ifdef YYPRINT
+ if (yytype < YYNTOKENS)
+ YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# else
+ YYUSE (yyoutput);
+# endif
+ switch (yytype)
+ {
+ default:
+ break;
+ }
+}
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+#else
+static void
+yy_symbol_print (yyoutput, yytype, yyvaluep)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE const * const yyvaluep;
+#endif
+{
+ if (yytype < YYNTOKENS)
+ YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
+ else
+ YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+
+ yy_symbol_value_print (yyoutput, yytype, yyvaluep);
+ YYFPRINTF (yyoutput, ")");
+}
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included). |
+`------------------------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop)
+#else
+static void
+yy_stack_print (yybottom, yytop)
+ yytype_int16 *yybottom;
+ yytype_int16 *yytop;
+#endif
+{
+ YYFPRINTF (stderr, "Stack now");
+ for (; yybottom <= yytop; yybottom++)
+ {
+ int yybot = *yybottom;
+ YYFPRINTF (stderr, " %d", yybot);
+ }
+ YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top) \
+do { \
+ if (yydebug) \
+ yy_stack_print ((Bottom), (Top)); \
+} while (YYID (0))
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced. |
+`------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_reduce_print (YYSTYPE *yyvsp, int yyrule)
+#else
+static void
+yy_reduce_print (yyvsp, yyrule)
+ YYSTYPE *yyvsp;
+ int yyrule;
+#endif
+{
+ int yynrhs = yyr2[yyrule];
+ int yyi;
+ unsigned long int yylno = yyrline[yyrule];
+ YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
+ yyrule - 1, yylno);
+ /* The symbols being reduced. */
+ for (yyi = 0; yyi < yynrhs; yyi++)
+ {
+ YYFPRINTF (stderr, " $%d = ", yyi + 1);
+ yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi],
+ &(yyvsp[(yyi + 1) - (yynrhs)])
+ );
+ YYFPRINTF (stderr, "\n");
+ }
+}
+
+# define YY_REDUCE_PRINT(Rule) \
+do { \
+ if (yydebug) \
+ yy_reduce_print (yyvsp, Rule); \
+} while (YYID (0))
+
+/* Nonzero means print parse trace. It is left uninitialized so that
+ multiple parsers can coexist. */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks. */
+#ifndef YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+ if the built-in stack extension method is used).
+
+ Do not make this value too large; the results are undefined if
+ YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
+ evaluated with infinite-precision integer arithmetic. */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+# if defined __GLIBC__ && defined _STRING_H
+# define yystrlen strlen
+# else
+/* Return the length of YYSTR. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static YYSIZE_T
+yystrlen (const char *yystr)
+#else
+static YYSIZE_T
+yystrlen (yystr)
+ const char *yystr;
+#endif
+{
+ YYSIZE_T yylen;
+ for (yylen = 0; yystr[yylen]; yylen++)
+ continue;
+ return yylen;
+}
+# endif
+# endif
+
+# ifndef yystpcpy
+# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
+# define yystpcpy stpcpy
+# else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+ YYDEST. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static char *
+yystpcpy (char *yydest, const char *yysrc)
+#else
+static char *
+yystpcpy (yydest, yysrc)
+ char *yydest;
+ const char *yysrc;
+#endif
+{
+ char *yyd = yydest;
+ const char *yys = yysrc;
+
+ while ((*yyd++ = *yys++) != '\0')
+ continue;
+
+ return yyd - 1;
+}
+# endif
+# endif
+
+# ifndef yytnamerr
+/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
+ quotes and backslashes, so that it's suitable for yyerror. The
+ heuristic is that double-quoting is unnecessary unless the string
+ contains an apostrophe, a comma, or backslash (other than
+ backslash-backslash). YYSTR is taken from yytname. If YYRES is
+ null, do not copy; instead, return the length of what the result
+ would have been. */
+static YYSIZE_T
+yytnamerr (char *yyres, const char *yystr)
+{
+ if (*yystr == '"')
+ {
+ YYSIZE_T yyn = 0;
+ char const *yyp = yystr;
+
+ for (;;)
+ switch (*++yyp)
+ {
+ case '\'':
+ case ',':
+ goto do_not_strip_quotes;
+
+ case '\\':
+ if (*++yyp != '\\')
+ goto do_not_strip_quotes;
+ /* Fall through. */
+ default:
+ if (yyres)
+ yyres[yyn] = *yyp;
+ yyn++;
+ break;
+
+ case '"':
+ if (yyres)
+ yyres[yyn] = '\0';
+ return yyn;
+ }
+ do_not_strip_quotes: ;
+ }
+
+ if (! yyres)
+ return yystrlen (yystr);
+
+ return yystpcpy (yyres, yystr) - yyres;
+}
+# endif
+
+/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message
+ about the unexpected token YYTOKEN for the state stack whose top is
+ YYSSP.
+
+ Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is
+ not large enough to hold the message. In that case, also set
+ *YYMSG_ALLOC to the required number of bytes. Return 2 if the
+ required number of bytes is too large to store. */
+static int
+yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg,
+ yytype_int16 *yyssp, int yytoken)
+{
+ YYSIZE_T yysize0 = yytnamerr (YY_NULL, yytname[yytoken]);
+ YYSIZE_T yysize = yysize0;
+ YYSIZE_T yysize1;
+ enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
+ /* Internationalized format string. */
+ const char *yyformat = YY_NULL;
+ /* Arguments of yyformat. */
+ char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
+ /* Number of reported tokens (one for the "unexpected", one per
+ "expected"). */
+ int yycount = 0;
+
+ /* There are many possibilities here to consider:
+ - Assume YYFAIL is not used. It's too flawed to consider. See
+ <http://lists.gnu.org/archive/html/bison-patches/2009-12/msg00024.html>
+ for details. YYERROR is fine as it does not invoke this
+ function.
+ - If this state is a consistent state with a default action, then
+ the only way this function was invoked is if the default action
+ is an error action. In that case, don't check for expected
+ tokens because there are none.
+ - The only way there can be no lookahead present (in yychar) is if
+ this state is a consistent state with a default action. Thus,
+ detecting the absence of a lookahead is sufficient to determine
+ that there is no unexpected or expected token to report. In that
+ case, just report a simple "syntax error".
+ - Don't assume there isn't a lookahead just because this state is a
+ consistent state with a default action. There might have been a
+ previous inconsistent state, consistent state with a non-default
+ action, or user semantic action that manipulated yychar.
+ - Of course, the expected token list depends on states to have
+ correct lookahead information, and it depends on the parser not
+ to perform extra reductions after fetching a lookahead from the
+ scanner and before detecting a syntax error. Thus, state merging
+ (from LALR or IELR) and default reductions corrupt the expected
+ token list. However, the list is correct for canonical LR with
+ one exception: it will still contain any token that will not be
+ accepted due to an error action in a later state.
+ */
+ if (yytoken != YYEMPTY)
+ {
+ int yyn = yypact[*yyssp];
+ yyarg[yycount++] = yytname[yytoken];
+ if (!yypact_value_is_default (yyn))
+ {
+ /* Start YYX at -YYN if negative to avoid negative indexes in
+ YYCHECK. In other words, skip the first -YYN actions for
+ this state because they are default actions. */
+ int yyxbegin = yyn < 0 ? -yyn : 0;
+ /* Stay within bounds of both yycheck and yytname. */
+ int yychecklim = YYLAST - yyn + 1;
+ int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+ int yyx;
+
+ for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+ if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR
+ && !yytable_value_is_error (yytable[yyx + yyn]))
+ {
+ if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
+ {
+ yycount = 1;
+ yysize = yysize0;
+ break;
+ }
+ yyarg[yycount++] = yytname[yyx];
+ yysize1 = yysize + yytnamerr (YY_NULL, yytname[yyx]);
+ if (! (yysize <= yysize1
+ && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+ return 2;
+ yysize = yysize1;
+ }
+ }
+ }
+
+ switch (yycount)
+ {
+# define YYCASE_(N, S) \
+ case N: \
+ yyformat = S; \
+ break
+ YYCASE_(0, YY_("syntax error"));
+ YYCASE_(1, YY_("syntax error, unexpected %s"));
+ YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s"));
+ YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s"));
+ YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s"));
+ YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"));
+# undef YYCASE_
+ }
+
+ yysize1 = yysize + yystrlen (yyformat);
+ if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+ return 2;
+ yysize = yysize1;
+
+ if (*yymsg_alloc < yysize)
+ {
+ *yymsg_alloc = 2 * yysize;
+ if (! (yysize <= *yymsg_alloc
+ && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM))
+ *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM;
+ return 1;
+ }
+
+ /* Avoid sprintf, as that infringes on the user's name space.
+ Don't have undefined behavior even if the translation
+ produced a string with the wrong number of "%s"s. */
+ {
+ char *yyp = *yymsg;
+ int yyi = 0;
+ while ((*yyp = *yyformat) != '\0')
+ if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount)
+ {
+ yyp += yytnamerr (yyp, yyarg[yyi++]);
+ yyformat += 2;
+ }
+ else
+ {
+ yyp++;
+ yyformat++;
+ }
+ }
+ return 0;
+}
+#endif /* YYERROR_VERBOSE */
+
+/*-----------------------------------------------.
+| Release the memory associated to this symbol. |
+`-----------------------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep)
+#else
+static void
+yydestruct (yymsg, yytype, yyvaluep)
+ const char *yymsg;
+ int yytype;
+ YYSTYPE *yyvaluep;
+#endif
+{
+ YYUSE (yyvaluep);
+
+ if (!yymsg)
+ yymsg = "Deleting";
+ YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+
+ switch (yytype)
+ {
+ case 57: /* "choice_entry" */
+
+ {
+ fprintf(stderr, "%s:%d: missing end statement for this entry\n",
+ (yyvaluep->menu)->file->name, (yyvaluep->menu)->lineno);
+ if (current_menu == (yyvaluep->menu))
+ menu_end_menu();
+};
+
+ break;
+ case 63: /* "if_entry" */
+
+ {
+ fprintf(stderr, "%s:%d: missing end statement for this entry\n",
+ (yyvaluep->menu)->file->name, (yyvaluep->menu)->lineno);
+ if (current_menu == (yyvaluep->menu))
+ menu_end_menu();
+};
+
+ break;
+ case 69: /* "menu_entry" */
+
+ {
+ fprintf(stderr, "%s:%d: missing end statement for this entry\n",
+ (yyvaluep->menu)->file->name, (yyvaluep->menu)->lineno);
+ if (current_menu == (yyvaluep->menu))
+ menu_end_menu();
+};
+
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+/* Prevent warnings from -Wmissing-prototypes. */
+#ifdef YYPARSE_PARAM
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void *YYPARSE_PARAM);
+#else
+int yyparse ();
+#endif
+#else /* ! YYPARSE_PARAM */
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void);
+#else
+int yyparse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+
+
+/* The lookahead symbol. */
+int yychar;
+
+/* The semantic value of the lookahead symbol. */
+YYSTYPE yylval;
+
+/* Number of syntax errors so far. */
+int yynerrs;
+
+
+/*----------.
+| yyparse. |
+`----------*/
+
+#ifdef YYPARSE_PARAM
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void *YYPARSE_PARAM)
+#else
+int
+yyparse (YYPARSE_PARAM)
+ void *YYPARSE_PARAM;
+#endif
+#else /* ! YYPARSE_PARAM */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void)
+#else
+int
+yyparse ()
+
+#endif
+#endif
+{
+ int yystate;
+ /* Number of tokens to shift before error messages enabled. */
+ int yyerrstatus;
+
+ /* The stacks and their tools:
+ `yyss': related to states.
+ `yyvs': related to semantic values.
+
+ Refer to the stacks through separate pointers, to allow yyoverflow
+ to reallocate them elsewhere. */
+
+ /* The state stack. */
+ yytype_int16 yyssa[YYINITDEPTH];
+ yytype_int16 *yyss;
+ yytype_int16 *yyssp;
+
+ /* The semantic value stack. */
+ YYSTYPE yyvsa[YYINITDEPTH];
+ YYSTYPE *yyvs;
+ YYSTYPE *yyvsp;
+
+ YYSIZE_T yystacksize;
+
+ int yyn;
+ int yyresult;
+ /* Lookahead token as an internal (translated) token number. */
+ int yytoken;
+ /* The variables used to return semantic value and location from the
+ action routines. */
+ YYSTYPE yyval;
+
+#if YYERROR_VERBOSE
+ /* Buffer for error messages, and its allocated size. */
+ char yymsgbuf[128];
+ char *yymsg = yymsgbuf;
+ YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
+#endif
+
+#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N))
+
+ /* The number of symbols on the RHS of the reduced rule.
+ Keep to zero when no symbol should be popped. */
+ int yylen = 0;
+
+ yytoken = 0;
+ yyss = yyssa;
+ yyvs = yyvsa;
+ yystacksize = YYINITDEPTH;
+
+ YYDPRINTF ((stderr, "Starting parse\n"));
+
+ yystate = 0;
+ yyerrstatus = 0;
+ yynerrs = 0;
+ yychar = YYEMPTY; /* Cause a token to be read. */
+
+ /* Initialize stack pointers.
+ Waste one element of value and location stack
+ so that they stay on the same level as the state stack.
+ The wasted elements are never initialized. */
+ yyssp = yyss;
+ yyvsp = yyvs;
+
+ goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate. |
+`------------------------------------------------------------*/
+ yynewstate:
+ /* In all cases, when you get here, the value and location stacks
+ have just been pushed. So pushing a state here evens the stacks. */
+ yyssp++;
+
+ yysetstate:
+ *yyssp = yystate;
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ {
+ /* Get the current used size of the three stacks, in elements. */
+ YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+ {
+ /* Give user a chance to reallocate the stack. Use copies of
+ these so that the &'s don't force the real ones into
+ memory. */
+ YYSTYPE *yyvs1 = yyvs;
+ yytype_int16 *yyss1 = yyss;
+
+ /* Each stack pointer address is followed by the size of the
+ data in use in that stack, in bytes. This used to be a
+ conditional around just the two extra args, but that might
+ be undefined if yyoverflow is a macro. */
+ yyoverflow (YY_("memory exhausted"),
+ &yyss1, yysize * sizeof (*yyssp),
+ &yyvs1, yysize * sizeof (*yyvsp),
+ &yystacksize);
+
+ yyss = yyss1;
+ yyvs = yyvs1;
+ }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+ goto yyexhaustedlab;
+# else
+ /* Extend the stack our own way. */
+ if (YYMAXDEPTH <= yystacksize)
+ goto yyexhaustedlab;
+ yystacksize *= 2;
+ if (YYMAXDEPTH < yystacksize)
+ yystacksize = YYMAXDEPTH;
+
+ {
+ yytype_int16 *yyss1 = yyss;
+ union yyalloc *yyptr =
+ (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+ if (! yyptr)
+ goto yyexhaustedlab;
+ YYSTACK_RELOCATE (yyss_alloc, yyss);
+ YYSTACK_RELOCATE (yyvs_alloc, yyvs);
+# undef YYSTACK_RELOCATE
+ if (yyss1 != yyssa)
+ YYSTACK_FREE (yyss1);
+ }
+# endif
+#endif /* no yyoverflow */
+
+ yyssp = yyss + yysize - 1;
+ yyvsp = yyvs + yysize - 1;
+
+ YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+ (unsigned long int) yystacksize));
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ YYABORT;
+ }
+
+ YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+ if (yystate == YYFINAL)
+ YYACCEPT;
+
+ goto yybackup;
+
+/*-----------.
+| yybackup. |
+`-----------*/
+yybackup:
+
+ /* Do appropriate processing given the current state. Read a
+ lookahead token if we need one and don't already have one. */
+
+ /* First try to decide what to do without reference to lookahead token. */
+ yyn = yypact[yystate];
+ if (yypact_value_is_default (yyn))
+ goto yydefault;
+
+ /* Not known => get a lookahead token if don't already have one. */
+
+ /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */
+ if (yychar == YYEMPTY)
+ {
+ YYDPRINTF ((stderr, "Reading a token: "));
+ yychar = YYLEX;
+ }
+
+ if (yychar <= YYEOF)
+ {
+ yychar = yytoken = YYEOF;
+ YYDPRINTF ((stderr, "Now at end of input.\n"));
+ }
+ else
+ {
+ yytoken = YYTRANSLATE (yychar);
+ YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+ }
+
+ /* If the proper action on seeing token YYTOKEN is to reduce or to
+ detect an error, take that action. */
+ yyn += yytoken;
+ if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+ goto yydefault;
+ yyn = yytable[yyn];
+ if (yyn <= 0)
+ {
+ if (yytable_value_is_error (yyn))
+ goto yyerrlab;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+
+ /* Count tokens shifted since error; after three, turn off error
+ status. */
+ if (yyerrstatus)
+ yyerrstatus--;
+
+ /* Shift the lookahead token. */
+ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+
+ /* Discard the shifted token. */
+ yychar = YYEMPTY;
+
+ yystate = yyn;
+ *++yyvsp = yylval;
+
+ goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state. |
+`-----------------------------------------------------------*/
+yydefault:
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ goto yyerrlab;
+ goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction. |
+`-----------------------------*/
+yyreduce:
+ /* yyn is the number of a rule to reduce with. */
+ yylen = yyr2[yyn];
+
+ /* If YYLEN is nonzero, implement the default value of the action:
+ `$$ = $1'.
+
+ Otherwise, the following line sets YYVAL to garbage.
+ This behavior is undocumented and Bison
+ users should not rely upon it. Assigning to YYVAL
+ unconditionally makes the parser a bit smaller, and it avoids a
+ GCC warning that YYVAL may be used uninitialized. */
+ yyval = yyvsp[1-yylen];
+
+
+ YY_REDUCE_PRINT (yyn);
+ switch (yyn)
+ {
+ case 10:
+
+ { zconf_error("unexpected end statement"); }
+ break;
+
+ case 11:
+
+ { zconf_error("unknown statement \"%s\"", (yyvsp[(2) - (4)].string)); }
+ break;
+
+ case 12:
+
+ {
+ zconf_error("unexpected option \"%s\"", kconf_id_strings + (yyvsp[(2) - (4)].id)->name);
+}
+ break;
+
+ case 13:
+
+ { zconf_error("invalid statement"); }
+ break;
+
+ case 28:
+
+ { zconf_error("unknown option \"%s\"", (yyvsp[(1) - (3)].string)); }
+ break;
+
+ case 29:
+
+ { zconf_error("invalid option"); }
+ break;
+
+ case 30:
+
+ {
+ struct symbol *sym = sym_lookup((yyvsp[(2) - (3)].string), 0);
+ sym->flags |= SYMBOL_OPTIONAL;
+ menu_add_entry(sym);
+ printd(DEBUG_PARSE, "%s:%d:config %s\n", zconf_curname(), zconf_lineno(), (yyvsp[(2) - (3)].string));
+}
+ break;
+
+ case 31:
+
+ {
+ menu_end_entry();
+ printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno());
+}
+ break;
+
+ case 32:
+
+ {
+ struct symbol *sym = sym_lookup((yyvsp[(2) - (3)].string), 0);
+ sym->flags |= SYMBOL_OPTIONAL;
+ menu_add_entry(sym);
+ printd(DEBUG_PARSE, "%s:%d:menuconfig %s\n", zconf_curname(), zconf_lineno(), (yyvsp[(2) - (3)].string));
+}
+ break;
+
+ case 33:
+
+ {
+ if (current_entry->prompt)
+ current_entry->prompt->type = P_MENU;
+ else
+ zconfprint("warning: menuconfig statement without prompt");
+ menu_end_entry();
+ printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno());
+}
+ break;
+
+ case 41:
+
+ {
+ menu_set_type((yyvsp[(1) - (3)].id)->stype);
+ printd(DEBUG_PARSE, "%s:%d:type(%u)\n",
+ zconf_curname(), zconf_lineno(),
+ (yyvsp[(1) - (3)].id)->stype);
+}
+ break;
+
+ case 42:
+
+ {
+ menu_add_prompt(P_PROMPT, (yyvsp[(2) - (4)].string), (yyvsp[(3) - (4)].expr));
+ printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno());
+}
+ break;
+
+ case 43:
+
+ {
+ menu_add_expr(P_DEFAULT, (yyvsp[(2) - (4)].expr), (yyvsp[(3) - (4)].expr));
+ if ((yyvsp[(1) - (4)].id)->stype != S_UNKNOWN)
+ menu_set_type((yyvsp[(1) - (4)].id)->stype);
+ printd(DEBUG_PARSE, "%s:%d:default(%u)\n",
+ zconf_curname(), zconf_lineno(),
+ (yyvsp[(1) - (4)].id)->stype);
+}
+ break;
+
+ case 44:
+
+ {
+ menu_add_symbol(P_SELECT, sym_lookup((yyvsp[(2) - (4)].string), 0), (yyvsp[(3) - (4)].expr));
+ printd(DEBUG_PARSE, "%s:%d:select\n", zconf_curname(), zconf_lineno());
+}
+ break;
+
+ case 45:
+
+ {
+ menu_add_expr(P_RANGE, expr_alloc_comp(E_RANGE,(yyvsp[(2) - (5)].symbol), (yyvsp[(3) - (5)].symbol)), (yyvsp[(4) - (5)].expr));
+ printd(DEBUG_PARSE, "%s:%d:range\n", zconf_curname(), zconf_lineno());
+}
+ break;
+
+ case 48:
+
+ {
+ const struct kconf_id *id = kconf_id_lookup((yyvsp[(2) - (3)].string), strlen((yyvsp[(2) - (3)].string)));
+ if (id && id->flags & TF_OPTION)
+ menu_add_option(id->token, (yyvsp[(3) - (3)].string));
+ else
+ zconfprint("warning: ignoring unknown option %s", (yyvsp[(2) - (3)].string));
+ free((yyvsp[(2) - (3)].string));
+}
+ break;
+
+ case 49:
+
+ { (yyval.string) = NULL; }
+ break;
+
+ case 50:
+
+ { (yyval.string) = (yyvsp[(2) - (2)].string); }
+ break;
+
+ case 51:
+
+ {
+ struct symbol *sym = sym_lookup((yyvsp[(2) - (3)].string), SYMBOL_CHOICE);
+ sym->flags |= SYMBOL_AUTO;
+ menu_add_entry(sym);
+ menu_add_expr(P_CHOICE, NULL, NULL);
+ printd(DEBUG_PARSE, "%s:%d:choice\n", zconf_curname(), zconf_lineno());
+}
+ break;
+
+ case 52:
+
+ {
+ (yyval.menu) = menu_add_menu();
+}
+ break;
+
+ case 53:
+
+ {
+ if (zconf_endtoken((yyvsp[(1) - (1)].id), T_CHOICE, T_ENDCHOICE)) {
+ menu_end_menu();
+ printd(DEBUG_PARSE, "%s:%d:endchoice\n", zconf_curname(), zconf_lineno());
+ }
+}
+ break;
+
+ case 61:
+
+ {
+ menu_add_prompt(P_PROMPT, (yyvsp[(2) - (4)].string), (yyvsp[(3) - (4)].expr));
+ printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno());
+}
+ break;
+
+ case 62:
+
+ {
+ if ((yyvsp[(1) - (3)].id)->stype == S_BOOLEAN || (yyvsp[(1) - (3)].id)->stype == S_TRISTATE) {
+ menu_set_type((yyvsp[(1) - (3)].id)->stype);
+ printd(DEBUG_PARSE, "%s:%d:type(%u)\n",
+ zconf_curname(), zconf_lineno(),
+ (yyvsp[(1) - (3)].id)->stype);
+ } else
+ YYERROR;
+}
+ break;
+
+ case 63:
+
+ {
+ current_entry->sym->flags |= SYMBOL_OPTIONAL;
+ printd(DEBUG_PARSE, "%s:%d:optional\n", zconf_curname(), zconf_lineno());
+}
+ break;
+
+ case 64:
+
+ {
+ if ((yyvsp[(1) - (4)].id)->stype == S_UNKNOWN) {
+ menu_add_symbol(P_DEFAULT, sym_lookup((yyvsp[(2) - (4)].string), 0), (yyvsp[(3) - (4)].expr));
+ printd(DEBUG_PARSE, "%s:%d:default\n",
+ zconf_curname(), zconf_lineno());
+ } else
+ YYERROR;
+}
+ break;
+
+ case 67:
+
+ {
+ printd(DEBUG_PARSE, "%s:%d:if\n", zconf_curname(), zconf_lineno());
+ menu_add_entry(NULL);
+ menu_add_dep((yyvsp[(2) - (3)].expr));
+ (yyval.menu) = menu_add_menu();
+}
+ break;
+
+ case 68:
+
+ {
+ if (zconf_endtoken((yyvsp[(1) - (1)].id), T_IF, T_ENDIF)) {
+ menu_end_menu();
+ printd(DEBUG_PARSE, "%s:%d:endif\n", zconf_curname(), zconf_lineno());
+ }
+}
+ break;
+
+ case 74:
+
+ {
+ menu_add_prompt(P_MENU, (yyvsp[(2) - (3)].string), NULL);
+}
+ break;
+
+ case 75:
+
+ {
+ menu_add_entry(NULL);
+ menu_add_prompt(P_MENU, (yyvsp[(2) - (3)].string), NULL);
+ printd(DEBUG_PARSE, "%s:%d:menu\n", zconf_curname(), zconf_lineno());
+}
+ break;
+
+ case 76:
+
+ {
+ (yyval.menu) = menu_add_menu();
+}
+ break;
+
+ case 77:
+
+ {
+ if (zconf_endtoken((yyvsp[(1) - (1)].id), T_MENU, T_ENDMENU)) {
+ menu_end_menu();
+ printd(DEBUG_PARSE, "%s:%d:endmenu\n", zconf_curname(), zconf_lineno());
+ }
+}
+ break;
+
+ case 83:
+
+ {
+ printd(DEBUG_PARSE, "%s:%d:source %s\n", zconf_curname(), zconf_lineno(), (yyvsp[(2) - (3)].string));
+ zconf_nextfile((yyvsp[(2) - (3)].string));
+}
+ break;
+
+ case 84:
+
+ {
+ menu_add_entry(NULL);
+ menu_add_prompt(P_COMMENT, (yyvsp[(2) - (3)].string), NULL);
+ printd(DEBUG_PARSE, "%s:%d:comment\n", zconf_curname(), zconf_lineno());
+}
+ break;
+
+ case 85:
+
+ {
+ menu_end_entry();
+}
+ break;
+
+ case 86:
+
+ {
+ printd(DEBUG_PARSE, "%s:%d:help\n", zconf_curname(), zconf_lineno());
+ zconf_starthelp();
+}
+ break;
+
+ case 87:
+
+ {
+ current_entry->help = (yyvsp[(2) - (2)].string);
+}
+ break;
+
+ case 92:
+
+ {
+ menu_add_dep((yyvsp[(3) - (4)].expr));
+ printd(DEBUG_PARSE, "%s:%d:depends on\n", zconf_curname(), zconf_lineno());
+}
+ break;
+
+ case 96:
+
+ {
+ menu_add_visibility((yyvsp[(2) - (2)].expr));
+}
+ break;
+
+ case 98:
+
+ {
+ menu_add_prompt(P_PROMPT, (yyvsp[(1) - (2)].string), (yyvsp[(2) - (2)].expr));
+}
+ break;
+
+ case 101:
+
+ { (yyval.id) = (yyvsp[(1) - (2)].id); }
+ break;
+
+ case 102:
+
+ { (yyval.id) = (yyvsp[(1) - (2)].id); }
+ break;
+
+ case 103:
+
+ { (yyval.id) = (yyvsp[(1) - (2)].id); }
+ break;
+
+ case 106:
+
+ { (yyval.expr) = NULL; }
+ break;
+
+ case 107:
+
+ { (yyval.expr) = (yyvsp[(2) - (2)].expr); }
+ break;
+
+ case 108:
+
+ { (yyval.expr) = expr_alloc_symbol((yyvsp[(1) - (1)].symbol)); }
+ break;
+
+ case 109:
+
+ { (yyval.expr) = expr_alloc_comp(E_LTH, (yyvsp[(1) - (3)].symbol), (yyvsp[(3) - (3)].symbol)); }
+ break;
+
+ case 110:
+
+ { (yyval.expr) = expr_alloc_comp(E_LEQ, (yyvsp[(1) - (3)].symbol), (yyvsp[(3) - (3)].symbol)); }
+ break;
+
+ case 111:
+
+ { (yyval.expr) = expr_alloc_comp(E_GTH, (yyvsp[(1) - (3)].symbol), (yyvsp[(3) - (3)].symbol)); }
+ break;
+
+ case 112:
+
+ { (yyval.expr) = expr_alloc_comp(E_GEQ, (yyvsp[(1) - (3)].symbol), (yyvsp[(3) - (3)].symbol)); }
+ break;
+
+ case 113:
+
+ { (yyval.expr) = expr_alloc_comp(E_EQUAL, (yyvsp[(1) - (3)].symbol), (yyvsp[(3) - (3)].symbol)); }
+ break;
+
+ case 114:
+
+ { (yyval.expr) = expr_alloc_comp(E_UNEQUAL, (yyvsp[(1) - (3)].symbol), (yyvsp[(3) - (3)].symbol)); }
+ break;
+
+ case 115:
+
+ { (yyval.expr) = (yyvsp[(2) - (3)].expr); }
+ break;
+
+ case 116:
+
+ { (yyval.expr) = expr_alloc_one(E_NOT, (yyvsp[(2) - (2)].expr)); }
+ break;
+
+ case 117:
+
+ { (yyval.expr) = expr_alloc_two(E_OR, (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr)); }
+ break;
+
+ case 118:
+
+ { (yyval.expr) = expr_alloc_two(E_AND, (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr)); }
+ break;
+
+ case 119:
+
+ { (yyval.symbol) = sym_lookup((yyvsp[(1) - (1)].string), 0); free((yyvsp[(1) - (1)].string)); }
+ break;
+
+ case 120:
+
+ { (yyval.symbol) = sym_lookup((yyvsp[(1) - (1)].string), SYMBOL_CONST); free((yyvsp[(1) - (1)].string)); }
+ break;
+
+ case 121:
+
+ { (yyval.string) = NULL; }
+ break;
+
+
+
+ default: break;
+ }
+ /* User semantic actions sometimes alter yychar, and that requires
+ that yytoken be updated with the new translation. We take the
+ approach of translating immediately before every use of yytoken.
+ One alternative is translating here after every semantic action,
+ but that translation would be missed if the semantic action invokes
+ YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or
+ if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an
+ incorrect destructor might then be invoked immediately. In the
+ case of YYERROR or YYBACKUP, subsequent parser actions might lead
+ to an incorrect destructor call or verbose syntax error message
+ before the lookahead is translated. */
+ YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
+
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+
+ *++yyvsp = yyval;
+
+ /* Now `shift' the result of the reduction. Determine what state
+ that goes to, based on the state we popped back to and the rule
+ number reduced by. */
+
+ yyn = yyr1[yyn];
+
+ yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+ if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+ yystate = yytable[yystate];
+ else
+ yystate = yydefgoto[yyn - YYNTOKENS];
+
+ goto yynewstate;
+
+
+/*------------------------------------.
+| yyerrlab -- here on detecting error |
+`------------------------------------*/
+yyerrlab:
+ /* Make sure we have latest lookahead translation. See comments at
+ user semantic actions for why this is necessary. */
+ yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar);
+
+ /* If not already recovering from an error, report this error. */
+ if (!yyerrstatus)
+ {
+ ++yynerrs;
+#if ! YYERROR_VERBOSE
+ yyerror (YY_("syntax error"));
+#else
+# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \
+ yyssp, yytoken)
+ {
+ char const *yymsgp = YY_("syntax error");
+ int yysyntax_error_status;
+ yysyntax_error_status = YYSYNTAX_ERROR;
+ if (yysyntax_error_status == 0)
+ yymsgp = yymsg;
+ else if (yysyntax_error_status == 1)
+ {
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+ yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc);
+ if (!yymsg)
+ {
+ yymsg = yymsgbuf;
+ yymsg_alloc = sizeof yymsgbuf;
+ yysyntax_error_status = 2;
+ }
+ else
+ {
+ yysyntax_error_status = YYSYNTAX_ERROR;
+ yymsgp = yymsg;
+ }
+ }
+ yyerror (yymsgp);
+ if (yysyntax_error_status == 2)
+ goto yyexhaustedlab;
+ }
+# undef YYSYNTAX_ERROR
+#endif
+ }
+
+
+
+ if (yyerrstatus == 3)
+ {
+ /* If just tried and failed to reuse lookahead token after an
+ error, discard it. */
+
+ if (yychar <= YYEOF)
+ {
+ /* Return failure if at end of input. */
+ if (yychar == YYEOF)
+ YYABORT;
+ }
+ else
+ {
+ yydestruct ("Error: discarding",
+ yytoken, &yylval);
+ yychar = YYEMPTY;
+ }
+ }
+
+ /* Else will try to reuse lookahead token after shifting the error
+ token. */
+ goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR. |
+`---------------------------------------------------*/
+yyerrorlab:
+
+ /* Pacify compilers like GCC when the user code never invokes
+ YYERROR and the label yyerrorlab therefore never appears in user
+ code. */
+ if (/*CONSTCOND*/ 0)
+ goto yyerrorlab;
+
+ /* Do not reclaim the symbols of the rule which action triggered
+ this YYERROR. */
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+ yystate = *yyssp;
+ goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR. |
+`-------------------------------------------------------------*/
+yyerrlab1:
+ yyerrstatus = 3; /* Each real token shifted decrements this. */
+
+ for (;;)
+ {
+ yyn = yypact[yystate];
+ if (!yypact_value_is_default (yyn))
+ {
+ yyn += YYTERROR;
+ if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+ {
+ yyn = yytable[yyn];
+ if (0 < yyn)
+ break;
+ }
+ }
+
+ /* Pop the current state because it cannot handle the error token. */
+ if (yyssp == yyss)
+ YYABORT;
+
+
+ yydestruct ("Error: popping",
+ yystos[yystate], yyvsp);
+ YYPOPSTACK (1);
+ yystate = *yyssp;
+ YY_STACK_PRINT (yyss, yyssp);
+ }
+
+ *++yyvsp = yylval;
+
+
+ /* Shift the error token. */
+ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+ yystate = yyn;
+ goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here. |
+`-------------------------------------*/
+yyacceptlab:
+ yyresult = 0;
+ goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here. |
+`-----------------------------------*/
+yyabortlab:
+ yyresult = 1;
+ goto yyreturn;
+
+#if !defined yyoverflow || YYERROR_VERBOSE
+/*-------------------------------------------------.
+| yyexhaustedlab -- memory exhaustion comes here. |
+`-------------------------------------------------*/
+yyexhaustedlab:
+ yyerror (YY_("memory exhausted"));
+ yyresult = 2;
+ /* Fall through. */
+#endif
+
+yyreturn:
+ if (yychar != YYEMPTY)
+ {
+ /* Make sure we have latest lookahead translation. See comments at
+ user semantic actions for why this is necessary. */
+ yytoken = YYTRANSLATE (yychar);
+ yydestruct ("Cleanup: discarding lookahead",
+ yytoken, &yylval);
+ }
+ /* Do not reclaim the symbols of the rule which action triggered
+ this YYABORT or YYACCEPT. */
+ YYPOPSTACK (yylen);
+ YY_STACK_PRINT (yyss, yyssp);
+ while (yyssp != yyss)
+ {
+ yydestruct ("Cleanup: popping",
+ yystos[*yyssp], yyvsp);
+ YYPOPSTACK (1);
+ }
+#ifndef yyoverflow
+ if (yyss != yyssa)
+ YYSTACK_FREE (yyss);
+#endif
+#if YYERROR_VERBOSE
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+#endif
+ /* Make sure YYID is used. */
+ return YYID (yyresult);
+}
+
+
+
+
+
+void conf_parse(const char *name)
+{
+ struct symbol *sym;
+ int i;
+
+ zconf_initscan(name);
+
+ sym_init();
+ _menu_init();
+ rootmenu.prompt = menu_add_prompt(P_MENU, "Linux Kernel Configuration", NULL);
+
+ if (getenv("ZCONF_DEBUG"))
+ zconfdebug = 1;
+ zconfparse();
+ if (zconfnerrs)
+ exit(1);
+ if (!modules_sym)
+ modules_sym = sym_find( "n" );
+
+ rootmenu.prompt->text = _(rootmenu.prompt->text);
+ rootmenu.prompt->text = sym_expand_string_value(rootmenu.prompt->text);
+
+ menu_finalize(&rootmenu);
+ for_all_symbols(i, sym) {
+ if (sym_check_deps(sym))
+ zconfnerrs++;
+ }
+ if (zconfnerrs)
+ exit(1);
+ sym_set_change_count(1);
+}
+
+static const char *zconf_tokenname(int token)
+{
+ switch (token) {
+ case T_MENU: return "menu";
+ case T_ENDMENU: return "endmenu";
+ case T_CHOICE: return "choice";
+ case T_ENDCHOICE: return "endchoice";
+ case T_IF: return "if";
+ case T_ENDIF: return "endif";
+ case T_DEPENDS: return "depends";
+ case T_VISIBLE: return "visible";
+ }
+ return "<token>";
+}
+
+static bool zconf_endtoken(const struct kconf_id *id, int starttoken, int endtoken)
+{
+ if (id->token != endtoken) {
+ zconf_error("unexpected '%s' within %s block",
+ kconf_id_strings + id->name, zconf_tokenname(starttoken));
+ zconfnerrs++;
+ return false;
+ }
+ if (current_menu->file != current_file) {
+ zconf_error("'%s' in different file than '%s'",
+ kconf_id_strings + id->name, zconf_tokenname(starttoken));
+ fprintf(stderr, "%s:%d: location of the '%s'\n",
+ current_menu->file->name, current_menu->lineno,
+ zconf_tokenname(starttoken));
+ zconfnerrs++;
+ return false;
+ }
+ return true;
+}
+
+static void zconfprint(const char *err, ...)
+{
+ va_list ap;
+
+ fprintf(stderr, "%s:%d: ", zconf_curname(), zconf_lineno());
+ va_start(ap, err);
+ vfprintf(stderr, err, ap);
+ va_end(ap);
+ fprintf(stderr, "\n");
+}
+
+static void zconf_error(const char *err, ...)
+{
+ va_list ap;
+
+ zconfnerrs++;
+ fprintf(stderr, "%s:%d: ", zconf_curname(), zconf_lineno());
+ va_start(ap, err);
+ vfprintf(stderr, err, ap);
+ va_end(ap);
+ fprintf(stderr, "\n");
+}
+
+static void zconferror(const char *err)
+{
+ fprintf(stderr, "%s:%d: %s\n", zconf_curname(), zconf_lineno() + 1, err);
+}
+
+static void print_quoted_string(FILE *out, const char *str)
+{
+ const char *p;
+ int len;
+
+ putc('"', out);
+ while ((p = strchr(str, '"'))) {
+ len = p - str;
+ if (len)
+ fprintf(out, "%.*s", len, str);
+ fputs("\\\"", out);
+ str = p + 1;
+ }
+ fputs(str, out);
+ putc('"', out);
+}
+
+static void print_symbol(FILE *out, struct menu *menu)
+{
+ struct symbol *sym = menu->sym;
+ struct property *prop;
+
+ if (sym_is_choice(sym))
+ fprintf(out, "\nchoice\n");
+ else
+ fprintf(out, "\nconfig %s\n", sym->name);
+ switch (sym->type) {
+ case S_BOOLEAN:
+ fputs(" boolean\n", out);
+ break;
+ case S_TRISTATE:
+ fputs(" tristate\n", out);
+ break;
+ case S_STRING:
+ fputs(" string\n", out);
+ break;
+ case S_INT:
+ fputs(" integer\n", out);
+ break;
+ case S_HEX:
+ fputs(" hex\n", out);
+ break;
+ default:
+ fputs(" ???\n", out);
+ break;
+ }
+ for (prop = sym->prop; prop; prop = prop->next) {
+ if (prop->menu != menu)
+ continue;
+ switch (prop->type) {
+ case P_PROMPT:
+ fputs(" prompt ", out);
+ print_quoted_string(out, prop->text);
+ if (!expr_is_yes(prop->visible.expr)) {
+ fputs(" if ", out);
+ expr_fprint(prop->visible.expr, out);
+ }
+ fputc('\n', out);
+ break;
+ case P_DEFAULT:
+ fputs( " default ", out);
+ expr_fprint(prop->expr, out);
+ if (!expr_is_yes(prop->visible.expr)) {
+ fputs(" if ", out);
+ expr_fprint(prop->visible.expr, out);
+ }
+ fputc('\n', out);
+ break;
+ case P_CHOICE:
+ fputs(" #choice value\n", out);
+ break;
+ case P_SELECT:
+ fputs( " select ", out);
+ expr_fprint(prop->expr, out);
+ fputc('\n', out);
+ break;
+ case P_RANGE:
+ fputs( " range ", out);
+ expr_fprint(prop->expr, out);
+ fputc('\n', out);
+ break;
+ case P_MENU:
+ fputs( " menu ", out);
+ print_quoted_string(out, prop->text);
+ fputc('\n', out);
+ break;
+ default:
+ fprintf(out, " unknown prop %d!\n", prop->type);
+ break;
+ }
+ }
+ if (menu->help) {
+ int len = strlen(menu->help);
+ while (menu->help[--len] == '\n')
+ menu->help[len] = 0;
+ fprintf(out, " help\n%s\n", menu->help);
+ }
+}
+
+void zconfdump(FILE *out)
+{
+ struct property *prop;
+ struct symbol *sym;
+ struct menu *menu;
+
+ menu = rootmenu.list;
+ while (menu) {
+ if ((sym = menu->sym))
+ print_symbol(out, menu);
+ else if ((prop = menu->prompt)) {
+ switch (prop->type) {
+ case P_COMMENT:
+ fputs("\ncomment ", out);
+ print_quoted_string(out, prop->text);
+ fputs("\n", out);
+ break;
+ case P_MENU:
+ fputs("\nmenu ", out);
+ print_quoted_string(out, prop->text);
+ fputs("\n", out);
+ break;
+ default:
+ ;
+ }
+ if (!expr_is_yes(prop->visible.expr)) {
+ fputs(" depends ", out);
+ expr_fprint(prop->visible.expr, out);
+ fputc('\n', out);
+ }
+ }
+
+ if (menu->list)
+ menu = menu->list;
+ else if (menu->next)
+ menu = menu->next;
+ else while ((menu = menu->parent)) {
+ if (menu->prompt && menu->prompt->type == P_MENU)
+ fputs("\nendmenu\n", out);
+ if (menu->next) {
+ menu = menu->next;
+ break;
+ }
+ }
+ }
+}
+
+#include "zconf.lex.c"
+#include "util.c"
+#include "confdata.c"
+#include "expr.c"
+#include "symbol.c"
+#include "menu.c"
+
diff --git a/local-symbols b/local-symbols
new file mode 100644
index 0000000..05f7cbc
--- /dev/null
+++ b/local-symbols
@@ -0,0 +1,60 @@
+BACKPORT_DIR=
+BACKPORTS_VERSION=
+BACKPORTED_KERNEL_VERSION=
+BACKPORTED_KERNEL_NAME=
+WIRELESS=
+NET_CORE=
+EXPERT=
+BP_MODULES=
+BPAUTO_BUILD_CORDIC=
+BPAUTO_CORDIC=
+BPAUTO_MII=
+BPAUTO_BUILD_LEDS=
+BPAUTO_NEW_LEDS=
+BPAUTO_LEDS_CLASS=
+BPAUTO_LEDS_TRIGGERS=
+BPAUTO_USERSEL_BUILD_ALL=
+BPAUTO_CRYPTO_CCM=
+BPAUTO_BUILD_CRYPTO_CCM=
+BPAUTO_CRYPTO_SKCIPHER=
+BPAUTO_WANT_DEV_COREDUMP=
+BPAUTO_BUILD_WANT_DEV_COREDUMP=
+BPAUTO_RHASHTABLE=
+BPAUTO_BUILD_HDMI=
+BPAUTO_HDMI=
+BPAUTO_FRAME_VECTOR=
+BPAUTO_BUILD_FRAME_VECTOR=
+CFG80211=
+NL80211_TESTMODE=
+CFG80211_DEVELOPER_WARNINGS=
+CFG80211_CERTIFICATION_ONUS=
+CFG80211_REG_CELLULAR_HINTS=
+CFG80211_REG_RELAX_NO_IR=
+CFG80211_DEFAULT_PS=
+CFG80211_DEBUGFS=
+CFG80211_INTERNAL_REGDB=
+CFG80211_CRDA_SUPPORT=
+CFG80211_WEXT=
+CFG80211_WEXT_EXPORT=
+LIB80211=
+LIB80211_CRYPT_WEP=
+LIB80211_CRYPT_CCMP=
+LIB80211_CRYPT_TKIP=
+LIB80211_DEBUG=
+WLAN=
+WIRELESS_WDS=
+PCMCIA_RAYCS=
+PCMCIA_WL3501=
+MAC80211_HWSIM=
+USB_NET_RNDIS_WLAN=
+WLAN_VENDOR_BROADCOM=
+BRCMUTIL=
+BRCMSMAC=
+BRCMFMAC=
+BRCMFMAC_PROTO_BCDC=
+BRCMFMAC_PROTO_MSGBUF=
+BRCMFMAC_SDIO=
+BRCMFMAC_USB=
+BRCMFMAC_PCIE=
+BRCM_TRACING=
+BRCMDBG=
diff --git a/net/Kconfig b/net/Kconfig
new file mode 100644
index 0000000..102f781
--- /dev/null
+++ b/net/Kconfig
@@ -0,0 +1,449 @@
+#
+# Network configuration
+#
+
+menuconfig NET
+ bool "Networking support"
+ select NLATTR
+ select GENERIC_NET_UTILS
+ select BPF
+ ---help---
+ Unless you really know what you are doing, you should say Y here.
+ The reason is that some programs need kernel networking support even
+ when running on a stand-alone machine that isn't connected to any
+ other computer.
+
+ If you are upgrading from an older kernel, you
+ should consider updating your networking tools too because changes
+ in the kernel and the tools often go hand in hand. The tools are
+ contained in the package net-tools, the location and version number
+ of which are given in <file:Documentation/Changes>.
+
+ For a general introduction to Linux networking, it is highly
+ recommended to read the NET-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+if NET
+
+config WANT_COMPAT_NETLINK_MESSAGES
+ bool
+ help
+ This option can be selected by other options that need compat
+ netlink messages.
+
+config COMPAT_NETLINK_MESSAGES
+ def_bool y
+ depends on COMPAT
+ depends on WEXT_CORE || WANT_COMPAT_NETLINK_MESSAGES
+ help
+ This option makes it possible to send different netlink messages
+ to tasks depending on whether the task is a compat task or not. To
+ achieve this, you need to set skb_shinfo(skb)->frag_list to the
+ compat skb before sending the skb, the netlink code will sort out
+ which message to actually pass to the task.
+
+ Newly written code should NEVER need this option but do
+ compat-independent messages instead!
+
+config NET_INGRESS
+ bool
+
+config NET_EGRESS
+ bool
+
+menu "Networking options"
+
+source "net/packet/Kconfig"
+source "net/unix/Kconfig"
+source "net/xfrm/Kconfig"
+source "net/iucv/Kconfig"
+source "net/smc/Kconfig"
+
+config INET
+ bool "TCP/IP networking"
+ select CRYPTO
+ select CRYPTO_AES
+ ---help---
+ These are the protocols used on the Internet and on most local
+ Ethernets. It is highly recommended to say Y here (this will enlarge
+ your kernel by about 400 KB), since some programs (e.g. the X window
+ system) use TCP/IP even if your machine is not connected to any
+ other computer. You will get the so-called loopback device which
+ allows you to ping yourself (great fun, that!).
+
+ For an excellent introduction to Linux networking, please read the
+ Linux Networking HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ If you say Y here and also to "/proc file system support" and
+ "Sysctl support" below, you can change various aspects of the
+ behavior of the TCP/IP code by writing to the (virtual) files in
+ /proc/sys/net/ipv4/*; the options are explained in the file
+ <file:Documentation/networking/ip-sysctl.txt>.
+
+ Short answer: say Y.
+
+if INET
+source "net/ipv4/Kconfig"
+source "net/ipv6/Kconfig"
+source "net/netlabel/Kconfig"
+
+endif # if INET
+
+config NETWORK_SECMARK
+ bool "Security Marking"
+ help
+ This enables security marking of network packets, similar
+ to nfmark, but designated for security purposes.
+ If you are unsure how to answer this question, answer N.
+
+config NET_PTP_CLASSIFY
+ def_bool n
+
+config NETWORK_PHY_TIMESTAMPING
+ bool "Timestamping in PHY devices"
+ select NET_PTP_CLASSIFY
+ help
+ This allows timestamping of network packets by PHYs with
+ hardware timestamping capabilities. This option adds some
+ overhead in the transmit and receive paths.
+
+ If you are unsure how to answer this question, answer N.
+
+menuconfig NETFILTER
+ bool "Network packet filtering framework (Netfilter)"
+ ---help---
+ Netfilter is a framework for filtering and mangling network packets
+ that pass through your Linux box.
+
+ The most common use of packet filtering is to run your Linux box as
+ a firewall protecting a local network from the Internet. The type of
+ firewall provided by this kernel support is called a "packet
+ filter", which means that it can reject individual network packets
+ based on type, source, destination etc. The other kind of firewall,
+ a "proxy-based" one, is more secure but more intrusive and more
+ bothersome to set up; it inspects the network traffic much more
+ closely, modifies it and has knowledge about the higher level
+ protocols, which a packet filter lacks. Moreover, proxy-based
+ firewalls often require changes to the programs running on the local
+ clients. Proxy-based firewalls don't need support by the kernel, but
+ they are often combined with a packet filter, which only works if
+ you say Y here.
+
+ You should also say Y here if you intend to use your Linux box as
+ the gateway to the Internet for a local network of machines without
+ globally valid IP addresses. This is called "masquerading": if one
+ of the computers on your local network wants to send something to
+ the outside, your box can "masquerade" as that computer, i.e. it
+ forwards the traffic to the intended outside destination, but
+ modifies the packets to make it look like they came from the
+ firewall box itself. It works both ways: if the outside host
+ replies, the Linux box will silently forward the traffic to the
+ correct local computer. This way, the computers on your local net
+ are completely invisible to the outside world, even though they can
+ reach the outside and can receive replies. It is even possible to
+ run globally visible servers from within a masqueraded local network
+ using a mechanism called portforwarding. Masquerading is also often
+ called NAT (Network Address Translation).
+
+ Another use of Netfilter is in transparent proxying: if a machine on
+ the local network tries to connect to an outside host, your Linux
+ box can transparently forward the traffic to a local server,
+ typically a caching proxy server.
+
+ Yet another use of Netfilter is building a bridging firewall. Using
+ a bridge with Network packet filtering enabled makes iptables "see"
+ the bridged traffic. For filtering on the lower network and Ethernet
+ protocols over the bridge, use ebtables (under bridge netfilter
+ configuration).
+
+ Various modules exist for netfilter which replace the previous
+ masquerading (ipmasqadm), packet filtering (ipchains), transparent
+ proxying, and portforwarding mechanisms. Please see
+ <file:Documentation/Changes> under "iptables" for the location of
+ these packages.
+
+if NETFILTER
+
+config NETFILTER_DEBUG
+ bool "Network packet filtering debugging"
+ depends on NETFILTER
+ help
+ You can say Y here if you want to get additional messages useful in
+ debugging the netfilter code.
+
+config NETFILTER_ADVANCED
+ bool "Advanced netfilter configuration"
+ depends on NETFILTER
+ default y
+ help
+ If you say Y here you can select between all the netfilter modules.
+ If you say N the more unusual ones will not be shown and the
+ basic ones needed by most people will default to 'M'.
+
+ If unsure, say Y.
+
+config BRIDGE_NETFILTER
+ tristate "Bridged IP/ARP packets filtering"
+ depends on BRIDGE
+ depends on NETFILTER && INET
+ depends on NETFILTER_ADVANCED
+ default m
+ ---help---
+ Enabling this option will let arptables resp. iptables see bridged
+ ARP resp. IP traffic. If you want a bridging firewall, you probably
+ want this option enabled.
+ Enabling or disabling this option doesn't enable or disable
+ ebtables.
+
+ If unsure, say N.
+
+source "net/netfilter/Kconfig"
+source "net/ipv4/netfilter/Kconfig"
+source "net/ipv6/netfilter/Kconfig"
+source "net/decnet/netfilter/Kconfig"
+source "net/bridge/netfilter/Kconfig"
+
+endif
+
+source "net/dccp/Kconfig"
+source "net/sctp/Kconfig"
+source "net/rds/Kconfig"
+source "net/tipc/Kconfig"
+source "net/atm/Kconfig"
+source "net/l2tp/Kconfig"
+source "net/802/Kconfig"
+source "net/bridge/Kconfig"
+source "net/dsa/Kconfig"
+source "net/8021q/Kconfig"
+source "net/decnet/Kconfig"
+source "net/llc/Kconfig"
+source "net/ipx/Kconfig"
+source "drivers/net/appletalk/Kconfig"
+source "net/x25/Kconfig"
+source "net/lapb/Kconfig"
+source "net/phonet/Kconfig"
+source "net/6lowpan/Kconfig"
+source "net/ieee802154/Kconfig"
+source "net/mac802154/Kconfig"
+source "net/sched/Kconfig"
+source "net/dcb/Kconfig"
+source "net/dns_resolver/Kconfig"
+source "net/batman-adv/Kconfig"
+source "net/openvswitch/Kconfig"
+source "net/vmw_vsock/Kconfig"
+source "net/netlink/Kconfig"
+source "net/mpls/Kconfig"
+source "net/hsr/Kconfig"
+source "net/switchdev/Kconfig"
+source "net/l3mdev/Kconfig"
+source "net/qrtr/Kconfig"
+source "net/ncsi/Kconfig"
+
+config RPS
+ bool
+ depends on SMP && SYSFS
+ default y
+
+config RFS_ACCEL
+ bool
+ depends on RPS
+ select CPU_RMAP
+ default y
+
+config XPS
+ bool
+ depends on SMP
+ default y
+
+config HWBM
+ bool
+
+config CGROUP_NET_PRIO
+ bool "Network priority cgroup"
+ depends on CGROUPS
+ select SOCK_CGROUP_DATA
+ ---help---
+ Cgroup subsystem for use in assigning processes to network priorities on
+ a per-interface basis.
+
+config CGROUP_NET_CLASSID
+ bool "Network classid cgroup"
+ depends on CGROUPS
+ select SOCK_CGROUP_DATA
+ ---help---
+ Cgroup subsystem for use as general purpose socket classid marker that is
+ being used in cls_cgroup and for netfilter matching.
+
+config NET_RX_BUSY_POLL
+ bool
+ default y
+
+config BQL
+ bool
+ depends on SYSFS
+ select DQL
+ default y
+
+config BPF_JIT
+ bool "enable BPF Just In Time compiler"
+ depends on HAVE_CBPF_JIT || HAVE_EBPF_JIT
+ depends on MODULES
+ ---help---
+ Berkeley Packet Filter filtering capabilities are normally handled
+ by an interpreter. This option allows kernel to generate a native
+ code when filter is loaded in memory. This should speedup
+ packet sniffing (libpcap/tcpdump).
+
+ Note, admin should enable this feature changing:
+ /proc/sys/net/core/bpf_jit_enable
+ /proc/sys/net/core/bpf_jit_harden (optional)
+ /proc/sys/net/core/bpf_jit_kallsyms (optional)
+
+config NET_FLOW_LIMIT
+ bool
+ depends on RPS
+ default y
+ ---help---
+ The network stack has to drop packets when a receive processing CPU's
+ backlog reaches netdev_max_backlog. If a few out of many active flows
+ generate the vast majority of load, drop their traffic earlier to
+ maintain capacity for the other flows. This feature provides servers
+ with many clients some protection against DoS by a single (spoofed)
+ flow that greatly exceeds average workload.
+
+menu "Network testing"
+
+config NET_PKTGEN
+ tristate "Packet Generator (USE WITH CAUTION)"
+ depends on INET && PROC_FS
+ ---help---
+ This module will inject preconfigured packets, at a configurable
+ rate, out of a given interface. It is used for network interface
+ stress testing and performance analysis. If you don't understand
+ what was just said, you don't need it: say N.
+
+ Documentation on how to use the packet generator can be found
+ at <file:Documentation/networking/pktgen.txt>.
+
+ To compile this code as a module, choose M here: the
+ module will be called pktgen.
+
+config NET_TCPPROBE
+ tristate "TCP connection probing"
+ depends on INET && PROC_FS && KPROBES
+ ---help---
+ This module allows for capturing the changes to TCP connection
+ state in response to incoming packets. It is used for debugging
+ TCP congestion avoidance modules. If you don't understand
+ what was just said, you don't need it: say N.
+
+ Documentation on how to use TCP connection probing can be found
+ at:
+
+ http://www.linuxfoundation.org/collaborate/workgroups/networking/tcpprobe
+
+ To compile this code as a module, choose M here: the
+ module will be called tcp_probe.
+
+config NET_DROP_MONITOR
+ tristate "Network packet drop alerting service"
+ depends on INET && TRACEPOINTS
+ ---help---
+ This feature provides an alerting service to userspace in the
+ event that packets are discarded in the network stack. Alerts
+ are broadcast via netlink socket to any listening user space
+ process. If you don't need network drop alerts, or if you are ok
+ just checking the various proc files and other utilities for
+ drop statistics, say N here.
+
+endmenu
+
+endmenu
+
+source "net/ax25/Kconfig"
+source "net/can/Kconfig"
+source "net/irda/Kconfig"
+source "net/bluetooth/Kconfig"
+source "net/rxrpc/Kconfig"
+source "net/kcm/Kconfig"
+source "net/strparser/Kconfig"
+
+config FIB_RULES
+ bool
+
+menuconfig WIRELESS
+ bool "Wireless"
+ depends on !S390
+ default y
+
+if WIRELESS
+
+source "net/wireless/Kconfig"
+source "net/mac80211/Kconfig"
+
+endif # WIRELESS
+
+source "net/wimax/Kconfig"
+
+source "net/rfkill/Kconfig"
+source "net/9p/Kconfig"
+source "net/caif/Kconfig"
+source "net/ceph/Kconfig"
+source "net/nfc/Kconfig"
+source "net/psample/Kconfig"
+source "net/ife/Kconfig"
+
+config LWTUNNEL
+ bool "Network light weight tunnels"
+ ---help---
+ This feature provides an infrastructure to support light weight
+ tunnels like mpls. There is no netdevice associated with a light
+ weight tunnel endpoint. Tunnel encapsulation parameters are stored
+ with light weight tunnel state associated with fib routes.
+
+config LWTUNNEL_BPF
+ bool "Execute BPF program as route nexthop action"
+ depends on LWTUNNEL
+ default y if LWTUNNEL=y
+ ---help---
+ Allows to run BPF programs as a nexthop action following a route
+ lookup for incoming and outgoing packets.
+
+config DST_CACHE
+ bool
+ default n
+
+config GRO_CELLS
+ bool
+ default n
+
+config NET_DEVLINK
+ tristate "Network physical/parent device Netlink interface"
+ help
+ Network physical/parent device Netlink interface provides
+ infrastructure to support access to physical chip-wide config and
+ monitoring.
+
+config MAY_USE_DEVLINK
+ tristate
+ default m if NET_DEVLINK=m
+ default y if NET_DEVLINK=y || NET_DEVLINK=n
+ help
+ Drivers using the devlink infrastructure should have a dependency
+ on MAY_USE_DEVLINK to ensure they do not cause link errors when
+ devlink is a loadable module and the driver using it is built-in.
+
+endif # if NET
+
+# Used by archs to tell that they support BPF JIT compiler plus which flavour.
+# Only one of the two can be selected for a specific arch since eBPF JIT supersedes
+# the cBPF JIT.
+
+# Classic BPF JIT (cBPF)
+config HAVE_CBPF_JIT
+ bool
+
+# Extended BPF JIT (eBPF)
+config HAVE_EBPF_JIT
+ bool
diff --git a/net/Makefile b/net/Makefile
new file mode 100644
index 0000000..36c0dad
--- /dev/null
+++ b/net/Makefile
@@ -0,0 +1,86 @@
+#
+# Makefile for the linux networking.
+#
+# 2 Sep 2000, Christoph Hellwig <hch@infradead.org>
+# Rewritten to use lists instead of if-statements.
+#
+
+obj-$(CONFIG_NET) := socket.o core/
+
+tmp-$(CONFIG_COMPAT) := compat.o
+obj-$(CONFIG_NET) += $(tmp-y)
+
+# LLC has to be linked before the files in net/802/
+obj-$(CONFIG_LLC) += llc/
+obj-$(CONFIG_NET) += ethernet/ 802/ sched/ netlink/ bpf/
+obj-$(CONFIG_NETFILTER) += netfilter/
+obj-$(CONFIG_INET) += ipv4/
+obj-$(CONFIG_XFRM) += xfrm/
+obj-$(CONFIG_UNIX) += unix/
+obj-$(CONFIG_NET) += ipv6/
+obj-$(CONFIG_PACKET) += packet/
+obj-$(CONFIG_NET_KEY) += key/
+obj-$(CONFIG_BRIDGE) += bridge/
+obj-$(CONFIG_NET_DSA) += dsa/
+obj-$(CONFIG_IPX) += ipx/
+obj-$(CONFIG_ATALK) += appletalk/
+obj-$(CONFIG_X25) += x25/
+obj-$(CONFIG_LAPB) += lapb/
+obj-$(CONFIG_NETROM) += netrom/
+obj-$(CONFIG_ROSE) += rose/
+obj-$(CONFIG_AX25) += ax25/
+obj-$(CONFIG_CAN) += can/
+obj-$(CONFIG_IRDA) += irda/
+obj-$(CONFIG_BT) += bluetooth/
+obj-$(CONFIG_SUNRPC) += sunrpc/
+obj-$(CONFIG_AF_RXRPC) += rxrpc/
+obj-$(CONFIG_AF_KCM) += kcm/
+obj-$(CONFIG_STREAM_PARSER) += strparser/
+obj-$(CONFIG_ATM) += atm/
+obj-$(CONFIG_L2TP) += l2tp/
+obj-$(CONFIG_DECNET) += decnet/
+obj-$(CONFIG_PHONET) += phonet/
+ifneq ($(CONFIG_VLAN_8021Q),)
+obj-y += 8021q/
+endif
+obj-$(CONFIG_IP_DCCP) += dccp/
+obj-$(CONFIG_IP_SCTP) += sctp/
+obj-$(CONFIG_RDS) += rds/
+obj-$(CPTCFG_WIRELESS) += wireless/
+obj-$(CONFIG_MAC80211) += mac80211/
+obj-$(CONFIG_TIPC) += tipc/
+obj-$(CONFIG_NETLABEL) += netlabel/
+obj-$(CONFIG_IUCV) += iucv/
+obj-$(CONFIG_SMC) += smc/
+obj-$(CONFIG_RFKILL) += rfkill/
+obj-$(CONFIG_NET_9P) += 9p/
+obj-$(CONFIG_CAIF) += caif/
+ifneq ($(CONFIG_DCB),)
+obj-y += dcb/
+endif
+obj-$(CONFIG_6LOWPAN) += 6lowpan/
+obj-$(CONFIG_IEEE802154) += ieee802154/
+obj-$(CONFIG_MAC802154) += mac802154/
+
+ifeq ($(CONFIG_NET),y)
+obj-$(CONFIG_SYSCTL) += sysctl_net.o
+endif
+obj-$(CONFIG_WIMAX) += wimax/
+obj-$(CONFIG_DNS_RESOLVER) += dns_resolver/
+obj-$(CONFIG_CEPH_LIB) += ceph/
+obj-$(CONFIG_BATMAN_ADV) += batman-adv/
+obj-$(CONFIG_NFC) += nfc/
+obj-$(CONFIG_PSAMPLE) += psample/
+obj-$(CONFIG_NET_IFE) += ife/
+obj-$(CONFIG_OPENVSWITCH) += openvswitch/
+obj-$(CONFIG_VSOCKETS) += vmw_vsock/
+obj-$(CONFIG_MPLS) += mpls/
+obj-$(CONFIG_HSR) += hsr/
+ifneq ($(CONFIG_NET_SWITCHDEV),)
+obj-y += switchdev/
+endif
+ifneq ($(CONFIG_NET_L3_MASTER_DEV),)
+obj-y += l3mdev/
+endif
+obj-$(CONFIG_QRTR) += qrtr/
+obj-$(CONFIG_NET_NCSI) += ncsi/
diff --git a/net/wireless/.gitignore b/net/wireless/.gitignore
new file mode 100644
index 0000000..c33451b
--- /dev/null
+++ b/net/wireless/.gitignore
@@ -0,0 +1 @@
+regdb.c
diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig
new file mode 100644
index 0000000..060211d
--- /dev/null
+++ b/net/wireless/Kconfig
@@ -0,0 +1,203 @@
+config CFG80211
+ tristate "cfg80211 - wireless configuration API"
+ depends on m
+ depends on RFKILL || !RFKILL
+ ---help---
+ cfg80211 is the Linux wireless LAN (802.11) configuration API.
+ Enable this if you have a wireless device.
+
+ For more information refer to documentation on the wireless wiki:
+
+ http://wireless.kernel.org/en/developers/Documentation/cfg80211
+
+ When built as a module it will be called cfg80211.
+
+config NL80211_TESTMODE
+ bool "nl80211 testmode command"
+ depends on CFG80211
+ help
+ The nl80211 testmode command helps implementing things like
+ factory calibration or validation tools for wireless chips.
+
+ Select this option ONLY for kernels that are specifically
+ built for such purposes.
+
+ Debugging tools that are supposed to end up in the hands of
+ users should better be implemented with debugfs.
+
+ Say N.
+
+config CFG80211_DEVELOPER_WARNINGS
+ bool "enable developer warnings"
+ depends on CFG80211
+ default n
+ help
+ This option enables some additional warnings that help
+ cfg80211 developers and driver developers, but beware that
+ they can also trigger due to races with userspace.
+
+ For example, when a driver reports that it was disconnected
+ from the AP, but the user disconnects manually at the same
+ time, the warning might trigger spuriously due to races.
+
+ Say Y only if you are developing cfg80211 or a driver based
+ on it (or mac80211).
+
+
+config CFG80211_CERTIFICATION_ONUS
+ bool "cfg80211 certification onus"
+ depends on CFG80211 && EXPERT
+ default n
+ ---help---
+ You should disable this option unless you are both capable
+ and willing to ensure your system will remain regulatory
+ compliant with the features available under this option.
+ Some options may still be under heavy development and
+ for whatever reason regulatory compliance has not or
+ cannot yet be verified. Regulatory verification may at
+ times only be possible until you have the final system
+ in place.
+
+ This option should only be enabled by system integrators
+ or distributions that have done work necessary to ensure
+ regulatory certification on the system with the enabled
+ features. Alternatively you can enable this option if
+ you are a wireless researcher and are working in a controlled
+ and approved environment by your local regulatory agency.
+
+config CFG80211_REG_CELLULAR_HINTS
+ bool "cfg80211 regulatory support for cellular base station hints"
+ depends on CFG80211_CERTIFICATION_ONUS
+ ---help---
+ This option enables support for parsing regulatory hints
+ from cellular base stations. If enabled and at least one driver
+ claims support for parsing cellular base station hints the
+ regulatory core will allow and parse these regulatory hints.
+ The regulatory core will only apply these regulatory hints on
+ drivers that support this feature. You should only enable this
+ feature if you have tested and validated this feature on your
+ systems.
+
+config CFG80211_REG_RELAX_NO_IR
+ bool "cfg80211 support for NO_IR relaxation"
+ depends on CFG80211_CERTIFICATION_ONUS
+ ---help---
+ This option enables support for relaxation of the NO_IR flag for
+ situations that certain regulatory bodies have provided clarifications
+ on how relaxation can occur. This feature has an inherent dependency on
+ userspace features which must have been properly tested and as such is
+ not enabled by default.
+
+ A relaxation feature example is allowing the operation of a P2P group
+ owner (GO) on channels marked with NO_IR if there is an additional BSS
+ interface which associated to an AP which userspace assumes or confirms
+ to be an authorized master, i.e., with radar detection support and DFS
+ capabilities. However, note that in order to not create daisy chain
+ scenarios, this relaxation is not allowed in cases where the BSS client
+ is associated to P2P GO and in addition the P2P GO instantiated on
+ a channel due to this relaxation should not allow connection from
+ non P2P clients.
+
+ The regulatory core will apply these relaxations only for drivers that
+ support this feature by declaring the appropriate channel flags and
+ capabilities in their registration flow.
+
+config CFG80211_DEFAULT_PS
+ bool "enable powersave by default"
+ depends on CFG80211
+ default y
+ help
+ This option enables powersave mode by default.
+
+ If this causes your applications to misbehave you should fix your
+ applications instead -- they need to register their network
+ latency requirement, see Documentation/power/pm_qos_interface.txt.
+
+config CFG80211_DEBUGFS
+ bool "cfg80211 DebugFS entries"
+ depends on CFG80211
+ depends on DEBUG_FS
+ ---help---
+ You can enable this if you want debugfs entries for cfg80211.
+
+ If unsure, say N.
+
+config CFG80211_INTERNAL_REGDB
+ bool "use statically compiled regulatory rules database" if EXPERT
+ default n
+ depends on CFG80211
+ ---help---
+ This option generates an internal data structure representing
+ the wireless regulatory rules described in net/wireless/db.txt
+ and includes code to query that database. This is an alternative
+ to using CRDA for defining regulatory rules for the kernel.
+
+ Using this option requires some parsing of the db.txt at build time,
+ the parser will be upkept with the latest wireless-regdb updates but
+ older wireless-regdb formats will be ignored. The parser may later
+ be replaced to avoid issues with conflicts on versions of
+ wireless-regdb.
+
+ For details see:
+
+ http://wireless.kernel.org/en/developers/Regulatory
+
+ Most distributions have a CRDA package. So if unsure, say N.
+
+config CFG80211_CRDA_SUPPORT
+ bool "support CRDA" if CFG80211_INTERNAL_REGDB
+ default y
+ depends on CFG80211
+ help
+ You should enable this option unless you know for sure you have no
+ need for it, for example when using internal regdb (above.)
+
+ If unsure, say Y.
+
+config CFG80211_WEXT
+ bool "cfg80211 wireless extensions compatibility" if !CFG80211_WEXT_EXPORT
+ depends on CFG80211
+ depends on WEXT_CORE
+ default y if CFG80211_WEXT_EXPORT
+ help
+ Enable this option if you need old userspace for wireless
+ extensions with cfg80211-based drivers.
+
+config CFG80211_WEXT_EXPORT
+ bool
+ depends on CFG80211
+ help
+ Drivers should select this option if they require cfg80211's
+ wext compatibility symbols to be exported.
+
+config LIB80211
+ tristate
+ depends on m
+ default n
+ help
+ This options enables a library of common routines used
+ by IEEE802.11 wireless LAN drivers.
+
+ Drivers should select this themselves if needed.
+
+config LIB80211_CRYPT_WEP
+ tristate
+ depends on m
+
+config LIB80211_CRYPT_CCMP
+ tristate
+ depends on m
+
+config LIB80211_CRYPT_TKIP
+ tristate
+ depends on m
+
+config LIB80211_DEBUG
+ bool "lib80211 debugging messages"
+ depends on LIB80211
+ default n
+ ---help---
+ You can enable this if you want verbose debugging messages
+ from lib80211.
+
+ If unsure, say N.
diff --git a/net/wireless/Makefile b/net/wireless/Makefile
new file mode 100644
index 0000000..e5310dd
--- /dev/null
+++ b/net/wireless/Makefile
@@ -0,0 +1,26 @@
+obj-$(CPTCFG_CFG80211) += cfg80211.o
+obj-$(CPTCFG_LIB80211) += lib80211.o
+obj-$(CPTCFG_LIB80211_CRYPT_WEP) += lib80211_crypt_wep.o
+obj-$(CPTCFG_LIB80211_CRYPT_CCMP) += lib80211_crypt_ccmp.o
+obj-$(CPTCFG_LIB80211_CRYPT_TKIP) += lib80211_crypt_tkip.o
+
+obj-$(CONFIG_WEXT_CORE) += wext-core.o
+obj-$(CONFIG_WEXT_PROC) += wext-proc.o
+obj-$(CONFIG_WEXT_SPY) += wext-spy.o
+obj-$(CONFIG_WEXT_PRIV) += wext-priv.o
+
+cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o
+cfg80211-y += mlme.o ibss.o sme.o chan.o ethtool.o mesh.o ap.o trace.o ocb.o
+cfg80211-$(CONFIG_OF) += of.o
+cfg80211-$(CPTCFG_CFG80211_DEBUGFS) += debugfs.o
+cfg80211-$(CPTCFG_CFG80211_WEXT) += wext-compat.o wext-sme.o
+cfg80211-$(CPTCFG_CFG80211_INTERNAL_REGDB) += regdb.o
+
+abs_src := $(if $(filter /%,$(src)),$(src),$(KBUILD_SRC)/$(src))
+
+CFLAGS_trace.o := -I$(abs_src)
+
+$(obj)/regdb.c: $(abs_src)/db.txt $(abs_src)/genregdb.awk
+ @$(AWK) -f $(abs_src)/genregdb.awk < $< > $@
+
+clean-files := regdb.c
diff --git a/net/wireless/ap.c b/net/wireless/ap.c
new file mode 100644
index 0000000..25666d3
--- /dev/null
+++ b/net/wireless/ap.c
@@ -0,0 +1,56 @@
+#include <linux/ieee80211.h>
+#include <linux/export.h>
+#include <net/cfg80211.h>
+#include "nl80211.h"
+#include "core.h"
+#include "rdev-ops.h"
+
+
+int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, bool notify)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ int err;
+
+ ASSERT_WDEV_LOCK(wdev);
+
+ if (!rdev->ops->stop_ap)
+ return -EOPNOTSUPP;
+
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
+ dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
+ return -EOPNOTSUPP;
+
+ if (!wdev->beacon_interval)
+ return -ENOENT;
+
+ err = rdev_stop_ap(rdev, dev);
+ if (!err) {
+ wdev->beacon_interval = 0;
+ memset(&wdev->chandef, 0, sizeof(wdev->chandef));
+ wdev->ssid_len = 0;
+ rdev_set_qos_map(rdev, dev, NULL);
+ if (notify)
+ nl80211_send_ap_stopped(wdev);
+
+ /* Should we apply the grace period during beaconing interface
+ * shutdown also?
+ */
+ cfg80211_sched_dfs_chan_update(rdev);
+ }
+
+ return err;
+}
+
+int cfg80211_stop_ap(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, bool notify)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ int err;
+
+ wdev_lock(wdev);
+ err = __cfg80211_stop_ap(rdev, dev, notify);
+ wdev_unlock(wdev);
+
+ return err;
+}
diff --git a/net/wireless/chan.c b/net/wireless/chan.c
new file mode 100644
index 0000000..82d1f6a
--- /dev/null
+++ b/net/wireless/chan.c
@@ -0,0 +1,1074 @@
+/*
+ * This file contains helper code to handle channel
+ * settings and keeping track of what is possible at
+ * any point in time.
+ *
+ * Copyright 2009 Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2013-2014 Intel Mobile Communications GmbH
+ */
+
+#include <linux/export.h>
+#include <net/cfg80211.h>
+#include "core.h"
+#include "rdev-ops.h"
+
+void cfg80211_chandef_create(struct cfg80211_chan_def *chandef,
+ struct ieee80211_channel *chan,
+ enum nl80211_channel_type chan_type)
+{
+ if (WARN_ON(!chan))
+ return;
+
+ chandef->chan = chan;
+ chandef->center_freq2 = 0;
+
+ switch (chan_type) {
+ case NL80211_CHAN_NO_HT:
+ chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
+ chandef->center_freq1 = chan->center_freq;
+ break;
+ case NL80211_CHAN_HT20:
+ chandef->width = NL80211_CHAN_WIDTH_20;
+ chandef->center_freq1 = chan->center_freq;
+ break;
+ case NL80211_CHAN_HT40PLUS:
+ chandef->width = NL80211_CHAN_WIDTH_40;
+ chandef->center_freq1 = chan->center_freq + 10;
+ break;
+ case NL80211_CHAN_HT40MINUS:
+ chandef->width = NL80211_CHAN_WIDTH_40;
+ chandef->center_freq1 = chan->center_freq - 10;
+ break;
+ default:
+ WARN_ON(1);
+ }
+}
+EXPORT_SYMBOL(cfg80211_chandef_create);
+
+bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef)
+{
+ u32 control_freq;
+
+ if (!chandef->chan)
+ return false;
+
+ control_freq = chandef->chan->center_freq;
+
+ switch (chandef->width) {
+ case NL80211_CHAN_WIDTH_5:
+ case NL80211_CHAN_WIDTH_10:
+ case NL80211_CHAN_WIDTH_20:
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ if (chandef->center_freq1 != control_freq)
+ return false;
+ if (chandef->center_freq2)
+ return false;
+ break;
+ case NL80211_CHAN_WIDTH_40:
+ if (chandef->center_freq1 != control_freq + 10 &&
+ chandef->center_freq1 != control_freq - 10)
+ return false;
+ if (chandef->center_freq2)
+ return false;
+ break;
+ case NL80211_CHAN_WIDTH_80P80:
+ if (chandef->center_freq1 != control_freq + 30 &&
+ chandef->center_freq1 != control_freq + 10 &&
+ chandef->center_freq1 != control_freq - 10 &&
+ chandef->center_freq1 != control_freq - 30)
+ return false;
+ if (!chandef->center_freq2)
+ return false;
+ /* adjacent is not allowed -- that's a 160 MHz channel */
+ if (chandef->center_freq1 - chandef->center_freq2 == 80 ||
+ chandef->center_freq2 - chandef->center_freq1 == 80)
+ return false;
+ break;
+ case NL80211_CHAN_WIDTH_80:
+ if (chandef->center_freq1 != control_freq + 30 &&
+ chandef->center_freq1 != control_freq + 10 &&
+ chandef->center_freq1 != control_freq - 10 &&
+ chandef->center_freq1 != control_freq - 30)
+ return false;
+ if (chandef->center_freq2)
+ return false;
+ break;
+ case NL80211_CHAN_WIDTH_160:
+ if (chandef->center_freq1 != control_freq + 70 &&
+ chandef->center_freq1 != control_freq + 50 &&
+ chandef->center_freq1 != control_freq + 30 &&
+ chandef->center_freq1 != control_freq + 10 &&
+ chandef->center_freq1 != control_freq - 10 &&
+ chandef->center_freq1 != control_freq - 30 &&
+ chandef->center_freq1 != control_freq - 50 &&
+ chandef->center_freq1 != control_freq - 70)
+ return false;
+ if (chandef->center_freq2)
+ return false;
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+EXPORT_SYMBOL(cfg80211_chandef_valid);
+
+static void chandef_primary_freqs(const struct cfg80211_chan_def *c,
+ u32 *pri40, u32 *pri80)
+{
+ int tmp;
+
+ switch (c->width) {
+ case NL80211_CHAN_WIDTH_40:
+ *pri40 = c->center_freq1;
+ *pri80 = 0;
+ break;
+ case NL80211_CHAN_WIDTH_80:
+ case NL80211_CHAN_WIDTH_80P80:
+ *pri80 = c->center_freq1;
+ /* n_P20 */
+ tmp = (30 + c->chan->center_freq - c->center_freq1)/20;
+ /* n_P40 */
+ tmp /= 2;
+ /* freq_P40 */
+ *pri40 = c->center_freq1 - 20 + 40 * tmp;
+ break;
+ case NL80211_CHAN_WIDTH_160:
+ /* n_P20 */
+ tmp = (70 + c->chan->center_freq - c->center_freq1)/20;
+ /* n_P40 */
+ tmp /= 2;
+ /* freq_P40 */
+ *pri40 = c->center_freq1 - 60 + 40 * tmp;
+ /* n_P80 */
+ tmp /= 2;
+ *pri80 = c->center_freq1 - 40 + 80 * tmp;
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ }
+}
+
+static int cfg80211_chandef_get_width(const struct cfg80211_chan_def *c)
+{
+ int width;
+
+ switch (c->width) {
+ case NL80211_CHAN_WIDTH_5:
+ width = 5;
+ break;
+ case NL80211_CHAN_WIDTH_10:
+ width = 10;
+ break;
+ case NL80211_CHAN_WIDTH_20:
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ width = 20;
+ break;
+ case NL80211_CHAN_WIDTH_40:
+ width = 40;
+ break;
+ case NL80211_CHAN_WIDTH_80P80:
+ case NL80211_CHAN_WIDTH_80:
+ width = 80;
+ break;
+ case NL80211_CHAN_WIDTH_160:
+ width = 160;
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ return -1;
+ }
+ return width;
+}
+
+const struct cfg80211_chan_def *
+cfg80211_chandef_compatible(const struct cfg80211_chan_def *c1,
+ const struct cfg80211_chan_def *c2)
+{
+ u32 c1_pri40, c1_pri80, c2_pri40, c2_pri80;
+
+ /* If they are identical, return */
+ if (cfg80211_chandef_identical(c1, c2))
+ return c1;
+
+ /* otherwise, must have same control channel */
+ if (c1->chan != c2->chan)
+ return NULL;
+
+ /*
+ * If they have the same width, but aren't identical,
+ * then they can't be compatible.
+ */
+ if (c1->width == c2->width)
+ return NULL;
+
+ /*
+ * can't be compatible if one of them is 5 or 10 MHz,
+ * but they don't have the same width.
+ */
+ if (c1->width == NL80211_CHAN_WIDTH_5 ||
+ c1->width == NL80211_CHAN_WIDTH_10 ||
+ c2->width == NL80211_CHAN_WIDTH_5 ||
+ c2->width == NL80211_CHAN_WIDTH_10)
+ return NULL;
+
+ if (c1->width == NL80211_CHAN_WIDTH_20_NOHT ||
+ c1->width == NL80211_CHAN_WIDTH_20)
+ return c2;
+
+ if (c2->width == NL80211_CHAN_WIDTH_20_NOHT ||
+ c2->width == NL80211_CHAN_WIDTH_20)
+ return c1;
+
+ chandef_primary_freqs(c1, &c1_pri40, &c1_pri80);
+ chandef_primary_freqs(c2, &c2_pri40, &c2_pri80);
+
+ if (c1_pri40 != c2_pri40)
+ return NULL;
+
+ WARN_ON(!c1_pri80 && !c2_pri80);
+ if (c1_pri80 && c2_pri80 && c1_pri80 != c2_pri80)
+ return NULL;
+
+ if (c1->width > c2->width)
+ return c1;
+ return c2;
+}
+EXPORT_SYMBOL(cfg80211_chandef_compatible);
+
+static void cfg80211_set_chans_dfs_state(struct wiphy *wiphy, u32 center_freq,
+ u32 bandwidth,
+ enum nl80211_dfs_state dfs_state)
+{
+ struct ieee80211_channel *c;
+ u32 freq;
+
+ for (freq = center_freq - bandwidth/2 + 10;
+ freq <= center_freq + bandwidth/2 - 10;
+ freq += 20) {
+ c = ieee80211_get_channel(wiphy, freq);
+ if (!c || !(c->flags & IEEE80211_CHAN_RADAR))
+ continue;
+
+ c->dfs_state = dfs_state;
+ c->dfs_state_entered = jiffies;
+ }
+}
+
+void cfg80211_set_dfs_state(struct wiphy *wiphy,
+ const struct cfg80211_chan_def *chandef,
+ enum nl80211_dfs_state dfs_state)
+{
+ int width;
+
+ if (WARN_ON(!cfg80211_chandef_valid(chandef)))
+ return;
+
+ width = cfg80211_chandef_get_width(chandef);
+ if (width < 0)
+ return;
+
+ cfg80211_set_chans_dfs_state(wiphy, chandef->center_freq1,
+ width, dfs_state);
+
+ if (!chandef->center_freq2)
+ return;
+ cfg80211_set_chans_dfs_state(wiphy, chandef->center_freq2,
+ width, dfs_state);
+}
+
+static u32 cfg80211_get_start_freq(u32 center_freq,
+ u32 bandwidth)
+{
+ u32 start_freq;
+
+ if (bandwidth <= 20)
+ start_freq = center_freq;
+ else
+ start_freq = center_freq - bandwidth/2 + 10;
+
+ return start_freq;
+}
+
+static u32 cfg80211_get_end_freq(u32 center_freq,
+ u32 bandwidth)
+{
+ u32 end_freq;
+
+ if (bandwidth <= 20)
+ end_freq = center_freq;
+ else
+ end_freq = center_freq + bandwidth/2 - 10;
+
+ return end_freq;
+}
+
+static int cfg80211_get_chans_dfs_required(struct wiphy *wiphy,
+ u32 center_freq,
+ u32 bandwidth)
+{
+ struct ieee80211_channel *c;
+ u32 freq, start_freq, end_freq;
+
+ start_freq = cfg80211_get_start_freq(center_freq, bandwidth);
+ end_freq = cfg80211_get_end_freq(center_freq, bandwidth);
+
+ for (freq = start_freq; freq <= end_freq; freq += 20) {
+ c = ieee80211_get_channel(wiphy, freq);
+ if (!c)
+ return -EINVAL;
+
+ if (c->flags & IEEE80211_CHAN_RADAR)
+ return 1;
+ }
+ return 0;
+}
+
+
+int cfg80211_chandef_dfs_required(struct wiphy *wiphy,
+ const struct cfg80211_chan_def *chandef,
+ enum nl80211_iftype iftype)
+{
+ int width;
+ int ret;
+
+ if (WARN_ON(!cfg80211_chandef_valid(chandef)))
+ return -EINVAL;
+
+ switch (iftype) {
+ case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_P2P_GO:
+ case NL80211_IFTYPE_MESH_POINT:
+ width = cfg80211_chandef_get_width(chandef);
+ if (width < 0)
+ return -EINVAL;
+
+ ret = cfg80211_get_chans_dfs_required(wiphy,
+ chandef->center_freq1,
+ width);
+ if (ret < 0)
+ return ret;
+ else if (ret > 0)
+ return BIT(chandef->width);
+
+ if (!chandef->center_freq2)
+ return 0;
+
+ ret = cfg80211_get_chans_dfs_required(wiphy,
+ chandef->center_freq2,
+ width);
+ if (ret < 0)
+ return ret;
+ else if (ret > 0)
+ return BIT(chandef->width);
+
+ break;
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_OCB:
+ case NL80211_IFTYPE_P2P_CLIENT:
+ case NL80211_IFTYPE_MONITOR:
+ case NL80211_IFTYPE_AP_VLAN:
+ case NL80211_IFTYPE_WDS:
+ case NL80211_IFTYPE_P2P_DEVICE:
+ case NL80211_IFTYPE_NAN:
+ break;
+ case NL80211_IFTYPE_UNSPECIFIED:
+ case NUM_NL80211_IFTYPES:
+ WARN_ON(1);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(cfg80211_chandef_dfs_required);
+
+static int cfg80211_get_chans_dfs_usable(struct wiphy *wiphy,
+ u32 center_freq,
+ u32 bandwidth)
+{
+ struct ieee80211_channel *c;
+ u32 freq, start_freq, end_freq;
+ int count = 0;
+
+ start_freq = cfg80211_get_start_freq(center_freq, bandwidth);
+ end_freq = cfg80211_get_end_freq(center_freq, bandwidth);
+
+ /*
+ * Check entire range of channels for the bandwidth.
+ * Check all channels are DFS channels (DFS_USABLE or
+ * DFS_AVAILABLE). Return number of usable channels
+ * (require CAC). Allow DFS and non-DFS channel mix.
+ */
+ for (freq = start_freq; freq <= end_freq; freq += 20) {
+ c = ieee80211_get_channel(wiphy, freq);
+ if (!c)
+ return -EINVAL;
+
+ if (c->flags & IEEE80211_CHAN_DISABLED)
+ return -EINVAL;
+
+ if (c->flags & IEEE80211_CHAN_RADAR) {
+ if (c->dfs_state == NL80211_DFS_UNAVAILABLE)
+ return -EINVAL;
+
+ if (c->dfs_state == NL80211_DFS_USABLE)
+ count++;
+ }
+ }
+
+ return count;
+}
+
+bool cfg80211_chandef_dfs_usable(struct wiphy *wiphy,
+ const struct cfg80211_chan_def *chandef)
+{
+ int width;
+ int r1, r2 = 0;
+
+ if (WARN_ON(!cfg80211_chandef_valid(chandef)))
+ return false;
+
+ width = cfg80211_chandef_get_width(chandef);
+ if (width < 0)
+ return false;
+
+ r1 = cfg80211_get_chans_dfs_usable(wiphy, chandef->center_freq1,
+ width);
+
+ if (r1 < 0)
+ return false;
+
+ switch (chandef->width) {
+ case NL80211_CHAN_WIDTH_80P80:
+ WARN_ON(!chandef->center_freq2);
+ r2 = cfg80211_get_chans_dfs_usable(wiphy,
+ chandef->center_freq2,
+ width);
+ if (r2 < 0)
+ return false;
+ break;
+ default:
+ WARN_ON(chandef->center_freq2);
+ break;
+ }
+
+ return (r1 + r2 > 0);
+}
+
+/*
+ * Checks if center frequency of chan falls with in the bandwidth
+ * range of chandef.
+ */
+bool cfg80211_is_sub_chan(struct cfg80211_chan_def *chandef,
+ struct ieee80211_channel *chan)
+{
+ int width;
+ u32 cf_offset, freq;
+
+ if (chandef->chan->center_freq == chan->center_freq)
+ return true;
+
+ width = cfg80211_chandef_get_width(chandef);
+ if (width <= 20)
+ return false;
+
+ cf_offset = width / 2 - 10;
+
+ for (freq = chandef->center_freq1 - width / 2 + 10;
+ freq <= chandef->center_freq1 + width / 2 - 10; freq += 20) {
+ if (chan->center_freq == freq)
+ return true;
+ }
+
+ if (!chandef->center_freq2)
+ return false;
+
+ for (freq = chandef->center_freq2 - width / 2 + 10;
+ freq <= chandef->center_freq2 + width / 2 - 10; freq += 20) {
+ if (chan->center_freq == freq)
+ return true;
+ }
+
+ return false;
+}
+
+bool cfg80211_beaconing_iface_active(struct wireless_dev *wdev)
+{
+ bool active = false;
+
+ ASSERT_WDEV_LOCK(wdev);
+
+ if (!wdev->chandef.chan)
+ return false;
+
+ switch (wdev->iftype) {
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_P2P_GO:
+ active = wdev->beacon_interval != 0;
+ break;
+ case NL80211_IFTYPE_ADHOC:
+ active = wdev->ssid_len != 0;
+ break;
+ case NL80211_IFTYPE_MESH_POINT:
+ active = wdev->mesh_id_len != 0;
+ break;
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_OCB:
+ case NL80211_IFTYPE_P2P_CLIENT:
+ case NL80211_IFTYPE_MONITOR:
+ case NL80211_IFTYPE_AP_VLAN:
+ case NL80211_IFTYPE_WDS:
+ case NL80211_IFTYPE_P2P_DEVICE:
+ /* Can NAN type be considered as beaconing interface? */
+ case NL80211_IFTYPE_NAN:
+ break;
+ case NL80211_IFTYPE_UNSPECIFIED:
+ case NUM_NL80211_IFTYPES:
+ WARN_ON(1);
+ }
+
+ return active;
+}
+
+static bool cfg80211_is_wiphy_oper_chan(struct wiphy *wiphy,
+ struct ieee80211_channel *chan)
+{
+ struct wireless_dev *wdev;
+
+ list_for_each_entry(wdev, &wiphy->wdev_list, list) {
+ wdev_lock(wdev);
+ if (!cfg80211_beaconing_iface_active(wdev)) {
+ wdev_unlock(wdev);
+ continue;
+ }
+
+ if (cfg80211_is_sub_chan(&wdev->chandef, chan)) {
+ wdev_unlock(wdev);
+ return true;
+ }
+ wdev_unlock(wdev);
+ }
+
+ return false;
+}
+
+bool cfg80211_any_wiphy_oper_chan(struct wiphy *wiphy,
+ struct ieee80211_channel *chan)
+{
+ struct cfg80211_registered_device *rdev;
+
+ ASSERT_RTNL();
+
+ if (!(chan->flags & IEEE80211_CHAN_RADAR))
+ return false;
+
+ list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
+ if (!reg_dfs_domain_same(wiphy, &rdev->wiphy))
+ continue;
+
+ if (cfg80211_is_wiphy_oper_chan(&rdev->wiphy, chan))
+ return true;
+ }
+
+ return false;
+}
+
+static bool cfg80211_get_chans_dfs_available(struct wiphy *wiphy,
+ u32 center_freq,
+ u32 bandwidth)
+{
+ struct ieee80211_channel *c;
+ u32 freq, start_freq, end_freq;
+
+ start_freq = cfg80211_get_start_freq(center_freq, bandwidth);
+ end_freq = cfg80211_get_end_freq(center_freq, bandwidth);
+
+ /*
+ * Check entire range of channels for the bandwidth.
+ * If any channel in between is disabled or has not
+ * had gone through CAC return false
+ */
+ for (freq = start_freq; freq <= end_freq; freq += 20) {
+ c = ieee80211_get_channel(wiphy, freq);
+ if (!c)
+ return false;
+
+ if (c->flags & IEEE80211_CHAN_DISABLED)
+ return false;
+
+ if ((c->flags & IEEE80211_CHAN_RADAR) &&
+ (c->dfs_state != NL80211_DFS_AVAILABLE))
+ return false;
+ }
+
+ return true;
+}
+
+static bool cfg80211_chandef_dfs_available(struct wiphy *wiphy,
+ const struct cfg80211_chan_def *chandef)
+{
+ int width;
+ int r;
+
+ if (WARN_ON(!cfg80211_chandef_valid(chandef)))
+ return false;
+
+ width = cfg80211_chandef_get_width(chandef);
+ if (width < 0)
+ return false;
+
+ r = cfg80211_get_chans_dfs_available(wiphy, chandef->center_freq1,
+ width);
+
+ /* If any of channels unavailable for cf1 just return */
+ if (!r)
+ return r;
+
+ switch (chandef->width) {
+ case NL80211_CHAN_WIDTH_80P80:
+ WARN_ON(!chandef->center_freq2);
+ r = cfg80211_get_chans_dfs_available(wiphy,
+ chandef->center_freq2,
+ width);
+ break;
+ default:
+ WARN_ON(chandef->center_freq2);
+ break;
+ }
+
+ return r;
+}
+
+static unsigned int cfg80211_get_chans_dfs_cac_time(struct wiphy *wiphy,
+ u32 center_freq,
+ u32 bandwidth)
+{
+ struct ieee80211_channel *c;
+ u32 start_freq, end_freq, freq;
+ unsigned int dfs_cac_ms = 0;
+
+ start_freq = cfg80211_get_start_freq(center_freq, bandwidth);
+ end_freq = cfg80211_get_end_freq(center_freq, bandwidth);
+
+ for (freq = start_freq; freq <= end_freq; freq += 20) {
+ c = ieee80211_get_channel(wiphy, freq);
+ if (!c)
+ return 0;
+
+ if (c->flags & IEEE80211_CHAN_DISABLED)
+ return 0;
+
+ if (!(c->flags & IEEE80211_CHAN_RADAR))
+ continue;
+
+ if (c->dfs_cac_ms > dfs_cac_ms)
+ dfs_cac_ms = c->dfs_cac_ms;
+ }
+
+ return dfs_cac_ms;
+}
+
+unsigned int
+cfg80211_chandef_dfs_cac_time(struct wiphy *wiphy,
+ const struct cfg80211_chan_def *chandef)
+{
+ int width;
+ unsigned int t1 = 0, t2 = 0;
+
+ if (WARN_ON(!cfg80211_chandef_valid(chandef)))
+ return 0;
+
+ width = cfg80211_chandef_get_width(chandef);
+ if (width < 0)
+ return 0;
+
+ t1 = cfg80211_get_chans_dfs_cac_time(wiphy,
+ chandef->center_freq1,
+ width);
+
+ if (!chandef->center_freq2)
+ return t1;
+
+ t2 = cfg80211_get_chans_dfs_cac_time(wiphy,
+ chandef->center_freq2,
+ width);
+
+ return max(t1, t2);
+}
+
+static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
+ u32 center_freq, u32 bandwidth,
+ u32 prohibited_flags)
+{
+ struct ieee80211_channel *c;
+ u32 freq, start_freq, end_freq;
+
+ start_freq = cfg80211_get_start_freq(center_freq, bandwidth);
+ end_freq = cfg80211_get_end_freq(center_freq, bandwidth);
+
+ for (freq = start_freq; freq <= end_freq; freq += 20) {
+ c = ieee80211_get_channel(wiphy, freq);
+ if (!c || c->flags & prohibited_flags)
+ return false;
+ }
+
+ return true;
+}
+
+bool cfg80211_chandef_usable(struct wiphy *wiphy,
+ const struct cfg80211_chan_def *chandef,
+ u32 prohibited_flags)
+{
+ struct ieee80211_sta_ht_cap *ht_cap;
+ struct ieee80211_sta_vht_cap *vht_cap;
+ u32 width, control_freq, cap;
+
+ if (WARN_ON(!cfg80211_chandef_valid(chandef)))
+ return false;
+
+ ht_cap = &wiphy->bands[chandef->chan->band]->ht_cap;
+ vht_cap = &wiphy->bands[chandef->chan->band]->vht_cap;
+
+ control_freq = chandef->chan->center_freq;
+
+ switch (chandef->width) {
+ case NL80211_CHAN_WIDTH_5:
+ width = 5;
+ break;
+ case NL80211_CHAN_WIDTH_10:
+ prohibited_flags |= IEEE80211_CHAN_NO_10MHZ;
+ width = 10;
+ break;
+ case NL80211_CHAN_WIDTH_20:
+ if (!ht_cap->ht_supported)
+ return false;
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ prohibited_flags |= IEEE80211_CHAN_NO_20MHZ;
+ width = 20;
+ break;
+ case NL80211_CHAN_WIDTH_40:
+ width = 40;
+ if (!ht_cap->ht_supported)
+ return false;
+ if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) ||
+ ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT)
+ return false;
+ if (chandef->center_freq1 < control_freq &&
+ chandef->chan->flags & IEEE80211_CHAN_NO_HT40MINUS)
+ return false;
+ if (chandef->center_freq1 > control_freq &&
+ chandef->chan->flags & IEEE80211_CHAN_NO_HT40PLUS)
+ return false;
+ break;
+ case NL80211_CHAN_WIDTH_80P80:
+ cap = vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK;
+ if (cap != IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ)
+ return false;
+ case NL80211_CHAN_WIDTH_80:
+ if (!vht_cap->vht_supported)
+ return false;
+ prohibited_flags |= IEEE80211_CHAN_NO_80MHZ;
+ width = 80;
+ break;
+ case NL80211_CHAN_WIDTH_160:
+ if (!vht_cap->vht_supported)
+ return false;
+ cap = vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK;
+ if (cap != IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ &&
+ cap != IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ)
+ return false;
+ prohibited_flags |= IEEE80211_CHAN_NO_160MHZ;
+ width = 160;
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ return false;
+ }
+
+ /*
+ * TODO: What if there are only certain 80/160/80+80 MHz channels
+ * allowed by the driver, or only certain combinations?
+ * For 40 MHz the driver can set the NO_HT40 flags, but for
+ * 80/160 MHz and in particular 80+80 MHz this isn't really
+ * feasible and we only have NO_80MHZ/NO_160MHZ so far but
+ * no way to cover 80+80 MHz or more complex restrictions.
+ * Note that such restrictions also need to be advertised to
+ * userspace, for example for P2P channel selection.
+ */
+
+ if (width > 20)
+ prohibited_flags |= IEEE80211_CHAN_NO_OFDM;
+
+ /* 5 and 10 MHz are only defined for the OFDM PHY */
+ if (width < 20)
+ prohibited_flags |= IEEE80211_CHAN_NO_OFDM;
+
+
+ if (!cfg80211_secondary_chans_ok(wiphy, chandef->center_freq1,
+ width, prohibited_flags))
+ return false;
+
+ if (!chandef->center_freq2)
+ return true;
+ return cfg80211_secondary_chans_ok(wiphy, chandef->center_freq2,
+ width, prohibited_flags);
+}
+EXPORT_SYMBOL(cfg80211_chandef_usable);
+
+/*
+ * Check if the channel can be used under permissive conditions mandated by
+ * some regulatory bodies, i.e., the channel is marked with
+ * IEEE80211_CHAN_IR_CONCURRENT and there is an additional station interface
+ * associated to an AP on the same channel or on the same UNII band
+ * (assuming that the AP is an authorized master).
+ * In addition allow operation on a channel on which indoor operation is
+ * allowed, iff we are currently operating in an indoor environment.
+ */
+static bool cfg80211_ir_permissive_chan(struct wiphy *wiphy,
+ enum nl80211_iftype iftype,
+ struct ieee80211_channel *chan)
+{
+ struct wireless_dev *wdev;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+
+ ASSERT_RTNL();
+
+ if (!IS_ENABLED(CPTCFG_CFG80211_REG_RELAX_NO_IR) ||
+ !(wiphy->regulatory_flags & REGULATORY_ENABLE_RELAX_NO_IR))
+ return false;
+
+ /* only valid for GO and TDLS off-channel (station/p2p-CL) */
+ if (iftype != NL80211_IFTYPE_P2P_GO &&
+ iftype != NL80211_IFTYPE_STATION &&
+ iftype != NL80211_IFTYPE_P2P_CLIENT)
+ return false;
+
+ if (regulatory_indoor_allowed() &&
+ (chan->flags & IEEE80211_CHAN_INDOOR_ONLY))
+ return true;
+
+ if (!(chan->flags & IEEE80211_CHAN_IR_CONCURRENT))
+ return false;
+
+ /*
+ * Generally, it is possible to rely on another device/driver to allow
+ * the IR concurrent relaxation, however, since the device can further
+ * enforce the relaxation (by doing a similar verifications as this),
+ * and thus fail the GO instantiation, consider only the interfaces of
+ * the current registered device.
+ */
+ list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
+ struct ieee80211_channel *other_chan = NULL;
+ int r1, r2;
+
+ wdev_lock(wdev);
+ if (wdev->iftype == NL80211_IFTYPE_STATION &&
+ wdev->current_bss)
+ other_chan = wdev->current_bss->pub.channel;
+
+ /*
+ * If a GO already operates on the same GO_CONCURRENT channel,
+ * this one (maybe the same one) can beacon as well. We allow
+ * the operation even if the station we relied on with
+ * GO_CONCURRENT is disconnected now. But then we must make sure
+ * we're not outdoor on an indoor-only channel.
+ */
+ if (iftype == NL80211_IFTYPE_P2P_GO &&
+ wdev->iftype == NL80211_IFTYPE_P2P_GO &&
+ wdev->beacon_interval &&
+ !(chan->flags & IEEE80211_CHAN_INDOOR_ONLY))
+ other_chan = wdev->chandef.chan;
+ wdev_unlock(wdev);
+
+ if (!other_chan)
+ continue;
+
+ if (chan == other_chan)
+ return true;
+
+ if (chan->band != NL80211_BAND_5GHZ)
+ continue;
+
+ r1 = cfg80211_get_unii(chan->center_freq);
+ r2 = cfg80211_get_unii(other_chan->center_freq);
+
+ if (r1 != -EINVAL && r1 == r2) {
+ /*
+ * At some locations channels 149-165 are considered a
+ * bundle, but at other locations, e.g., Indonesia,
+ * channels 149-161 are considered a bundle while
+ * channel 165 is left out and considered to be in a
+ * different bundle. Thus, in case that there is a
+ * station interface connected to an AP on channel 165,
+ * it is assumed that channels 149-161 are allowed for
+ * GO operations. However, having a station interface
+ * connected to an AP on channels 149-161, does not
+ * allow GO operation on channel 165.
+ */
+ if (chan->center_freq == 5825 &&
+ other_chan->center_freq != 5825)
+ continue;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static bool _cfg80211_reg_can_beacon(struct wiphy *wiphy,
+ struct cfg80211_chan_def *chandef,
+ enum nl80211_iftype iftype,
+ bool check_no_ir)
+{
+ bool res;
+ u32 prohibited_flags = IEEE80211_CHAN_DISABLED |
+ IEEE80211_CHAN_RADAR;
+
+ trace_cfg80211_reg_can_beacon(wiphy, chandef, iftype, check_no_ir);
+
+ if (check_no_ir)
+ prohibited_flags |= IEEE80211_CHAN_NO_IR;
+
+ if (cfg80211_chandef_dfs_required(wiphy, chandef, iftype) > 0 &&
+ cfg80211_chandef_dfs_available(wiphy, chandef)) {
+ /* We can skip IEEE80211_CHAN_NO_IR if chandef dfs available */
+ prohibited_flags = IEEE80211_CHAN_DISABLED;
+ }
+
+ res = cfg80211_chandef_usable(wiphy, chandef, prohibited_flags);
+
+ trace_cfg80211_return_bool(res);
+ return res;
+}
+
+bool cfg80211_reg_can_beacon(struct wiphy *wiphy,
+ struct cfg80211_chan_def *chandef,
+ enum nl80211_iftype iftype)
+{
+ return _cfg80211_reg_can_beacon(wiphy, chandef, iftype, true);
+}
+EXPORT_SYMBOL(cfg80211_reg_can_beacon);
+
+bool cfg80211_reg_can_beacon_relax(struct wiphy *wiphy,
+ struct cfg80211_chan_def *chandef,
+ enum nl80211_iftype iftype)
+{
+ bool check_no_ir;
+
+ ASSERT_RTNL();
+
+ /*
+ * Under certain conditions suggested by some regulatory bodies a
+ * GO/STA can IR on channels marked with IEEE80211_NO_IR. Set this flag
+ * only if such relaxations are not enabled and the conditions are not
+ * met.
+ */
+ check_no_ir = !cfg80211_ir_permissive_chan(wiphy, iftype,
+ chandef->chan);
+
+ return _cfg80211_reg_can_beacon(wiphy, chandef, iftype, check_no_ir);
+}
+EXPORT_SYMBOL(cfg80211_reg_can_beacon_relax);
+
+int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev,
+ struct cfg80211_chan_def *chandef)
+{
+ if (!rdev->ops->set_monitor_channel)
+ return -EOPNOTSUPP;
+ if (!cfg80211_has_monitors_only(rdev))
+ return -EBUSY;
+
+ return rdev_set_monitor_channel(rdev, chandef);
+}
+
+void
+cfg80211_get_chan_state(struct wireless_dev *wdev,
+ struct ieee80211_channel **chan,
+ enum cfg80211_chan_mode *chanmode,
+ u8 *radar_detect)
+{
+ int ret;
+
+ *chan = NULL;
+ *chanmode = CHAN_MODE_UNDEFINED;
+
+ ASSERT_WDEV_LOCK(wdev);
+
+ if (wdev->netdev && !netif_running(wdev->netdev))
+ return;
+
+ switch (wdev->iftype) {
+ case NL80211_IFTYPE_ADHOC:
+ if (wdev->current_bss) {
+ *chan = wdev->current_bss->pub.channel;
+ *chanmode = (wdev->ibss_fixed &&
+ !wdev->ibss_dfs_possible)
+ ? CHAN_MODE_SHARED
+ : CHAN_MODE_EXCLUSIVE;
+
+ /* consider worst-case - IBSS can try to return to the
+ * original user-specified channel as creator */
+ if (wdev->ibss_dfs_possible)
+ *radar_detect |= BIT(wdev->chandef.width);
+ return;
+ }
+ break;
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_P2P_CLIENT:
+ if (wdev->current_bss) {
+ *chan = wdev->current_bss->pub.channel;
+ *chanmode = CHAN_MODE_SHARED;
+ return;
+ }
+ break;
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_P2P_GO:
+ if (wdev->cac_started) {
+ *chan = wdev->chandef.chan;
+ *chanmode = CHAN_MODE_SHARED;
+ *radar_detect |= BIT(wdev->chandef.width);
+ } else if (wdev->beacon_interval) {
+ *chan = wdev->chandef.chan;
+ *chanmode = CHAN_MODE_SHARED;
+
+ ret = cfg80211_chandef_dfs_required(wdev->wiphy,
+ &wdev->chandef,
+ wdev->iftype);
+ WARN_ON(ret < 0);
+ if (ret > 0)
+ *radar_detect |= BIT(wdev->chandef.width);
+ }
+ return;
+ case NL80211_IFTYPE_MESH_POINT:
+ if (wdev->mesh_id_len) {
+ *chan = wdev->chandef.chan;
+ *chanmode = CHAN_MODE_SHARED;
+
+ ret = cfg80211_chandef_dfs_required(wdev->wiphy,
+ &wdev->chandef,
+ wdev->iftype);
+ WARN_ON(ret < 0);
+ if (ret > 0)
+ *radar_detect |= BIT(wdev->chandef.width);
+ }
+ return;
+ case NL80211_IFTYPE_OCB:
+ if (wdev->chandef.chan) {
+ *chan = wdev->chandef.chan;
+ *chanmode = CHAN_MODE_SHARED;
+ return;
+ }
+ break;
+ case NL80211_IFTYPE_MONITOR:
+ case NL80211_IFTYPE_AP_VLAN:
+ case NL80211_IFTYPE_WDS:
+ case NL80211_IFTYPE_P2P_DEVICE:
+ case NL80211_IFTYPE_NAN:
+ /* these interface types don't really have a channel */
+ return;
+ case NL80211_IFTYPE_UNSPECIFIED:
+ case NUM_NL80211_IFTYPES:
+ WARN_ON(1);
+ }
+}
diff --git a/net/wireless/core.c b/net/wireless/core.c
new file mode 100644
index 0000000..5b429aa
--- /dev/null
+++ b/net/wireless/core.c
@@ -0,0 +1,1415 @@
+/*
+ * This is the linux wireless configuration interface.
+ *
+ * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2013-2014 Intel Mobile Communications GmbH
+ * Copyright 2015 Intel Deutschland GmbH
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/if.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/nl80211.h>
+#include <linux/debugfs.h>
+#include <linux/notifier.h>
+#include <linux/device.h>
+#include <linux/etherdevice.h>
+#include <linux/rtnetlink.h>
+#include <linux/sched.h>
+#include <net/genetlink.h>
+#include <net/cfg80211.h>
+#include "nl80211.h"
+#include "core.h"
+#include "sysfs.h"
+#include "debugfs.h"
+#include "wext-compat.h"
+#include "rdev-ops.h"
+
+/* name for sysfs, %d is appended */
+#define PHY_NAME "phy"
+
+MODULE_AUTHOR("Johannes Berg");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("wireless configuration support");
+MODULE_ALIAS_GENL_FAMILY(NL80211_GENL_NAME);
+
+/* RCU-protected (and RTNL for writers) */
+LIST_HEAD(cfg80211_rdev_list);
+int cfg80211_rdev_list_generation;
+
+/* for debugfs */
+static struct dentry *ieee80211_debugfs_dir;
+
+/* for the cleanup, scan and event works */
+struct workqueue_struct *cfg80211_wq;
+
+static bool cfg80211_disable_40mhz_24ghz;
+module_param(cfg80211_disable_40mhz_24ghz, bool, 0644);
+MODULE_PARM_DESC(cfg80211_disable_40mhz_24ghz,
+ "Disable 40MHz support in the 2.4GHz band");
+
+struct cfg80211_registered_device *cfg80211_rdev_by_wiphy_idx(int wiphy_idx)
+{
+ struct cfg80211_registered_device *result = NULL, *rdev;
+
+ ASSERT_RTNL();
+
+ list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
+ if (rdev->wiphy_idx == wiphy_idx) {
+ result = rdev;
+ break;
+ }
+ }
+
+ return result;
+}
+
+int get_wiphy_idx(struct wiphy *wiphy)
+{
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+
+ return rdev->wiphy_idx;
+}
+
+struct wiphy *wiphy_idx_to_wiphy(int wiphy_idx)
+{
+ struct cfg80211_registered_device *rdev;
+
+ ASSERT_RTNL();
+
+ rdev = cfg80211_rdev_by_wiphy_idx(wiphy_idx);
+ if (!rdev)
+ return NULL;
+ return &rdev->wiphy;
+}
+
+static int cfg80211_dev_check_name(struct cfg80211_registered_device *rdev,
+ const char *newname)
+{
+ struct cfg80211_registered_device *rdev2;
+ int wiphy_idx, taken = -1, digits;
+
+ ASSERT_RTNL();
+
+ /* prohibit calling the thing phy%d when %d is not its number */
+ sscanf(newname, PHY_NAME "%d%n", &wiphy_idx, &taken);
+ if (taken == strlen(newname) && wiphy_idx != rdev->wiphy_idx) {
+ /* count number of places needed to print wiphy_idx */
+ digits = 1;
+ while (wiphy_idx /= 10)
+ digits++;
+ /*
+ * deny the name if it is phy<idx> where <idx> is printed
+ * without leading zeroes. taken == strlen(newname) here
+ */
+ if (taken == strlen(PHY_NAME) + digits)
+ return -EINVAL;
+ }
+
+ /* Ensure another device does not already have this name. */
+ list_for_each_entry(rdev2, &cfg80211_rdev_list, list)
+ if (strcmp(newname, wiphy_name(&rdev2->wiphy)) == 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+int cfg80211_dev_rename(struct cfg80211_registered_device *rdev,
+ char *newname)
+{
+ int result;
+
+ ASSERT_RTNL();
+
+ /* Ignore nop renames */
+ if (strcmp(newname, wiphy_name(&rdev->wiphy)) == 0)
+ return 0;
+
+ result = cfg80211_dev_check_name(rdev, newname);
+ if (result < 0)
+ return result;
+
+ result = device_rename(&rdev->wiphy.dev, newname);
+ if (result)
+ return result;
+
+ if (rdev->wiphy.debugfsdir &&
+ !debugfs_rename(rdev->wiphy.debugfsdir->d_parent,
+ rdev->wiphy.debugfsdir,
+ rdev->wiphy.debugfsdir->d_parent,
+ newname))
+ pr_err("failed to rename debugfs dir to %s!\n", newname);
+
+ nl80211_notify_wiphy(rdev, NL80211_CMD_NEW_WIPHY);
+
+ return 0;
+}
+
+int cfg80211_switch_netns(struct cfg80211_registered_device *rdev,
+ struct net *net)
+{
+ struct wireless_dev *wdev;
+ int err = 0;
+
+ if (!(rdev->wiphy.flags & WIPHY_FLAG_NETNS_OK))
+ return -EOPNOTSUPP;
+
+ list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
+ if (!wdev->netdev)
+ continue;
+ wdev->netdev->features &= ~NETIF_F_NETNS_LOCAL;
+ err = dev_change_net_namespace(wdev->netdev, net, "wlan%d");
+ if (err)
+ break;
+ wdev->netdev->features |= NETIF_F_NETNS_LOCAL;
+ }
+
+ if (err) {
+ /* failed -- clean up to old netns */
+ net = wiphy_net(&rdev->wiphy);
+
+ list_for_each_entry_continue_reverse(wdev,
+ &rdev->wiphy.wdev_list,
+ list) {
+ if (!wdev->netdev)
+ continue;
+ wdev->netdev->features &= ~NETIF_F_NETNS_LOCAL;
+ err = dev_change_net_namespace(wdev->netdev, net,
+ "wlan%d");
+ WARN_ON(err);
+ wdev->netdev->features |= NETIF_F_NETNS_LOCAL;
+ }
+
+ return err;
+ }
+
+ wiphy_net_set(&rdev->wiphy, net);
+
+ err = device_rename(&rdev->wiphy.dev, dev_name(&rdev->wiphy.dev));
+ WARN_ON(err);
+
+ return 0;
+}
+
+static void cfg80211_rfkill_poll(struct rfkill *rfkill, void *data)
+{
+ struct cfg80211_registered_device *rdev = data;
+
+ rdev_rfkill_poll(rdev);
+}
+
+void cfg80211_stop_p2p_device(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev)
+{
+ ASSERT_RTNL();
+
+ if (WARN_ON(wdev->iftype != NL80211_IFTYPE_P2P_DEVICE))
+ return;
+
+ if (!wdev_running(wdev))
+ return;
+
+ rdev_stop_p2p_device(rdev, wdev);
+ wdev->is_running = false;
+
+ rdev->opencount--;
+
+ if (rdev->scan_req && rdev->scan_req->wdev == wdev) {
+ if (WARN_ON(!rdev->scan_req->notified))
+ rdev->scan_req->info.aborted = true;
+ ___cfg80211_scan_done(rdev, false);
+ }
+}
+
+void cfg80211_stop_nan(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev)
+{
+ ASSERT_RTNL();
+
+ if (WARN_ON(wdev->iftype != NL80211_IFTYPE_NAN))
+ return;
+
+ if (!wdev_running(wdev))
+ return;
+
+ rdev_stop_nan(rdev, wdev);
+ wdev->is_running = false;
+
+ rdev->opencount--;
+}
+
+void cfg80211_shutdown_all_interfaces(struct wiphy *wiphy)
+{
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+ struct wireless_dev *wdev;
+
+ ASSERT_RTNL();
+
+ list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
+ if (wdev->netdev) {
+ dev_close(wdev->netdev);
+ continue;
+ }
+ /* otherwise, check iftype */
+ switch (wdev->iftype) {
+ case NL80211_IFTYPE_P2P_DEVICE:
+ cfg80211_stop_p2p_device(rdev, wdev);
+ break;
+ case NL80211_IFTYPE_NAN:
+ cfg80211_stop_nan(rdev, wdev);
+ break;
+ default:
+ break;
+ }
+ }
+}
+EXPORT_SYMBOL_GPL(cfg80211_shutdown_all_interfaces);
+
+static int cfg80211_rfkill_set_block(void *data, bool blocked)
+{
+ struct cfg80211_registered_device *rdev = data;
+
+ if (!blocked)
+ return 0;
+
+ rtnl_lock();
+ cfg80211_shutdown_all_interfaces(&rdev->wiphy);
+ rtnl_unlock();
+
+ return 0;
+}
+
+static void cfg80211_rfkill_sync_work(struct work_struct *work)
+{
+ struct cfg80211_registered_device *rdev;
+
+ rdev = container_of(work, struct cfg80211_registered_device, rfkill_sync);
+ cfg80211_rfkill_set_block(rdev, rfkill_blocked(rdev->rfkill));
+}
+
+static void cfg80211_event_work(struct work_struct *work)
+{
+ struct cfg80211_registered_device *rdev;
+
+ rdev = container_of(work, struct cfg80211_registered_device,
+ event_work);
+
+ rtnl_lock();
+ cfg80211_process_rdev_events(rdev);
+ rtnl_unlock();
+}
+
+void cfg80211_destroy_ifaces(struct cfg80211_registered_device *rdev)
+{
+ struct wireless_dev *wdev, *tmp;
+
+ ASSERT_RTNL();
+
+ list_for_each_entry_safe(wdev, tmp, &rdev->wiphy.wdev_list, list) {
+ if (wdev->nl_owner_dead)
+ rdev_del_virtual_intf(rdev, wdev);
+ }
+}
+
+static void cfg80211_destroy_iface_wk(struct work_struct *work)
+{
+ struct cfg80211_registered_device *rdev;
+
+ rdev = container_of(work, struct cfg80211_registered_device,
+ destroy_work);
+
+ rtnl_lock();
+ cfg80211_destroy_ifaces(rdev);
+ rtnl_unlock();
+}
+
+static void cfg80211_sched_scan_stop_wk(struct work_struct *work)
+{
+ struct cfg80211_registered_device *rdev;
+ struct cfg80211_sched_scan_request *req, *tmp;
+
+ rdev = container_of(work, struct cfg80211_registered_device,
+ sched_scan_stop_wk);
+
+ rtnl_lock();
+ list_for_each_entry_safe(req, tmp, &rdev->sched_scan_req_list, list) {
+ if (req->nl_owner_dead)
+ cfg80211_stop_sched_scan_req(rdev, req, false);
+ }
+ rtnl_unlock();
+}
+
+static void cfg80211_propagate_radar_detect_wk(struct work_struct *work)
+{
+ struct cfg80211_registered_device *rdev;
+
+ rdev = container_of(work, struct cfg80211_registered_device,
+ propagate_radar_detect_wk);
+
+ rtnl_lock();
+
+ regulatory_propagate_dfs_state(&rdev->wiphy, &rdev->radar_chandef,
+ NL80211_DFS_UNAVAILABLE,
+ NL80211_RADAR_DETECTED);
+
+ rtnl_unlock();
+}
+
+static void cfg80211_propagate_cac_done_wk(struct work_struct *work)
+{
+ struct cfg80211_registered_device *rdev;
+
+ rdev = container_of(work, struct cfg80211_registered_device,
+ propagate_cac_done_wk);
+
+ rtnl_lock();
+
+ regulatory_propagate_dfs_state(&rdev->wiphy, &rdev->cac_done_chandef,
+ NL80211_DFS_AVAILABLE,
+ NL80211_RADAR_CAC_FINISHED);
+
+ rtnl_unlock();
+}
+
+/* exported functions */
+
+struct wiphy *wiphy_new_nm(const struct cfg80211_ops *ops, int sizeof_priv,
+ const char *requested_name)
+{
+ static atomic_t wiphy_counter = ATOMIC_INIT(0);
+
+ struct cfg80211_registered_device *rdev;
+ int alloc_size;
+
+ /*
+ * Make sure the padding is >= the rest of the struct so that we
+ * always keep it large enough to pad out the entire original
+ * kernel's struct. We really only need to make sure it's larger
+ * than the kernel compat is compiled against, but since it'll
+ * only increase in size make sure it's larger than the current
+ * version of it. Subtract since it's included.
+ */
+ BUILD_BUG_ON(WIPHY_COMPAT_PAD_SIZE <
+ sizeof(struct wiphy) - WIPHY_COMPAT_PAD_SIZE);
+
+ WARN_ON(ops->add_key && (!ops->del_key || !ops->set_default_key));
+ WARN_ON(ops->auth && (!ops->assoc || !ops->deauth || !ops->disassoc));
+ WARN_ON(ops->connect && !ops->disconnect);
+ WARN_ON(ops->join_ibss && !ops->leave_ibss);
+ WARN_ON(ops->add_virtual_intf && !ops->del_virtual_intf);
+ WARN_ON(ops->add_station && !ops->del_station);
+ WARN_ON(ops->add_mpath && !ops->del_mpath);
+ WARN_ON(ops->join_mesh && !ops->leave_mesh);
+ WARN_ON(ops->start_p2p_device && !ops->stop_p2p_device);
+ WARN_ON(ops->start_ap && !ops->stop_ap);
+ WARN_ON(ops->join_ocb && !ops->leave_ocb);
+ WARN_ON(ops->suspend && !ops->resume);
+ WARN_ON(ops->sched_scan_start && !ops->sched_scan_stop);
+ WARN_ON(ops->remain_on_channel && !ops->cancel_remain_on_channel);
+ WARN_ON(ops->tdls_channel_switch && !ops->tdls_cancel_channel_switch);
+ WARN_ON(ops->add_tx_ts && !ops->del_tx_ts);
+
+ alloc_size = sizeof(*rdev) + sizeof_priv;
+
+ rdev = kzalloc(alloc_size, GFP_KERNEL);
+ if (!rdev)
+ return NULL;
+
+ rdev->ops = ops;
+
+ rdev->wiphy_idx = atomic_inc_return(&wiphy_counter);
+
+ if (unlikely(rdev->wiphy_idx < 0)) {
+ /* ugh, wrapped! */
+ atomic_dec(&wiphy_counter);
+ kfree(rdev);
+ return NULL;
+ }
+
+ /* atomic_inc_return makes it start at 1, make it start at 0 */
+ rdev->wiphy_idx--;
+
+ /* give it a proper name */
+ if (requested_name && requested_name[0]) {
+ int rv;
+
+ rtnl_lock();
+ rv = cfg80211_dev_check_name(rdev, requested_name);
+
+ if (rv < 0) {
+ rtnl_unlock();
+ goto use_default_name;
+ }
+
+ rv = dev_set_name(&rdev->wiphy.dev, "%s", requested_name);
+ rtnl_unlock();
+ if (rv)
+ goto use_default_name;
+ } else {
+use_default_name:
+ /* NOTE: This is *probably* safe w/out holding rtnl because of
+ * the restrictions on phy names. Probably this call could
+ * fail if some other part of the kernel (re)named a device
+ * phyX. But, might should add some locking and check return
+ * value, and use a different name if this one exists?
+ */
+ dev_set_name(&rdev->wiphy.dev, PHY_NAME "%d", rdev->wiphy_idx);
+ }
+
+ INIT_LIST_HEAD(&rdev->wiphy.wdev_list);
+ INIT_LIST_HEAD(&rdev->beacon_registrations);
+ spin_lock_init(&rdev->beacon_registrations_lock);
+ spin_lock_init(&rdev->bss_lock);
+ INIT_LIST_HEAD(&rdev->bss_list);
+ INIT_LIST_HEAD(&rdev->sched_scan_req_list);
+ INIT_WORK(&rdev->scan_done_wk, __cfg80211_scan_done);
+ INIT_LIST_HEAD(&rdev->mlme_unreg);
+ spin_lock_init(&rdev->mlme_unreg_lock);
+ INIT_WORK(&rdev->mlme_unreg_wk, cfg80211_mlme_unreg_wk);
+ INIT_DELAYED_WORK(&rdev->dfs_update_channels_wk,
+ cfg80211_dfs_channels_update_work);
+ device_initialize(&rdev->wiphy.dev);
+ rdev->wiphy.dev.class = &ieee80211_class;
+ rdev->wiphy.dev.platform_data = rdev;
+ device_enable_async_suspend(&rdev->wiphy.dev);
+
+ INIT_WORK(&rdev->destroy_work, cfg80211_destroy_iface_wk);
+ INIT_WORK(&rdev->sched_scan_stop_wk, cfg80211_sched_scan_stop_wk);
+ INIT_WORK(&rdev->sched_scan_res_wk, cfg80211_sched_scan_results_wk);
+ INIT_WORK(&rdev->propagate_radar_detect_wk,
+ cfg80211_propagate_radar_detect_wk);
+ INIT_WORK(&rdev->propagate_cac_done_wk, cfg80211_propagate_cac_done_wk);
+
+#ifdef CPTCFG_CFG80211_DEFAULT_PS
+ rdev->wiphy.flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT;
+#endif
+
+ wiphy_net_set(&rdev->wiphy, &init_net);
+
+ rdev->rfkill_ops.set_block = cfg80211_rfkill_set_block;
+ rdev->rfkill = rfkill_alloc(dev_name(&rdev->wiphy.dev),
+ &rdev->wiphy.dev, RFKILL_TYPE_WLAN,
+ &rdev->rfkill_ops, rdev);
+
+ if (!rdev->rfkill) {
+ kfree(rdev);
+ return NULL;
+ }
+
+ INIT_WORK(&rdev->rfkill_sync, cfg80211_rfkill_sync_work);
+ INIT_WORK(&rdev->conn_work, cfg80211_conn_work);
+ INIT_WORK(&rdev->event_work, cfg80211_event_work);
+
+ init_waitqueue_head(&rdev->dev_wait);
+
+ /*
+ * Initialize wiphy parameters to IEEE 802.11 MIB default values.
+ * Fragmentation and RTS threshold are disabled by default with the
+ * special -1 value.
+ */
+ rdev->wiphy.retry_short = 7;
+ rdev->wiphy.retry_long = 4;
+ rdev->wiphy.frag_threshold = (u32) -1;
+ rdev->wiphy.rts_threshold = (u32) -1;
+ rdev->wiphy.coverage_class = 0;
+
+ rdev->wiphy.max_num_csa_counters = 1;
+
+ rdev->wiphy.max_sched_scan_plans = 1;
+ rdev->wiphy.max_sched_scan_plan_interval = U32_MAX;
+
+ return &rdev->wiphy;
+}
+EXPORT_SYMBOL(wiphy_new_nm);
+
+static int wiphy_verify_combinations(struct wiphy *wiphy)
+{
+ const struct ieee80211_iface_combination *c;
+ int i, j;
+
+ for (i = 0; i < wiphy->n_iface_combinations; i++) {
+ u32 cnt = 0;
+ u16 all_iftypes = 0;
+
+ c = &wiphy->iface_combinations[i];
+
+ /*
+ * Combinations with just one interface aren't real,
+ * however we make an exception for DFS.
+ */
+ if (WARN_ON((c->max_interfaces < 2) && !c->radar_detect_widths))
+ return -EINVAL;
+
+ /* Need at least one channel */
+ if (WARN_ON(!c->num_different_channels))
+ return -EINVAL;
+
+ /*
+ * Put a sane limit on maximum number of different
+ * channels to simplify channel accounting code.
+ */
+ if (WARN_ON(c->num_different_channels >
+ CFG80211_MAX_NUM_DIFFERENT_CHANNELS))
+ return -EINVAL;
+
+ /* DFS only works on one channel. */
+ if (WARN_ON(c->radar_detect_widths &&
+ (c->num_different_channels > 1)))
+ return -EINVAL;
+
+ if (WARN_ON(!c->n_limits))
+ return -EINVAL;
+
+ for (j = 0; j < c->n_limits; j++) {
+ u16 types = c->limits[j].types;
+
+ /* interface types shouldn't overlap */
+ if (WARN_ON(types & all_iftypes))
+ return -EINVAL;
+ all_iftypes |= types;
+
+ if (WARN_ON(!c->limits[j].max))
+ return -EINVAL;
+
+ /* Shouldn't list software iftypes in combinations! */
+ if (WARN_ON(wiphy->software_iftypes & types))
+ return -EINVAL;
+
+ /* Only a single P2P_DEVICE can be allowed */
+ if (WARN_ON(types & BIT(NL80211_IFTYPE_P2P_DEVICE) &&
+ c->limits[j].max > 1))
+ return -EINVAL;
+
+ /* Only a single NAN can be allowed */
+ if (WARN_ON(types & BIT(NL80211_IFTYPE_NAN) &&
+ c->limits[j].max > 1))
+ return -EINVAL;
+
+ /*
+ * This isn't well-defined right now. If you have an
+ * IBSS interface, then its beacon interval may change
+ * by joining other networks, and nothing prevents it
+ * from doing that.
+ * So technically we probably shouldn't even allow AP
+ * and IBSS in the same interface, but it seems that
+ * some drivers support that, possibly only with fixed
+ * beacon intervals for IBSS.
+ */
+ if (WARN_ON(types & BIT(NL80211_IFTYPE_ADHOC) &&
+ c->beacon_int_min_gcd)) {
+ return -EINVAL;
+ }
+
+ cnt += c->limits[j].max;
+ /*
+ * Don't advertise an unsupported type
+ * in a combination.
+ */
+ if (WARN_ON((wiphy->interface_modes & types) != types))
+ return -EINVAL;
+ }
+
+#ifndef CPTCFG_WIRELESS_WDS
+ if (WARN_ON(all_iftypes & BIT(NL80211_IFTYPE_WDS)))
+ return -EINVAL;
+#endif
+
+ /* You can't even choose that many! */
+ if (WARN_ON(cnt < c->max_interfaces))
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int wiphy_register(struct wiphy *wiphy)
+{
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+ int res;
+ enum nl80211_band band;
+ struct ieee80211_supported_band *sband;
+ bool have_band = false;
+ int i;
+ u16 ifmodes = wiphy->interface_modes;
+
+#ifdef CONFIG_PM
+ if (WARN_ON(wiphy->wowlan &&
+ (wiphy->wowlan->flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) &&
+ !(wiphy->wowlan->flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY)))
+ return -EINVAL;
+ if (WARN_ON(wiphy->wowlan &&
+ !wiphy->wowlan->flags && !wiphy->wowlan->n_patterns &&
+ !wiphy->wowlan->tcp))
+ return -EINVAL;
+#endif
+ if (WARN_ON((wiphy->features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH) &&
+ (!rdev->ops->tdls_channel_switch ||
+ !rdev->ops->tdls_cancel_channel_switch)))
+ return -EINVAL;
+
+ if (WARN_ON((wiphy->interface_modes & BIT(NL80211_IFTYPE_NAN)) &&
+ (!rdev->ops->start_nan || !rdev->ops->stop_nan ||
+ !rdev->ops->add_nan_func || !rdev->ops->del_nan_func ||
+ !(wiphy->nan_supported_bands & BIT(NL80211_BAND_2GHZ)))))
+ return -EINVAL;
+
+#ifndef CPTCFG_WIRELESS_WDS
+ if (WARN_ON(wiphy->interface_modes & BIT(NL80211_IFTYPE_WDS)))
+ return -EINVAL;
+#endif
+
+ /*
+ * if a wiphy has unsupported modes for regulatory channel enforcement,
+ * opt-out of enforcement checking
+ */
+ if (wiphy->interface_modes & ~(BIT(NL80211_IFTYPE_STATION) |
+ BIT(NL80211_IFTYPE_P2P_CLIENT) |
+ BIT(NL80211_IFTYPE_AP) |
+ BIT(NL80211_IFTYPE_P2P_GO) |
+ BIT(NL80211_IFTYPE_ADHOC) |
+ BIT(NL80211_IFTYPE_P2P_DEVICE) |
+ BIT(NL80211_IFTYPE_NAN) |
+ BIT(NL80211_IFTYPE_AP_VLAN) |
+ BIT(NL80211_IFTYPE_MONITOR)))
+ wiphy->regulatory_flags |= REGULATORY_IGNORE_STALE_KICKOFF;
+
+ if (WARN_ON((wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) &&
+ (wiphy->regulatory_flags &
+ (REGULATORY_CUSTOM_REG |
+ REGULATORY_STRICT_REG |
+ REGULATORY_COUNTRY_IE_FOLLOW_POWER |
+ REGULATORY_COUNTRY_IE_IGNORE))))
+ return -EINVAL;
+
+ if (WARN_ON(wiphy->coalesce &&
+ (!wiphy->coalesce->n_rules ||
+ !wiphy->coalesce->n_patterns) &&
+ (!wiphy->coalesce->pattern_min_len ||
+ wiphy->coalesce->pattern_min_len >
+ wiphy->coalesce->pattern_max_len)))
+ return -EINVAL;
+
+ if (WARN_ON(wiphy->ap_sme_capa &&
+ !(wiphy->flags & WIPHY_FLAG_HAVE_AP_SME)))
+ return -EINVAL;
+
+ if (WARN_ON(wiphy->addresses && !wiphy->n_addresses))
+ return -EINVAL;
+
+ if (WARN_ON(wiphy->addresses &&
+ !is_zero_ether_addr(wiphy->perm_addr) &&
+ memcmp(wiphy->perm_addr, wiphy->addresses[0].addr,
+ ETH_ALEN)))
+ return -EINVAL;
+
+ if (WARN_ON(wiphy->max_acl_mac_addrs &&
+ (!(wiphy->flags & WIPHY_FLAG_HAVE_AP_SME) ||
+ !rdev->ops->set_mac_acl)))
+ return -EINVAL;
+
+ /* assure only valid behaviours are flagged by driver
+ * hence subtract 2 as bit 0 is invalid.
+ */
+ if (WARN_ON(wiphy->bss_select_support &&
+ (wiphy->bss_select_support & ~(BIT(__NL80211_BSS_SELECT_ATTR_AFTER_LAST) - 2))))
+ return -EINVAL;
+
+ if (WARN_ON(wiphy_ext_feature_isset(&rdev->wiphy,
+ NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X) &&
+ (!rdev->ops->set_pmk || !rdev->ops->del_pmk)))
+ return -EINVAL;
+
+ if (wiphy->addresses)
+ memcpy(wiphy->perm_addr, wiphy->addresses[0].addr, ETH_ALEN);
+
+ /* sanity check ifmodes */
+ WARN_ON(!ifmodes);
+ ifmodes &= ((1 << NUM_NL80211_IFTYPES) - 1) & ~1;
+ if (WARN_ON(ifmodes != wiphy->interface_modes))
+ wiphy->interface_modes = ifmodes;
+
+ res = wiphy_verify_combinations(wiphy);
+ if (res)
+ return res;
+
+ /* sanity check supported bands/channels */
+ for (band = 0; band < NUM_NL80211_BANDS; band++) {
+ sband = wiphy->bands[band];
+ if (!sband)
+ continue;
+
+ sband->band = band;
+ if (WARN_ON(!sband->n_channels))
+ return -EINVAL;
+ /*
+ * on 60GHz band, there are no legacy rates, so
+ * n_bitrates is 0
+ */
+ if (WARN_ON(band != NL80211_BAND_60GHZ &&
+ !sband->n_bitrates))
+ return -EINVAL;
+
+ /*
+ * Since cfg80211_disable_40mhz_24ghz is global, we can
+ * modify the sband's ht data even if the driver uses a
+ * global structure for that.
+ */
+ if (cfg80211_disable_40mhz_24ghz &&
+ band == NL80211_BAND_2GHZ &&
+ sband->ht_cap.ht_supported) {
+ sband->ht_cap.cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+ sband->ht_cap.cap &= ~IEEE80211_HT_CAP_SGI_40;
+ }
+
+ /*
+ * Since we use a u32 for rate bitmaps in
+ * ieee80211_get_response_rate, we cannot
+ * have more than 32 legacy rates.
+ */
+ if (WARN_ON(sband->n_bitrates > 32))
+ return -EINVAL;
+
+ for (i = 0; i < sband->n_channels; i++) {
+ sband->channels[i].orig_flags =
+ sband->channels[i].flags;
+ sband->channels[i].orig_mag = INT_MAX;
+ sband->channels[i].orig_mpwr =
+ sband->channels[i].max_power;
+ sband->channels[i].band = band;
+ }
+
+ have_band = true;
+ }
+
+ if (!have_band) {
+ WARN_ON(1);
+ return -EINVAL;
+ }
+
+#ifdef CONFIG_PM
+ if (WARN_ON(rdev->wiphy.wowlan && rdev->wiphy.wowlan->n_patterns &&
+ (!rdev->wiphy.wowlan->pattern_min_len ||
+ rdev->wiphy.wowlan->pattern_min_len >
+ rdev->wiphy.wowlan->pattern_max_len)))
+ return -EINVAL;
+#endif
+
+ /* check and set up bitrates */
+ ieee80211_set_bitrate_flags(wiphy);
+
+ rdev->wiphy.features |= NL80211_FEATURE_SCAN_FLUSH;
+
+ rtnl_lock();
+ res = device_add(&rdev->wiphy.dev);
+ if (res) {
+ rtnl_unlock();
+ return res;
+ }
+
+ /* set up regulatory info */
+ wiphy_regulatory_register(wiphy);
+
+ list_add_rcu(&rdev->list, &cfg80211_rdev_list);
+ cfg80211_rdev_list_generation++;
+
+ /* add to debugfs */
+ rdev->wiphy.debugfsdir =
+ debugfs_create_dir(wiphy_name(&rdev->wiphy),
+ ieee80211_debugfs_dir);
+ if (IS_ERR(rdev->wiphy.debugfsdir))
+ rdev->wiphy.debugfsdir = NULL;
+
+ cfg80211_debugfs_rdev_add(rdev);
+ nl80211_notify_wiphy(rdev, NL80211_CMD_NEW_WIPHY);
+
+ if (wiphy->regulatory_flags & REGULATORY_CUSTOM_REG) {
+ struct regulatory_request request;
+
+ request.wiphy_idx = get_wiphy_idx(wiphy);
+ request.initiator = NL80211_REGDOM_SET_BY_DRIVER;
+ request.alpha2[0] = '9';
+ request.alpha2[1] = '9';
+
+ nl80211_send_reg_change_event(&request);
+ }
+
+ /* Check that nobody globally advertises any capabilities they do not
+ * advertise on all possible interface types.
+ */
+ if (wiphy->extended_capabilities_len &&
+ wiphy->num_iftype_ext_capab &&
+ wiphy->iftype_ext_capab) {
+ u8 supported_on_all, j;
+ const struct wiphy_iftype_ext_capab *capab;
+
+ capab = wiphy->iftype_ext_capab;
+ for (j = 0; j < wiphy->extended_capabilities_len; j++) {
+ if (capab[0].extended_capabilities_len > j)
+ supported_on_all =
+ capab[0].extended_capabilities[j];
+ else
+ supported_on_all = 0x00;
+ for (i = 1; i < wiphy->num_iftype_ext_capab; i++) {
+ if (j >= capab[i].extended_capabilities_len) {
+ supported_on_all = 0x00;
+ break;
+ }
+ supported_on_all &=
+ capab[i].extended_capabilities[j];
+ }
+ if (WARN_ON(wiphy->extended_capabilities[j] &
+ ~supported_on_all))
+ break;
+ }
+ }
+
+ rdev->wiphy.registered = true;
+ rtnl_unlock();
+
+ res = rfkill_register(rdev->rfkill);
+ if (res) {
+ rfkill_destroy(rdev->rfkill);
+ rdev->rfkill = NULL;
+ wiphy_unregister(&rdev->wiphy);
+ return res;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(wiphy_register);
+
+void wiphy_rfkill_start_polling(struct wiphy *wiphy)
+{
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+
+ if (!rdev->ops->rfkill_poll)
+ return;
+ rdev->rfkill_ops.poll = cfg80211_rfkill_poll;
+ rfkill_resume_polling(rdev->rfkill);
+}
+EXPORT_SYMBOL(wiphy_rfkill_start_polling);
+
+void wiphy_rfkill_stop_polling(struct wiphy *wiphy)
+{
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+
+ rfkill_pause_polling(rdev->rfkill);
+}
+EXPORT_SYMBOL(wiphy_rfkill_stop_polling);
+
+void wiphy_unregister(struct wiphy *wiphy)
+{
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+
+ wait_event(rdev->dev_wait, ({
+ int __count;
+ rtnl_lock();
+ __count = rdev->opencount;
+ rtnl_unlock();
+ __count == 0; }));
+
+ if (rdev->rfkill)
+ rfkill_unregister(rdev->rfkill);
+
+ rtnl_lock();
+ nl80211_notify_wiphy(rdev, NL80211_CMD_DEL_WIPHY);
+ rdev->wiphy.registered = false;
+
+ WARN_ON(!list_empty(&rdev->wiphy.wdev_list));
+
+ /*
+ * First remove the hardware from everywhere, this makes
+ * it impossible to find from userspace.
+ */
+ debugfs_remove_recursive(rdev->wiphy.debugfsdir);
+ list_del_rcu(&rdev->list);
+ synchronize_rcu();
+
+ /*
+ * If this device got a regulatory hint tell core its
+ * free to listen now to a new shiny device regulatory hint
+ */
+ wiphy_regulatory_deregister(wiphy);
+
+ cfg80211_rdev_list_generation++;
+ device_del(&rdev->wiphy.dev);
+
+ rtnl_unlock();
+
+ flush_work(&rdev->scan_done_wk);
+ cancel_work_sync(&rdev->conn_work);
+ flush_work(&rdev->event_work);
+ cancel_delayed_work_sync(&rdev->dfs_update_channels_wk);
+ flush_work(&rdev->destroy_work);
+ flush_work(&rdev->sched_scan_stop_wk);
+ flush_work(&rdev->mlme_unreg_wk);
+ flush_work(&rdev->propagate_radar_detect_wk);
+ flush_work(&rdev->propagate_cac_done_wk);
+
+#ifdef CONFIG_PM
+ if (rdev->wiphy.wowlan_config && rdev->ops->set_wakeup)
+ rdev_set_wakeup(rdev, false);
+#endif
+ cfg80211_rdev_free_wowlan(rdev);
+ cfg80211_rdev_free_coalesce(rdev);
+}
+EXPORT_SYMBOL(wiphy_unregister);
+
+void cfg80211_dev_free(struct cfg80211_registered_device *rdev)
+{
+ struct cfg80211_internal_bss *scan, *tmp;
+ struct cfg80211_beacon_registration *reg, *treg;
+ rfkill_destroy(rdev->rfkill);
+ list_for_each_entry_safe(reg, treg, &rdev->beacon_registrations, list) {
+ list_del(®->list);
+ kfree(reg);
+ }
+ list_for_each_entry_safe(scan, tmp, &rdev->bss_list, list)
+ cfg80211_put_bss(&rdev->wiphy, &scan->pub);
+ kfree(rdev);
+}
+
+void wiphy_free(struct wiphy *wiphy)
+{
+ put_device(&wiphy->dev);
+}
+EXPORT_SYMBOL(wiphy_free);
+
+void wiphy_rfkill_set_hw_state(struct wiphy *wiphy, bool blocked)
+{
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+
+ if (rfkill_set_hw_state(rdev->rfkill, blocked))
+ schedule_work(&rdev->rfkill_sync);
+}
+EXPORT_SYMBOL(wiphy_rfkill_set_hw_state);
+
+void cfg80211_cqm_config_free(struct wireless_dev *wdev)
+{
+ kfree(wdev->cqm_config);
+ wdev->cqm_config = NULL;
+}
+
+void cfg80211_unregister_wdev(struct wireless_dev *wdev)
+{
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+
+ ASSERT_RTNL();
+
+ if (WARN_ON(wdev->netdev))
+ return;
+
+ nl80211_notify_iface(rdev, wdev, NL80211_CMD_DEL_INTERFACE);
+
+ list_del_rcu(&wdev->list);
+ rdev->devlist_generation++;
+
+ switch (wdev->iftype) {
+ case NL80211_IFTYPE_P2P_DEVICE:
+ cfg80211_mlme_purge_registrations(wdev);
+ cfg80211_stop_p2p_device(rdev, wdev);
+ break;
+ case NL80211_IFTYPE_NAN:
+ cfg80211_stop_nan(rdev, wdev);
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ break;
+ }
+
+ cfg80211_cqm_config_free(wdev);
+}
+EXPORT_SYMBOL(cfg80211_unregister_wdev);
+
+static const struct device_type wiphy_type = {
+ .name = "wlan",
+};
+
+void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev,
+ enum nl80211_iftype iftype, int num)
+{
+ ASSERT_RTNL();
+
+ rdev->num_running_ifaces += num;
+ if (iftype == NL80211_IFTYPE_MONITOR)
+ rdev->num_running_monitor_ifaces += num;
+}
+
+void __cfg80211_leave(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev)
+{
+ struct net_device *dev = wdev->netdev;
+ struct cfg80211_sched_scan_request *pos, *tmp;
+
+ ASSERT_RTNL();
+ ASSERT_WDEV_LOCK(wdev);
+
+ switch (wdev->iftype) {
+ case NL80211_IFTYPE_ADHOC:
+ __cfg80211_leave_ibss(rdev, dev, true);
+ break;
+ case NL80211_IFTYPE_P2P_CLIENT:
+ case NL80211_IFTYPE_STATION:
+ list_for_each_entry_safe(pos, tmp, &rdev->sched_scan_req_list,
+ list) {
+ if (dev == pos->dev)
+ cfg80211_stop_sched_scan_req(rdev, pos, false);
+ }
+
+#ifdef CPTCFG_CFG80211_WEXT
+ kfree(wdev->wext.ie);
+ wdev->wext.ie = NULL;
+ wdev->wext.ie_len = 0;
+ wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
+#endif
+ cfg80211_disconnect(rdev, dev,
+ WLAN_REASON_DEAUTH_LEAVING, true);
+ break;
+ case NL80211_IFTYPE_MESH_POINT:
+ __cfg80211_leave_mesh(rdev, dev);
+ break;
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_P2P_GO:
+ __cfg80211_stop_ap(rdev, dev, true);
+ break;
+ case NL80211_IFTYPE_OCB:
+ __cfg80211_leave_ocb(rdev, dev);
+ break;
+ case NL80211_IFTYPE_WDS:
+ /* must be handled by mac80211/driver, has no APIs */
+ break;
+ case NL80211_IFTYPE_P2P_DEVICE:
+ case NL80211_IFTYPE_NAN:
+ /* cannot happen, has no netdev */
+ break;
+ case NL80211_IFTYPE_AP_VLAN:
+ case NL80211_IFTYPE_MONITOR:
+ /* nothing to do */
+ break;
+ case NL80211_IFTYPE_UNSPECIFIED:
+ case NUM_NL80211_IFTYPES:
+ /* invalid */
+ break;
+ }
+}
+
+void cfg80211_leave(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev)
+{
+ wdev_lock(wdev);
+ __cfg80211_leave(rdev, wdev);
+ wdev_unlock(wdev);
+}
+
+void cfg80211_stop_iface(struct wiphy *wiphy, struct wireless_dev *wdev,
+ gfp_t gfp)
+{
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+ struct cfg80211_event *ev;
+ unsigned long flags;
+
+ trace_cfg80211_stop_iface(wiphy, wdev);
+
+ ev = kzalloc(sizeof(*ev), gfp);
+ if (!ev)
+ return;
+
+ ev->type = EVENT_STOPPED;
+
+ spin_lock_irqsave(&wdev->event_lock, flags);
+ list_add_tail(&ev->list, &wdev->event_list);
+ spin_unlock_irqrestore(&wdev->event_lock, flags);
+ queue_work(cfg80211_wq, &rdev->event_work);
+}
+EXPORT_SYMBOL(cfg80211_stop_iface);
+
+static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
+ unsigned long state, void *ptr)
+{
+ struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_registered_device *rdev;
+ struct cfg80211_sched_scan_request *pos, *tmp;
+
+ if (!wdev)
+ return NOTIFY_DONE;
+
+ rdev = wiphy_to_rdev(wdev->wiphy);
+
+ WARN_ON(wdev->iftype == NL80211_IFTYPE_UNSPECIFIED);
+
+ switch (state) {
+ case NETDEV_POST_INIT:
+ SET_NETDEV_DEVTYPE(dev, &wiphy_type);
+ break;
+ case NETDEV_REGISTER:
+ /*
+ * NB: cannot take rdev->mtx here because this may be
+ * called within code protected by it when interfaces
+ * are added with nl80211.
+ */
+ mutex_init(&wdev->mtx);
+ INIT_LIST_HEAD(&wdev->event_list);
+ spin_lock_init(&wdev->event_lock);
+ INIT_LIST_HEAD(&wdev->mgmt_registrations);
+ spin_lock_init(&wdev->mgmt_registrations_lock);
+
+ /*
+ * We get here also when the interface changes network namespaces,
+ * as it's registered into the new one, but we don't want it to
+ * change ID in that case. Checking if the ID is already assigned
+ * works, because 0 isn't considered a valid ID and the memory is
+ * 0-initialized.
+ */
+ if (!wdev->identifier)
+ wdev->identifier = ++rdev->wdev_id;
+ list_add_rcu(&wdev->list, &rdev->wiphy.wdev_list);
+ rdev->devlist_generation++;
+ /* can only change netns with wiphy */
+ dev->features |= NETIF_F_NETNS_LOCAL;
+
+ if (sysfs_create_link(&dev->dev.kobj, &rdev->wiphy.dev.kobj,
+ "phy80211")) {
+ pr_err("failed to add phy80211 symlink to netdev!\n");
+ }
+ wdev->netdev = dev;
+#ifdef CPTCFG_CFG80211_WEXT
+#ifdef CONFIG_WIRELESS_EXT
+ if (!dev->wireless_handlers)
+ dev->wireless_handlers = &cfg80211_wext_handler;
+#else
+ printk_once(KERN_WARNING "cfg80211: wext will not work because "
+ "kernel was compiled with CONFIG_WIRELESS_EXT=n. "
+ "Tools using wext interface, like iwconfig will "
+ "not work.\n");
+#endif
+ wdev->wext.default_key = -1;
+ wdev->wext.default_mgmt_key = -1;
+ wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
+#endif
+
+ if (wdev->wiphy->flags & WIPHY_FLAG_PS_ON_BY_DEFAULT)
+ wdev->ps = true;
+ else
+ wdev->ps = false;
+ /* allow mac80211 to determine the timeout */
+ wdev->ps_timeout = -1;
+
+ if ((wdev->iftype == NL80211_IFTYPE_STATION ||
+ wdev->iftype == NL80211_IFTYPE_P2P_CLIENT ||
+ wdev->iftype == NL80211_IFTYPE_ADHOC) && !wdev->use_4addr)
+ dev->priv_flags |= IFF_DONT_BRIDGE;
+
+ INIT_WORK(&wdev->disconnect_wk, cfg80211_autodisconnect_wk);
+
+ nl80211_notify_iface(rdev, wdev, NL80211_CMD_NEW_INTERFACE);
+ break;
+ case NETDEV_GOING_DOWN:
+ cfg80211_leave(rdev, wdev);
+ break;
+ case NETDEV_DOWN:
+ cfg80211_update_iface_num(rdev, wdev->iftype, -1);
+ if (rdev->scan_req && rdev->scan_req->wdev == wdev) {
+ if (WARN_ON(!rdev->scan_req->notified))
+ rdev->scan_req->info.aborted = true;
+ ___cfg80211_scan_done(rdev, false);
+ }
+
+ list_for_each_entry_safe(pos, tmp,
+ &rdev->sched_scan_req_list, list) {
+ if (WARN_ON(pos && pos->dev == wdev->netdev))
+ cfg80211_stop_sched_scan_req(rdev, pos, false);
+ }
+
+ rdev->opencount--;
+ wake_up(&rdev->dev_wait);
+ break;
+ case NETDEV_UP:
+ cfg80211_update_iface_num(rdev, wdev->iftype, 1);
+ wdev_lock(wdev);
+ switch (wdev->iftype) {
+#ifdef CPTCFG_CFG80211_WEXT
+ case NL80211_IFTYPE_ADHOC:
+ cfg80211_ibss_wext_join(rdev, wdev);
+ break;
+ case NL80211_IFTYPE_STATION:
+ cfg80211_mgd_wext_connect(rdev, wdev);
+ break;
+#endif
+#ifdef CONFIG_MAC80211_MESH
+ case NL80211_IFTYPE_MESH_POINT:
+ {
+ /* backward compat code... */
+ struct mesh_setup setup;
+ memcpy(&setup, &default_mesh_setup,
+ sizeof(setup));
+ /* back compat only needed for mesh_id */
+ setup.mesh_id = wdev->ssid;
+ setup.mesh_id_len = wdev->mesh_id_up_len;
+ if (wdev->mesh_id_up_len)
+ __cfg80211_join_mesh(rdev, dev,
+ &setup,
+ &default_mesh_config);
+ break;
+ }
+#endif
+ default:
+ break;
+ }
+ wdev_unlock(wdev);
+ rdev->opencount++;
+
+ /*
+ * Configure power management to the driver here so that its
+ * correctly set also after interface type changes etc.
+ */
+ if ((wdev->iftype == NL80211_IFTYPE_STATION ||
+ wdev->iftype == NL80211_IFTYPE_P2P_CLIENT) &&
+ rdev->ops->set_power_mgmt &&
+ rdev_set_power_mgmt(rdev, dev, wdev->ps,
+ wdev->ps_timeout)) {
+ /* assume this means it's off */
+ wdev->ps = false;
+ }
+ break;
+ case NETDEV_UNREGISTER:
+ /*
+ * It is possible to get NETDEV_UNREGISTER
+ * multiple times. To detect that, check
+ * that the interface is still on the list
+ * of registered interfaces, and only then
+ * remove and clean it up.
+ */
+ if (!list_empty(&wdev->list)) {
+ nl80211_notify_iface(rdev, wdev,
+ NL80211_CMD_DEL_INTERFACE);
+ sysfs_remove_link(&dev->dev.kobj, "phy80211");
+ list_del_rcu(&wdev->list);
+ rdev->devlist_generation++;
+ cfg80211_mlme_purge_registrations(wdev);
+#ifdef CPTCFG_CFG80211_WEXT
+ kzfree(wdev->wext.keys);
+#endif
+ flush_work(&wdev->disconnect_wk);
+ cfg80211_cqm_config_free(wdev);
+ }
+ /*
+ * synchronise (so that we won't find this netdev
+ * from other code any more) and then clear the list
+ * head so that the above code can safely check for
+ * !list_empty() to avoid double-cleanup.
+ */
+ synchronize_rcu();
+ INIT_LIST_HEAD(&wdev->list);
+ /*
+ * Ensure that all events have been processed and
+ * freed.
+ */
+ cfg80211_process_wdev_events(wdev);
+
+ if (WARN_ON(wdev->current_bss)) {
+ cfg80211_unhold_bss(wdev->current_bss);
+ cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub);
+ wdev->current_bss = NULL;
+ }
+ break;
+ case NETDEV_PRE_UP:
+ if (!(wdev->wiphy->interface_modes & BIT(wdev->iftype)))
+ return notifier_from_errno(-EOPNOTSUPP);
+ if (rfkill_blocked(rdev->rfkill))
+ return notifier_from_errno(-ERFKILL);
+ break;
+ default:
+ return NOTIFY_DONE;
+ }
+
+ wireless_nlevent_flush();
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block cfg80211_netdev_notifier = {
+ .notifier_call = cfg80211_netdev_notifier_call,
+};
+
+static void __net_exit cfg80211_pernet_exit(struct net *net)
+{
+ struct cfg80211_registered_device *rdev;
+
+ rtnl_lock();
+ list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
+ if (net_eq(wiphy_net(&rdev->wiphy), net))
+ WARN_ON(cfg80211_switch_netns(rdev, &init_net));
+ }
+ rtnl_unlock();
+}
+
+static struct pernet_operations cfg80211_pernet_ops = {
+ .exit = cfg80211_pernet_exit,
+};
+
+static int __init cfg80211_init(void)
+{
+ int err;
+
+ err = register_pernet_device(&cfg80211_pernet_ops);
+ if (err)
+ goto out_fail_pernet;
+
+ err = wiphy_sysfs_init();
+ if (err)
+ goto out_fail_sysfs;
+
+ err = register_netdevice_notifier(&cfg80211_netdev_notifier);
+ if (err)
+ goto out_fail_notifier;
+
+ err = nl80211_init();
+ if (err)
+ goto out_fail_nl80211;
+
+ ieee80211_debugfs_dir = debugfs_create_dir("ieee80211", NULL);
+
+ err = regulatory_init();
+ if (err)
+ goto out_fail_reg;
+
+ cfg80211_wq = alloc_ordered_workqueue("cfg80211", WQ_MEM_RECLAIM);
+ if (!cfg80211_wq) {
+ err = -ENOMEM;
+ goto out_fail_wq;
+ }
+
+ return 0;
+
+out_fail_wq:
+ regulatory_exit();
+out_fail_reg:
+ debugfs_remove(ieee80211_debugfs_dir);
+ nl80211_exit();
+out_fail_nl80211:
+ unregister_netdevice_notifier(&cfg80211_netdev_notifier);
+out_fail_notifier:
+ wiphy_sysfs_exit();
+out_fail_sysfs:
+ unregister_pernet_device(&cfg80211_pernet_ops);
+out_fail_pernet:
+ return err;
+}
+subsys_initcall(cfg80211_init);
+
+static void __exit cfg80211_exit(void)
+{
+ debugfs_remove(ieee80211_debugfs_dir);
+ nl80211_exit();
+ unregister_netdevice_notifier(&cfg80211_netdev_notifier);
+ wiphy_sysfs_exit();
+ regulatory_exit();
+ unregister_pernet_device(&cfg80211_pernet_ops);
+ destroy_workqueue(cfg80211_wq);
+}
+module_exit(cfg80211_exit);
diff --git a/net/wireless/core.h b/net/wireless/core.h
new file mode 100644
index 0000000..8842d07
--- /dev/null
+++ b/net/wireless/core.h
@@ -0,0 +1,519 @@
+/*
+ * Wireless configuration interface internals.
+ *
+ * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
+ */
+#ifndef __NET_WIRELESS_CORE_H
+#define __NET_WIRELESS_CORE_H
+#include <linux/list.h>
+#include <linux/netdevice.h>
+#include <linux/rbtree.h>
+#include <linux/debugfs.h>
+#include <linux/rfkill.h>
+#include <linux/workqueue.h>
+#include <linux/rtnetlink.h>
+#include <net/genetlink.h>
+#include <net/cfg80211.h>
+#include "reg.h"
+
+
+#define WIPHY_IDX_INVALID -1
+
+struct cfg80211_registered_device {
+ const struct cfg80211_ops *ops;
+ struct list_head list;
+
+ /* rfkill support */
+ struct rfkill_ops rfkill_ops;
+ struct rfkill *rfkill;
+ struct work_struct rfkill_sync;
+
+ /* ISO / IEC 3166 alpha2 for which this device is receiving
+ * country IEs on, this can help disregard country IEs from APs
+ * on the same alpha2 quickly. The alpha2 may differ from
+ * cfg80211_regdomain's alpha2 when an intersection has occurred.
+ * If the AP is reconfigured this can also be used to tell us if
+ * the country on the country IE changed. */
+ char country_ie_alpha2[2];
+
+ /*
+ * the driver requests the regulatory core to set this regulatory
+ * domain as the wiphy's. Only used for %REGULATORY_WIPHY_SELF_MANAGED
+ * devices using the regulatory_set_wiphy_regd() API
+ */
+ const struct ieee80211_regdomain *requested_regd;
+
+ /* If a Country IE has been received this tells us the environment
+ * which its telling us its in. This defaults to ENVIRON_ANY */
+ enum environment_cap env;
+
+ /* wiphy index, internal only */
+ int wiphy_idx;
+
+ /* protected by RTNL */
+ int devlist_generation, wdev_id;
+ int opencount;
+ wait_queue_head_t dev_wait;
+
+ struct list_head beacon_registrations;
+ spinlock_t beacon_registrations_lock;
+
+ struct list_head mlme_unreg;
+ spinlock_t mlme_unreg_lock;
+ struct work_struct mlme_unreg_wk;
+
+ /* protected by RTNL only */
+ int num_running_ifaces;
+ int num_running_monitor_ifaces;
+
+ /* BSSes/scanning */
+ spinlock_t bss_lock;
+ struct list_head bss_list;
+ struct rb_root bss_tree;
+ u32 bss_generation;
+ u32 bss_entries;
+ struct cfg80211_scan_request *scan_req; /* protected by RTNL */
+ struct sk_buff *scan_msg;
+ struct list_head sched_scan_req_list;
+ unsigned long suspend_at;
+ struct work_struct scan_done_wk;
+
+ struct genl_info *cur_cmd_info;
+
+ struct work_struct conn_work;
+ struct work_struct event_work;
+
+ struct delayed_work dfs_update_channels_wk;
+
+ /* netlink port which started critical protocol (0 means not started) */
+ u32 crit_proto_nlportid;
+
+ struct cfg80211_coalesce *coalesce;
+
+ struct work_struct destroy_work;
+ struct work_struct sched_scan_stop_wk;
+ struct work_struct sched_scan_res_wk;
+
+ struct cfg80211_chan_def radar_chandef;
+ struct work_struct propagate_radar_detect_wk;
+
+ struct cfg80211_chan_def cac_done_chandef;
+ struct work_struct propagate_cac_done_wk;
+
+ /* must be last because of the way we do wiphy_priv(),
+ * and it should at least be aligned to NETDEV_ALIGN */
+ struct wiphy wiphy __aligned(NETDEV_ALIGN);
+};
+
+static inline
+struct cfg80211_registered_device *wiphy_to_rdev(struct wiphy *wiphy)
+{
+ BUG_ON(!wiphy);
+ return container_of(wiphy, struct cfg80211_registered_device, wiphy);
+}
+
+static inline void
+cfg80211_rdev_free_wowlan(struct cfg80211_registered_device *rdev)
+{
+#ifdef CONFIG_PM
+ int i;
+
+ if (!rdev->wiphy.wowlan_config)
+ return;
+ for (i = 0; i < rdev->wiphy.wowlan_config->n_patterns; i++)
+ kfree(rdev->wiphy.wowlan_config->patterns[i].mask);
+ kfree(rdev->wiphy.wowlan_config->patterns);
+ if (rdev->wiphy.wowlan_config->tcp &&
+ rdev->wiphy.wowlan_config->tcp->sock)
+ sock_release(rdev->wiphy.wowlan_config->tcp->sock);
+ kfree(rdev->wiphy.wowlan_config->tcp);
+ kfree(rdev->wiphy.wowlan_config->nd_config);
+ kfree(rdev->wiphy.wowlan_config);
+#endif
+}
+
+extern struct workqueue_struct *cfg80211_wq;
+extern struct list_head cfg80211_rdev_list;
+extern int cfg80211_rdev_list_generation;
+
+struct cfg80211_internal_bss {
+ struct list_head list;
+ struct list_head hidden_list;
+ struct rb_node rbn;
+ u64 ts_boottime;
+ unsigned long ts;
+ unsigned long refcount;
+ atomic_t hold;
+
+ /* time at the start of the reception of the first octet of the
+ * timestamp field of the last beacon/probe received for this BSS.
+ * The time is the TSF of the BSS specified by %parent_bssid.
+ */
+ u64 parent_tsf;
+
+ /* the BSS according to which %parent_tsf is set. This is set to
+ * the BSS that the interface that requested the scan was connected to
+ * when the beacon/probe was received.
+ */
+ u8 parent_bssid[ETH_ALEN] __aligned(2);
+
+ /* must be last because of priv member */
+ struct cfg80211_bss pub;
+};
+
+static inline struct cfg80211_internal_bss *bss_from_pub(struct cfg80211_bss *pub)
+{
+ return container_of(pub, struct cfg80211_internal_bss, pub);
+}
+
+static inline void cfg80211_hold_bss(struct cfg80211_internal_bss *bss)
+{
+ atomic_inc(&bss->hold);
+}
+
+static inline void cfg80211_unhold_bss(struct cfg80211_internal_bss *bss)
+{
+ int r = atomic_dec_return(&bss->hold);
+ WARN_ON(r < 0);
+}
+
+
+struct cfg80211_registered_device *cfg80211_rdev_by_wiphy_idx(int wiphy_idx);
+int get_wiphy_idx(struct wiphy *wiphy);
+
+struct wiphy *wiphy_idx_to_wiphy(int wiphy_idx);
+
+int cfg80211_switch_netns(struct cfg80211_registered_device *rdev,
+ struct net *net);
+
+static inline void wdev_lock(struct wireless_dev *wdev)
+ __acquires(wdev)
+{
+ mutex_lock(&wdev->mtx);
+ __acquire(wdev->mtx);
+}
+
+static inline void wdev_unlock(struct wireless_dev *wdev)
+ __releases(wdev)
+{
+ __release(wdev->mtx);
+ mutex_unlock(&wdev->mtx);
+}
+
+#define ASSERT_WDEV_LOCK(wdev) lockdep_assert_held(&(wdev)->mtx)
+
+static inline bool cfg80211_has_monitors_only(struct cfg80211_registered_device *rdev)
+{
+ ASSERT_RTNL();
+
+ return rdev->num_running_ifaces == rdev->num_running_monitor_ifaces &&
+ rdev->num_running_ifaces > 0;
+}
+
+enum cfg80211_event_type {
+ EVENT_CONNECT_RESULT,
+ EVENT_ROAMED,
+ EVENT_DISCONNECTED,
+ EVENT_IBSS_JOINED,
+ EVENT_STOPPED,
+};
+
+struct cfg80211_event {
+ struct list_head list;
+ enum cfg80211_event_type type;
+
+ union {
+ struct cfg80211_connect_resp_params cr;
+ struct cfg80211_roam_info rm;
+ struct {
+ const u8 *ie;
+ size_t ie_len;
+ u16 reason;
+ bool locally_generated;
+ } dc;
+ struct {
+ u8 bssid[ETH_ALEN];
+ struct ieee80211_channel *channel;
+ } ij;
+ };
+};
+
+struct cfg80211_cached_keys {
+ struct key_params params[CFG80211_MAX_WEP_KEYS];
+ u8 data[CFG80211_MAX_WEP_KEYS][WLAN_KEY_LEN_WEP104];
+ int def;
+};
+
+enum cfg80211_chan_mode {
+ CHAN_MODE_UNDEFINED,
+ CHAN_MODE_SHARED,
+ CHAN_MODE_EXCLUSIVE,
+};
+
+struct cfg80211_beacon_registration {
+ struct list_head list;
+ u32 nlportid;
+};
+
+struct cfg80211_cqm_config {
+ u32 rssi_hyst;
+ s32 last_rssi_event_value;
+ int n_rssi_thresholds;
+ s32 rssi_thresholds[0];
+};
+
+void cfg80211_destroy_ifaces(struct cfg80211_registered_device *rdev);
+
+/* free object */
+void cfg80211_dev_free(struct cfg80211_registered_device *rdev);
+
+int cfg80211_dev_rename(struct cfg80211_registered_device *rdev,
+ char *newname);
+
+void ieee80211_set_bitrate_flags(struct wiphy *wiphy);
+
+void cfg80211_bss_expire(struct cfg80211_registered_device *rdev);
+void cfg80211_bss_age(struct cfg80211_registered_device *rdev,
+ unsigned long age_secs);
+
+/* IBSS */
+int cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct cfg80211_ibss_params *params,
+ struct cfg80211_cached_keys *connkeys);
+void cfg80211_clear_ibss(struct net_device *dev, bool nowext);
+int __cfg80211_leave_ibss(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, bool nowext);
+int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, bool nowext);
+void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid,
+ struct ieee80211_channel *channel);
+int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev);
+
+/* mesh */
+extern const struct mesh_config default_mesh_config;
+extern const struct mesh_setup default_mesh_setup;
+int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct mesh_setup *setup,
+ const struct mesh_config *conf);
+int cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct mesh_setup *setup,
+ const struct mesh_config *conf);
+int __cfg80211_leave_mesh(struct cfg80211_registered_device *rdev,
+ struct net_device *dev);
+int cfg80211_leave_mesh(struct cfg80211_registered_device *rdev,
+ struct net_device *dev);
+int cfg80211_set_mesh_channel(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev,
+ struct cfg80211_chan_def *chandef);
+
+/* OCB */
+int __cfg80211_join_ocb(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct ocb_setup *setup);
+int cfg80211_join_ocb(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct ocb_setup *setup);
+int __cfg80211_leave_ocb(struct cfg80211_registered_device *rdev,
+ struct net_device *dev);
+int cfg80211_leave_ocb(struct cfg80211_registered_device *rdev,
+ struct net_device *dev);
+
+/* AP */
+int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, bool notify);
+int cfg80211_stop_ap(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, bool notify);
+
+/* MLME */
+int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct ieee80211_channel *chan,
+ enum nl80211_auth_type auth_type,
+ const u8 *bssid,
+ const u8 *ssid, int ssid_len,
+ const u8 *ie, int ie_len,
+ const u8 *key, int key_len, int key_idx,
+ const u8 *auth_data, int auth_data_len);
+int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct ieee80211_channel *chan,
+ const u8 *bssid,
+ const u8 *ssid, int ssid_len,
+ struct cfg80211_assoc_request *req);
+int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, const u8 *bssid,
+ const u8 *ie, int ie_len, u16 reason,
+ bool local_state_change);
+int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, const u8 *bssid,
+ const u8 *ie, int ie_len, u16 reason,
+ bool local_state_change);
+void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,
+ struct net_device *dev);
+int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_pid,
+ u16 frame_type, const u8 *match_data,
+ int match_len);
+void cfg80211_mlme_unreg_wk(struct work_struct *wk);
+void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlpid);
+void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev);
+int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev,
+ struct cfg80211_mgmt_tx_params *params,
+ u64 *cookie);
+void cfg80211_oper_and_ht_capa(struct ieee80211_ht_cap *ht_capa,
+ const struct ieee80211_ht_cap *ht_capa_mask);
+void cfg80211_oper_and_vht_capa(struct ieee80211_vht_cap *vht_capa,
+ const struct ieee80211_vht_cap *vht_capa_mask);
+
+/* SME events */
+int cfg80211_connect(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct cfg80211_connect_params *connect,
+ struct cfg80211_cached_keys *connkeys,
+ const u8 *prev_bssid);
+void __cfg80211_connect_result(struct net_device *dev,
+ struct cfg80211_connect_resp_params *params,
+ bool wextev);
+void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,
+ size_t ie_len, u16 reason, bool from_ap);
+int cfg80211_disconnect(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, u16 reason,
+ bool wextev);
+void __cfg80211_roamed(struct wireless_dev *wdev,
+ struct cfg80211_roam_info *info);
+int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev);
+void cfg80211_autodisconnect_wk(struct work_struct *work);
+
+/* SME implementation */
+void cfg80211_conn_work(struct work_struct *work);
+void cfg80211_sme_scan_done(struct net_device *dev);
+bool cfg80211_sme_rx_assoc_resp(struct wireless_dev *wdev, u16 status);
+void cfg80211_sme_rx_auth(struct wireless_dev *wdev, const u8 *buf, size_t len);
+void cfg80211_sme_disassoc(struct wireless_dev *wdev);
+void cfg80211_sme_deauth(struct wireless_dev *wdev);
+void cfg80211_sme_auth_timeout(struct wireless_dev *wdev);
+void cfg80211_sme_assoc_timeout(struct wireless_dev *wdev);
+void cfg80211_sme_abandon_assoc(struct wireless_dev *wdev);
+
+/* internal helpers */
+bool cfg80211_supported_cipher_suite(struct wiphy *wiphy, u32 cipher);
+int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev,
+ struct key_params *params, int key_idx,
+ bool pairwise, const u8 *mac_addr);
+void __cfg80211_scan_done(struct work_struct *wk);
+void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev,
+ bool send_message);
+void cfg80211_add_sched_scan_req(struct cfg80211_registered_device *rdev,
+ struct cfg80211_sched_scan_request *req);
+int cfg80211_sched_scan_req_possible(struct cfg80211_registered_device *rdev,
+ bool want_multi);
+void cfg80211_sched_scan_results_wk(struct work_struct *work);
+int cfg80211_stop_sched_scan_req(struct cfg80211_registered_device *rdev,
+ struct cfg80211_sched_scan_request *req,
+ bool driver_initiated);
+int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev,
+ u64 reqid, bool driver_initiated);
+void cfg80211_upload_connect_keys(struct wireless_dev *wdev);
+int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, enum nl80211_iftype ntype,
+ struct vif_params *params);
+void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev);
+void cfg80211_process_wdev_events(struct wireless_dev *wdev);
+
+bool cfg80211_does_bw_fit_range(const struct ieee80211_freq_range *freq_range,
+ u32 center_freq_khz, u32 bw_khz);
+
+/**
+ * cfg80211_chandef_dfs_usable - checks if chandef is DFS usable
+ * @wiphy: the wiphy to validate against
+ * @chandef: the channel definition to check
+ *
+ * Checks if chandef is usable and we can/need start CAC on such channel.
+ *
+ * Return: Return true if all channels available and at least
+ * one channel require CAC (NL80211_DFS_USABLE)
+ */
+bool cfg80211_chandef_dfs_usable(struct wiphy *wiphy,
+ const struct cfg80211_chan_def *chandef);
+
+void cfg80211_set_dfs_state(struct wiphy *wiphy,
+ const struct cfg80211_chan_def *chandef,
+ enum nl80211_dfs_state dfs_state);
+
+void cfg80211_dfs_channels_update_work(struct work_struct *work);
+
+unsigned int
+cfg80211_chandef_dfs_cac_time(struct wiphy *wiphy,
+ const struct cfg80211_chan_def *chandef);
+
+void cfg80211_sched_dfs_chan_update(struct cfg80211_registered_device *rdev);
+
+bool cfg80211_any_wiphy_oper_chan(struct wiphy *wiphy,
+ struct ieee80211_channel *chan);
+
+bool cfg80211_beaconing_iface_active(struct wireless_dev *wdev);
+
+bool cfg80211_is_sub_chan(struct cfg80211_chan_def *chandef,
+ struct ieee80211_channel *chan);
+
+static inline unsigned int elapsed_jiffies_msecs(unsigned long start)
+{
+ unsigned long end = jiffies;
+
+ if (end >= start)
+ return jiffies_to_msecs(end - start);
+
+ return jiffies_to_msecs(end + (ULONG_MAX - start) + 1);
+}
+
+void
+cfg80211_get_chan_state(struct wireless_dev *wdev,
+ struct ieee80211_channel **chan,
+ enum cfg80211_chan_mode *chanmode,
+ u8 *radar_detect);
+
+int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev,
+ struct cfg80211_chan_def *chandef);
+
+int ieee80211_get_ratemask(struct ieee80211_supported_band *sband,
+ const u8 *rates, unsigned int n_rates,
+ u32 *mask);
+
+int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev,
+ enum nl80211_iftype iftype, u32 beacon_int);
+
+void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev,
+ enum nl80211_iftype iftype, int num);
+
+void __cfg80211_leave(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev);
+void cfg80211_leave(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev);
+
+void cfg80211_stop_p2p_device(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev);
+
+void cfg80211_stop_nan(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev);
+
+#define CFG80211_MAX_NUM_DIFFERENT_CHANNELS 10
+
+#ifdef CPTCFG_CFG80211_DEVELOPER_WARNINGS
+#define CFG80211_DEV_WARN_ON(cond) WARN_ON(cond)
+#else
+/*
+ * Trick to enable using it as a condition,
+ * and also not give a warning when it's
+ * not used that way.
+ */
+#define CFG80211_DEV_WARN_ON(cond) ({bool __r = (cond); __r; })
+#endif
+
+void cfg80211_cqm_config_free(struct wireless_dev *wdev);
+
+#endif /* __NET_WIRELESS_CORE_H */
diff --git a/net/wireless/db.txt b/net/wireless/db.txt
new file mode 100644
index 0000000..f9dd801
--- /dev/null
+++ b/net/wireless/db.txt
@@ -0,0 +1,1304 @@
+# This is the world regulatory domain
+country 00:
+ (2402 - 2472 @ 40), (20)
+ # Channel 12 - 13.
+ (2457 - 2482 @ 20), (20), NO-IR, AUTO-BW
+ # Channel 14. Only JP enables this and for 802.11b only
+ (2474 - 2494 @ 20), (20), NO-IR, NO-OFDM
+ # Channel 36 - 48
+ (5170 - 5250 @ 80), (20), NO-IR, AUTO-BW
+ # Channel 52 - 64
+ (5250 - 5330 @ 80), (20), NO-IR, DFS, AUTO-BW
+ # Channel 100 - 144
+ (5490 - 5730 @ 160), (20), NO-IR, DFS
+ # Channel 149 - 165
+ (5735 - 5835 @ 80), (20), NO-IR
+ # IEEE 802.11ad (60GHz), channels 1..3
+ (57240 - 63720 @ 2160), (0)
+
+
+country AD:
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20)
+ (5250 - 5330 @ 80), (20), DFS
+ (5490 - 5710 @ 80), (27), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+country AE: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (17), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country AF: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+# Source:
+# http://pucanguilla.org/Downloads/January2005-Anguilla%20Table%20of%20Allocations.pdf
+country AI: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+country AL: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20.00), AUTO-BW
+ (5250 - 5330 @ 80), (20.00), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27.00), DFS
+
+country AM: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 20), (18)
+ (5250 - 5330 @ 20), (18), DFS
+
+country AN: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+country AR: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (17), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country AS: DFS-FCC
+ (2402 - 2472 @ 40), (30)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country AT: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+country AU: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (17), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country AW: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+country AZ: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (18), AUTO-BW
+ (5250 - 5330 @ 80), (18), DFS, AUTO-BW
+
+country BA: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+country BB: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5735 - 5835 @ 80), (30)
+
+country BD: DFS-JP
+ (2402 - 2482 @ 40), (20)
+ (5735 - 5835 @ 80), (30)
+
+country BE: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+country BF: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (17), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+# Bulgarian rules as defined by the Communications Regulation Commission in the
+# following documents:
+#
+# Rules for carrying out electronic communications through radio equipment using
+# radio spectrum, which does not need to be individually assigned (the Rules):
+# http://www.crc.bg/files/_bg/Pravila_09_06_2015.pdf
+#
+# List of radio equipment that uses harmonized within the European Union bands
+# and electronic communications terminal equipment (the List):
+# http://www.crc.bg/files/_bg/Spisak_2015.pdf
+#
+# Note: The transmit power limits in the 5250-5350 MHz and 5470-5725 MHz bands
+# can be raised by 3 dBm if TPC is enabled. Refer to BDS EN 301 893 for details.
+country BG: DFS-ETSI
+ # Wideband data transmission systems (WDTS) in the 2.4GHz ISM band, ref:
+ # I.22 of the List, BDS EN 300 328
+ (2402 - 2482 @ 40), (20)
+ # 5 GHz Radio Local Area Networks (RLANs), ref:
+ # II.H01 of the List, BDS EN 301 893
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ # II.H01 of the List, I.54 from the List, BDS EN 301 893
+ (5490 - 5710 @ 160), (27), DFS
+ # Short range devices (SRDs) in the 5725-5875 MHz frequency range, ref:
+ # I.43 of the List, BDS EN 300 440-2, BDS EN 300 440-1
+ (5725 - 5875 @ 80), (14)
+ # 60 GHz Multiple-Gigabit RLAN Systems, ref:
+ # II.H03 of the List, BDS EN 302 567-2
+ (57000 - 66000 @ 2160), (40), NO-OUTDOOR
+
+country BH: DFS-JP
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 20), (20)
+ (5250 - 5330 @ 20), (20), DFS
+ (5735 - 5835 @ 20), (20)
+
+country BL: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+country BM: DFS-FCC
+ (2402 - 2472 @ 40), (30)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country BN: DFS-JP
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5735 - 5835 @ 80), (20)
+
+country BO: DFS-JP
+ (2402 - 2482 @ 40), (20)
+ (5250 - 5330 @ 80), (30), DFS
+ (5735 - 5835 @ 80), (30)
+
+country BR: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (17), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country BS: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+# Source:
+# http://www.bicma.gov.bt/paper/publication/nrrpart4.pdf
+country BT: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+country BY: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+country BZ: DFS-JP
+ (2402 - 2482 @ 40), (30)
+ (5735 - 5835 @ 80), (30)
+
+country CA: DFS-FCC
+ (2402 - 2472 @ 40), (30)
+ (5170 - 5250 @ 80), (17), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5600 @ 80), (24), DFS
+ (5650 - 5730 @ 80), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+# Source:
+# http://www.art-rca.org
+country CF: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 40), (17)
+ (5250 - 5330 @ 40), (24), DFS
+ (5490 - 5730 @ 40), (24), DFS
+ (5735 - 5835 @ 40), (30)
+
+country CH: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+country CI: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (17), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country CL: DFS-JP
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5735 - 5835 @ 80), (20)
+
+country CN: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5735 - 5835 @ 80), (30)
+ # 60 GHz band channels 1,4: 28dBm, channels 2,3: 44dBm
+ # ref: http://www.miit.gov.cn/n11293472/n11505629/n11506593/n11960250/n11960606/n11960700/n12330791.files/n12330790.pdf
+ (57240 - 59400 @ 2160), (28)
+ (59400 - 63720 @ 2160), (44)
+ (63720 - 65880 @ 2160), (28)
+
+country CO: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (17), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country CR: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 20), (17)
+ (5250 - 5330 @ 20), (24), DFS
+ (5490 - 5730 @ 20), (24), DFS
+ (5735 - 5835 @ 20), (30)
+
+# http://www.mincom.gob.cu/?q=marcoregulatorio
+# - Redes Informáticas
+# Resolución 127, 2011 - Reglamento Banda 2,4 GHz.
+country CU: DFS-FCC
+ (2400 - 2483.5 @ 40), (200 mW)
+
+country CX: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country CY: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+# Data from http://www.ctu.eu/164/download/VOR/VOR-12-08-2005-34.pdf
+# and http://www.ctu.eu/164/download/VOR/VOR-12-05-2007-6-AN.pdf
+# Power at 5250 - 5350 MHz and 5470 - 5725 MHz can be doubled if TPC is
+# implemented.
+country CZ: DFS-ETSI
+ (2400 - 2483.5 @ 40), (100 mW)
+ (5150 - 5250 @ 80), (200 mW), NO-OUTDOOR, AUTO-BW
+ (5250 - 5350 @ 80), (100 mW), NO-OUTDOOR, DFS, AUTO-BW
+ (5470 - 5725 @ 160), (500 mW), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+# Allocation for the 2.4 GHz band (Vfg 10 / 2013, Allgemeinzuteilung von
+# Frequenzen für die Nutzung in lokalen Netzwerken; Wireless Local Area
+# Networks (WLAN-Funkanwendungen).
+# https://www.bundesnetzagentur.de/SharedDocs/Downloads/DE/Sachgebiete/Telekommunikation/Unternehmen_Institutionen/Frequenzen/Allgemeinzuteilungen/2013_10_WLAN_2,4GHz_pdf.pdf
+#
+# Allocation for the 5 GHz band (Vfg. 7 / 2010, Allgemeinzuteilung von
+# Frequenzen in den Bereichen 5150 MHz - 5350 MHz und 5470 MHz - 5725 MHz für
+# Funkanwendungen zur breitbandigen Datenübertragung, WAS/WLAN („Wireless
+# Access Systems including Wireless Local Area Networks“).
+# https://www.bundesnetzagentur.de/SharedDocs/Downloads/DE/Sachgebiete/Telekommunikation/Unternehmen_Institutionen/Frequenzen/Allgemeinzuteilungen/2010_07_WLAN_5GHz_pdf.pdf
+# The values for the 5 GHz have been reduced by a factor of 2 (3db) for non TPC
+# devices (in other words: devices with TPC can use twice the tx power of this
+# table). Note that the docs do not require TPC for 5150--5250; the reduction
+# to 100mW thus is not strictly required -- however the conservative 100mW
+# limit is used here as the non-interference with radar and satellite
+# apps relies on the attenuation by the building walls only in the
+# absence of DFS; the neighbour countries have 100mW limit here as well.
+#
+# The ETSI EN 300 440-1 standard for short range devices in the 5 GHz band has
+# been implemented in Germany:
+# https://www.bundesnetzagentur.de/SharedDocs/Downloads/DE/Sachgebiete/Telekommunikation/Unternehmen_Institutionen/Frequenzen/Allgemeinzuteilungen/2014_69_SRD_pdf.pdf
+#
+# Allocation for the 60 GHz band (Allgemeinzuteilung von Frequenzen im
+# Bereich 57 GHz - 66 GHz für Funkanwendungen für weitbandige
+# Datenübertragungssysteme; „Multiple Gigabit WAS/RLAN Systems (MGWS)“).
+# https://www.bundesnetzagentur.de/SharedDocs/Downloads/DE/Sachgebiete/Telekommunikation/Unternehmen_Institutionen/Frequenzen/Allgemeinzuteilungen/2011_08_MGWS_pdf.pdf
+
+country DE: DFS-ETSI
+ (2400 - 2483.5 @ 40), (100 mW)
+ (5150 - 5250 @ 80), (100 mW), NO-OUTDOOR, AUTO-BW
+ (5250 - 5350 @ 80), (100 mW), NO-OUTDOOR, DFS, AUTO-BW
+ (5470 - 5725 @ 160), (500 mW), DFS
+ # short range devices (ETSI EN 300 440-1)
+ (5725 - 5875 @ 80), (25 mW)
+ # 60 GHz band channels 1-4 (ETSI EN 302 567)
+ (57000 - 66000 @ 2160), (40)
+
+country DK: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+# Source:
+# http://www.ntrcdom.org/index.php?option=com_content&view=category&layout=blog&id=10&Itemid=55
+country DM: DFS-FCC
+ (2402 - 2472 @ 40), (30)
+ (5170 - 5250 @ 80), (17), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5735 - 5835 @ 80), (30)
+
+country DO: DFS-FCC
+ (2402 - 2472 @ 40), (30)
+ (5170 - 5250 @ 80), (17), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5735 - 5835 @ 80), (30)
+
+country DZ: DFS-JP
+ (2402 - 2482 @ 40), (20)
+ (5170.000 - 5250.000 @ 80.000), (23.00), AUTO-BW
+ (5250.000 - 5330.000 @ 80.000), (23.00), DFS, AUTO-BW
+ (5490.000 - 5670.000 @ 160.000), (23.00), DFS
+
+country EC: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 20), (17)
+ (5250 - 5330 @ 20), (24), DFS
+ (5490 - 5730 @ 20), (24), DFS
+ (5735 - 5835 @ 20), (30)
+
+country EE: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+country EG: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 40), (20)
+ (5250 - 5330 @ 40), (20), DFS
+
+# Orden IET/787/2013, de 25 de abril, por la que se aprueba
+# el cuadro nacional de atribución de frecuencias.
+# http://www.boe.es/diario_boe/txt.php?id=BOE-A-2013-4845
+#
+# more info at "Cuadro nacional de atribución de frecuencias (CNAF)":
+# http://www.minetur.gob.es/telecomunicaciones/espectro/paginas/cnaf.aspx
+
+country ES: DFS-ETSI
+ (2400 - 2483.5 @ 40), (100 mW)
+ (5150 - 5250 @ 80), (200 mW), NO-OUTDOOR, AUTO-BW
+ (5250 - 5350 @ 80), (100 mW), NO-OUTDOOR, DFS, AUTO-BW
+ (5470 - 5725 @ 160), (500 mW), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+country ET: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+country FI: DFS-ETSI
+ (2400 - 2483.5 @ 40), (20)
+ (5150 - 5250 @ 80), (23), NO-OUTDOOR, AUTO-BW
+ (5250 - 5350 @ 80), (20), NO-OUTDOOR, DFS, AUTO-BW
+ (5470 - 5725 @ 160), (27), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+country FM: DFS-FCC
+ (2402 - 2472 @ 40), (30)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country FR: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+country GB: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+country GD: DFS-FCC
+ (2402 - 2472 @ 40), (30)
+ (5170 - 5250 @ 80), (17), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country GE: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (18), AUTO-BW
+ (5250 - 5330 @ 80), (18), DFS, AUTO-BW
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+country GF: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+country GH: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (17), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country GL: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+country GP: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+country GR: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+country GT: DFS-FCC
+ (2402 - 2472 @ 40), (30)
+ (5170 - 5250 @ 80), (17), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5735 - 5835 @ 80), (30)
+
+country GU: DFS-FCC
+ (2402 - 2472 @ 40), (30)
+ (5170 - 5250 @ 20), (17)
+ (5250 - 5330 @ 20), (24), DFS
+ (5490 - 5730 @ 20), (24), DFS
+ (5735 - 5835 @ 20), (30)
+
+country GY:
+ (2402 - 2482 @ 40), (30)
+ (5735 - 5835 @ 80), (30)
+
+country HK: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (17), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country HN: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (17), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country HR: DFS-ETSI
+ (2400 - 2483.5 @ 40), (20)
+ (5150 - 5250 @ 80), (23), NO-OUTDOOR, AUTO-BW
+ (5250 - 5350 @ 80), (20), NO-OUTDOOR, DFS, AUTO-BW
+ (5470 - 5725 @ 160), (27), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+country HT: DFS-FCC
+ (2402 - 2472 @ 40), (30)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country HU: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+country ID: DFS-JP
+ # ref: http://www.postel.go.id/content/ID/regulasi/standardisasi/kepdir/bwa%205,8%20ghz.pdf
+ (2402 - 2482 @ 20), (20)
+ (5735 - 5815 @ 20), (23)
+
+country IE: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+country IL: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5150 - 5250 @ 80), (200 mW), NO-OUTDOOR, AUTO-BW
+ (5250 - 5350 @ 80), (200 mW), NO-OUTDOOR, DFS, AUTO-BW
+
+country IN: DFS-JP
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5735 - 5835 @ 80), (20)
+
+country IR: DFS-JP
+ (2402 - 2482 @ 40), (20)
+ (5735 - 5835 @ 80), (30)
+
+country IS: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+country IT: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+country JM: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (17), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country JO: DFS-JP
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23)
+ (5735 - 5835 @ 80), (23)
+
+country JP: DFS-JP
+ (2402 - 2482 @ 40), (20)
+ (2474 - 2494 @ 20), (20), NO-OFDM
+ (4910 - 4990 @ 40), (23)
+ (5030 - 5090 @ 40), (23)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (23), DFS
+ # 60 GHz band channels 2-4 at 10mW,
+ # ref: http://www.arib.or.jp/english/html/overview/doc/1-STD-T74v1_1.pdf
+ (59000 - 66000 @ 2160), (10 mW)
+
+country KE: DFS-JP
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23)
+ (5490 - 5570 @ 80), (30), DFS
+ (5735 - 5775 @ 40), (23)
+
+country KH: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+# Source
+# http://ntrc.kn/?page_id=7
+country KN: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+ (5735 - 5815 @ 80), (30)
+
+country KP: DFS-JP
+ (2402 - 2482 @ 20), (20)
+ (5170 - 5250 @ 20), (20)
+ (5250 - 5330 @ 20), (20), DFS
+ (5490 - 5630 @ 20), (30), DFS
+ (5735 - 5815 @ 20), (30)
+
+country KR: DFS-JP
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+ (5735 - 5835 @ 80), (30)
+
+country KW: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+
+country KY: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country KZ:
+ (2402 - 2482 @ 40), (20)
+
+country LB: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (17), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+# Source:
+# http://www.ntrc.org.lc/operational_structures.htm
+country LC: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+ (5735 - 5815 @ 80), (30)
+
+country LI: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+country LK: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 20), (17)
+ (5250 - 5330 @ 20), (24), DFS
+ (5490 - 5730 @ 20), (24), DFS
+ (5735 - 5835 @ 20), (30)
+
+# Source:
+# http://lca.org.ls/images/documents/lesotho_national_frequency_allocation_plan.pdf
+country LS: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+country LT: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+country LU: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+country LV: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+country MA: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+
+country MC: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+# Source:
+# http://www.cnfr.md/index.php?pag=sec&id=117&l=en
+country MD: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+# Source:
+# http://www.cept.org/files/1050/Tools%20and%20Services/EFIS%20-%20ECO%20Frequency%20Information%20System/National%20frequency%20tables/Montenegro%20NAFT%20-%202010.pdf
+country ME: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+country MF: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+country MH: DFS-FCC
+ (2402 - 2472 @ 40), (30)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country MK: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+country MN: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country MO: DFS-FCC
+ (2402 - 2482 @ 40), (23)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (30), DFS
+ (5735 - 5835 @ 80), (30)
+
+country MP: DFS-FCC
+ (2402 - 2472 @ 40), (30)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country MQ: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+# Source:
+# http://www.are.mr/pdfs/telec_freq_TNAbf_2010.pdf
+country MR: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+country MT: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+country MU: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+# Source:
+# http://www.cam.gov.mv/docs/tech_standards/TAM-TS-100-2004-WLAN.pdf
+country MV: DFS-ETSI
+ (2400 - 2483.5 @ 40), (100 mW)
+ (5150 - 5250 @ 80), (200 mW), AUTO-BW
+ (5250 - 5350 @ 80), (100 mW), DFS, AUTO-BW
+ (5725 - 5850 @ 80), (100 mW)
+
+country MW: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+country MX: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (17), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country MY: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5650 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (24)
+
+country NG: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5250 - 5330 @ 80), (30), DFS
+ (5735 - 5835 @ 80), (30)
+
+country NI: DFS-FCC
+ (2402 - 2472 @ 40), (30)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+# Regulation on the use of frequency space without a license and
+# without notification 2015
+#
+# http://wetten.overheid.nl/BWBR0036378/2015-03-05
+
+country NL: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), NO-OUTDOOR, AUTO-BW
+ (5250 - 5330 @ 80), (20), NO-OUTDOOR, DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+ # short range devices (ETSI EN 300 440-1)
+ (5725 - 5875 @ 80), (25 mW)
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+# Data from http://www.lovdata.no/dokument/SF/forskrift/2012-01-19-77
+# Power at 5250 - 5350 MHz, 5470 - 5725 MHz and 5815 – 5850 MHz can
+# be doubled if TPC is implemented.
+# Up to 2W (or 4W with TPC) is allowed in the 5725 – 5795 MHz band
+# which has been merged with 5470 - 5725 MHz to allow wide channels
+country NO: DFS-ETSI
+ (2400 - 2483.5 @ 40), (100 mW)
+ (5150 - 5250 @ 80), (200 mW), AUTO-BW
+ (5250 - 5350 @ 80), (100 mW), DFS, AUTO-BW
+ (5470 - 5795 @ 160), (500 mW), DFS
+ (5815 - 5850 @ 35), (2000 mW), DFS
+ (17100 - 17300 @ 200), (100 mW)
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+country NP: DFS-JP
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5735 - 5835 @ 80), (20)
+
+country NZ: DFS-ETSI
+ (2402 - 2482 @ 40), (30)
+ (5170 - 5250 @ 80), (17), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country OM: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+country PA: DFS-FCC
+ (2402 - 2472 @ 40), (30)
+ (5170 - 5250 @ 80), (17), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5735 - 5835 @ 80), (30)
+
+country PE: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (17), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country PF: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+country PG: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (17), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country PH: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (17), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country PK: DFS-JP
+ (2402 - 2482 @ 40), (20)
+ (5735 - 5835 @ 80), (30)
+
+country PL: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+country PM: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+country PR: DFS-FCC
+ (2402 - 2472 @ 40), (30)
+ (5170 - 5250 @ 80), (17), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country PT: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+country PW: DFS-FCC
+ (2402 - 2472 @ 40), (30)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country PY: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country QA: DFS-JP
+ (2402 - 2482 @ 40), (20)
+ (5735 - 5835 @ 80), (30)
+
+country RE: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+country RO: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+
+# Source:
+# http://www.ratel.rs/upload/documents/Plan_namene/Plan_namene-sl_glasnik.pdf
+country RS: DFS-ETSI
+ (2400 - 2483.5 @ 40), (100 mW)
+ (5150 - 5350 @ 40), (200 mW), NO-OUTDOOR
+ (5470 - 5725 @ 20), (1000 mW), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+country RU: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5650 - 5730 @ 80), (30), DFS
+ (5735 - 5835 @ 80), (30)
+ # 60 GHz band channels 1-4, ref: Changes to NLA 124_Order №129_22042015.pdf
+ (57000 - 66000 @ 2160), (40)
+
+country RW: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (17), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country SA: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+country SE: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+country SG: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (17), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country SI: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+country SK: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+# Source:
+# Regulation N° 2004-005 ART/DG/DRC/D.Rég
+country SN: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (17), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country SR: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+country SV: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 20), (17)
+ (5250 - 5330 @ 20), (23), DFS
+ (5735 - 5835 @ 20), (30)
+
+country SY:
+ (2402 - 2482 @ 40), (20)
+
+# Source:
+# http://www.telecommission.tc/Spectrum-plan20110324-101210.html
+country TC: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country TD: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+country TG: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 40), (20)
+ (5250 - 5330 @ 40), (20), DFS
+ (5490 - 5710 @ 40), (27), DFS
+
+country TH: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (17), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country TN: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+
+country TR: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+country TT: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (17), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+# Source:
+# Table of Frequency Allocations of Republic of China (Taiwan) / Nov 2014:
+# http://www.motc.gov.tw/websitedowndoc?file=post/201411171137330.doc& \
+# filedisplay=Table+of+radio+frequency+allocation.doc
+# LP0002 Low-power Radio-frequency Devices Technical Regulations / 28 Jun 2011:
+# http://www.ncc.gov.tw/english/show_file.aspx?table_name=news&file_sn=681
+# (section 3.10.1, 4.7)
+country TW: DFS-FCC
+ (2400 - 2483.5 @ 40), (30)
+ # Follow US 5.15 ~ 5.25 GHz: 30 dBm for master mode, 23 dBm for clients
+ (5150 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5350 @ 80), (23), DFS, AUTO-BW
+ (5470 - 5725 @ 160), (23), DFS
+ (5725 - 5850 @ 80), (30)
+
+country TZ:
+ (2402 - 2482 @ 40), (20)
+ (5735 - 5835 @ 80), (30)
+
+# Source:
+# #914 / 06 Sep 2007: http://www.ucrf.gov.ua/uk/doc/nkrz/1196068874
+# #1174 / 23 Oct 2008: http://www.nkrz.gov.ua/uk/activities/ruling/1225269361
+# (appendix 8)
+# Listed 5GHz range is a lowest common denominator for all related
+# rules in the referenced laws. Such a range is used because of
+# disputable definitions there.
+country UA: DFS-ETSI
+ (2400 - 2483.5 @ 40), (20), NO-OUTDOOR
+ (5150 - 5250 @ 80), (20), NO-OUTDOOR, AUTO-BW
+ (5250 - 5350 @ 80), (20), DFS, NO-OUTDOOR, AUTO-BW
+ (5490 - 5670 @ 160), (20), DFS
+ (5735 - 5835 @ 80), (20)
+ # 60 GHz band channels 1-4, ref: Etsi En 302 567
+ (57000 - 66000 @ 2160), (40)
+
+country UG: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country US: DFS-FCC
+ (2402 - 2472 @ 40), (30)
+ # 5.15 ~ 5.25 GHz: 30 dBm for master mode, 23 dBm for clients
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (23), DFS
+ (5735 - 5835 @ 80), (30)
+ # 60g band
+ # reference: http://cfr.regstoday.com/47cfr15.aspx#47_CFR_15p255
+ # channels 1,2,3, EIRP=40dBm(43dBm peak)
+ (57240 - 63720 @ 2160), (40)
+
+country UY: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5735 - 5835 @ 80), (30)
+
+# Source:
+# http://cemc.uz/article/1976/
+country UZ: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+
+# Source:
+# http://www.ntrc.vc/regulations/Jun_2006_Spectrum_Managment_Regulations.pdf
+country VC: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+# Source:
+# Official Gazette (Gaceta Oficial) concerning Unlicensed transmitter use
+# (10 June 2013)
+# http://www.conatel.gob.ve/
+country VE: DFS-FCC
+ (2402 - 2482 @ 40), (30)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5735 - 5835 @ 80), (30)
+
+country VI: DFS-FCC
+ (2402 - 2472 @ 40), (30)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country VN: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (17)
+ (5250 - 5330 @ 80), (24), DFS
+ (5490 - 5730 @ 80), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+# Source:
+# http://www.trr.vu/attachments/category/130/GURL_for_Short-range_Radiocommunication_Devices2.pdf
+country VU: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (17), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country WF: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+country WS: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 40), (20)
+ (5250 - 5330 @ 40), (20), DFS
+ (5490 - 5710 @ 40), (27), DFS
+
+country YE:
+ (2402 - 2482 @ 40), (20)
+
+country YT: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
+country ZA: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30)
+
+country ZW: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (27), DFS
+
diff --git a/net/wireless/debugfs.c b/net/wireless/debugfs.c
new file mode 100644
index 0000000..30fc6eb
--- /dev/null
+++ b/net/wireless/debugfs.c
@@ -0,0 +1,117 @@
+/*
+ * cfg80211 debugfs
+ *
+ * Copyright 2009 Luis R. Rodriguez <lrodriguez@atheros.com>
+ * Copyright 2007 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/slab.h>
+#include "core.h"
+#include "debugfs.h"
+
+#define DEBUGFS_READONLY_FILE(name, buflen, fmt, value...) \
+static ssize_t name## _read(struct file *file, char __user *userbuf, \
+ size_t count, loff_t *ppos) \
+{ \
+ struct wiphy *wiphy = file->private_data; \
+ char buf[buflen]; \
+ int res; \
+ \
+ res = scnprintf(buf, buflen, fmt "\n", ##value); \
+ return simple_read_from_buffer(userbuf, count, ppos, buf, res); \
+} \
+ \
+static const struct file_operations name## _ops = { \
+ .read = name## _read, \
+ .open = simple_open, \
+ .llseek = generic_file_llseek, \
+}
+
+DEBUGFS_READONLY_FILE(rts_threshold, 20, "%d",
+ wiphy->rts_threshold);
+DEBUGFS_READONLY_FILE(fragmentation_threshold, 20, "%d",
+ wiphy->frag_threshold);
+DEBUGFS_READONLY_FILE(short_retry_limit, 20, "%d",
+ wiphy->retry_short);
+DEBUGFS_READONLY_FILE(long_retry_limit, 20, "%d",
+ wiphy->retry_long);
+
+static int ht_print_chan(struct ieee80211_channel *chan,
+ char *buf, int buf_size, int offset)
+{
+ if (WARN_ON(offset > buf_size))
+ return 0;
+
+ if (chan->flags & IEEE80211_CHAN_DISABLED)
+ return scnprintf(buf + offset,
+ buf_size - offset,
+ "%d Disabled\n",
+ chan->center_freq);
+
+ return scnprintf(buf + offset,
+ buf_size - offset,
+ "%d HT40 %c%c\n",
+ chan->center_freq,
+ (chan->flags & IEEE80211_CHAN_NO_HT40MINUS) ?
+ ' ' : '-',
+ (chan->flags & IEEE80211_CHAN_NO_HT40PLUS) ?
+ ' ' : '+');
+}
+
+static ssize_t ht40allow_map_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct wiphy *wiphy = file->private_data;
+ char *buf;
+ unsigned int offset = 0, buf_size = PAGE_SIZE, i, r;
+ enum nl80211_band band;
+ struct ieee80211_supported_band *sband;
+
+ buf = kzalloc(buf_size, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ rtnl_lock();
+
+ for (band = 0; band < NUM_NL80211_BANDS; band++) {
+ sband = wiphy->bands[band];
+ if (!sband)
+ continue;
+ for (i = 0; i < sband->n_channels; i++)
+ offset += ht_print_chan(&sband->channels[i],
+ buf, buf_size, offset);
+ }
+
+ rtnl_unlock();
+
+ r = simple_read_from_buffer(user_buf, count, ppos, buf, offset);
+
+ kfree(buf);
+
+ return r;
+}
+
+static const struct file_operations ht40allow_map_ops = {
+ .read = ht40allow_map_read,
+ .open = simple_open,
+ .llseek = default_llseek,
+};
+
+#define DEBUGFS_ADD(name) \
+ debugfs_create_file(#name, 0444, phyd, &rdev->wiphy, &name## _ops)
+
+void cfg80211_debugfs_rdev_add(struct cfg80211_registered_device *rdev)
+{
+ struct dentry *phyd = rdev->wiphy.debugfsdir;
+
+ DEBUGFS_ADD(rts_threshold);
+ DEBUGFS_ADD(fragmentation_threshold);
+ DEBUGFS_ADD(short_retry_limit);
+ DEBUGFS_ADD(long_retry_limit);
+ DEBUGFS_ADD(ht40allow_map);
+}
diff --git a/net/wireless/debugfs.h b/net/wireless/debugfs.h
new file mode 100644
index 0000000..22175a1
--- /dev/null
+++ b/net/wireless/debugfs.h
@@ -0,0 +1,11 @@
+#ifndef __CFG80211_DEBUGFS_H
+#define __CFG80211_DEBUGFS_H
+
+#ifdef CPTCFG_CFG80211_DEBUGFS
+void cfg80211_debugfs_rdev_add(struct cfg80211_registered_device *rdev);
+#else
+static inline
+void cfg80211_debugfs_rdev_add(struct cfg80211_registered_device *rdev) {}
+#endif
+
+#endif /* __CFG80211_DEBUGFS_H */
diff --git a/net/wireless/ethtool.c b/net/wireless/ethtool.c
new file mode 100644
index 0000000..e9e9129
--- /dev/null
+++ b/net/wireless/ethtool.c
@@ -0,0 +1,24 @@
+#include <linux/utsname.h>
+#include <net/cfg80211.h>
+#include "core.h"
+#include "rdev-ops.h"
+
+void cfg80211_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+
+ strlcpy(info->driver, wiphy_dev(wdev->wiphy)->driver->name,
+ sizeof(info->driver));
+
+ strlcpy(info->version, init_utsname()->release, sizeof(info->version));
+
+ if (wdev->wiphy->fw_version[0])
+ strlcpy(info->fw_version, wdev->wiphy->fw_version,
+ sizeof(info->fw_version));
+ else
+ strlcpy(info->fw_version, "N/A", sizeof(info->fw_version));
+
+ strlcpy(info->bus_info, dev_name(wiphy_dev(wdev->wiphy)),
+ sizeof(info->bus_info));
+}
+EXPORT_SYMBOL(cfg80211_get_drvinfo);
diff --git a/net/wireless/genregdb.awk b/net/wireless/genregdb.awk
new file mode 100644
index 0000000..baf2426
--- /dev/null
+++ b/net/wireless/genregdb.awk
@@ -0,0 +1,158 @@
+#!/usr/bin/awk -f
+#
+# genregdb.awk -- generate regdb.c from db.txt
+#
+# Actually, it reads from stdin (presumed to be db.txt) and writes
+# to stdout (presumed to be regdb.c), but close enough...
+#
+# Copyright 2009 John W. Linville <linville@tuxdriver.com>
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+BEGIN {
+ active = 0
+ rules = 0;
+ print "/*"
+ print " * DO NOT EDIT -- file generated from data in db.txt"
+ print " */"
+ print ""
+ print "#include <linux/nl80211.h>"
+ print "#include <net/cfg80211.h>"
+ print "#include \"regdb.h\""
+ print ""
+ regdb = "const struct ieee80211_regdomain *reg_regdb[] = {\n"
+}
+
+function parse_country_head() {
+ country=$2
+ sub(/:/, "", country)
+ printf "static const struct ieee80211_regdomain regdom_%s = {\n", country
+ printf "\t.alpha2 = \"%s\",\n", country
+ if ($NF ~ /DFS-ETSI/)
+ printf "\t.dfs_region = NL80211_DFS_ETSI,\n"
+ else if ($NF ~ /DFS-FCC/)
+ printf "\t.dfs_region = NL80211_DFS_FCC,\n"
+ else if ($NF ~ /DFS-JP/)
+ printf "\t.dfs_region = NL80211_DFS_JP,\n"
+ printf "\t.reg_rules = {\n"
+ active = 1
+ regdb = regdb "\t®dom_" country ",\n"
+}
+
+function parse_reg_rule()
+{
+ flag_starts_at = 7
+
+ start = $1
+ sub(/\(/, "", start)
+ end = $3
+ bw = $5
+ sub(/\),/, "", bw)
+ gain = 0
+ power = $6
+ # power might be in mW...
+ units = $7
+ dfs_cac = 0
+
+ sub(/\(/, "", power)
+ sub(/\),/, "", power)
+ sub(/\),/, "", units)
+ sub(/\)/, "", units)
+
+ if (units == "mW") {
+ flag_starts_at = 8
+ power = 10 * log(power)/log(10)
+ if ($8 ~ /[[:digit:]]/) {
+ flag_starts_at = 9
+ dfs_cac = $8
+ }
+ } else {
+ if ($7 ~ /[[:digit:]]/) {
+ flag_starts_at = 8
+ dfs_cac = $7
+ }
+ }
+ sub(/\(/, "", dfs_cac)
+ sub(/\),/, "", dfs_cac)
+ flagstr = ""
+ for (i=flag_starts_at; i<=NF; i++)
+ flagstr = flagstr $i
+ split(flagstr, flagarray, ",")
+ flags = ""
+ for (arg in flagarray) {
+ if (flagarray[arg] == "NO-OFDM") {
+ flags = flags "\n\t\t\tNL80211_RRF_NO_OFDM | "
+ } else if (flagarray[arg] == "NO-CCK") {
+ flags = flags "\n\t\t\tNL80211_RRF_NO_CCK | "
+ } else if (flagarray[arg] == "NO-INDOOR") {
+ flags = flags "\n\t\t\tNL80211_RRF_NO_INDOOR | "
+ } else if (flagarray[arg] == "NO-OUTDOOR") {
+ flags = flags "\n\t\t\tNL80211_RRF_NO_OUTDOOR | "
+ } else if (flagarray[arg] == "DFS") {
+ flags = flags "\n\t\t\tNL80211_RRF_DFS | "
+ } else if (flagarray[arg] == "PTP-ONLY") {
+ flags = flags "\n\t\t\tNL80211_RRF_PTP_ONLY | "
+ } else if (flagarray[arg] == "PTMP-ONLY") {
+ flags = flags "\n\t\t\tNL80211_RRF_PTMP_ONLY | "
+ } else if (flagarray[arg] == "PASSIVE-SCAN") {
+ flags = flags "\n\t\t\tNL80211_RRF_NO_IR | "
+ } else if (flagarray[arg] == "NO-IBSS") {
+ flags = flags "\n\t\t\tNL80211_RRF_NO_IR | "
+ } else if (flagarray[arg] == "NO-IR") {
+ flags = flags "\n\t\t\tNL80211_RRF_NO_IR | "
+ } else if (flagarray[arg] == "AUTO-BW") {
+ flags = flags "\n\t\t\tNL80211_RRF_AUTO_BW | "
+ }
+
+ }
+ flags = flags "0"
+ printf "\t\tREG_RULE_EXT(%d, %d, %d, %d, %.0f, %d, %s),\n", start, end, bw, gain, power, dfs_cac, flags
+ rules++
+}
+
+function print_tail_country()
+{
+ active = 0
+ printf "\t},\n"
+ printf "\t.n_reg_rules = %d\n", rules
+ printf "};\n\n"
+ rules = 0;
+}
+
+/^[ \t]*#/ {
+ # Ignore
+}
+
+!active && /^[ \t]*$/ {
+ # Ignore
+}
+
+!active && /country/ {
+ parse_country_head()
+}
+
+active && /^[ \t]*\(/ {
+ parse_reg_rule()
+}
+
+active && /^[ \t]*$/ {
+ print_tail_country()
+}
+
+END {
+ if (active)
+ print_tail_country()
+ print regdb "};"
+ print ""
+ print "int reg_regdb_size = ARRAY_SIZE(reg_regdb);"
+}
diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c
new file mode 100644
index 0000000..f066e9b
--- /dev/null
+++ b/net/wireless/ibss.c
@@ -0,0 +1,545 @@
+/*
+ * Some IBSS support code for cfg80211.
+ *
+ * Copyright 2009 Johannes Berg <johannes@sipsolutions.net>
+ */
+
+#include <linux/etherdevice.h>
+#include <linux/if_arp.h>
+#include <linux/slab.h>
+#include <linux/export.h>
+#include <net/cfg80211.h>
+#include "wext-compat.h"
+#include "nl80211.h"
+#include "rdev-ops.h"
+
+
+void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid,
+ struct ieee80211_channel *channel)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_bss *bss;
+#ifdef CPTCFG_CFG80211_WEXT
+ union iwreq_data wrqu;
+#endif
+
+ if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
+ return;
+
+ if (!wdev->ssid_len)
+ return;
+
+ bss = cfg80211_get_bss(wdev->wiphy, channel, bssid, NULL, 0,
+ IEEE80211_BSS_TYPE_IBSS, IEEE80211_PRIVACY_ANY);
+
+ if (WARN_ON(!bss))
+ return;
+
+ if (wdev->current_bss) {
+ cfg80211_unhold_bss(wdev->current_bss);
+ cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub);
+ }
+
+ cfg80211_hold_bss(bss_from_pub(bss));
+ wdev->current_bss = bss_from_pub(bss);
+
+ if (!(wdev->wiphy->flags & WIPHY_FLAG_HAS_STATIC_WEP))
+ cfg80211_upload_connect_keys(wdev);
+
+ nl80211_send_ibss_bssid(wiphy_to_rdev(wdev->wiphy), dev, bssid,
+ GFP_KERNEL);
+#ifdef CPTCFG_CFG80211_WEXT
+ memset(&wrqu, 0, sizeof(wrqu));
+ memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN);
+ wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
+#endif
+}
+
+void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid,
+ struct ieee80211_channel *channel, gfp_t gfp)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+ struct cfg80211_event *ev;
+ unsigned long flags;
+
+ trace_cfg80211_ibss_joined(dev, bssid, channel);
+
+ if (WARN_ON(!channel))
+ return;
+
+ ev = kzalloc(sizeof(*ev), gfp);
+ if (!ev)
+ return;
+
+ ev->type = EVENT_IBSS_JOINED;
+ memcpy(ev->ij.bssid, bssid, ETH_ALEN);
+ ev->ij.channel = channel;
+
+ spin_lock_irqsave(&wdev->event_lock, flags);
+ list_add_tail(&ev->list, &wdev->event_list);
+ spin_unlock_irqrestore(&wdev->event_lock, flags);
+ queue_work(cfg80211_wq, &rdev->event_work);
+}
+EXPORT_SYMBOL(cfg80211_ibss_joined);
+
+static int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct cfg80211_ibss_params *params,
+ struct cfg80211_cached_keys *connkeys)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ int err;
+
+ ASSERT_WDEV_LOCK(wdev);
+
+ if (wdev->ssid_len)
+ return -EALREADY;
+
+ if (!params->basic_rates) {
+ /*
+ * If no rates were explicitly configured,
+ * use the mandatory rate set for 11b or
+ * 11a for maximum compatibility.
+ */
+ struct ieee80211_supported_band *sband =
+ rdev->wiphy.bands[params->chandef.chan->band];
+ int j;
+ u32 flag = params->chandef.chan->band == NL80211_BAND_5GHZ ?
+ IEEE80211_RATE_MANDATORY_A :
+ IEEE80211_RATE_MANDATORY_B;
+
+ for (j = 0; j < sband->n_bitrates; j++) {
+ if (sband->bitrates[j].flags & flag)
+ params->basic_rates |= BIT(j);
+ }
+ }
+
+ if (WARN_ON(connkeys && connkeys->def < 0))
+ return -EINVAL;
+
+ if (WARN_ON(wdev->connect_keys))
+ kzfree(wdev->connect_keys);
+ wdev->connect_keys = connkeys;
+
+ wdev->ibss_fixed = params->channel_fixed;
+ wdev->ibss_dfs_possible = params->userspace_handles_dfs;
+ wdev->chandef = params->chandef;
+#ifdef CPTCFG_CFG80211_WEXT
+ wdev->wext.ibss.chandef = params->chandef;
+#endif
+ err = rdev_join_ibss(rdev, dev, params);
+ if (err) {
+ wdev->connect_keys = NULL;
+ return err;
+ }
+
+ memcpy(wdev->ssid, params->ssid, params->ssid_len);
+ wdev->ssid_len = params->ssid_len;
+
+ return 0;
+}
+
+int cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct cfg80211_ibss_params *params,
+ struct cfg80211_cached_keys *connkeys)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ int err;
+
+ ASSERT_RTNL();
+
+ wdev_lock(wdev);
+ err = __cfg80211_join_ibss(rdev, dev, params, connkeys);
+ wdev_unlock(wdev);
+
+ return err;
+}
+
+static void __cfg80211_clear_ibss(struct net_device *dev, bool nowext)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+ int i;
+
+ ASSERT_WDEV_LOCK(wdev);
+
+ kzfree(wdev->connect_keys);
+ wdev->connect_keys = NULL;
+
+ rdev_set_qos_map(rdev, dev, NULL);
+
+ /*
+ * Delete all the keys ... pairwise keys can't really
+ * exist any more anyway, but default keys might.
+ */
+ if (rdev->ops->del_key)
+ for (i = 0; i < 6; i++)
+ rdev_del_key(rdev, dev, i, false, NULL);
+
+ if (wdev->current_bss) {
+ cfg80211_unhold_bss(wdev->current_bss);
+ cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub);
+ }
+
+ wdev->current_bss = NULL;
+ wdev->ssid_len = 0;
+ memset(&wdev->chandef, 0, sizeof(wdev->chandef));
+#ifdef CPTCFG_CFG80211_WEXT
+ if (!nowext)
+ wdev->wext.ibss.ssid_len = 0;
+#endif
+ cfg80211_sched_dfs_chan_update(rdev);
+}
+
+void cfg80211_clear_ibss(struct net_device *dev, bool nowext)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+
+ wdev_lock(wdev);
+ __cfg80211_clear_ibss(dev, nowext);
+ wdev_unlock(wdev);
+}
+
+int __cfg80211_leave_ibss(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, bool nowext)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ int err;
+
+ ASSERT_WDEV_LOCK(wdev);
+
+ if (!wdev->ssid_len)
+ return -ENOLINK;
+
+ err = rdev_leave_ibss(rdev, dev);
+
+ if (err)
+ return err;
+
+ __cfg80211_clear_ibss(dev, nowext);
+
+ return 0;
+}
+
+int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, bool nowext)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ int err;
+
+ wdev_lock(wdev);
+ err = __cfg80211_leave_ibss(rdev, dev, nowext);
+ wdev_unlock(wdev);
+
+ return err;
+}
+
+#ifdef CPTCFG_CFG80211_WEXT
+int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev)
+{
+ struct cfg80211_cached_keys *ck = NULL;
+ enum nl80211_band band;
+ int i, err;
+
+ ASSERT_WDEV_LOCK(wdev);
+
+ if (!wdev->wext.ibss.beacon_interval)
+ wdev->wext.ibss.beacon_interval = 100;
+
+ /* try to find an IBSS channel if none requested ... */
+ if (!wdev->wext.ibss.chandef.chan) {
+ struct ieee80211_channel *new_chan = NULL;
+
+ for (band = 0; band < NUM_NL80211_BANDS; band++) {
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_channel *chan;
+
+ sband = rdev->wiphy.bands[band];
+ if (!sband)
+ continue;
+
+ for (i = 0; i < sband->n_channels; i++) {
+ chan = &sband->channels[i];
+ if (chan->flags & IEEE80211_CHAN_NO_IR)
+ continue;
+ if (chan->flags & IEEE80211_CHAN_DISABLED)
+ continue;
+ new_chan = chan;
+ break;
+ }
+
+ if (new_chan)
+ break;
+ }
+
+ if (!new_chan)
+ return -EINVAL;
+
+ cfg80211_chandef_create(&wdev->wext.ibss.chandef, new_chan,
+ NL80211_CHAN_NO_HT);
+ }
+
+ /* don't join -- SSID is not there */
+ if (!wdev->wext.ibss.ssid_len)
+ return 0;
+
+ if (!netif_running(wdev->netdev))
+ return 0;
+
+ if (wdev->wext.keys)
+ wdev->wext.keys->def = wdev->wext.default_key;
+
+ wdev->wext.ibss.privacy = wdev->wext.default_key != -1;
+
+ if (wdev->wext.keys && wdev->wext.keys->def != -1) {
+ ck = kmemdup(wdev->wext.keys, sizeof(*ck), GFP_KERNEL);
+ if (!ck)
+ return -ENOMEM;
+ for (i = 0; i < CFG80211_MAX_WEP_KEYS; i++)
+ ck->params[i].key = ck->data[i];
+ }
+ err = __cfg80211_join_ibss(rdev, wdev->netdev,
+ &wdev->wext.ibss, ck);
+ if (err)
+ kfree(ck);
+
+ return err;
+}
+
+int cfg80211_ibss_wext_siwfreq(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_freq *wextfreq, char *extra)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+ struct ieee80211_channel *chan = NULL;
+ int err, freq;
+
+ /* call only for ibss! */
+ if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
+ return -EINVAL;
+
+ if (!rdev->ops->join_ibss)
+ return -EOPNOTSUPP;
+
+ freq = cfg80211_wext_freq(wextfreq);
+ if (freq < 0)
+ return freq;
+
+ if (freq) {
+ chan = ieee80211_get_channel(wdev->wiphy, freq);
+ if (!chan)
+ return -EINVAL;
+ if (chan->flags & IEEE80211_CHAN_NO_IR ||
+ chan->flags & IEEE80211_CHAN_DISABLED)
+ return -EINVAL;
+ }
+
+ if (wdev->wext.ibss.chandef.chan == chan)
+ return 0;
+
+ wdev_lock(wdev);
+ err = 0;
+ if (wdev->ssid_len)
+ err = __cfg80211_leave_ibss(rdev, dev, true);
+ wdev_unlock(wdev);
+
+ if (err)
+ return err;
+
+ if (chan) {
+ cfg80211_chandef_create(&wdev->wext.ibss.chandef, chan,
+ NL80211_CHAN_NO_HT);
+ wdev->wext.ibss.channel_fixed = true;
+ } else {
+ /* cfg80211_ibss_wext_join will pick one if needed */
+ wdev->wext.ibss.channel_fixed = false;
+ }
+
+ wdev_lock(wdev);
+ err = cfg80211_ibss_wext_join(rdev, wdev);
+ wdev_unlock(wdev);
+
+ return err;
+}
+
+int cfg80211_ibss_wext_giwfreq(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_freq *freq, char *extra)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct ieee80211_channel *chan = NULL;
+
+ /* call only for ibss! */
+ if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
+ return -EINVAL;
+
+ wdev_lock(wdev);
+ if (wdev->current_bss)
+ chan = wdev->current_bss->pub.channel;
+ else if (wdev->wext.ibss.chandef.chan)
+ chan = wdev->wext.ibss.chandef.chan;
+ wdev_unlock(wdev);
+
+ if (chan) {
+ freq->m = chan->center_freq;
+ freq->e = 6;
+ return 0;
+ }
+
+ /* no channel if not joining */
+ return -EINVAL;
+}
+
+int cfg80211_ibss_wext_siwessid(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *data, char *ssid)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+ size_t len = data->length;
+ int err;
+
+ /* call only for ibss! */
+ if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
+ return -EINVAL;
+
+ if (!rdev->ops->join_ibss)
+ return -EOPNOTSUPP;
+
+ wdev_lock(wdev);
+ err = 0;
+ if (wdev->ssid_len)
+ err = __cfg80211_leave_ibss(rdev, dev, true);
+ wdev_unlock(wdev);
+
+ if (err)
+ return err;
+
+ /* iwconfig uses nul termination in SSID.. */
+ if (len > 0 && ssid[len - 1] == '\0')
+ len--;
+
+ memcpy(wdev->ssid, ssid, len);
+ wdev->wext.ibss.ssid = wdev->ssid;
+ wdev->wext.ibss.ssid_len = len;
+
+ wdev_lock(wdev);
+ err = cfg80211_ibss_wext_join(rdev, wdev);
+ wdev_unlock(wdev);
+
+ return err;
+}
+
+int cfg80211_ibss_wext_giwessid(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *data, char *ssid)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+
+ /* call only for ibss! */
+ if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
+ return -EINVAL;
+
+ data->flags = 0;
+
+ wdev_lock(wdev);
+ if (wdev->ssid_len) {
+ data->flags = 1;
+ data->length = wdev->ssid_len;
+ memcpy(ssid, wdev->ssid, data->length);
+ } else if (wdev->wext.ibss.ssid && wdev->wext.ibss.ssid_len) {
+ data->flags = 1;
+ data->length = wdev->wext.ibss.ssid_len;
+ memcpy(ssid, wdev->wext.ibss.ssid, data->length);
+ }
+ wdev_unlock(wdev);
+
+ return 0;
+}
+
+int cfg80211_ibss_wext_siwap(struct net_device *dev,
+ struct iw_request_info *info,
+ struct sockaddr *ap_addr, char *extra)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+ u8 *bssid = ap_addr->sa_data;
+ int err;
+
+ /* call only for ibss! */
+ if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
+ return -EINVAL;
+
+ if (!rdev->ops->join_ibss)
+ return -EOPNOTSUPP;
+
+ if (ap_addr->sa_family != ARPHRD_ETHER)
+ return -EINVAL;
+
+ /* automatic mode */
+ if (is_zero_ether_addr(bssid) || is_broadcast_ether_addr(bssid))
+ bssid = NULL;
+
+ if (bssid && !is_valid_ether_addr(bssid))
+ return -EINVAL;
+
+ /* both automatic */
+ if (!bssid && !wdev->wext.ibss.bssid)
+ return 0;
+
+ /* fixed already - and no change */
+ if (wdev->wext.ibss.bssid && bssid &&
+ ether_addr_equal(bssid, wdev->wext.ibss.bssid))
+ return 0;
+
+ wdev_lock(wdev);
+ err = 0;
+ if (wdev->ssid_len)
+ err = __cfg80211_leave_ibss(rdev, dev, true);
+ wdev_unlock(wdev);
+
+ if (err)
+ return err;
+
+ if (bssid) {
+ memcpy(wdev->wext.bssid, bssid, ETH_ALEN);
+ wdev->wext.ibss.bssid = wdev->wext.bssid;
+ } else
+ wdev->wext.ibss.bssid = NULL;
+
+ wdev_lock(wdev);
+ err = cfg80211_ibss_wext_join(rdev, wdev);
+ wdev_unlock(wdev);
+
+ return err;
+}
+
+int cfg80211_ibss_wext_giwap(struct net_device *dev,
+ struct iw_request_info *info,
+ struct sockaddr *ap_addr, char *extra)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+
+ /* call only for ibss! */
+ if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
+ return -EINVAL;
+
+ ap_addr->sa_family = ARPHRD_ETHER;
+
+ wdev_lock(wdev);
+ if (wdev->current_bss)
+ memcpy(ap_addr->sa_data, wdev->current_bss->pub.bssid, ETH_ALEN);
+ else if (wdev->wext.ibss.bssid)
+ memcpy(ap_addr->sa_data, wdev->wext.ibss.bssid, ETH_ALEN);
+ else
+ eth_zero_addr(ap_addr->sa_data);
+
+ wdev_unlock(wdev);
+
+ return 0;
+}
+#endif
diff --git a/net/wireless/lib80211.c b/net/wireless/lib80211.c
new file mode 100644
index 0000000..4596115
--- /dev/null
+++ b/net/wireless/lib80211.c
@@ -0,0 +1,257 @@
+/*
+ * lib80211 -- common bits for IEEE802.11 drivers
+ *
+ * Copyright(c) 2008 John W. Linville <linville@tuxdriver.com>
+ *
+ * Portions copied from old ieee80211 component, w/ original copyright
+ * notices below:
+ *
+ * Host AP crypto routines
+ *
+ * Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi>
+ * Portions Copyright (C) 2004, Intel Corporation <jketreno@linux.intel.com>
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/ctype.h>
+#include <linux/ieee80211.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#include <net/lib80211.h>
+
+#define DRV_NAME "lib80211"
+
+#define DRV_DESCRIPTION "common routines for IEEE802.11 drivers"
+
+MODULE_DESCRIPTION(DRV_DESCRIPTION);
+MODULE_AUTHOR("John W. Linville <linville@tuxdriver.com>");
+MODULE_LICENSE("GPL");
+
+struct lib80211_crypto_alg {
+ struct list_head list;
+ struct lib80211_crypto_ops *ops;
+};
+
+static LIST_HEAD(lib80211_crypto_algs);
+static DEFINE_SPINLOCK(lib80211_crypto_lock);
+
+static void lib80211_crypt_deinit_entries(struct lib80211_crypt_info *info,
+ int force);
+static void lib80211_crypt_quiescing(struct lib80211_crypt_info *info);
+static void lib80211_crypt_deinit_handler(unsigned long data);
+
+int lib80211_crypt_info_init(struct lib80211_crypt_info *info, char *name,
+ spinlock_t *lock)
+{
+ memset(info, 0, sizeof(*info));
+
+ info->name = name;
+ info->lock = lock;
+
+ INIT_LIST_HEAD(&info->crypt_deinit_list);
+ setup_timer(&info->crypt_deinit_timer, lib80211_crypt_deinit_handler,
+ (unsigned long)info);
+
+ return 0;
+}
+EXPORT_SYMBOL(lib80211_crypt_info_init);
+
+void lib80211_crypt_info_free(struct lib80211_crypt_info *info)
+{
+ int i;
+
+ lib80211_crypt_quiescing(info);
+ del_timer_sync(&info->crypt_deinit_timer);
+ lib80211_crypt_deinit_entries(info, 1);
+
+ for (i = 0; i < NUM_WEP_KEYS; i++) {
+ struct lib80211_crypt_data *crypt = info->crypt[i];
+ if (crypt) {
+ if (crypt->ops) {
+ crypt->ops->deinit(crypt->priv);
+ module_put(crypt->ops->owner);
+ }
+ kfree(crypt);
+ info->crypt[i] = NULL;
+ }
+ }
+}
+EXPORT_SYMBOL(lib80211_crypt_info_free);
+
+static void lib80211_crypt_deinit_entries(struct lib80211_crypt_info *info,
+ int force)
+{
+ struct lib80211_crypt_data *entry, *next;
+ unsigned long flags;
+
+ spin_lock_irqsave(info->lock, flags);
+ list_for_each_entry_safe(entry, next, &info->crypt_deinit_list, list) {
+ if (atomic_read(&entry->refcnt) != 0 && !force)
+ continue;
+
+ list_del(&entry->list);
+
+ if (entry->ops) {
+ entry->ops->deinit(entry->priv);
+ module_put(entry->ops->owner);
+ }
+ kfree(entry);
+ }
+ spin_unlock_irqrestore(info->lock, flags);
+}
+
+/* After this, crypt_deinit_list won't accept new members */
+static void lib80211_crypt_quiescing(struct lib80211_crypt_info *info)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(info->lock, flags);
+ info->crypt_quiesced = 1;
+ spin_unlock_irqrestore(info->lock, flags);
+}
+
+static void lib80211_crypt_deinit_handler(unsigned long data)
+{
+ struct lib80211_crypt_info *info = (struct lib80211_crypt_info *)data;
+ unsigned long flags;
+
+ lib80211_crypt_deinit_entries(info, 0);
+
+ spin_lock_irqsave(info->lock, flags);
+ if (!list_empty(&info->crypt_deinit_list) && !info->crypt_quiesced) {
+ printk(KERN_DEBUG "%s: entries remaining in delayed crypt "
+ "deletion list\n", info->name);
+ info->crypt_deinit_timer.expires = jiffies + HZ;
+ add_timer(&info->crypt_deinit_timer);
+ }
+ spin_unlock_irqrestore(info->lock, flags);
+}
+
+void lib80211_crypt_delayed_deinit(struct lib80211_crypt_info *info,
+ struct lib80211_crypt_data **crypt)
+{
+ struct lib80211_crypt_data *tmp;
+ unsigned long flags;
+
+ if (*crypt == NULL)
+ return;
+
+ tmp = *crypt;
+ *crypt = NULL;
+
+ /* must not run ops->deinit() while there may be pending encrypt or
+ * decrypt operations. Use a list of delayed deinits to avoid needing
+ * locking. */
+
+ spin_lock_irqsave(info->lock, flags);
+ if (!info->crypt_quiesced) {
+ list_add(&tmp->list, &info->crypt_deinit_list);
+ if (!timer_pending(&info->crypt_deinit_timer)) {
+ info->crypt_deinit_timer.expires = jiffies + HZ;
+ add_timer(&info->crypt_deinit_timer);
+ }
+ }
+ spin_unlock_irqrestore(info->lock, flags);
+}
+EXPORT_SYMBOL(lib80211_crypt_delayed_deinit);
+
+int lib80211_register_crypto_ops(struct lib80211_crypto_ops *ops)
+{
+ unsigned long flags;
+ struct lib80211_crypto_alg *alg;
+
+ alg = kzalloc(sizeof(*alg), GFP_KERNEL);
+ if (alg == NULL)
+ return -ENOMEM;
+
+ alg->ops = ops;
+
+ spin_lock_irqsave(&lib80211_crypto_lock, flags);
+ list_add(&alg->list, &lib80211_crypto_algs);
+ spin_unlock_irqrestore(&lib80211_crypto_lock, flags);
+
+ printk(KERN_DEBUG "lib80211_crypt: registered algorithm '%s'\n",
+ ops->name);
+
+ return 0;
+}
+EXPORT_SYMBOL(lib80211_register_crypto_ops);
+
+int lib80211_unregister_crypto_ops(struct lib80211_crypto_ops *ops)
+{
+ struct lib80211_crypto_alg *alg;
+ unsigned long flags;
+
+ spin_lock_irqsave(&lib80211_crypto_lock, flags);
+ list_for_each_entry(alg, &lib80211_crypto_algs, list) {
+ if (alg->ops == ops)
+ goto found;
+ }
+ spin_unlock_irqrestore(&lib80211_crypto_lock, flags);
+ return -EINVAL;
+
+ found:
+ printk(KERN_DEBUG "lib80211_crypt: unregistered algorithm '%s'\n",
+ ops->name);
+ list_del(&alg->list);
+ spin_unlock_irqrestore(&lib80211_crypto_lock, flags);
+ kfree(alg);
+ return 0;
+}
+EXPORT_SYMBOL(lib80211_unregister_crypto_ops);
+
+struct lib80211_crypto_ops *lib80211_get_crypto_ops(const char *name)
+{
+ struct lib80211_crypto_alg *alg;
+ unsigned long flags;
+
+ spin_lock_irqsave(&lib80211_crypto_lock, flags);
+ list_for_each_entry(alg, &lib80211_crypto_algs, list) {
+ if (strcmp(alg->ops->name, name) == 0)
+ goto found;
+ }
+ spin_unlock_irqrestore(&lib80211_crypto_lock, flags);
+ return NULL;
+
+ found:
+ spin_unlock_irqrestore(&lib80211_crypto_lock, flags);
+ return alg->ops;
+}
+EXPORT_SYMBOL(lib80211_get_crypto_ops);
+
+static void *lib80211_crypt_null_init(int keyidx)
+{
+ return (void *)1;
+}
+
+static void lib80211_crypt_null_deinit(void *priv)
+{
+}
+
+static struct lib80211_crypto_ops lib80211_crypt_null = {
+ .name = "NULL",
+ .init = lib80211_crypt_null_init,
+ .deinit = lib80211_crypt_null_deinit,
+ .owner = THIS_MODULE,
+};
+
+static int __init lib80211_init(void)
+{
+ pr_info(DRV_DESCRIPTION "\n");
+ return lib80211_register_crypto_ops(&lib80211_crypt_null);
+}
+
+static void __exit lib80211_exit(void)
+{
+ lib80211_unregister_crypto_ops(&lib80211_crypt_null);
+ BUG_ON(!list_empty(&lib80211_crypto_algs));
+}
+
+module_init(lib80211_init);
+module_exit(lib80211_exit);
diff --git a/net/wireless/lib80211_crypt_ccmp.c b/net/wireless/lib80211_crypt_ccmp.c
new file mode 100644
index 0000000..eb10af4
--- /dev/null
+++ b/net/wireless/lib80211_crypt_ccmp.c
@@ -0,0 +1,479 @@
+/*
+ * lib80211 crypt: host-based CCMP encryption implementation for lib80211
+ *
+ * Copyright (c) 2003-2004, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2008, John W. Linville <linville@tuxdriver.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation. See README and COPYING for
+ * more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/if_ether.h>
+#include <linux/if_arp.h>
+#include <asm/string.h>
+#include <linux/wireless.h>
+
+#include <linux/ieee80211.h>
+
+#include <linux/crypto.h>
+
+#include <net/lib80211.h>
+
+MODULE_AUTHOR("Jouni Malinen");
+MODULE_DESCRIPTION("Host AP crypt: CCMP");
+MODULE_LICENSE("GPL");
+
+#define AES_BLOCK_LEN 16
+#define CCMP_HDR_LEN 8
+#define CCMP_MIC_LEN 8
+#define CCMP_TK_LEN 16
+#define CCMP_PN_LEN 6
+
+struct lib80211_ccmp_data {
+ u8 key[CCMP_TK_LEN];
+ int key_set;
+
+ u8 tx_pn[CCMP_PN_LEN];
+ u8 rx_pn[CCMP_PN_LEN];
+
+ u32 dot11RSNAStatsCCMPFormatErrors;
+ u32 dot11RSNAStatsCCMPReplays;
+ u32 dot11RSNAStatsCCMPDecryptErrors;
+
+ int key_idx;
+
+ struct crypto_cipher *tfm;
+
+ /* scratch buffers for virt_to_page() (crypto API) */
+ u8 tx_b0[AES_BLOCK_LEN], tx_b[AES_BLOCK_LEN],
+ tx_e[AES_BLOCK_LEN], tx_s0[AES_BLOCK_LEN];
+ u8 rx_b0[AES_BLOCK_LEN], rx_b[AES_BLOCK_LEN], rx_a[AES_BLOCK_LEN];
+};
+
+static inline void lib80211_ccmp_aes_encrypt(struct crypto_cipher *tfm,
+ const u8 pt[16], u8 ct[16])
+{
+ crypto_cipher_encrypt_one(tfm, ct, pt);
+}
+
+static void *lib80211_ccmp_init(int key_idx)
+{
+ struct lib80211_ccmp_data *priv;
+
+ priv = kzalloc(sizeof(*priv), GFP_ATOMIC);
+ if (priv == NULL)
+ goto fail;
+ priv->key_idx = key_idx;
+
+ priv->tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(priv->tfm)) {
+ priv->tfm = NULL;
+ goto fail;
+ }
+
+ return priv;
+
+ fail:
+ if (priv) {
+ if (priv->tfm)
+ crypto_free_cipher(priv->tfm);
+ kfree(priv);
+ }
+
+ return NULL;
+}
+
+static void lib80211_ccmp_deinit(void *priv)
+{
+ struct lib80211_ccmp_data *_priv = priv;
+ if (_priv && _priv->tfm)
+ crypto_free_cipher(_priv->tfm);
+ kfree(priv);
+}
+
+static inline void xor_block(u8 * b, u8 * a, size_t len)
+{
+ int i;
+ for (i = 0; i < len; i++)
+ b[i] ^= a[i];
+}
+
+static void ccmp_init_blocks(struct crypto_cipher *tfm,
+ struct ieee80211_hdr *hdr,
+ u8 * pn, size_t dlen, u8 * b0, u8 * auth, u8 * s0)
+{
+ u8 *pos, qc = 0;
+ size_t aad_len;
+ int a4_included, qc_included;
+ u8 aad[2 * AES_BLOCK_LEN];
+
+ a4_included = ieee80211_has_a4(hdr->frame_control);
+ qc_included = ieee80211_is_data_qos(hdr->frame_control);
+
+ aad_len = 22;
+ if (a4_included)
+ aad_len += 6;
+ if (qc_included) {
+ pos = (u8 *) & hdr->addr4;
+ if (a4_included)
+ pos += 6;
+ qc = *pos & 0x0f;
+ aad_len += 2;
+ }
+
+ /* CCM Initial Block:
+ * Flag (Include authentication header, M=3 (8-octet MIC),
+ * L=1 (2-octet Dlen))
+ * Nonce: 0x00 | A2 | PN
+ * Dlen */
+ b0[0] = 0x59;
+ b0[1] = qc;
+ memcpy(b0 + 2, hdr->addr2, ETH_ALEN);
+ memcpy(b0 + 8, pn, CCMP_PN_LEN);
+ b0[14] = (dlen >> 8) & 0xff;
+ b0[15] = dlen & 0xff;
+
+ /* AAD:
+ * FC with bits 4..6 and 11..13 masked to zero; 14 is always one
+ * A1 | A2 | A3
+ * SC with bits 4..15 (seq#) masked to zero
+ * A4 (if present)
+ * QC (if present)
+ */
+ pos = (u8 *) hdr;
+ aad[0] = 0; /* aad_len >> 8 */
+ aad[1] = aad_len & 0xff;
+ aad[2] = pos[0] & 0x8f;
+ aad[3] = pos[1] & 0xc7;
+ memcpy(aad + 4, hdr->addr1, 3 * ETH_ALEN);
+ pos = (u8 *) & hdr->seq_ctrl;
+ aad[22] = pos[0] & 0x0f;
+ aad[23] = 0; /* all bits masked */
+ memset(aad + 24, 0, 8);
+ if (a4_included)
+ memcpy(aad + 24, hdr->addr4, ETH_ALEN);
+ if (qc_included) {
+ aad[a4_included ? 30 : 24] = qc;
+ /* rest of QC masked */
+ }
+
+ /* Start with the first block and AAD */
+ lib80211_ccmp_aes_encrypt(tfm, b0, auth);
+ xor_block(auth, aad, AES_BLOCK_LEN);
+ lib80211_ccmp_aes_encrypt(tfm, auth, auth);
+ xor_block(auth, &aad[AES_BLOCK_LEN], AES_BLOCK_LEN);
+ lib80211_ccmp_aes_encrypt(tfm, auth, auth);
+ b0[0] &= 0x07;
+ b0[14] = b0[15] = 0;
+ lib80211_ccmp_aes_encrypt(tfm, b0, s0);
+}
+
+static int lib80211_ccmp_hdr(struct sk_buff *skb, int hdr_len,
+ u8 *aeskey, int keylen, void *priv)
+{
+ struct lib80211_ccmp_data *key = priv;
+ int i;
+ u8 *pos;
+
+ if (skb_headroom(skb) < CCMP_HDR_LEN || skb->len < hdr_len)
+ return -1;
+
+ if (aeskey != NULL && keylen >= CCMP_TK_LEN)
+ memcpy(aeskey, key->key, CCMP_TK_LEN);
+
+ pos = skb_push(skb, CCMP_HDR_LEN);
+ memmove(pos, pos + CCMP_HDR_LEN, hdr_len);
+ pos += hdr_len;
+
+ i = CCMP_PN_LEN - 1;
+ while (i >= 0) {
+ key->tx_pn[i]++;
+ if (key->tx_pn[i] != 0)
+ break;
+ i--;
+ }
+
+ *pos++ = key->tx_pn[5];
+ *pos++ = key->tx_pn[4];
+ *pos++ = 0;
+ *pos++ = (key->key_idx << 6) | (1 << 5) /* Ext IV included */ ;
+ *pos++ = key->tx_pn[3];
+ *pos++ = key->tx_pn[2];
+ *pos++ = key->tx_pn[1];
+ *pos++ = key->tx_pn[0];
+
+ return CCMP_HDR_LEN;
+}
+
+static int lib80211_ccmp_encrypt(struct sk_buff *skb, int hdr_len, void *priv)
+{
+ struct lib80211_ccmp_data *key = priv;
+ int data_len, i, blocks, last, len;
+ u8 *pos, *mic;
+ struct ieee80211_hdr *hdr;
+ u8 *b0 = key->tx_b0;
+ u8 *b = key->tx_b;
+ u8 *e = key->tx_e;
+ u8 *s0 = key->tx_s0;
+
+ if (skb_tailroom(skb) < CCMP_MIC_LEN || skb->len < hdr_len)
+ return -1;
+
+ data_len = skb->len - hdr_len;
+ len = lib80211_ccmp_hdr(skb, hdr_len, NULL, 0, priv);
+ if (len < 0)
+ return -1;
+
+ pos = skb->data + hdr_len + CCMP_HDR_LEN;
+ hdr = (struct ieee80211_hdr *)skb->data;
+ ccmp_init_blocks(key->tfm, hdr, key->tx_pn, data_len, b0, b, s0);
+
+ blocks = DIV_ROUND_UP(data_len, AES_BLOCK_LEN);
+ last = data_len % AES_BLOCK_LEN;
+
+ for (i = 1; i <= blocks; i++) {
+ len = (i == blocks && last) ? last : AES_BLOCK_LEN;
+ /* Authentication */
+ xor_block(b, pos, len);
+ lib80211_ccmp_aes_encrypt(key->tfm, b, b);
+ /* Encryption, with counter */
+ b0[14] = (i >> 8) & 0xff;
+ b0[15] = i & 0xff;
+ lib80211_ccmp_aes_encrypt(key->tfm, b0, e);
+ xor_block(pos, e, len);
+ pos += len;
+ }
+
+ mic = skb_put(skb, CCMP_MIC_LEN);
+ for (i = 0; i < CCMP_MIC_LEN; i++)
+ mic[i] = b[i] ^ s0[i];
+
+ return 0;
+}
+
+/*
+ * deal with seq counter wrapping correctly.
+ * refer to timer_after() for jiffies wrapping handling
+ */
+static inline int ccmp_replay_check(u8 *pn_n, u8 *pn_o)
+{
+ u32 iv32_n, iv16_n;
+ u32 iv32_o, iv16_o;
+
+ iv32_n = (pn_n[0] << 24) | (pn_n[1] << 16) | (pn_n[2] << 8) | pn_n[3];
+ iv16_n = (pn_n[4] << 8) | pn_n[5];
+
+ iv32_o = (pn_o[0] << 24) | (pn_o[1] << 16) | (pn_o[2] << 8) | pn_o[3];
+ iv16_o = (pn_o[4] << 8) | pn_o[5];
+
+ if ((s32)iv32_n - (s32)iv32_o < 0 ||
+ (iv32_n == iv32_o && iv16_n <= iv16_o))
+ return 1;
+ return 0;
+}
+
+static int lib80211_ccmp_decrypt(struct sk_buff *skb, int hdr_len, void *priv)
+{
+ struct lib80211_ccmp_data *key = priv;
+ u8 keyidx, *pos;
+ struct ieee80211_hdr *hdr;
+ u8 *b0 = key->rx_b0;
+ u8 *b = key->rx_b;
+ u8 *a = key->rx_a;
+ u8 pn[6];
+ int i, blocks, last, len;
+ size_t data_len = skb->len - hdr_len - CCMP_HDR_LEN - CCMP_MIC_LEN;
+ u8 *mic = skb->data + skb->len - CCMP_MIC_LEN;
+
+ if (skb->len < hdr_len + CCMP_HDR_LEN + CCMP_MIC_LEN) {
+ key->dot11RSNAStatsCCMPFormatErrors++;
+ return -1;
+ }
+
+ hdr = (struct ieee80211_hdr *)skb->data;
+ pos = skb->data + hdr_len;
+ keyidx = pos[3];
+ if (!(keyidx & (1 << 5))) {
+ net_dbg_ratelimited("CCMP: received packet without ExtIV flag from %pM\n",
+ hdr->addr2);
+ key->dot11RSNAStatsCCMPFormatErrors++;
+ return -2;
+ }
+ keyidx >>= 6;
+ if (key->key_idx != keyidx) {
+ net_dbg_ratelimited("CCMP: RX tkey->key_idx=%d frame keyidx=%d\n",
+ key->key_idx, keyidx);
+ return -6;
+ }
+ if (!key->key_set) {
+ net_dbg_ratelimited("CCMP: received packet from %pM with keyid=%d that does not have a configured key\n",
+ hdr->addr2, keyidx);
+ return -3;
+ }
+
+ pn[0] = pos[7];
+ pn[1] = pos[6];
+ pn[2] = pos[5];
+ pn[3] = pos[4];
+ pn[4] = pos[1];
+ pn[5] = pos[0];
+ pos += 8;
+
+ if (ccmp_replay_check(pn, key->rx_pn)) {
+#ifdef CPTCFG_LIB80211_DEBUG
+ net_dbg_ratelimited("CCMP: replay detected: STA=%pM previous PN %02x%02x%02x%02x%02x%02x received PN %02x%02x%02x%02x%02x%02x\n",
+ hdr->addr2,
+ key->rx_pn[0], key->rx_pn[1], key->rx_pn[2],
+ key->rx_pn[3], key->rx_pn[4], key->rx_pn[5],
+ pn[0], pn[1], pn[2], pn[3], pn[4], pn[5]);
+#endif
+ key->dot11RSNAStatsCCMPReplays++;
+ return -4;
+ }
+
+ ccmp_init_blocks(key->tfm, hdr, pn, data_len, b0, a, b);
+ xor_block(mic, b, CCMP_MIC_LEN);
+
+ blocks = DIV_ROUND_UP(data_len, AES_BLOCK_LEN);
+ last = data_len % AES_BLOCK_LEN;
+
+ for (i = 1; i <= blocks; i++) {
+ len = (i == blocks && last) ? last : AES_BLOCK_LEN;
+ /* Decrypt, with counter */
+ b0[14] = (i >> 8) & 0xff;
+ b0[15] = i & 0xff;
+ lib80211_ccmp_aes_encrypt(key->tfm, b0, b);
+ xor_block(pos, b, len);
+ /* Authentication */
+ xor_block(a, pos, len);
+ lib80211_ccmp_aes_encrypt(key->tfm, a, a);
+ pos += len;
+ }
+
+ if (memcmp(mic, a, CCMP_MIC_LEN) != 0) {
+ net_dbg_ratelimited("CCMP: decrypt failed: STA=%pM\n",
+ hdr->addr2);
+ key->dot11RSNAStatsCCMPDecryptErrors++;
+ return -5;
+ }
+
+ memcpy(key->rx_pn, pn, CCMP_PN_LEN);
+
+ /* Remove hdr and MIC */
+ memmove(skb->data + CCMP_HDR_LEN, skb->data, hdr_len);
+ skb_pull(skb, CCMP_HDR_LEN);
+ skb_trim(skb, skb->len - CCMP_MIC_LEN);
+
+ return keyidx;
+}
+
+static int lib80211_ccmp_set_key(void *key, int len, u8 * seq, void *priv)
+{
+ struct lib80211_ccmp_data *data = priv;
+ int keyidx;
+ struct crypto_cipher *tfm = data->tfm;
+
+ keyidx = data->key_idx;
+ memset(data, 0, sizeof(*data));
+ data->key_idx = keyidx;
+ data->tfm = tfm;
+ if (len == CCMP_TK_LEN) {
+ memcpy(data->key, key, CCMP_TK_LEN);
+ data->key_set = 1;
+ if (seq) {
+ data->rx_pn[0] = seq[5];
+ data->rx_pn[1] = seq[4];
+ data->rx_pn[2] = seq[3];
+ data->rx_pn[3] = seq[2];
+ data->rx_pn[4] = seq[1];
+ data->rx_pn[5] = seq[0];
+ }
+ crypto_cipher_setkey(data->tfm, data->key, CCMP_TK_LEN);
+ } else if (len == 0)
+ data->key_set = 0;
+ else
+ return -1;
+
+ return 0;
+}
+
+static int lib80211_ccmp_get_key(void *key, int len, u8 * seq, void *priv)
+{
+ struct lib80211_ccmp_data *data = priv;
+
+ if (len < CCMP_TK_LEN)
+ return -1;
+
+ if (!data->key_set)
+ return 0;
+ memcpy(key, data->key, CCMP_TK_LEN);
+
+ if (seq) {
+ seq[0] = data->tx_pn[5];
+ seq[1] = data->tx_pn[4];
+ seq[2] = data->tx_pn[3];
+ seq[3] = data->tx_pn[2];
+ seq[4] = data->tx_pn[1];
+ seq[5] = data->tx_pn[0];
+ }
+
+ return CCMP_TK_LEN;
+}
+
+static void lib80211_ccmp_print_stats(struct seq_file *m, void *priv)
+{
+ struct lib80211_ccmp_data *ccmp = priv;
+
+ seq_printf(m,
+ "key[%d] alg=CCMP key_set=%d "
+ "tx_pn=%02x%02x%02x%02x%02x%02x "
+ "rx_pn=%02x%02x%02x%02x%02x%02x "
+ "format_errors=%d replays=%d decrypt_errors=%d\n",
+ ccmp->key_idx, ccmp->key_set,
+ ccmp->tx_pn[0], ccmp->tx_pn[1], ccmp->tx_pn[2],
+ ccmp->tx_pn[3], ccmp->tx_pn[4], ccmp->tx_pn[5],
+ ccmp->rx_pn[0], ccmp->rx_pn[1], ccmp->rx_pn[2],
+ ccmp->rx_pn[3], ccmp->rx_pn[4], ccmp->rx_pn[5],
+ ccmp->dot11RSNAStatsCCMPFormatErrors,
+ ccmp->dot11RSNAStatsCCMPReplays,
+ ccmp->dot11RSNAStatsCCMPDecryptErrors);
+}
+
+static struct lib80211_crypto_ops lib80211_crypt_ccmp = {
+ .name = "CCMP",
+ .init = lib80211_ccmp_init,
+ .deinit = lib80211_ccmp_deinit,
+ .encrypt_mpdu = lib80211_ccmp_encrypt,
+ .decrypt_mpdu = lib80211_ccmp_decrypt,
+ .encrypt_msdu = NULL,
+ .decrypt_msdu = NULL,
+ .set_key = lib80211_ccmp_set_key,
+ .get_key = lib80211_ccmp_get_key,
+ .print_stats = lib80211_ccmp_print_stats,
+ .extra_mpdu_prefix_len = CCMP_HDR_LEN,
+ .extra_mpdu_postfix_len = CCMP_MIC_LEN,
+ .owner = THIS_MODULE,
+};
+
+static int __init lib80211_crypto_ccmp_init(void)
+{
+ return lib80211_register_crypto_ops(&lib80211_crypt_ccmp);
+}
+
+static void __exit lib80211_crypto_ccmp_exit(void)
+{
+ lib80211_unregister_crypto_ops(&lib80211_crypt_ccmp);
+}
+
+module_init(lib80211_crypto_ccmp_init);
+module_exit(lib80211_crypto_ccmp_exit);
diff --git a/net/wireless/lib80211_crypt_tkip.c b/net/wireless/lib80211_crypt_tkip.c
new file mode 100644
index 0000000..e41a4c2
--- /dev/null
+++ b/net/wireless/lib80211_crypt_tkip.c
@@ -0,0 +1,771 @@
+/*
+ * lib80211 crypt: host-based TKIP encryption implementation for lib80211
+ *
+ * Copyright (c) 2003-2004, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2008, John W. Linville <linville@tuxdriver.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation. See README and COPYING for
+ * more details.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include <linux/scatterlist.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/mm.h>
+#include <linux/if_ether.h>
+#include <linux/if_arp.h>
+#include <asm/string.h>
+
+#include <linux/wireless.h>
+#include <linux/ieee80211.h>
+#include <net/iw_handler.h>
+
+#include <crypto/hash.h>
+#include <crypto/skcipher.h>
+#include <linux/crc32.h>
+
+#include <net/lib80211.h>
+
+MODULE_AUTHOR("Jouni Malinen");
+MODULE_DESCRIPTION("lib80211 crypt: TKIP");
+MODULE_LICENSE("GPL");
+
+#define TKIP_HDR_LEN 8
+
+struct lib80211_tkip_data {
+#define TKIP_KEY_LEN 32
+ u8 key[TKIP_KEY_LEN];
+ int key_set;
+
+ u32 tx_iv32;
+ u16 tx_iv16;
+ u16 tx_ttak[5];
+ int tx_phase1_done;
+
+ u32 rx_iv32;
+ u16 rx_iv16;
+ u16 rx_ttak[5];
+ int rx_phase1_done;
+ u32 rx_iv32_new;
+ u16 rx_iv16_new;
+
+ u32 dot11RSNAStatsTKIPReplays;
+ u32 dot11RSNAStatsTKIPICVErrors;
+ u32 dot11RSNAStatsTKIPLocalMICFailures;
+
+ int key_idx;
+
+ struct crypto_skcipher *rx_tfm_arc4;
+ struct crypto_ahash *rx_tfm_michael;
+ struct crypto_skcipher *tx_tfm_arc4;
+ struct crypto_ahash *tx_tfm_michael;
+
+ /* scratch buffers for virt_to_page() (crypto API) */
+ u8 rx_hdr[16], tx_hdr[16];
+
+ unsigned long flags;
+};
+
+static unsigned long lib80211_tkip_set_flags(unsigned long flags, void *priv)
+{
+ struct lib80211_tkip_data *_priv = priv;
+ unsigned long old_flags = _priv->flags;
+ _priv->flags = flags;
+ return old_flags;
+}
+
+static unsigned long lib80211_tkip_get_flags(void *priv)
+{
+ struct lib80211_tkip_data *_priv = priv;
+ return _priv->flags;
+}
+
+static void *lib80211_tkip_init(int key_idx)
+{
+ struct lib80211_tkip_data *priv;
+
+ priv = kzalloc(sizeof(*priv), GFP_ATOMIC);
+ if (priv == NULL)
+ goto fail;
+
+ priv->key_idx = key_idx;
+
+ priv->tx_tfm_arc4 = crypto_alloc_skcipher("ecb(arc4)", 0,
+ CRYPTO_ALG_ASYNC);
+ if (IS_ERR(priv->tx_tfm_arc4)) {
+ priv->tx_tfm_arc4 = NULL;
+ goto fail;
+ }
+
+ priv->tx_tfm_michael = crypto_alloc_ahash("michael_mic", 0,
+ CRYPTO_ALG_ASYNC);
+ if (IS_ERR(priv->tx_tfm_michael)) {
+ priv->tx_tfm_michael = NULL;
+ goto fail;
+ }
+
+ priv->rx_tfm_arc4 = crypto_alloc_skcipher("ecb(arc4)", 0,
+ CRYPTO_ALG_ASYNC);
+ if (IS_ERR(priv->rx_tfm_arc4)) {
+ priv->rx_tfm_arc4 = NULL;
+ goto fail;
+ }
+
+ priv->rx_tfm_michael = crypto_alloc_ahash("michael_mic", 0,
+ CRYPTO_ALG_ASYNC);
+ if (IS_ERR(priv->rx_tfm_michael)) {
+ priv->rx_tfm_michael = NULL;
+ goto fail;
+ }
+
+ return priv;
+
+ fail:
+ if (priv) {
+ crypto_free_ahash(priv->tx_tfm_michael);
+ crypto_free_skcipher(priv->tx_tfm_arc4);
+ crypto_free_ahash(priv->rx_tfm_michael);
+ crypto_free_skcipher(priv->rx_tfm_arc4);
+ kfree(priv);
+ }
+
+ return NULL;
+}
+
+static void lib80211_tkip_deinit(void *priv)
+{
+ struct lib80211_tkip_data *_priv = priv;
+ if (_priv) {
+ crypto_free_ahash(_priv->tx_tfm_michael);
+ crypto_free_skcipher(_priv->tx_tfm_arc4);
+ crypto_free_ahash(_priv->rx_tfm_michael);
+ crypto_free_skcipher(_priv->rx_tfm_arc4);
+ }
+ kfree(priv);
+}
+
+static inline u16 RotR1(u16 val)
+{
+ return (val >> 1) | (val << 15);
+}
+
+static inline u8 Lo8(u16 val)
+{
+ return val & 0xff;
+}
+
+static inline u8 Hi8(u16 val)
+{
+ return val >> 8;
+}
+
+static inline u16 Lo16(u32 val)
+{
+ return val & 0xffff;
+}
+
+static inline u16 Hi16(u32 val)
+{
+ return val >> 16;
+}
+
+static inline u16 Mk16(u8 hi, u8 lo)
+{
+ return lo | (((u16) hi) << 8);
+}
+
+static inline u16 Mk16_le(__le16 * v)
+{
+ return le16_to_cpu(*v);
+}
+
+static const u16 Sbox[256] = {
+ 0xC6A5, 0xF884, 0xEE99, 0xF68D, 0xFF0D, 0xD6BD, 0xDEB1, 0x9154,
+ 0x6050, 0x0203, 0xCEA9, 0x567D, 0xE719, 0xB562, 0x4DE6, 0xEC9A,
+ 0x8F45, 0x1F9D, 0x8940, 0xFA87, 0xEF15, 0xB2EB, 0x8EC9, 0xFB0B,
+ 0x41EC, 0xB367, 0x5FFD, 0x45EA, 0x23BF, 0x53F7, 0xE496, 0x9B5B,
+ 0x75C2, 0xE11C, 0x3DAE, 0x4C6A, 0x6C5A, 0x7E41, 0xF502, 0x834F,
+ 0x685C, 0x51F4, 0xD134, 0xF908, 0xE293, 0xAB73, 0x6253, 0x2A3F,
+ 0x080C, 0x9552, 0x4665, 0x9D5E, 0x3028, 0x37A1, 0x0A0F, 0x2FB5,
+ 0x0E09, 0x2436, 0x1B9B, 0xDF3D, 0xCD26, 0x4E69, 0x7FCD, 0xEA9F,
+ 0x121B, 0x1D9E, 0x5874, 0x342E, 0x362D, 0xDCB2, 0xB4EE, 0x5BFB,
+ 0xA4F6, 0x764D, 0xB761, 0x7DCE, 0x527B, 0xDD3E, 0x5E71, 0x1397,
+ 0xA6F5, 0xB968, 0x0000, 0xC12C, 0x4060, 0xE31F, 0x79C8, 0xB6ED,
+ 0xD4BE, 0x8D46, 0x67D9, 0x724B, 0x94DE, 0x98D4, 0xB0E8, 0x854A,
+ 0xBB6B, 0xC52A, 0x4FE5, 0xED16, 0x86C5, 0x9AD7, 0x6655, 0x1194,
+ 0x8ACF, 0xE910, 0x0406, 0xFE81, 0xA0F0, 0x7844, 0x25BA, 0x4BE3,
+ 0xA2F3, 0x5DFE, 0x80C0, 0x058A, 0x3FAD, 0x21BC, 0x7048, 0xF104,
+ 0x63DF, 0x77C1, 0xAF75, 0x4263, 0x2030, 0xE51A, 0xFD0E, 0xBF6D,
+ 0x814C, 0x1814, 0x2635, 0xC32F, 0xBEE1, 0x35A2, 0x88CC, 0x2E39,
+ 0x9357, 0x55F2, 0xFC82, 0x7A47, 0xC8AC, 0xBAE7, 0x322B, 0xE695,
+ 0xC0A0, 0x1998, 0x9ED1, 0xA37F, 0x4466, 0x547E, 0x3BAB, 0x0B83,
+ 0x8CCA, 0xC729, 0x6BD3, 0x283C, 0xA779, 0xBCE2, 0x161D, 0xAD76,
+ 0xDB3B, 0x6456, 0x744E, 0x141E, 0x92DB, 0x0C0A, 0x486C, 0xB8E4,
+ 0x9F5D, 0xBD6E, 0x43EF, 0xC4A6, 0x39A8, 0x31A4, 0xD337, 0xF28B,
+ 0xD532, 0x8B43, 0x6E59, 0xDAB7, 0x018C, 0xB164, 0x9CD2, 0x49E0,
+ 0xD8B4, 0xACFA, 0xF307, 0xCF25, 0xCAAF, 0xF48E, 0x47E9, 0x1018,
+ 0x6FD5, 0xF088, 0x4A6F, 0x5C72, 0x3824, 0x57F1, 0x73C7, 0x9751,
+ 0xCB23, 0xA17C, 0xE89C, 0x3E21, 0x96DD, 0x61DC, 0x0D86, 0x0F85,
+ 0xE090, 0x7C42, 0x71C4, 0xCCAA, 0x90D8, 0x0605, 0xF701, 0x1C12,
+ 0xC2A3, 0x6A5F, 0xAEF9, 0x69D0, 0x1791, 0x9958, 0x3A27, 0x27B9,
+ 0xD938, 0xEB13, 0x2BB3, 0x2233, 0xD2BB, 0xA970, 0x0789, 0x33A7,
+ 0x2DB6, 0x3C22, 0x1592, 0xC920, 0x8749, 0xAAFF, 0x5078, 0xA57A,
+ 0x038F, 0x59F8, 0x0980, 0x1A17, 0x65DA, 0xD731, 0x84C6, 0xD0B8,
+ 0x82C3, 0x29B0, 0x5A77, 0x1E11, 0x7BCB, 0xA8FC, 0x6DD6, 0x2C3A,
+};
+
+static inline u16 _S_(u16 v)
+{
+ u16 t = Sbox[Hi8(v)];
+ return Sbox[Lo8(v)] ^ ((t << 8) | (t >> 8));
+}
+
+#define PHASE1_LOOP_COUNT 8
+
+static void tkip_mixing_phase1(u16 * TTAK, const u8 * TK, const u8 * TA,
+ u32 IV32)
+{
+ int i, j;
+
+ /* Initialize the 80-bit TTAK from TSC (IV32) and TA[0..5] */
+ TTAK[0] = Lo16(IV32);
+ TTAK[1] = Hi16(IV32);
+ TTAK[2] = Mk16(TA[1], TA[0]);
+ TTAK[3] = Mk16(TA[3], TA[2]);
+ TTAK[4] = Mk16(TA[5], TA[4]);
+
+ for (i = 0; i < PHASE1_LOOP_COUNT; i++) {
+ j = 2 * (i & 1);
+ TTAK[0] += _S_(TTAK[4] ^ Mk16(TK[1 + j], TK[0 + j]));
+ TTAK[1] += _S_(TTAK[0] ^ Mk16(TK[5 + j], TK[4 + j]));
+ TTAK[2] += _S_(TTAK[1] ^ Mk16(TK[9 + j], TK[8 + j]));
+ TTAK[3] += _S_(TTAK[2] ^ Mk16(TK[13 + j], TK[12 + j]));
+ TTAK[4] += _S_(TTAK[3] ^ Mk16(TK[1 + j], TK[0 + j])) + i;
+ }
+}
+
+static void tkip_mixing_phase2(u8 * WEPSeed, const u8 * TK, const u16 * TTAK,
+ u16 IV16)
+{
+ /* Make temporary area overlap WEP seed so that the final copy can be
+ * avoided on little endian hosts. */
+ u16 *PPK = (u16 *) & WEPSeed[4];
+
+ /* Step 1 - make copy of TTAK and bring in TSC */
+ PPK[0] = TTAK[0];
+ PPK[1] = TTAK[1];
+ PPK[2] = TTAK[2];
+ PPK[3] = TTAK[3];
+ PPK[4] = TTAK[4];
+ PPK[5] = TTAK[4] + IV16;
+
+ /* Step 2 - 96-bit bijective mixing using S-box */
+ PPK[0] += _S_(PPK[5] ^ Mk16_le((__le16 *) & TK[0]));
+ PPK[1] += _S_(PPK[0] ^ Mk16_le((__le16 *) & TK[2]));
+ PPK[2] += _S_(PPK[1] ^ Mk16_le((__le16 *) & TK[4]));
+ PPK[3] += _S_(PPK[2] ^ Mk16_le((__le16 *) & TK[6]));
+ PPK[4] += _S_(PPK[3] ^ Mk16_le((__le16 *) & TK[8]));
+ PPK[5] += _S_(PPK[4] ^ Mk16_le((__le16 *) & TK[10]));
+
+ PPK[0] += RotR1(PPK[5] ^ Mk16_le((__le16 *) & TK[12]));
+ PPK[1] += RotR1(PPK[0] ^ Mk16_le((__le16 *) & TK[14]));
+ PPK[2] += RotR1(PPK[1]);
+ PPK[3] += RotR1(PPK[2]);
+ PPK[4] += RotR1(PPK[3]);
+ PPK[5] += RotR1(PPK[4]);
+
+ /* Step 3 - bring in last of TK bits, assign 24-bit WEP IV value
+ * WEPSeed[0..2] is transmitted as WEP IV */
+ WEPSeed[0] = Hi8(IV16);
+ WEPSeed[1] = (Hi8(IV16) | 0x20) & 0x7F;
+ WEPSeed[2] = Lo8(IV16);
+ WEPSeed[3] = Lo8((PPK[5] ^ Mk16_le((__le16 *) & TK[0])) >> 1);
+
+#ifdef __BIG_ENDIAN
+ {
+ int i;
+ for (i = 0; i < 6; i++)
+ PPK[i] = (PPK[i] << 8) | (PPK[i] >> 8);
+ }
+#endif
+}
+
+static int lib80211_tkip_hdr(struct sk_buff *skb, int hdr_len,
+ u8 * rc4key, int keylen, void *priv)
+{
+ struct lib80211_tkip_data *tkey = priv;
+ u8 *pos;
+ struct ieee80211_hdr *hdr;
+
+ hdr = (struct ieee80211_hdr *)skb->data;
+
+ if (skb_headroom(skb) < TKIP_HDR_LEN || skb->len < hdr_len)
+ return -1;
+
+ if (rc4key == NULL || keylen < 16)
+ return -1;
+
+ if (!tkey->tx_phase1_done) {
+ tkip_mixing_phase1(tkey->tx_ttak, tkey->key, hdr->addr2,
+ tkey->tx_iv32);
+ tkey->tx_phase1_done = 1;
+ }
+ tkip_mixing_phase2(rc4key, tkey->key, tkey->tx_ttak, tkey->tx_iv16);
+
+ pos = skb_push(skb, TKIP_HDR_LEN);
+ memmove(pos, pos + TKIP_HDR_LEN, hdr_len);
+ pos += hdr_len;
+
+ *pos++ = *rc4key;
+ *pos++ = *(rc4key + 1);
+ *pos++ = *(rc4key + 2);
+ *pos++ = (tkey->key_idx << 6) | (1 << 5) /* Ext IV included */ ;
+ *pos++ = tkey->tx_iv32 & 0xff;
+ *pos++ = (tkey->tx_iv32 >> 8) & 0xff;
+ *pos++ = (tkey->tx_iv32 >> 16) & 0xff;
+ *pos++ = (tkey->tx_iv32 >> 24) & 0xff;
+
+ tkey->tx_iv16++;
+ if (tkey->tx_iv16 == 0) {
+ tkey->tx_phase1_done = 0;
+ tkey->tx_iv32++;
+ }
+
+ return TKIP_HDR_LEN;
+}
+
+static int lib80211_tkip_encrypt(struct sk_buff *skb, int hdr_len, void *priv)
+{
+ struct lib80211_tkip_data *tkey = priv;
+ SKCIPHER_REQUEST_ON_STACK(req, tkey->tx_tfm_arc4);
+ int len;
+ u8 rc4key[16], *pos, *icv;
+ u32 crc;
+ struct scatterlist sg;
+ int err;
+
+ if (tkey->flags & IEEE80211_CRYPTO_TKIP_COUNTERMEASURES) {
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+ net_dbg_ratelimited("TKIP countermeasures: dropped TX packet to %pM\n",
+ hdr->addr1);
+ return -1;
+ }
+
+ if (skb_tailroom(skb) < 4 || skb->len < hdr_len)
+ return -1;
+
+ len = skb->len - hdr_len;
+ pos = skb->data + hdr_len;
+
+ if ((lib80211_tkip_hdr(skb, hdr_len, rc4key, 16, priv)) < 0)
+ return -1;
+
+ crc = ~crc32_le(~0, pos, len);
+ icv = skb_put(skb, 4);
+ icv[0] = crc;
+ icv[1] = crc >> 8;
+ icv[2] = crc >> 16;
+ icv[3] = crc >> 24;
+
+ crypto_skcipher_setkey(tkey->tx_tfm_arc4, rc4key, 16);
+ sg_init_one(&sg, pos, len + 4);
+ skcipher_request_set_tfm(req, tkey->tx_tfm_arc4);
+ skcipher_request_set_callback(req, 0, NULL, NULL);
+ skcipher_request_set_crypt(req, &sg, &sg, len + 4, NULL);
+ err = crypto_skcipher_encrypt(req);
+ skcipher_request_zero(req);
+ return err;
+}
+
+/*
+ * deal with seq counter wrapping correctly.
+ * refer to timer_after() for jiffies wrapping handling
+ */
+static inline int tkip_replay_check(u32 iv32_n, u16 iv16_n,
+ u32 iv32_o, u16 iv16_o)
+{
+ if ((s32)iv32_n - (s32)iv32_o < 0 ||
+ (iv32_n == iv32_o && iv16_n <= iv16_o))
+ return 1;
+ return 0;
+}
+
+static int lib80211_tkip_decrypt(struct sk_buff *skb, int hdr_len, void *priv)
+{
+ struct lib80211_tkip_data *tkey = priv;
+ SKCIPHER_REQUEST_ON_STACK(req, tkey->rx_tfm_arc4);
+ u8 rc4key[16];
+ u8 keyidx, *pos;
+ u32 iv32;
+ u16 iv16;
+ struct ieee80211_hdr *hdr;
+ u8 icv[4];
+ u32 crc;
+ struct scatterlist sg;
+ int plen;
+ int err;
+
+ hdr = (struct ieee80211_hdr *)skb->data;
+
+ if (tkey->flags & IEEE80211_CRYPTO_TKIP_COUNTERMEASURES) {
+ net_dbg_ratelimited("TKIP countermeasures: dropped received packet from %pM\n",
+ hdr->addr2);
+ return -1;
+ }
+
+ if (skb->len < hdr_len + TKIP_HDR_LEN + 4)
+ return -1;
+
+ pos = skb->data + hdr_len;
+ keyidx = pos[3];
+ if (!(keyidx & (1 << 5))) {
+ net_dbg_ratelimited("TKIP: received packet without ExtIV flag from %pM\n",
+ hdr->addr2);
+ return -2;
+ }
+ keyidx >>= 6;
+ if (tkey->key_idx != keyidx) {
+ net_dbg_ratelimited("TKIP: RX tkey->key_idx=%d frame keyidx=%d\n",
+ tkey->key_idx, keyidx);
+ return -6;
+ }
+ if (!tkey->key_set) {
+ net_dbg_ratelimited("TKIP: received packet from %pM with keyid=%d that does not have a configured key\n",
+ hdr->addr2, keyidx);
+ return -3;
+ }
+ iv16 = (pos[0] << 8) | pos[2];
+ iv32 = pos[4] | (pos[5] << 8) | (pos[6] << 16) | (pos[7] << 24);
+ pos += TKIP_HDR_LEN;
+
+ if (tkip_replay_check(iv32, iv16, tkey->rx_iv32, tkey->rx_iv16)) {
+#ifdef CPTCFG_LIB80211_DEBUG
+ net_dbg_ratelimited("TKIP: replay detected: STA=%pM previous TSC %08x%04x received TSC %08x%04x\n",
+ hdr->addr2, tkey->rx_iv32, tkey->rx_iv16,
+ iv32, iv16);
+#endif
+ tkey->dot11RSNAStatsTKIPReplays++;
+ return -4;
+ }
+
+ if (iv32 != tkey->rx_iv32 || !tkey->rx_phase1_done) {
+ tkip_mixing_phase1(tkey->rx_ttak, tkey->key, hdr->addr2, iv32);
+ tkey->rx_phase1_done = 1;
+ }
+ tkip_mixing_phase2(rc4key, tkey->key, tkey->rx_ttak, iv16);
+
+ plen = skb->len - hdr_len - 12;
+
+ crypto_skcipher_setkey(tkey->rx_tfm_arc4, rc4key, 16);
+ sg_init_one(&sg, pos, plen + 4);
+ skcipher_request_set_tfm(req, tkey->rx_tfm_arc4);
+ skcipher_request_set_callback(req, 0, NULL, NULL);
+ skcipher_request_set_crypt(req, &sg, &sg, plen + 4, NULL);
+ err = crypto_skcipher_decrypt(req);
+ skcipher_request_zero(req);
+ if (err) {
+ net_dbg_ratelimited("TKIP: failed to decrypt received packet from %pM\n",
+ hdr->addr2);
+ return -7;
+ }
+
+ crc = ~crc32_le(~0, pos, plen);
+ icv[0] = crc;
+ icv[1] = crc >> 8;
+ icv[2] = crc >> 16;
+ icv[3] = crc >> 24;
+ if (memcmp(icv, pos + plen, 4) != 0) {
+ if (iv32 != tkey->rx_iv32) {
+ /* Previously cached Phase1 result was already lost, so
+ * it needs to be recalculated for the next packet. */
+ tkey->rx_phase1_done = 0;
+ }
+#ifdef CPTCFG_LIB80211_DEBUG
+ net_dbg_ratelimited("TKIP: ICV error detected: STA=%pM\n",
+ hdr->addr2);
+#endif
+ tkey->dot11RSNAStatsTKIPICVErrors++;
+ return -5;
+ }
+
+ /* Update real counters only after Michael MIC verification has
+ * completed */
+ tkey->rx_iv32_new = iv32;
+ tkey->rx_iv16_new = iv16;
+
+ /* Remove IV and ICV */
+ memmove(skb->data + TKIP_HDR_LEN, skb->data, hdr_len);
+ skb_pull(skb, TKIP_HDR_LEN);
+ skb_trim(skb, skb->len - 4);
+
+ return keyidx;
+}
+
+static int michael_mic(struct crypto_ahash *tfm_michael, u8 * key, u8 * hdr,
+ u8 * data, size_t data_len, u8 * mic)
+{
+ AHASH_REQUEST_ON_STACK(req, tfm_michael);
+ struct scatterlist sg[2];
+ int err;
+
+ if (tfm_michael == NULL) {
+ pr_warn("%s(): tfm_michael == NULL\n", __func__);
+ return -1;
+ }
+ sg_init_table(sg, 2);
+ sg_set_buf(&sg[0], hdr, 16);
+ sg_set_buf(&sg[1], data, data_len);
+
+ if (crypto_ahash_setkey(tfm_michael, key, 8))
+ return -1;
+
+ ahash_request_set_tfm(req, tfm_michael);
+ ahash_request_set_callback(req, 0, NULL, NULL);
+ ahash_request_set_crypt(req, sg, mic, data_len + 16);
+ err = crypto_ahash_digest(req);
+ ahash_request_zero(req);
+ return err;
+}
+
+static void michael_mic_hdr(struct sk_buff *skb, u8 * hdr)
+{
+ struct ieee80211_hdr *hdr11;
+
+ hdr11 = (struct ieee80211_hdr *)skb->data;
+
+ switch (le16_to_cpu(hdr11->frame_control) &
+ (IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS)) {
+ case IEEE80211_FCTL_TODS:
+ memcpy(hdr, hdr11->addr3, ETH_ALEN); /* DA */
+ memcpy(hdr + ETH_ALEN, hdr11->addr2, ETH_ALEN); /* SA */
+ break;
+ case IEEE80211_FCTL_FROMDS:
+ memcpy(hdr, hdr11->addr1, ETH_ALEN); /* DA */
+ memcpy(hdr + ETH_ALEN, hdr11->addr3, ETH_ALEN); /* SA */
+ break;
+ case IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS:
+ memcpy(hdr, hdr11->addr3, ETH_ALEN); /* DA */
+ memcpy(hdr + ETH_ALEN, hdr11->addr4, ETH_ALEN); /* SA */
+ break;
+ default:
+ memcpy(hdr, hdr11->addr1, ETH_ALEN); /* DA */
+ memcpy(hdr + ETH_ALEN, hdr11->addr2, ETH_ALEN); /* SA */
+ break;
+ }
+
+ if (ieee80211_is_data_qos(hdr11->frame_control)) {
+ hdr[12] = le16_to_cpu(*((__le16 *)ieee80211_get_qos_ctl(hdr11)))
+ & IEEE80211_QOS_CTL_TID_MASK;
+ } else
+ hdr[12] = 0; /* priority */
+
+ hdr[13] = hdr[14] = hdr[15] = 0; /* reserved */
+}
+
+static int lib80211_michael_mic_add(struct sk_buff *skb, int hdr_len,
+ void *priv)
+{
+ struct lib80211_tkip_data *tkey = priv;
+ u8 *pos;
+
+ if (skb_tailroom(skb) < 8 || skb->len < hdr_len) {
+ printk(KERN_DEBUG "Invalid packet for Michael MIC add "
+ "(tailroom=%d hdr_len=%d skb->len=%d)\n",
+ skb_tailroom(skb), hdr_len, skb->len);
+ return -1;
+ }
+
+ michael_mic_hdr(skb, tkey->tx_hdr);
+ pos = skb_put(skb, 8);
+ if (michael_mic(tkey->tx_tfm_michael, &tkey->key[16], tkey->tx_hdr,
+ skb->data + hdr_len, skb->len - 8 - hdr_len, pos))
+ return -1;
+
+ return 0;
+}
+
+static void lib80211_michael_mic_failure(struct net_device *dev,
+ struct ieee80211_hdr *hdr,
+ int keyidx)
+{
+ union iwreq_data wrqu;
+ struct iw_michaelmicfailure ev;
+
+ /* TODO: needed parameters: count, keyid, key type, TSC */
+ memset(&ev, 0, sizeof(ev));
+ ev.flags = keyidx & IW_MICFAILURE_KEY_ID;
+ if (hdr->addr1[0] & 0x01)
+ ev.flags |= IW_MICFAILURE_GROUP;
+ else
+ ev.flags |= IW_MICFAILURE_PAIRWISE;
+ ev.src_addr.sa_family = ARPHRD_ETHER;
+ memcpy(ev.src_addr.sa_data, hdr->addr2, ETH_ALEN);
+ memset(&wrqu, 0, sizeof(wrqu));
+ wrqu.data.length = sizeof(ev);
+ wireless_send_event(dev, IWEVMICHAELMICFAILURE, &wrqu, (char *)&ev);
+}
+
+static int lib80211_michael_mic_verify(struct sk_buff *skb, int keyidx,
+ int hdr_len, void *priv)
+{
+ struct lib80211_tkip_data *tkey = priv;
+ u8 mic[8];
+
+ if (!tkey->key_set)
+ return -1;
+
+ michael_mic_hdr(skb, tkey->rx_hdr);
+ if (michael_mic(tkey->rx_tfm_michael, &tkey->key[24], tkey->rx_hdr,
+ skb->data + hdr_len, skb->len - 8 - hdr_len, mic))
+ return -1;
+ if (memcmp(mic, skb->data + skb->len - 8, 8) != 0) {
+ struct ieee80211_hdr *hdr;
+ hdr = (struct ieee80211_hdr *)skb->data;
+ printk(KERN_DEBUG "%s: Michael MIC verification failed for "
+ "MSDU from %pM keyidx=%d\n",
+ skb->dev ? skb->dev->name : "N/A", hdr->addr2,
+ keyidx);
+ if (skb->dev)
+ lib80211_michael_mic_failure(skb->dev, hdr, keyidx);
+ tkey->dot11RSNAStatsTKIPLocalMICFailures++;
+ return -1;
+ }
+
+ /* Update TSC counters for RX now that the packet verification has
+ * completed. */
+ tkey->rx_iv32 = tkey->rx_iv32_new;
+ tkey->rx_iv16 = tkey->rx_iv16_new;
+
+ skb_trim(skb, skb->len - 8);
+
+ return 0;
+}
+
+static int lib80211_tkip_set_key(void *key, int len, u8 * seq, void *priv)
+{
+ struct lib80211_tkip_data *tkey = priv;
+ int keyidx;
+ struct crypto_ahash *tfm = tkey->tx_tfm_michael;
+ struct crypto_skcipher *tfm2 = tkey->tx_tfm_arc4;
+ struct crypto_ahash *tfm3 = tkey->rx_tfm_michael;
+ struct crypto_skcipher *tfm4 = tkey->rx_tfm_arc4;
+
+ keyidx = tkey->key_idx;
+ memset(tkey, 0, sizeof(*tkey));
+ tkey->key_idx = keyidx;
+ tkey->tx_tfm_michael = tfm;
+ tkey->tx_tfm_arc4 = tfm2;
+ tkey->rx_tfm_michael = tfm3;
+ tkey->rx_tfm_arc4 = tfm4;
+ if (len == TKIP_KEY_LEN) {
+ memcpy(tkey->key, key, TKIP_KEY_LEN);
+ tkey->key_set = 1;
+ tkey->tx_iv16 = 1; /* TSC is initialized to 1 */
+ if (seq) {
+ tkey->rx_iv32 = (seq[5] << 24) | (seq[4] << 16) |
+ (seq[3] << 8) | seq[2];
+ tkey->rx_iv16 = (seq[1] << 8) | seq[0];
+ }
+ } else if (len == 0)
+ tkey->key_set = 0;
+ else
+ return -1;
+
+ return 0;
+}
+
+static int lib80211_tkip_get_key(void *key, int len, u8 * seq, void *priv)
+{
+ struct lib80211_tkip_data *tkey = priv;
+
+ if (len < TKIP_KEY_LEN)
+ return -1;
+
+ if (!tkey->key_set)
+ return 0;
+ memcpy(key, tkey->key, TKIP_KEY_LEN);
+
+ if (seq) {
+ /* Return the sequence number of the last transmitted frame. */
+ u16 iv16 = tkey->tx_iv16;
+ u32 iv32 = tkey->tx_iv32;
+ if (iv16 == 0)
+ iv32--;
+ iv16--;
+ seq[0] = tkey->tx_iv16;
+ seq[1] = tkey->tx_iv16 >> 8;
+ seq[2] = tkey->tx_iv32;
+ seq[3] = tkey->tx_iv32 >> 8;
+ seq[4] = tkey->tx_iv32 >> 16;
+ seq[5] = tkey->tx_iv32 >> 24;
+ }
+
+ return TKIP_KEY_LEN;
+}
+
+static void lib80211_tkip_print_stats(struct seq_file *m, void *priv)
+{
+ struct lib80211_tkip_data *tkip = priv;
+ seq_printf(m,
+ "key[%d] alg=TKIP key_set=%d "
+ "tx_pn=%02x%02x%02x%02x%02x%02x "
+ "rx_pn=%02x%02x%02x%02x%02x%02x "
+ "replays=%d icv_errors=%d local_mic_failures=%d\n",
+ tkip->key_idx, tkip->key_set,
+ (tkip->tx_iv32 >> 24) & 0xff,
+ (tkip->tx_iv32 >> 16) & 0xff,
+ (tkip->tx_iv32 >> 8) & 0xff,
+ tkip->tx_iv32 & 0xff,
+ (tkip->tx_iv16 >> 8) & 0xff,
+ tkip->tx_iv16 & 0xff,
+ (tkip->rx_iv32 >> 24) & 0xff,
+ (tkip->rx_iv32 >> 16) & 0xff,
+ (tkip->rx_iv32 >> 8) & 0xff,
+ tkip->rx_iv32 & 0xff,
+ (tkip->rx_iv16 >> 8) & 0xff,
+ tkip->rx_iv16 & 0xff,
+ tkip->dot11RSNAStatsTKIPReplays,
+ tkip->dot11RSNAStatsTKIPICVErrors,
+ tkip->dot11RSNAStatsTKIPLocalMICFailures);
+}
+
+static struct lib80211_crypto_ops lib80211_crypt_tkip = {
+ .name = "TKIP",
+ .init = lib80211_tkip_init,
+ .deinit = lib80211_tkip_deinit,
+ .encrypt_mpdu = lib80211_tkip_encrypt,
+ .decrypt_mpdu = lib80211_tkip_decrypt,
+ .encrypt_msdu = lib80211_michael_mic_add,
+ .decrypt_msdu = lib80211_michael_mic_verify,
+ .set_key = lib80211_tkip_set_key,
+ .get_key = lib80211_tkip_get_key,
+ .print_stats = lib80211_tkip_print_stats,
+ .extra_mpdu_prefix_len = 4 + 4, /* IV + ExtIV */
+ .extra_mpdu_postfix_len = 4, /* ICV */
+ .extra_msdu_postfix_len = 8, /* MIC */
+ .get_flags = lib80211_tkip_get_flags,
+ .set_flags = lib80211_tkip_set_flags,
+ .owner = THIS_MODULE,
+};
+
+static int __init lib80211_crypto_tkip_init(void)
+{
+ return lib80211_register_crypto_ops(&lib80211_crypt_tkip);
+}
+
+static void __exit lib80211_crypto_tkip_exit(void)
+{
+ lib80211_unregister_crypto_ops(&lib80211_crypt_tkip);
+}
+
+module_init(lib80211_crypto_tkip_init);
+module_exit(lib80211_crypto_tkip_exit);
diff --git a/net/wireless/lib80211_crypt_wep.c b/net/wireless/lib80211_crypt_wep.c
new file mode 100644
index 0000000..d05f58b
--- /dev/null
+++ b/net/wireless/lib80211_crypt_wep.c
@@ -0,0 +1,297 @@
+/*
+ * lib80211 crypt: host-based WEP encryption implementation for lib80211
+ *
+ * Copyright (c) 2002-2004, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2008, John W. Linville <linville@tuxdriver.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation. See README and COPYING for
+ * more details.
+ */
+
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include <linux/scatterlist.h>
+#include <linux/skbuff.h>
+#include <linux/mm.h>
+#include <asm/string.h>
+
+#include <net/lib80211.h>
+
+#include <crypto/skcipher.h>
+#include <linux/crc32.h>
+
+MODULE_AUTHOR("Jouni Malinen");
+MODULE_DESCRIPTION("lib80211 crypt: WEP");
+MODULE_LICENSE("GPL");
+
+struct lib80211_wep_data {
+ u32 iv;
+#define WEP_KEY_LEN 13
+ u8 key[WEP_KEY_LEN + 1];
+ u8 key_len;
+ u8 key_idx;
+ struct crypto_skcipher *tx_tfm;
+ struct crypto_skcipher *rx_tfm;
+};
+
+static void *lib80211_wep_init(int keyidx)
+{
+ struct lib80211_wep_data *priv;
+
+ priv = kzalloc(sizeof(*priv), GFP_ATOMIC);
+ if (priv == NULL)
+ goto fail;
+ priv->key_idx = keyidx;
+
+ priv->tx_tfm = crypto_alloc_skcipher("ecb(arc4)", 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(priv->tx_tfm)) {
+ priv->tx_tfm = NULL;
+ goto fail;
+ }
+
+ priv->rx_tfm = crypto_alloc_skcipher("ecb(arc4)", 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(priv->rx_tfm)) {
+ priv->rx_tfm = NULL;
+ goto fail;
+ }
+ /* start WEP IV from a random value */
+ get_random_bytes(&priv->iv, 4);
+
+ return priv;
+
+ fail:
+ if (priv) {
+ crypto_free_skcipher(priv->tx_tfm);
+ crypto_free_skcipher(priv->rx_tfm);
+ kfree(priv);
+ }
+ return NULL;
+}
+
+static void lib80211_wep_deinit(void *priv)
+{
+ struct lib80211_wep_data *_priv = priv;
+ if (_priv) {
+ crypto_free_skcipher(_priv->tx_tfm);
+ crypto_free_skcipher(_priv->rx_tfm);
+ }
+ kfree(priv);
+}
+
+/* Add WEP IV/key info to a frame that has at least 4 bytes of headroom */
+static int lib80211_wep_build_iv(struct sk_buff *skb, int hdr_len,
+ u8 *key, int keylen, void *priv)
+{
+ struct lib80211_wep_data *wep = priv;
+ u32 klen;
+ u8 *pos;
+
+ if (skb_headroom(skb) < 4 || skb->len < hdr_len)
+ return -1;
+
+ pos = skb_push(skb, 4);
+ memmove(pos, pos + 4, hdr_len);
+ pos += hdr_len;
+
+ klen = 3 + wep->key_len;
+
+ wep->iv++;
+
+ /* Fluhrer, Mantin, and Shamir have reported weaknesses in the key
+ * scheduling algorithm of RC4. At least IVs (KeyByte + 3, 0xff, N)
+ * can be used to speedup attacks, so avoid using them. */
+ if ((wep->iv & 0xff00) == 0xff00) {
+ u8 B = (wep->iv >> 16) & 0xff;
+ if (B >= 3 && B < klen)
+ wep->iv += 0x0100;
+ }
+
+ /* Prepend 24-bit IV to RC4 key and TX frame */
+ *pos++ = (wep->iv >> 16) & 0xff;
+ *pos++ = (wep->iv >> 8) & 0xff;
+ *pos++ = wep->iv & 0xff;
+ *pos++ = wep->key_idx << 6;
+
+ return 0;
+}
+
+/* Perform WEP encryption on given skb that has at least 4 bytes of headroom
+ * for IV and 4 bytes of tailroom for ICV. Both IV and ICV will be transmitted,
+ * so the payload length increases with 8 bytes.
+ *
+ * WEP frame payload: IV + TX key idx, RC4(data), ICV = RC4(CRC32(data))
+ */
+static int lib80211_wep_encrypt(struct sk_buff *skb, int hdr_len, void *priv)
+{
+ struct lib80211_wep_data *wep = priv;
+ SKCIPHER_REQUEST_ON_STACK(req, wep->tx_tfm);
+ u32 crc, klen, len;
+ u8 *pos, *icv;
+ struct scatterlist sg;
+ u8 key[WEP_KEY_LEN + 3];
+ int err;
+
+ /* other checks are in lib80211_wep_build_iv */
+ if (skb_tailroom(skb) < 4)
+ return -1;
+
+ /* add the IV to the frame */
+ if (lib80211_wep_build_iv(skb, hdr_len, NULL, 0, priv))
+ return -1;
+
+ /* Copy the IV into the first 3 bytes of the key */
+ skb_copy_from_linear_data_offset(skb, hdr_len, key, 3);
+
+ /* Copy rest of the WEP key (the secret part) */
+ memcpy(key + 3, wep->key, wep->key_len);
+
+ len = skb->len - hdr_len - 4;
+ pos = skb->data + hdr_len + 4;
+ klen = 3 + wep->key_len;
+
+ /* Append little-endian CRC32 over only the data and encrypt it to produce ICV */
+ crc = ~crc32_le(~0, pos, len);
+ icv = skb_put(skb, 4);
+ icv[0] = crc;
+ icv[1] = crc >> 8;
+ icv[2] = crc >> 16;
+ icv[3] = crc >> 24;
+
+ crypto_skcipher_setkey(wep->tx_tfm, key, klen);
+ sg_init_one(&sg, pos, len + 4);
+ skcipher_request_set_tfm(req, wep->tx_tfm);
+ skcipher_request_set_callback(req, 0, NULL, NULL);
+ skcipher_request_set_crypt(req, &sg, &sg, len + 4, NULL);
+ err = crypto_skcipher_encrypt(req);
+ skcipher_request_zero(req);
+ return err;
+}
+
+/* Perform WEP decryption on given buffer. Buffer includes whole WEP part of
+ * the frame: IV (4 bytes), encrypted payload (including SNAP header),
+ * ICV (4 bytes). len includes both IV and ICV.
+ *
+ * Returns 0 if frame was decrypted successfully and ICV was correct and -1 on
+ * failure. If frame is OK, IV and ICV will be removed.
+ */
+static int lib80211_wep_decrypt(struct sk_buff *skb, int hdr_len, void *priv)
+{
+ struct lib80211_wep_data *wep = priv;
+ SKCIPHER_REQUEST_ON_STACK(req, wep->rx_tfm);
+ u32 crc, klen, plen;
+ u8 key[WEP_KEY_LEN + 3];
+ u8 keyidx, *pos, icv[4];
+ struct scatterlist sg;
+ int err;
+
+ if (skb->len < hdr_len + 8)
+ return -1;
+
+ pos = skb->data + hdr_len;
+ key[0] = *pos++;
+ key[1] = *pos++;
+ key[2] = *pos++;
+ keyidx = *pos++ >> 6;
+ if (keyidx != wep->key_idx)
+ return -1;
+
+ klen = 3 + wep->key_len;
+
+ /* Copy rest of the WEP key (the secret part) */
+ memcpy(key + 3, wep->key, wep->key_len);
+
+ /* Apply RC4 to data and compute CRC32 over decrypted data */
+ plen = skb->len - hdr_len - 8;
+
+ crypto_skcipher_setkey(wep->rx_tfm, key, klen);
+ sg_init_one(&sg, pos, plen + 4);
+ skcipher_request_set_tfm(req, wep->rx_tfm);
+ skcipher_request_set_callback(req, 0, NULL, NULL);
+ skcipher_request_set_crypt(req, &sg, &sg, plen + 4, NULL);
+ err = crypto_skcipher_decrypt(req);
+ skcipher_request_zero(req);
+ if (err)
+ return -7;
+
+ crc = ~crc32_le(~0, pos, plen);
+ icv[0] = crc;
+ icv[1] = crc >> 8;
+ icv[2] = crc >> 16;
+ icv[3] = crc >> 24;
+ if (memcmp(icv, pos + plen, 4) != 0) {
+ /* ICV mismatch - drop frame */
+ return -2;
+ }
+
+ /* Remove IV and ICV */
+ memmove(skb->data + 4, skb->data, hdr_len);
+ skb_pull(skb, 4);
+ skb_trim(skb, skb->len - 4);
+
+ return 0;
+}
+
+static int lib80211_wep_set_key(void *key, int len, u8 * seq, void *priv)
+{
+ struct lib80211_wep_data *wep = priv;
+
+ if (len < 0 || len > WEP_KEY_LEN)
+ return -1;
+
+ memcpy(wep->key, key, len);
+ wep->key_len = len;
+
+ return 0;
+}
+
+static int lib80211_wep_get_key(void *key, int len, u8 * seq, void *priv)
+{
+ struct lib80211_wep_data *wep = priv;
+
+ if (len < wep->key_len)
+ return -1;
+
+ memcpy(key, wep->key, wep->key_len);
+
+ return wep->key_len;
+}
+
+static void lib80211_wep_print_stats(struct seq_file *m, void *priv)
+{
+ struct lib80211_wep_data *wep = priv;
+ seq_printf(m, "key[%d] alg=WEP len=%d\n", wep->key_idx, wep->key_len);
+}
+
+static struct lib80211_crypto_ops lib80211_crypt_wep = {
+ .name = "WEP",
+ .init = lib80211_wep_init,
+ .deinit = lib80211_wep_deinit,
+ .encrypt_mpdu = lib80211_wep_encrypt,
+ .decrypt_mpdu = lib80211_wep_decrypt,
+ .encrypt_msdu = NULL,
+ .decrypt_msdu = NULL,
+ .set_key = lib80211_wep_set_key,
+ .get_key = lib80211_wep_get_key,
+ .print_stats = lib80211_wep_print_stats,
+ .extra_mpdu_prefix_len = 4, /* IV */
+ .extra_mpdu_postfix_len = 4, /* ICV */
+ .owner = THIS_MODULE,
+};
+
+static int __init lib80211_crypto_wep_init(void)
+{
+ return lib80211_register_crypto_ops(&lib80211_crypt_wep);
+}
+
+static void __exit lib80211_crypto_wep_exit(void)
+{
+ lib80211_unregister_crypto_ops(&lib80211_crypt_wep);
+}
+
+module_init(lib80211_crypto_wep_init);
+module_exit(lib80211_crypto_wep_exit);
diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c
new file mode 100644
index 0000000..ec0b1c2
--- /dev/null
+++ b/net/wireless/mesh.c
@@ -0,0 +1,282 @@
+#include <linux/ieee80211.h>
+#include <linux/export.h>
+#include <net/cfg80211.h>
+#include "nl80211.h"
+#include "core.h"
+#include "rdev-ops.h"
+
+/* Default values, timeouts in ms */
+#define MESH_TTL 31
+#define MESH_DEFAULT_ELEMENT_TTL 31
+#define MESH_MAX_RETR 3
+#define MESH_RET_T 100
+#define MESH_CONF_T 100
+#define MESH_HOLD_T 100
+
+#define MESH_PATH_TIMEOUT 5000
+#define MESH_RANN_INTERVAL 5000
+#define MESH_PATH_TO_ROOT_TIMEOUT 6000
+#define MESH_ROOT_INTERVAL 5000
+#define MESH_ROOT_CONFIRMATION_INTERVAL 2000
+#define MESH_DEFAULT_PLINK_TIMEOUT 1800 /* timeout in seconds */
+
+/*
+ * Minimum interval between two consecutive PREQs originated by the same
+ * interface
+ */
+#define MESH_PREQ_MIN_INT 10
+#define MESH_PERR_MIN_INT 100
+#define MESH_DIAM_TRAVERSAL_TIME 50
+
+#define MESH_RSSI_THRESHOLD 0
+
+/*
+ * A path will be refreshed if it is used PATH_REFRESH_TIME milliseconds
+ * before timing out. This way it will remain ACTIVE and no data frames
+ * will be unnecessarily held in the pending queue.
+ */
+#define MESH_PATH_REFRESH_TIME 1000
+#define MESH_MIN_DISCOVERY_TIMEOUT (2 * MESH_DIAM_TRAVERSAL_TIME)
+
+/* Default maximum number of established plinks per interface */
+#define MESH_MAX_ESTAB_PLINKS 32
+
+#define MESH_MAX_PREQ_RETRIES 4
+
+#define MESH_SYNC_NEIGHBOR_OFFSET_MAX 50
+
+#define MESH_DEFAULT_BEACON_INTERVAL 1000 /* in 1024 us units (=TUs) */
+#define MESH_DEFAULT_DTIM_PERIOD 2
+#define MESH_DEFAULT_AWAKE_WINDOW 10 /* in 1024 us units (=TUs) */
+
+const struct mesh_config default_mesh_config = {
+ .dot11MeshRetryTimeout = MESH_RET_T,
+ .dot11MeshConfirmTimeout = MESH_CONF_T,
+ .dot11MeshHoldingTimeout = MESH_HOLD_T,
+ .dot11MeshMaxRetries = MESH_MAX_RETR,
+ .dot11MeshTTL = MESH_TTL,
+ .element_ttl = MESH_DEFAULT_ELEMENT_TTL,
+ .auto_open_plinks = true,
+ .dot11MeshMaxPeerLinks = MESH_MAX_ESTAB_PLINKS,
+ .dot11MeshNbrOffsetMaxNeighbor = MESH_SYNC_NEIGHBOR_OFFSET_MAX,
+ .dot11MeshHWMPactivePathTimeout = MESH_PATH_TIMEOUT,
+ .dot11MeshHWMPpreqMinInterval = MESH_PREQ_MIN_INT,
+ .dot11MeshHWMPperrMinInterval = MESH_PERR_MIN_INT,
+ .dot11MeshHWMPnetDiameterTraversalTime = MESH_DIAM_TRAVERSAL_TIME,
+ .dot11MeshHWMPmaxPREQretries = MESH_MAX_PREQ_RETRIES,
+ .path_refresh_time = MESH_PATH_REFRESH_TIME,
+ .min_discovery_timeout = MESH_MIN_DISCOVERY_TIMEOUT,
+ .dot11MeshHWMPRannInterval = MESH_RANN_INTERVAL,
+ .dot11MeshGateAnnouncementProtocol = false,
+ .dot11MeshForwarding = true,
+ .rssi_threshold = MESH_RSSI_THRESHOLD,
+ .ht_opmode = IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED,
+ .dot11MeshHWMPactivePathToRootTimeout = MESH_PATH_TO_ROOT_TIMEOUT,
+ .dot11MeshHWMProotInterval = MESH_ROOT_INTERVAL,
+ .dot11MeshHWMPconfirmationInterval = MESH_ROOT_CONFIRMATION_INTERVAL,
+ .power_mode = NL80211_MESH_POWER_ACTIVE,
+ .dot11MeshAwakeWindowDuration = MESH_DEFAULT_AWAKE_WINDOW,
+ .plink_timeout = MESH_DEFAULT_PLINK_TIMEOUT,
+};
+
+const struct mesh_setup default_mesh_setup = {
+ /* cfg80211_join_mesh() will pick a channel if needed */
+ .sync_method = IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET,
+ .path_sel_proto = IEEE80211_PATH_PROTOCOL_HWMP,
+ .path_metric = IEEE80211_PATH_METRIC_AIRTIME,
+ .auth_id = 0, /* open */
+ .ie = NULL,
+ .ie_len = 0,
+ .is_secure = false,
+ .user_mpm = false,
+ .beacon_interval = MESH_DEFAULT_BEACON_INTERVAL,
+ .dtim_period = MESH_DEFAULT_DTIM_PERIOD,
+};
+
+int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct mesh_setup *setup,
+ const struct mesh_config *conf)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ int err;
+
+ BUILD_BUG_ON(IEEE80211_MAX_SSID_LEN != IEEE80211_MAX_MESH_ID_LEN);
+
+ ASSERT_WDEV_LOCK(wdev);
+
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
+ return -EOPNOTSUPP;
+
+ if (!(rdev->wiphy.flags & WIPHY_FLAG_MESH_AUTH) &&
+ setup->is_secure)
+ return -EOPNOTSUPP;
+
+ if (wdev->mesh_id_len)
+ return -EALREADY;
+
+ if (!setup->mesh_id_len)
+ return -EINVAL;
+
+ if (!rdev->ops->join_mesh)
+ return -EOPNOTSUPP;
+
+ if (!setup->chandef.chan) {
+ /* if no channel explicitly given, use preset channel */
+ setup->chandef = wdev->preset_chandef;
+ }
+
+ if (!setup->chandef.chan) {
+ /* if we don't have that either, use the first usable channel */
+ enum nl80211_band band;
+
+ for (band = 0; band < NUM_NL80211_BANDS; band++) {
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_channel *chan;
+ int i;
+
+ sband = rdev->wiphy.bands[band];
+ if (!sband)
+ continue;
+
+ for (i = 0; i < sband->n_channels; i++) {
+ chan = &sband->channels[i];
+ if (chan->flags & (IEEE80211_CHAN_NO_IR |
+ IEEE80211_CHAN_DISABLED |
+ IEEE80211_CHAN_RADAR))
+ continue;
+ setup->chandef.chan = chan;
+ break;
+ }
+
+ if (setup->chandef.chan)
+ break;
+ }
+
+ /* no usable channel ... */
+ if (!setup->chandef.chan)
+ return -EINVAL;
+
+ setup->chandef.width = NL80211_CHAN_WIDTH_20_NOHT;
+ setup->chandef.center_freq1 = setup->chandef.chan->center_freq;
+ }
+
+ /*
+ * check if basic rates are available otherwise use mandatory rates as
+ * basic rates
+ */
+ if (!setup->basic_rates) {
+ enum nl80211_bss_scan_width scan_width;
+ struct ieee80211_supported_band *sband =
+ rdev->wiphy.bands[setup->chandef.chan->band];
+ scan_width = cfg80211_chandef_to_scan_width(&setup->chandef);
+ setup->basic_rates = ieee80211_mandatory_rates(sband,
+ scan_width);
+ }
+
+ if (!cfg80211_reg_can_beacon(&rdev->wiphy, &setup->chandef,
+ NL80211_IFTYPE_MESH_POINT))
+ return -EINVAL;
+
+ err = rdev_join_mesh(rdev, dev, conf, setup);
+ if (!err) {
+ memcpy(wdev->ssid, setup->mesh_id, setup->mesh_id_len);
+ wdev->mesh_id_len = setup->mesh_id_len;
+ wdev->chandef = setup->chandef;
+ wdev->beacon_interval = setup->beacon_interval;
+ }
+
+ return err;
+}
+
+int cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct mesh_setup *setup,
+ const struct mesh_config *conf)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ int err;
+
+ wdev_lock(wdev);
+ err = __cfg80211_join_mesh(rdev, dev, setup, conf);
+ wdev_unlock(wdev);
+
+ return err;
+}
+
+int cfg80211_set_mesh_channel(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev,
+ struct cfg80211_chan_def *chandef)
+{
+ int err;
+
+ /*
+ * Workaround for libertas (only!), it puts the interface
+ * into mesh mode but doesn't implement join_mesh. Instead,
+ * it is configured via sysfs and then joins the mesh when
+ * you set the channel. Note that the libertas mesh isn't
+ * compatible with 802.11 mesh.
+ */
+ if (rdev->ops->libertas_set_mesh_channel) {
+ if (chandef->width != NL80211_CHAN_WIDTH_20_NOHT)
+ return -EINVAL;
+
+ if (!netif_running(wdev->netdev))
+ return -ENETDOWN;
+
+ err = rdev_libertas_set_mesh_channel(rdev, wdev->netdev,
+ chandef->chan);
+ if (!err)
+ wdev->chandef = *chandef;
+
+ return err;
+ }
+
+ if (wdev->mesh_id_len)
+ return -EBUSY;
+
+ wdev->preset_chandef = *chandef;
+ return 0;
+}
+
+int __cfg80211_leave_mesh(struct cfg80211_registered_device *rdev,
+ struct net_device *dev)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ int err;
+
+ ASSERT_WDEV_LOCK(wdev);
+
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
+ return -EOPNOTSUPP;
+
+ if (!rdev->ops->leave_mesh)
+ return -EOPNOTSUPP;
+
+ if (!wdev->mesh_id_len)
+ return -ENOTCONN;
+
+ err = rdev_leave_mesh(rdev, dev);
+ if (!err) {
+ wdev->mesh_id_len = 0;
+ wdev->beacon_interval = 0;
+ memset(&wdev->chandef, 0, sizeof(wdev->chandef));
+ rdev_set_qos_map(rdev, dev, NULL);
+ cfg80211_sched_dfs_chan_update(rdev);
+ }
+
+ return err;
+}
+
+int cfg80211_leave_mesh(struct cfg80211_registered_device *rdev,
+ struct net_device *dev)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ int err;
+
+ wdev_lock(wdev);
+ err = __cfg80211_leave_mesh(rdev, dev);
+ wdev_unlock(wdev);
+
+ return err;
+}
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
new file mode 100644
index 0000000..2141427
--- /dev/null
+++ b/net/wireless/mlme.c
@@ -0,0 +1,901 @@
+/*
+ * cfg80211 MLME SAP interface
+ *
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2015 Intel Deutschland GmbH
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/etherdevice.h>
+#include <linux/netdevice.h>
+#include <linux/nl80211.h>
+#include <linux/slab.h>
+#include <linux/wireless.h>
+#include <net/cfg80211.h>
+#include <net/iw_handler.h>
+#include "core.h"
+#include "nl80211.h"
+#include "rdev-ops.h"
+
+
+void cfg80211_rx_assoc_resp(struct net_device *dev, struct cfg80211_bss *bss,
+ const u8 *buf, size_t len, int uapsd_queues)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct wiphy *wiphy = wdev->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
+ struct cfg80211_connect_resp_params cr;
+
+ memset(&cr, 0, sizeof(cr));
+ cr.status = (int)le16_to_cpu(mgmt->u.assoc_resp.status_code);
+ cr.bssid = mgmt->bssid;
+ cr.bss = bss;
+ cr.resp_ie = mgmt->u.assoc_resp.variable;
+ cr.resp_ie_len =
+ len - offsetof(struct ieee80211_mgmt, u.assoc_resp.variable);
+ cr.timeout_reason = NL80211_TIMEOUT_UNSPECIFIED;
+
+ trace_cfg80211_send_rx_assoc(dev, bss);
+
+ /*
+ * This is a bit of a hack, we don't notify userspace of
+ * a (re-)association reply if we tried to send a reassoc
+ * and got a reject -- we only try again with an assoc
+ * frame instead of reassoc.
+ */
+ if (cfg80211_sme_rx_assoc_resp(wdev, cr.status)) {
+ cfg80211_unhold_bss(bss_from_pub(bss));
+ cfg80211_put_bss(wiphy, bss);
+ return;
+ }
+
+ nl80211_send_rx_assoc(rdev, dev, buf, len, GFP_KERNEL, uapsd_queues);
+ /* update current_bss etc., consumes the bss reference */
+ __cfg80211_connect_result(dev, &cr, cr.status == WLAN_STATUS_SUCCESS);
+}
+EXPORT_SYMBOL(cfg80211_rx_assoc_resp);
+
+static void cfg80211_process_auth(struct wireless_dev *wdev,
+ const u8 *buf, size_t len)
+{
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+
+ nl80211_send_rx_auth(rdev, wdev->netdev, buf, len, GFP_KERNEL);
+ cfg80211_sme_rx_auth(wdev, buf, len);
+}
+
+static void cfg80211_process_deauth(struct wireless_dev *wdev,
+ const u8 *buf, size_t len)
+{
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
+ const u8 *bssid = mgmt->bssid;
+ u16 reason_code = le16_to_cpu(mgmt->u.deauth.reason_code);
+ bool from_ap = !ether_addr_equal(mgmt->sa, wdev->netdev->dev_addr);
+
+ nl80211_send_deauth(rdev, wdev->netdev, buf, len, GFP_KERNEL);
+
+ if (!wdev->current_bss ||
+ !ether_addr_equal(wdev->current_bss->pub.bssid, bssid))
+ return;
+
+ __cfg80211_disconnected(wdev->netdev, NULL, 0, reason_code, from_ap);
+ cfg80211_sme_deauth(wdev);
+}
+
+static void cfg80211_process_disassoc(struct wireless_dev *wdev,
+ const u8 *buf, size_t len)
+{
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
+ const u8 *bssid = mgmt->bssid;
+ u16 reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code);
+ bool from_ap = !ether_addr_equal(mgmt->sa, wdev->netdev->dev_addr);
+
+ nl80211_send_disassoc(rdev, wdev->netdev, buf, len, GFP_KERNEL);
+
+ if (WARN_ON(!wdev->current_bss ||
+ !ether_addr_equal(wdev->current_bss->pub.bssid, bssid)))
+ return;
+
+ __cfg80211_disconnected(wdev->netdev, NULL, 0, reason_code, from_ap);
+ cfg80211_sme_disassoc(wdev);
+}
+
+void cfg80211_rx_mlme_mgmt(struct net_device *dev, const u8 *buf, size_t len)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct ieee80211_mgmt *mgmt = (void *)buf;
+
+ ASSERT_WDEV_LOCK(wdev);
+
+ trace_cfg80211_rx_mlme_mgmt(dev, buf, len);
+
+ if (WARN_ON(len < 2))
+ return;
+
+ if (ieee80211_is_auth(mgmt->frame_control))
+ cfg80211_process_auth(wdev, buf, len);
+ else if (ieee80211_is_deauth(mgmt->frame_control))
+ cfg80211_process_deauth(wdev, buf, len);
+ else if (ieee80211_is_disassoc(mgmt->frame_control))
+ cfg80211_process_disassoc(wdev, buf, len);
+}
+EXPORT_SYMBOL(cfg80211_rx_mlme_mgmt);
+
+void cfg80211_auth_timeout(struct net_device *dev, const u8 *addr)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct wiphy *wiphy = wdev->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+
+ trace_cfg80211_send_auth_timeout(dev, addr);
+
+ nl80211_send_auth_timeout(rdev, dev, addr, GFP_KERNEL);
+ cfg80211_sme_auth_timeout(wdev);
+}
+EXPORT_SYMBOL(cfg80211_auth_timeout);
+
+void cfg80211_assoc_timeout(struct net_device *dev, struct cfg80211_bss *bss)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct wiphy *wiphy = wdev->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+
+ trace_cfg80211_send_assoc_timeout(dev, bss->bssid);
+
+ nl80211_send_assoc_timeout(rdev, dev, bss->bssid, GFP_KERNEL);
+ cfg80211_sme_assoc_timeout(wdev);
+
+ cfg80211_unhold_bss(bss_from_pub(bss));
+ cfg80211_put_bss(wiphy, bss);
+}
+EXPORT_SYMBOL(cfg80211_assoc_timeout);
+
+void cfg80211_abandon_assoc(struct net_device *dev, struct cfg80211_bss *bss)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct wiphy *wiphy = wdev->wiphy;
+
+ cfg80211_sme_abandon_assoc(wdev);
+
+ cfg80211_unhold_bss(bss_from_pub(bss));
+ cfg80211_put_bss(wiphy, bss);
+}
+EXPORT_SYMBOL(cfg80211_abandon_assoc);
+
+void cfg80211_tx_mlme_mgmt(struct net_device *dev, const u8 *buf, size_t len)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct ieee80211_mgmt *mgmt = (void *)buf;
+
+ ASSERT_WDEV_LOCK(wdev);
+
+ trace_cfg80211_tx_mlme_mgmt(dev, buf, len);
+
+ if (WARN_ON(len < 2))
+ return;
+
+ if (ieee80211_is_deauth(mgmt->frame_control))
+ cfg80211_process_deauth(wdev, buf, len);
+ else
+ cfg80211_process_disassoc(wdev, buf, len);
+}
+EXPORT_SYMBOL(cfg80211_tx_mlme_mgmt);
+
+void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr,
+ enum nl80211_key_type key_type, int key_id,
+ const u8 *tsc, gfp_t gfp)
+{
+ struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+#ifdef CPTCFG_CFG80211_WEXT
+ union iwreq_data wrqu;
+ char *buf = kmalloc(128, gfp);
+
+ if (buf) {
+ sprintf(buf, "MLME-MICHAELMICFAILURE.indication("
+ "keyid=%d %scast addr=%pM)", key_id,
+ key_type == NL80211_KEYTYPE_GROUP ? "broad" : "uni",
+ addr);
+ memset(&wrqu, 0, sizeof(wrqu));
+ wrqu.data.length = strlen(buf);
+ wireless_send_event(dev, IWEVCUSTOM, &wrqu, buf);
+ kfree(buf);
+ }
+#endif
+
+ trace_cfg80211_michael_mic_failure(dev, addr, key_type, key_id, tsc);
+ nl80211_michael_mic_failure(rdev, dev, addr, key_type, key_id, tsc, gfp);
+}
+EXPORT_SYMBOL(cfg80211_michael_mic_failure);
+
+/* some MLME handling for userspace SME */
+int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct ieee80211_channel *chan,
+ enum nl80211_auth_type auth_type,
+ const u8 *bssid,
+ const u8 *ssid, int ssid_len,
+ const u8 *ie, int ie_len,
+ const u8 *key, int key_len, int key_idx,
+ const u8 *auth_data, int auth_data_len)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_auth_request req = {
+ .ie = ie,
+ .ie_len = ie_len,
+ .auth_data = auth_data,
+ .auth_data_len = auth_data_len,
+ .auth_type = auth_type,
+ .key = key,
+ .key_len = key_len,
+ .key_idx = key_idx,
+ };
+ int err;
+
+ ASSERT_WDEV_LOCK(wdev);
+
+ if (auth_type == NL80211_AUTHTYPE_SHARED_KEY)
+ if (!key || !key_len || key_idx < 0 || key_idx > 3)
+ return -EINVAL;
+
+ if (wdev->current_bss &&
+ ether_addr_equal(bssid, wdev->current_bss->pub.bssid))
+ return -EALREADY;
+
+ req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
+ IEEE80211_BSS_TYPE_ESS,
+ IEEE80211_PRIVACY_ANY);
+ if (!req.bss)
+ return -ENOENT;
+
+ err = rdev_auth(rdev, dev, &req);
+
+ cfg80211_put_bss(&rdev->wiphy, req.bss);
+ return err;
+}
+
+/* Do a logical ht_capa &= ht_capa_mask. */
+void cfg80211_oper_and_ht_capa(struct ieee80211_ht_cap *ht_capa,
+ const struct ieee80211_ht_cap *ht_capa_mask)
+{
+ int i;
+ u8 *p1, *p2;
+ if (!ht_capa_mask) {
+ memset(ht_capa, 0, sizeof(*ht_capa));
+ return;
+ }
+
+ p1 = (u8*)(ht_capa);
+ p2 = (u8*)(ht_capa_mask);
+ for (i = 0; i<sizeof(*ht_capa); i++)
+ p1[i] &= p2[i];
+}
+
+/* Do a logical ht_capa &= ht_capa_mask. */
+void cfg80211_oper_and_vht_capa(struct ieee80211_vht_cap *vht_capa,
+ const struct ieee80211_vht_cap *vht_capa_mask)
+{
+ int i;
+ u8 *p1, *p2;
+ if (!vht_capa_mask) {
+ memset(vht_capa, 0, sizeof(*vht_capa));
+ return;
+ }
+
+ p1 = (u8*)(vht_capa);
+ p2 = (u8*)(vht_capa_mask);
+ for (i = 0; i < sizeof(*vht_capa); i++)
+ p1[i] &= p2[i];
+}
+
+int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct ieee80211_channel *chan,
+ const u8 *bssid,
+ const u8 *ssid, int ssid_len,
+ struct cfg80211_assoc_request *req)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ int err;
+
+ ASSERT_WDEV_LOCK(wdev);
+
+ if (wdev->current_bss &&
+ (!req->prev_bssid || !ether_addr_equal(wdev->current_bss->pub.bssid,
+ req->prev_bssid)))
+ return -EALREADY;
+
+ cfg80211_oper_and_ht_capa(&req->ht_capa_mask,
+ rdev->wiphy.ht_capa_mod_mask);
+ cfg80211_oper_and_vht_capa(&req->vht_capa_mask,
+ rdev->wiphy.vht_capa_mod_mask);
+
+ req->bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
+ IEEE80211_BSS_TYPE_ESS,
+ IEEE80211_PRIVACY_ANY);
+ if (!req->bss)
+ return -ENOENT;
+
+ err = rdev_assoc(rdev, dev, req);
+ if (!err)
+ cfg80211_hold_bss(bss_from_pub(req->bss));
+ else
+ cfg80211_put_bss(&rdev->wiphy, req->bss);
+
+ return err;
+}
+
+int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, const u8 *bssid,
+ const u8 *ie, int ie_len, u16 reason,
+ bool local_state_change)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_deauth_request req = {
+ .bssid = bssid,
+ .reason_code = reason,
+ .ie = ie,
+ .ie_len = ie_len,
+ .local_state_change = local_state_change,
+ };
+
+ ASSERT_WDEV_LOCK(wdev);
+
+ if (local_state_change &&
+ (!wdev->current_bss ||
+ !ether_addr_equal(wdev->current_bss->pub.bssid, bssid)))
+ return 0;
+
+ if (ether_addr_equal(wdev->disconnect_bssid, bssid) ||
+ (wdev->current_bss &&
+ ether_addr_equal(wdev->current_bss->pub.bssid, bssid)))
+ wdev->conn_owner_nlportid = 0;
+
+ return rdev_deauth(rdev, dev, &req);
+}
+
+int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, const u8 *bssid,
+ const u8 *ie, int ie_len, u16 reason,
+ bool local_state_change)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_disassoc_request req = {
+ .reason_code = reason,
+ .local_state_change = local_state_change,
+ .ie = ie,
+ .ie_len = ie_len,
+ };
+ int err;
+
+ ASSERT_WDEV_LOCK(wdev);
+
+ if (!wdev->current_bss)
+ return -ENOTCONN;
+
+ if (ether_addr_equal(wdev->current_bss->pub.bssid, bssid))
+ req.bss = &wdev->current_bss->pub;
+ else
+ return -ENOTCONN;
+
+ err = rdev_disassoc(rdev, dev, &req);
+ if (err)
+ return err;
+
+ /* driver should have reported the disassoc */
+ WARN_ON(wdev->current_bss);
+ return 0;
+}
+
+void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,
+ struct net_device *dev)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ u8 bssid[ETH_ALEN];
+
+ ASSERT_WDEV_LOCK(wdev);
+
+ if (!rdev->ops->deauth)
+ return;
+
+ if (!wdev->current_bss)
+ return;
+
+ memcpy(bssid, wdev->current_bss->pub.bssid, ETH_ALEN);
+ cfg80211_mlme_deauth(rdev, dev, bssid, NULL, 0,
+ WLAN_REASON_DEAUTH_LEAVING, false);
+}
+
+struct cfg80211_mgmt_registration {
+ struct list_head list;
+ struct wireless_dev *wdev;
+
+ u32 nlportid;
+
+ int match_len;
+
+ __le16 frame_type;
+
+ u8 match[];
+};
+
+static void
+cfg80211_process_mlme_unregistrations(struct cfg80211_registered_device *rdev)
+{
+ struct cfg80211_mgmt_registration *reg;
+
+ ASSERT_RTNL();
+
+ spin_lock_bh(&rdev->mlme_unreg_lock);
+ while ((reg = list_first_entry_or_null(&rdev->mlme_unreg,
+ struct cfg80211_mgmt_registration,
+ list))) {
+ list_del(®->list);
+ spin_unlock_bh(&rdev->mlme_unreg_lock);
+
+ if (rdev->ops->mgmt_frame_register) {
+ u16 frame_type = le16_to_cpu(reg->frame_type);
+
+ rdev_mgmt_frame_register(rdev, reg->wdev,
+ frame_type, false);
+ }
+
+ kfree(reg);
+
+ spin_lock_bh(&rdev->mlme_unreg_lock);
+ }
+ spin_unlock_bh(&rdev->mlme_unreg_lock);
+}
+
+void cfg80211_mlme_unreg_wk(struct work_struct *wk)
+{
+ struct cfg80211_registered_device *rdev;
+
+ rdev = container_of(wk, struct cfg80211_registered_device,
+ mlme_unreg_wk);
+
+ rtnl_lock();
+ cfg80211_process_mlme_unregistrations(rdev);
+ rtnl_unlock();
+}
+
+int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_portid,
+ u16 frame_type, const u8 *match_data,
+ int match_len)
+{
+ struct wiphy *wiphy = wdev->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+ struct cfg80211_mgmt_registration *reg, *nreg;
+ int err = 0;
+ u16 mgmt_type;
+
+ if (!wdev->wiphy->mgmt_stypes)
+ return -EOPNOTSUPP;
+
+ if ((frame_type & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT)
+ return -EINVAL;
+
+ if (frame_type & ~(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE))
+ return -EINVAL;
+
+ mgmt_type = (frame_type & IEEE80211_FCTL_STYPE) >> 4;
+ if (!(wdev->wiphy->mgmt_stypes[wdev->iftype].rx & BIT(mgmt_type)))
+ return -EINVAL;
+
+ nreg = kzalloc(sizeof(*reg) + match_len, GFP_KERNEL);
+ if (!nreg)
+ return -ENOMEM;
+
+ spin_lock_bh(&wdev->mgmt_registrations_lock);
+
+ list_for_each_entry(reg, &wdev->mgmt_registrations, list) {
+ int mlen = min(match_len, reg->match_len);
+
+ if (frame_type != le16_to_cpu(reg->frame_type))
+ continue;
+
+ if (memcmp(reg->match, match_data, mlen) == 0) {
+ err = -EALREADY;
+ break;
+ }
+ }
+
+ if (err) {
+ kfree(nreg);
+ goto out;
+ }
+
+ memcpy(nreg->match, match_data, match_len);
+ nreg->match_len = match_len;
+ nreg->nlportid = snd_portid;
+ nreg->frame_type = cpu_to_le16(frame_type);
+ nreg->wdev = wdev;
+ list_add(&nreg->list, &wdev->mgmt_registrations);
+ spin_unlock_bh(&wdev->mgmt_registrations_lock);
+
+ /* process all unregistrations to avoid driver confusion */
+ cfg80211_process_mlme_unregistrations(rdev);
+
+ if (rdev->ops->mgmt_frame_register)
+ rdev_mgmt_frame_register(rdev, wdev, frame_type, true);
+
+ return 0;
+
+ out:
+ spin_unlock_bh(&wdev->mgmt_registrations_lock);
+
+ return err;
+}
+
+void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlportid)
+{
+ struct wiphy *wiphy = wdev->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+ struct cfg80211_mgmt_registration *reg, *tmp;
+
+ spin_lock_bh(&wdev->mgmt_registrations_lock);
+
+ list_for_each_entry_safe(reg, tmp, &wdev->mgmt_registrations, list) {
+ if (reg->nlportid != nlportid)
+ continue;
+
+ list_del(®->list);
+ spin_lock(&rdev->mlme_unreg_lock);
+ list_add_tail(®->list, &rdev->mlme_unreg);
+ spin_unlock(&rdev->mlme_unreg_lock);
+
+ schedule_work(&rdev->mlme_unreg_wk);
+ }
+
+ spin_unlock_bh(&wdev->mgmt_registrations_lock);
+
+ if (nlportid && rdev->crit_proto_nlportid == nlportid) {
+ rdev->crit_proto_nlportid = 0;
+ rdev_crit_proto_stop(rdev, wdev);
+ }
+
+ if (nlportid == wdev->ap_unexpected_nlportid)
+ wdev->ap_unexpected_nlportid = 0;
+}
+
+void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev)
+{
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+
+ spin_lock_bh(&wdev->mgmt_registrations_lock);
+ spin_lock(&rdev->mlme_unreg_lock);
+ list_splice_tail_init(&wdev->mgmt_registrations, &rdev->mlme_unreg);
+ spin_unlock(&rdev->mlme_unreg_lock);
+ spin_unlock_bh(&wdev->mgmt_registrations_lock);
+
+ cfg80211_process_mlme_unregistrations(rdev);
+}
+
+int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev,
+ struct cfg80211_mgmt_tx_params *params, u64 *cookie)
+{
+ const struct ieee80211_mgmt *mgmt;
+ u16 stype;
+
+ if (!wdev->wiphy->mgmt_stypes)
+ return -EOPNOTSUPP;
+
+ if (!rdev->ops->mgmt_tx)
+ return -EOPNOTSUPP;
+
+ if (params->len < 24 + 1)
+ return -EINVAL;
+
+ mgmt = (const struct ieee80211_mgmt *)params->buf;
+
+ if (!ieee80211_is_mgmt(mgmt->frame_control))
+ return -EINVAL;
+
+ stype = le16_to_cpu(mgmt->frame_control) & IEEE80211_FCTL_STYPE;
+ if (!(wdev->wiphy->mgmt_stypes[wdev->iftype].tx & BIT(stype >> 4)))
+ return -EINVAL;
+
+ if (ieee80211_is_action(mgmt->frame_control) &&
+ mgmt->u.action.category != WLAN_CATEGORY_PUBLIC) {
+ int err = 0;
+
+ wdev_lock(wdev);
+
+ switch (wdev->iftype) {
+ case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_P2P_CLIENT:
+ if (!wdev->current_bss) {
+ err = -ENOTCONN;
+ break;
+ }
+
+ if (!ether_addr_equal(wdev->current_bss->pub.bssid,
+ mgmt->bssid)) {
+ err = -ENOTCONN;
+ break;
+ }
+
+ /*
+ * check for IBSS DA must be done by driver as
+ * cfg80211 doesn't track the stations
+ */
+ if (wdev->iftype == NL80211_IFTYPE_ADHOC)
+ break;
+
+ /* for station, check that DA is the AP */
+ if (!ether_addr_equal(wdev->current_bss->pub.bssid,
+ mgmt->da)) {
+ err = -ENOTCONN;
+ break;
+ }
+ break;
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_P2P_GO:
+ case NL80211_IFTYPE_AP_VLAN:
+ if (!ether_addr_equal(mgmt->bssid, wdev_address(wdev)))
+ err = -EINVAL;
+ break;
+ case NL80211_IFTYPE_MESH_POINT:
+ if (!ether_addr_equal(mgmt->sa, mgmt->bssid)) {
+ err = -EINVAL;
+ break;
+ }
+ /*
+ * check for mesh DA must be done by driver as
+ * cfg80211 doesn't track the stations
+ */
+ break;
+ case NL80211_IFTYPE_P2P_DEVICE:
+ /*
+ * fall through, P2P device only supports
+ * public action frames
+ */
+ case NL80211_IFTYPE_NAN:
+ default:
+ err = -EOPNOTSUPP;
+ break;
+ }
+ wdev_unlock(wdev);
+
+ if (err)
+ return err;
+ }
+
+ if (!ether_addr_equal(mgmt->sa, wdev_address(wdev))) {
+ /* Allow random TA to be used with Public Action frames if the
+ * driver has indicated support for this. Otherwise, only allow
+ * the local address to be used.
+ */
+ if (!ieee80211_is_action(mgmt->frame_control) ||
+ mgmt->u.action.category != WLAN_CATEGORY_PUBLIC)
+ return -EINVAL;
+ if (!wdev->current_bss &&
+ !wiphy_ext_feature_isset(
+ &rdev->wiphy,
+ NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA))
+ return -EINVAL;
+ if (wdev->current_bss &&
+ !wiphy_ext_feature_isset(
+ &rdev->wiphy,
+ NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA_CONNECTED))
+ return -EINVAL;
+ }
+
+ /* Transmit the Action frame as requested by user space */
+ return rdev_mgmt_tx(rdev, wdev, params, cookie);
+}
+
+bool cfg80211_rx_mgmt(struct wireless_dev *wdev, int freq, int sig_mbm,
+ const u8 *buf, size_t len, u32 flags)
+{
+ struct wiphy *wiphy = wdev->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+ struct cfg80211_mgmt_registration *reg;
+ const struct ieee80211_txrx_stypes *stypes =
+ &wiphy->mgmt_stypes[wdev->iftype];
+ struct ieee80211_mgmt *mgmt = (void *)buf;
+ const u8 *data;
+ int data_len;
+ bool result = false;
+ __le16 ftype = mgmt->frame_control &
+ cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE);
+ u16 stype;
+
+ trace_cfg80211_rx_mgmt(wdev, freq, sig_mbm);
+ stype = (le16_to_cpu(mgmt->frame_control) & IEEE80211_FCTL_STYPE) >> 4;
+
+ if (!(stypes->rx & BIT(stype))) {
+ trace_cfg80211_return_bool(false);
+ return false;
+ }
+
+ data = buf + ieee80211_hdrlen(mgmt->frame_control);
+ data_len = len - ieee80211_hdrlen(mgmt->frame_control);
+
+ spin_lock_bh(&wdev->mgmt_registrations_lock);
+
+ list_for_each_entry(reg, &wdev->mgmt_registrations, list) {
+ if (reg->frame_type != ftype)
+ continue;
+
+ if (reg->match_len > data_len)
+ continue;
+
+ if (memcmp(reg->match, data, reg->match_len))
+ continue;
+
+ /* found match! */
+
+ /* Indicate the received Action frame to user space */
+ if (nl80211_send_mgmt(rdev, wdev, reg->nlportid,
+ freq, sig_mbm,
+ buf, len, flags, GFP_ATOMIC))
+ continue;
+
+ result = true;
+ break;
+ }
+
+ spin_unlock_bh(&wdev->mgmt_registrations_lock);
+
+ trace_cfg80211_return_bool(result);
+ return result;
+}
+EXPORT_SYMBOL(cfg80211_rx_mgmt);
+
+void cfg80211_sched_dfs_chan_update(struct cfg80211_registered_device *rdev)
+{
+ cancel_delayed_work(&rdev->dfs_update_channels_wk);
+ queue_delayed_work(cfg80211_wq, &rdev->dfs_update_channels_wk, 0);
+}
+
+void cfg80211_dfs_channels_update_work(struct work_struct *work)
+{
+ struct delayed_work *delayed_work = to_delayed_work(work);
+ struct cfg80211_registered_device *rdev;
+ struct cfg80211_chan_def chandef;
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_channel *c;
+ struct wiphy *wiphy;
+ bool check_again = false;
+ unsigned long timeout, next_time = 0;
+ unsigned long time_dfs_update;
+ enum nl80211_radar_event radar_event;
+ int bandid, i;
+
+ rdev = container_of(delayed_work, struct cfg80211_registered_device,
+ dfs_update_channels_wk);
+ wiphy = &rdev->wiphy;
+
+ rtnl_lock();
+ for (bandid = 0; bandid < NUM_NL80211_BANDS; bandid++) {
+ sband = wiphy->bands[bandid];
+ if (!sband)
+ continue;
+
+ for (i = 0; i < sband->n_channels; i++) {
+ c = &sband->channels[i];
+
+ if (!(c->flags & IEEE80211_CHAN_RADAR))
+ continue;
+
+ if (c->dfs_state != NL80211_DFS_UNAVAILABLE &&
+ c->dfs_state != NL80211_DFS_AVAILABLE)
+ continue;
+
+ if (c->dfs_state == NL80211_DFS_UNAVAILABLE) {
+ time_dfs_update = IEEE80211_DFS_MIN_NOP_TIME_MS;
+ radar_event = NL80211_RADAR_NOP_FINISHED;
+ } else {
+ if (regulatory_pre_cac_allowed(wiphy) ||
+ cfg80211_any_wiphy_oper_chan(wiphy, c))
+ continue;
+
+ time_dfs_update = REG_PRE_CAC_EXPIRY_GRACE_MS;
+ radar_event = NL80211_RADAR_PRE_CAC_EXPIRED;
+ }
+
+ timeout = c->dfs_state_entered +
+ msecs_to_jiffies(time_dfs_update);
+
+ if (time_after_eq(jiffies, timeout)) {
+ c->dfs_state = NL80211_DFS_USABLE;
+ c->dfs_state_entered = jiffies;
+
+ cfg80211_chandef_create(&chandef, c,
+ NL80211_CHAN_NO_HT);
+
+ nl80211_radar_notify(rdev, &chandef,
+ radar_event, NULL,
+ GFP_ATOMIC);
+
+ regulatory_propagate_dfs_state(wiphy, &chandef,
+ c->dfs_state,
+ radar_event);
+ continue;
+ }
+
+ if (!check_again)
+ next_time = timeout - jiffies;
+ else
+ next_time = min(next_time, timeout - jiffies);
+ check_again = true;
+ }
+ }
+ rtnl_unlock();
+
+ /* reschedule if there are other channels waiting to be cleared again */
+ if (check_again)
+ queue_delayed_work(cfg80211_wq, &rdev->dfs_update_channels_wk,
+ next_time);
+}
+
+
+void cfg80211_radar_event(struct wiphy *wiphy,
+ struct cfg80211_chan_def *chandef,
+ gfp_t gfp)
+{
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+
+ trace_cfg80211_radar_event(wiphy, chandef);
+
+ /* only set the chandef supplied channel to unavailable, in
+ * case the radar is detected on only one of multiple channels
+ * spanned by the chandef.
+ */
+ cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_UNAVAILABLE);
+
+ cfg80211_sched_dfs_chan_update(rdev);
+
+ nl80211_radar_notify(rdev, chandef, NL80211_RADAR_DETECTED, NULL, gfp);
+
+ memcpy(&rdev->radar_chandef, chandef, sizeof(struct cfg80211_chan_def));
+ queue_work(cfg80211_wq, &rdev->propagate_radar_detect_wk);
+}
+EXPORT_SYMBOL(cfg80211_radar_event);
+
+void cfg80211_cac_event(struct net_device *netdev,
+ const struct cfg80211_chan_def *chandef,
+ enum nl80211_radar_event event, gfp_t gfp)
+{
+ struct wireless_dev *wdev = netdev->ieee80211_ptr;
+ struct wiphy *wiphy = wdev->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+ unsigned long timeout;
+
+ trace_cfg80211_cac_event(netdev, event);
+
+ if (WARN_ON(!wdev->cac_started))
+ return;
+
+ if (WARN_ON(!wdev->chandef.chan))
+ return;
+
+ switch (event) {
+ case NL80211_RADAR_CAC_FINISHED:
+ timeout = wdev->cac_start_time +
+ msecs_to_jiffies(wdev->cac_time_ms);
+ WARN_ON(!time_after_eq(jiffies, timeout));
+ cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_AVAILABLE);
+ memcpy(&rdev->cac_done_chandef, chandef,
+ sizeof(struct cfg80211_chan_def));
+ queue_work(cfg80211_wq, &rdev->propagate_cac_done_wk);
+ cfg80211_sched_dfs_chan_update(rdev);
+ break;
+ case NL80211_RADAR_CAC_ABORTED:
+ break;
+ default:
+ WARN_ON(1);
+ return;
+ }
+ wdev->cac_started = false;
+
+ nl80211_radar_notify(rdev, chandef, event, netdev, gfp);
+}
+EXPORT_SYMBOL(cfg80211_cac_event);
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
new file mode 100644
index 0000000..b2fdebf
--- /dev/null
+++ b/net/wireless/nl80211.c
@@ -0,0 +1,15373 @@
+/*
+ * This is the new netlink-based wireless configuration interface.
+ *
+ * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2013-2014 Intel Mobile Communications GmbH
+ * Copyright 2015-2017 Intel Deutschland GmbH
+ */
+
+#include <linux/if.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/if_ether.h>
+#include <linux/ieee80211.h>
+#include <linux/nl80211.h>
+#include <linux/rtnetlink.h>
+#include <linux/netlink.h>
+#include <linux/etherdevice.h>
+#include <net/net_namespace.h>
+#include <net/genetlink.h>
+#include <net/cfg80211.h>
+#include <net/sock.h>
+#include <net/inet_connection_sock.h>
+#include "core.h"
+#include "nl80211.h"
+#include "reg.h"
+#include "rdev-ops.h"
+
+static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev,
+ struct genl_info *info,
+ struct cfg80211_crypto_settings *settings,
+ int cipher_limit);
+
+/* the netlink family */
+static struct genl_family nl80211_fam;
+
+/* multicast groups */
+enum nl80211_multicast_groups {
+ NL80211_MCGRP_CONFIG,
+ NL80211_MCGRP_SCAN,
+ NL80211_MCGRP_REGULATORY,
+ NL80211_MCGRP_MLME,
+ NL80211_MCGRP_VENDOR,
+ NL80211_MCGRP_NAN,
+ NL80211_MCGRP_TESTMODE /* keep last - ifdef! */
+};
+
+static __genl_const struct genl_multicast_group nl80211_mcgrps[] = {
+ [NL80211_MCGRP_CONFIG] = { .name = NL80211_MULTICAST_GROUP_CONFIG },
+ [NL80211_MCGRP_SCAN] = { .name = NL80211_MULTICAST_GROUP_SCAN },
+ [NL80211_MCGRP_REGULATORY] = { .name = NL80211_MULTICAST_GROUP_REG },
+ [NL80211_MCGRP_MLME] = { .name = NL80211_MULTICAST_GROUP_MLME },
+ [NL80211_MCGRP_VENDOR] = { .name = NL80211_MULTICAST_GROUP_VENDOR },
+ [NL80211_MCGRP_NAN] = { .name = NL80211_MULTICAST_GROUP_NAN },
+#ifdef CPTCFG_NL80211_TESTMODE
+ [NL80211_MCGRP_TESTMODE] = { .name = NL80211_MULTICAST_GROUP_TESTMODE }
+#endif
+};
+
+/* returns ERR_PTR values */
+static struct wireless_dev *
+__cfg80211_wdev_from_attrs(struct net *netns, struct nlattr **attrs)
+{
+ struct cfg80211_registered_device *rdev;
+ struct wireless_dev *result = NULL;
+ bool have_ifidx = attrs[NL80211_ATTR_IFINDEX];
+ bool have_wdev_id = attrs[NL80211_ATTR_WDEV];
+ u64 wdev_id;
+ int wiphy_idx = -1;
+ int ifidx = -1;
+
+ ASSERT_RTNL();
+
+ if (!have_ifidx && !have_wdev_id)
+ return ERR_PTR(-EINVAL);
+
+ if (have_ifidx)
+ ifidx = nla_get_u32(attrs[NL80211_ATTR_IFINDEX]);
+ if (have_wdev_id) {
+ wdev_id = nla_get_u64(attrs[NL80211_ATTR_WDEV]);
+ wiphy_idx = wdev_id >> 32;
+ }
+
+ list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
+ struct wireless_dev *wdev;
+
+ if (wiphy_net(&rdev->wiphy) != netns)
+ continue;
+
+ if (have_wdev_id && rdev->wiphy_idx != wiphy_idx)
+ continue;
+
+ list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
+ if (have_ifidx && wdev->netdev &&
+ wdev->netdev->ifindex == ifidx) {
+ result = wdev;
+ break;
+ }
+ if (have_wdev_id && wdev->identifier == (u32)wdev_id) {
+ result = wdev;
+ break;
+ }
+ }
+
+ if (result)
+ break;
+ }
+
+ if (result)
+ return result;
+ return ERR_PTR(-ENODEV);
+}
+
+static struct cfg80211_registered_device *
+__cfg80211_rdev_from_attrs(struct net *netns, struct nlattr **attrs)
+{
+ struct cfg80211_registered_device *rdev = NULL, *tmp;
+ struct net_device *netdev;
+
+ ASSERT_RTNL();
+
+ if (!attrs[NL80211_ATTR_WIPHY] &&
+ !attrs[NL80211_ATTR_IFINDEX] &&
+ !attrs[NL80211_ATTR_WDEV])
+ return ERR_PTR(-EINVAL);
+
+ if (attrs[NL80211_ATTR_WIPHY])
+ rdev = cfg80211_rdev_by_wiphy_idx(
+ nla_get_u32(attrs[NL80211_ATTR_WIPHY]));
+
+ if (attrs[NL80211_ATTR_WDEV]) {
+ u64 wdev_id = nla_get_u64(attrs[NL80211_ATTR_WDEV]);
+ struct wireless_dev *wdev;
+ bool found = false;
+
+ tmp = cfg80211_rdev_by_wiphy_idx(wdev_id >> 32);
+ if (tmp) {
+ /* make sure wdev exists */
+ list_for_each_entry(wdev, &tmp->wiphy.wdev_list, list) {
+ if (wdev->identifier != (u32)wdev_id)
+ continue;
+ found = true;
+ break;
+ }
+
+ if (!found)
+ tmp = NULL;
+
+ if (rdev && tmp != rdev)
+ return ERR_PTR(-EINVAL);
+ rdev = tmp;
+ }
+ }
+
+ if (attrs[NL80211_ATTR_IFINDEX]) {
+ int ifindex = nla_get_u32(attrs[NL80211_ATTR_IFINDEX]);
+
+ netdev = __dev_get_by_index(netns, ifindex);
+ if (netdev) {
+ if (netdev->ieee80211_ptr)
+ tmp = wiphy_to_rdev(
+ netdev->ieee80211_ptr->wiphy);
+ else
+ tmp = NULL;
+
+ /* not wireless device -- return error */
+ if (!tmp)
+ return ERR_PTR(-EINVAL);
+
+ /* mismatch -- return error */
+ if (rdev && tmp != rdev)
+ return ERR_PTR(-EINVAL);
+
+ rdev = tmp;
+ }
+ }
+
+ if (!rdev)
+ return ERR_PTR(-ENODEV);
+
+ if (netns != wiphy_net(&rdev->wiphy))
+ return ERR_PTR(-ENODEV);
+
+ return rdev;
+}
+
+/*
+ * This function returns a pointer to the driver
+ * that the genl_info item that is passed refers to.
+ *
+ * The result of this can be a PTR_ERR and hence must
+ * be checked with IS_ERR() for errors.
+ */
+static struct cfg80211_registered_device *
+cfg80211_get_dev_from_info(struct net *netns, struct genl_info *info)
+{
+ return __cfg80211_rdev_from_attrs(netns, info->attrs);
+}
+
+/* policy for the attributes */
+static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
+ [NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
+ [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING,
+ .len = 20-1 },
+ [NL80211_ATTR_WIPHY_TXQ_PARAMS] = { .type = NLA_NESTED },
+
+ [NL80211_ATTR_WIPHY_FREQ] = { .type = NLA_U32 },
+ [NL80211_ATTR_WIPHY_CHANNEL_TYPE] = { .type = NLA_U32 },
+ [NL80211_ATTR_CHANNEL_WIDTH] = { .type = NLA_U32 },
+ [NL80211_ATTR_CENTER_FREQ1] = { .type = NLA_U32 },
+ [NL80211_ATTR_CENTER_FREQ2] = { .type = NLA_U32 },
+
+ [NL80211_ATTR_WIPHY_RETRY_SHORT] = { .type = NLA_U8 },
+ [NL80211_ATTR_WIPHY_RETRY_LONG] = { .type = NLA_U8 },
+ [NL80211_ATTR_WIPHY_FRAG_THRESHOLD] = { .type = NLA_U32 },
+ [NL80211_ATTR_WIPHY_RTS_THRESHOLD] = { .type = NLA_U32 },
+ [NL80211_ATTR_WIPHY_COVERAGE_CLASS] = { .type = NLA_U8 },
+ [NL80211_ATTR_WIPHY_DYN_ACK] = { .type = NLA_FLAG },
+
+ [NL80211_ATTR_IFTYPE] = { .type = NLA_U32 },
+ [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 },
+ [NL80211_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 },
+
+ [NL80211_ATTR_MAC] = { .len = ETH_ALEN },
+ [NL80211_ATTR_PREV_BSSID] = { .len = ETH_ALEN },
+
+ [NL80211_ATTR_KEY] = { .type = NLA_NESTED, },
+ [NL80211_ATTR_KEY_DATA] = { .type = NLA_BINARY,
+ .len = WLAN_MAX_KEY_LEN },
+ [NL80211_ATTR_KEY_IDX] = { .type = NLA_U8 },
+ [NL80211_ATTR_KEY_CIPHER] = { .type = NLA_U32 },
+ [NL80211_ATTR_KEY_DEFAULT] = { .type = NLA_FLAG },
+ [NL80211_ATTR_KEY_SEQ] = { .type = NLA_BINARY, .len = 16 },
+ [NL80211_ATTR_KEY_TYPE] = { .type = NLA_U32 },
+
+ [NL80211_ATTR_BEACON_INTERVAL] = { .type = NLA_U32 },
+ [NL80211_ATTR_DTIM_PERIOD] = { .type = NLA_U32 },
+ [NL80211_ATTR_BEACON_HEAD] = { .type = NLA_BINARY,
+ .len = IEEE80211_MAX_DATA_LEN },
+ [NL80211_ATTR_BEACON_TAIL] = { .type = NLA_BINARY,
+ .len = IEEE80211_MAX_DATA_LEN },
+ [NL80211_ATTR_STA_AID] = { .type = NLA_U16 },
+ [NL80211_ATTR_STA_FLAGS] = { .type = NLA_NESTED },
+ [NL80211_ATTR_STA_LISTEN_INTERVAL] = { .type = NLA_U16 },
+ [NL80211_ATTR_STA_SUPPORTED_RATES] = { .type = NLA_BINARY,
+ .len = NL80211_MAX_SUPP_RATES },
+ [NL80211_ATTR_STA_PLINK_ACTION] = { .type = NLA_U8 },
+ [NL80211_ATTR_STA_VLAN] = { .type = NLA_U32 },
+ [NL80211_ATTR_MNTR_FLAGS] = { /* NLA_NESTED can't be empty */ },
+ [NL80211_ATTR_MESH_ID] = { .type = NLA_BINARY,
+ .len = IEEE80211_MAX_MESH_ID_LEN },
+ [NL80211_ATTR_MPATH_NEXT_HOP] = { .type = NLA_U32 },
+
+ [NL80211_ATTR_REG_ALPHA2] = { .type = NLA_STRING, .len = 2 },
+ [NL80211_ATTR_REG_RULES] = { .type = NLA_NESTED },
+
+ [NL80211_ATTR_BSS_CTS_PROT] = { .type = NLA_U8 },
+ [NL80211_ATTR_BSS_SHORT_PREAMBLE] = { .type = NLA_U8 },
+ [NL80211_ATTR_BSS_SHORT_SLOT_TIME] = { .type = NLA_U8 },
+ [NL80211_ATTR_BSS_BASIC_RATES] = { .type = NLA_BINARY,
+ .len = NL80211_MAX_SUPP_RATES },
+ [NL80211_ATTR_BSS_HT_OPMODE] = { .type = NLA_U16 },
+
+ [NL80211_ATTR_MESH_CONFIG] = { .type = NLA_NESTED },
+ [NL80211_ATTR_SUPPORT_MESH_AUTH] = { .type = NLA_FLAG },
+
+ [NL80211_ATTR_HT_CAPABILITY] = { .len = NL80211_HT_CAPABILITY_LEN },
+
+ [NL80211_ATTR_MGMT_SUBTYPE] = { .type = NLA_U8 },
+ [NL80211_ATTR_IE] = { .type = NLA_BINARY,
+ .len = IEEE80211_MAX_DATA_LEN },
+ [NL80211_ATTR_SCAN_FREQUENCIES] = { .type = NLA_NESTED },
+ [NL80211_ATTR_SCAN_SSIDS] = { .type = NLA_NESTED },
+
+ [NL80211_ATTR_SSID] = { .type = NLA_BINARY,
+ .len = IEEE80211_MAX_SSID_LEN },
+ [NL80211_ATTR_AUTH_TYPE] = { .type = NLA_U32 },
+ [NL80211_ATTR_REASON_CODE] = { .type = NLA_U16 },
+ [NL80211_ATTR_FREQ_FIXED] = { .type = NLA_FLAG },
+ [NL80211_ATTR_TIMED_OUT] = { .type = NLA_FLAG },
+ [NL80211_ATTR_USE_MFP] = { .type = NLA_U32 },
+ [NL80211_ATTR_STA_FLAGS2] = {
+ .len = sizeof(struct nl80211_sta_flag_update),
+ },
+ [NL80211_ATTR_CONTROL_PORT] = { .type = NLA_FLAG },
+ [NL80211_ATTR_CONTROL_PORT_ETHERTYPE] = { .type = NLA_U16 },
+ [NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT] = { .type = NLA_FLAG },
+ [NL80211_ATTR_PRIVACY] = { .type = NLA_FLAG },
+ [NL80211_ATTR_CIPHER_SUITE_GROUP] = { .type = NLA_U32 },
+ [NL80211_ATTR_WPA_VERSIONS] = { .type = NLA_U32 },
+ [NL80211_ATTR_PID] = { .type = NLA_U32 },
+ [NL80211_ATTR_4ADDR] = { .type = NLA_U8 },
+ [NL80211_ATTR_PMKID] = { .type = NLA_BINARY,
+ .len = WLAN_PMKID_LEN },
+ [NL80211_ATTR_DURATION] = { .type = NLA_U32 },
+ [NL80211_ATTR_COOKIE] = { .type = NLA_U64 },
+ [NL80211_ATTR_TX_RATES] = { .type = NLA_NESTED },
+ [NL80211_ATTR_FRAME] = { .type = NLA_BINARY,
+ .len = IEEE80211_MAX_DATA_LEN },
+ [NL80211_ATTR_FRAME_MATCH] = { .type = NLA_BINARY, },
+ [NL80211_ATTR_PS_STATE] = { .type = NLA_U32 },
+ [NL80211_ATTR_CQM] = { .type = NLA_NESTED, },
+ [NL80211_ATTR_LOCAL_STATE_CHANGE] = { .type = NLA_FLAG },
+ [NL80211_ATTR_AP_ISOLATE] = { .type = NLA_U8 },
+ [NL80211_ATTR_WIPHY_TX_POWER_SETTING] = { .type = NLA_U32 },
+ [NL80211_ATTR_WIPHY_TX_POWER_LEVEL] = { .type = NLA_U32 },
+ [NL80211_ATTR_FRAME_TYPE] = { .type = NLA_U16 },
+ [NL80211_ATTR_WIPHY_ANTENNA_TX] = { .type = NLA_U32 },
+ [NL80211_ATTR_WIPHY_ANTENNA_RX] = { .type = NLA_U32 },
+ [NL80211_ATTR_MCAST_RATE] = { .type = NLA_U32 },
+ [NL80211_ATTR_OFFCHANNEL_TX_OK] = { .type = NLA_FLAG },
+ [NL80211_ATTR_KEY_DEFAULT_TYPES] = { .type = NLA_NESTED },
+ [NL80211_ATTR_WOWLAN_TRIGGERS] = { .type = NLA_NESTED },
+ [NL80211_ATTR_STA_PLINK_STATE] = { .type = NLA_U8 },
+ [NL80211_ATTR_SCHED_SCAN_INTERVAL] = { .type = NLA_U32 },
+ [NL80211_ATTR_REKEY_DATA] = { .type = NLA_NESTED },
+ [NL80211_ATTR_SCAN_SUPP_RATES] = { .type = NLA_NESTED },
+ [NL80211_ATTR_HIDDEN_SSID] = { .type = NLA_U32 },
+ [NL80211_ATTR_IE_PROBE_RESP] = { .type = NLA_BINARY,
+ .len = IEEE80211_MAX_DATA_LEN },
+ [NL80211_ATTR_IE_ASSOC_RESP] = { .type = NLA_BINARY,
+ .len = IEEE80211_MAX_DATA_LEN },
+ [NL80211_ATTR_ROAM_SUPPORT] = { .type = NLA_FLAG },
+ [NL80211_ATTR_SCHED_SCAN_MATCH] = { .type = NLA_NESTED },
+ [NL80211_ATTR_TX_NO_CCK_RATE] = { .type = NLA_FLAG },
+ [NL80211_ATTR_TDLS_ACTION] = { .type = NLA_U8 },
+ [NL80211_ATTR_TDLS_DIALOG_TOKEN] = { .type = NLA_U8 },
+ [NL80211_ATTR_TDLS_OPERATION] = { .type = NLA_U8 },
+ [NL80211_ATTR_TDLS_SUPPORT] = { .type = NLA_FLAG },
+ [NL80211_ATTR_TDLS_EXTERNAL_SETUP] = { .type = NLA_FLAG },
+ [NL80211_ATTR_TDLS_INITIATOR] = { .type = NLA_FLAG },
+ [NL80211_ATTR_DONT_WAIT_FOR_ACK] = { .type = NLA_FLAG },
+ [NL80211_ATTR_PROBE_RESP] = { .type = NLA_BINARY,
+ .len = IEEE80211_MAX_DATA_LEN },
+ [NL80211_ATTR_DFS_REGION] = { .type = NLA_U8 },
+ [NL80211_ATTR_DISABLE_HT] = { .type = NLA_FLAG },
+ [NL80211_ATTR_HT_CAPABILITY_MASK] = {
+ .len = NL80211_HT_CAPABILITY_LEN
+ },
+ [NL80211_ATTR_NOACK_MAP] = { .type = NLA_U16 },
+ [NL80211_ATTR_INACTIVITY_TIMEOUT] = { .type = NLA_U16 },
+ [NL80211_ATTR_BG_SCAN_PERIOD] = { .type = NLA_U16 },
+ [NL80211_ATTR_WDEV] = { .type = NLA_U64 },
+ [NL80211_ATTR_USER_REG_HINT_TYPE] = { .type = NLA_U32 },
+ [NL80211_ATTR_AUTH_DATA] = { .type = NLA_BINARY, },
+ [NL80211_ATTR_VHT_CAPABILITY] = { .len = NL80211_VHT_CAPABILITY_LEN },
+ [NL80211_ATTR_SCAN_FLAGS] = { .type = NLA_U32 },
+ [NL80211_ATTR_P2P_CTWINDOW] = { .type = NLA_U8 },
+ [NL80211_ATTR_P2P_OPPPS] = { .type = NLA_U8 },
+ [NL80211_ATTR_ACL_POLICY] = {. type = NLA_U32 },
+ [NL80211_ATTR_MAC_ADDRS] = { .type = NLA_NESTED },
+ [NL80211_ATTR_STA_CAPABILITY] = { .type = NLA_U16 },
+ [NL80211_ATTR_STA_EXT_CAPABILITY] = { .type = NLA_BINARY, },
+ [NL80211_ATTR_SPLIT_WIPHY_DUMP] = { .type = NLA_FLAG, },
+ [NL80211_ATTR_DISABLE_VHT] = { .type = NLA_FLAG },
+ [NL80211_ATTR_VHT_CAPABILITY_MASK] = {
+ .len = NL80211_VHT_CAPABILITY_LEN,
+ },
+ [NL80211_ATTR_MDID] = { .type = NLA_U16 },
+ [NL80211_ATTR_IE_RIC] = { .type = NLA_BINARY,
+ .len = IEEE80211_MAX_DATA_LEN },
+ [NL80211_ATTR_PEER_AID] = { .type = NLA_U16 },
+ [NL80211_ATTR_CH_SWITCH_COUNT] = { .type = NLA_U32 },
+ [NL80211_ATTR_CH_SWITCH_BLOCK_TX] = { .type = NLA_FLAG },
+ [NL80211_ATTR_CSA_IES] = { .type = NLA_NESTED },
+ [NL80211_ATTR_CSA_C_OFF_BEACON] = { .type = NLA_BINARY },
+ [NL80211_ATTR_CSA_C_OFF_PRESP] = { .type = NLA_BINARY },
+ [NL80211_ATTR_STA_SUPPORTED_CHANNELS] = { .type = NLA_BINARY },
+ [NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES] = { .type = NLA_BINARY },
+ [NL80211_ATTR_HANDLE_DFS] = { .type = NLA_FLAG },
+ [NL80211_ATTR_OPMODE_NOTIF] = { .type = NLA_U8 },
+ [NL80211_ATTR_VENDOR_ID] = { .type = NLA_U32 },
+ [NL80211_ATTR_VENDOR_SUBCMD] = { .type = NLA_U32 },
+ [NL80211_ATTR_VENDOR_DATA] = { .type = NLA_BINARY },
+ [NL80211_ATTR_QOS_MAP] = { .type = NLA_BINARY,
+ .len = IEEE80211_QOS_MAP_LEN_MAX },
+ [NL80211_ATTR_MAC_HINT] = { .len = ETH_ALEN },
+ [NL80211_ATTR_WIPHY_FREQ_HINT] = { .type = NLA_U32 },
+ [NL80211_ATTR_TDLS_PEER_CAPABILITY] = { .type = NLA_U32 },
+ [NL80211_ATTR_SOCKET_OWNER] = { .type = NLA_FLAG },
+ [NL80211_ATTR_CSA_C_OFFSETS_TX] = { .type = NLA_BINARY },
+ [NL80211_ATTR_USE_RRM] = { .type = NLA_FLAG },
+ [NL80211_ATTR_TSID] = { .type = NLA_U8 },
+ [NL80211_ATTR_USER_PRIO] = { .type = NLA_U8 },
+ [NL80211_ATTR_ADMITTED_TIME] = { .type = NLA_U16 },
+ [NL80211_ATTR_SMPS_MODE] = { .type = NLA_U8 },
+ [NL80211_ATTR_MAC_MASK] = { .len = ETH_ALEN },
+ [NL80211_ATTR_WIPHY_SELF_MANAGED_REG] = { .type = NLA_FLAG },
+ [NL80211_ATTR_NETNS_FD] = { .type = NLA_U32 },
+ [NL80211_ATTR_SCHED_SCAN_DELAY] = { .type = NLA_U32 },
+ [NL80211_ATTR_REG_INDOOR] = { .type = NLA_FLAG },
+ [NL80211_ATTR_PBSS] = { .type = NLA_FLAG },
+ [NL80211_ATTR_BSS_SELECT] = { .type = NLA_NESTED },
+ [NL80211_ATTR_STA_SUPPORT_P2P_PS] = { .type = NLA_U8 },
+ [NL80211_ATTR_MU_MIMO_GROUP_DATA] = {
+ .len = VHT_MUMIMO_GROUPS_DATA_LEN
+ },
+ [NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR] = { .len = ETH_ALEN },
+ [NL80211_ATTR_NAN_MASTER_PREF] = { .type = NLA_U8 },
+ [NL80211_ATTR_BANDS] = { .type = NLA_U32 },
+ [NL80211_ATTR_NAN_FUNC] = { .type = NLA_NESTED },
+ [NL80211_ATTR_FILS_KEK] = { .type = NLA_BINARY,
+ .len = FILS_MAX_KEK_LEN },
+ [NL80211_ATTR_FILS_NONCES] = { .len = 2 * FILS_NONCE_LEN },
+ [NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED] = { .type = NLA_FLAG, },
+ [NL80211_ATTR_BSSID] = { .len = ETH_ALEN },
+ [NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI] = { .type = NLA_S8 },
+ [NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST] = {
+ .len = sizeof(struct nl80211_bss_select_rssi_adjust)
+ },
+ [NL80211_ATTR_TIMEOUT_REASON] = { .type = NLA_U32 },
+ [NL80211_ATTR_FILS_ERP_USERNAME] = { .type = NLA_BINARY,
+ .len = FILS_ERP_MAX_USERNAME_LEN },
+ [NL80211_ATTR_FILS_ERP_REALM] = { .type = NLA_BINARY,
+ .len = FILS_ERP_MAX_REALM_LEN },
+ [NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM] = { .type = NLA_U16 },
+ [NL80211_ATTR_FILS_ERP_RRK] = { .type = NLA_BINARY,
+ .len = FILS_ERP_MAX_RRK_LEN },
+ [NL80211_ATTR_FILS_CACHE_ID] = { .len = 2 },
+ [NL80211_ATTR_PMK] = { .type = NLA_BINARY, .len = PMK_MAX_LEN },
+ [NL80211_ATTR_SCHED_SCAN_MULTI] = { .type = NLA_FLAG },
+ [NL80211_ATTR_KLV_TYPE] = { .type = NLA_U32 },
+ [NL80211_ATTR_KLV_INTVL] = { .type = NLA_U32 },
+ [NL80211_ATTR_KLV_INDEX] = { .type = NLA_U32 },
+ [NL80211_ATTR_KLV_TRIG] = { .type = NLA_U32 },
+ [NL80211_ATTR_KLV_PAYLOAD] = { .type = NLA_BINARY,
+ .len = CFG80211_KEEP_ALIVE_PAYLOAD_MAX_LENGTH },
+};
+
+/* policy for the key attributes */
+static const struct nla_policy nl80211_key_policy[NL80211_KEY_MAX + 1] = {
+ [NL80211_KEY_DATA] = { .type = NLA_BINARY, .len = WLAN_MAX_KEY_LEN },
+ [NL80211_KEY_IDX] = { .type = NLA_U8 },
+ [NL80211_KEY_CIPHER] = { .type = NLA_U32 },
+ [NL80211_KEY_SEQ] = { .type = NLA_BINARY, .len = 16 },
+ [NL80211_KEY_DEFAULT] = { .type = NLA_FLAG },
+ [NL80211_KEY_DEFAULT_MGMT] = { .type = NLA_FLAG },
+ [NL80211_KEY_TYPE] = { .type = NLA_U32 },
+ [NL80211_KEY_DEFAULT_TYPES] = { .type = NLA_NESTED },
+};
+
+/* policy for the key default flags */
+static const struct nla_policy
+nl80211_key_default_policy[NUM_NL80211_KEY_DEFAULT_TYPES] = {
+ [NL80211_KEY_DEFAULT_TYPE_UNICAST] = { .type = NLA_FLAG },
+ [NL80211_KEY_DEFAULT_TYPE_MULTICAST] = { .type = NLA_FLAG },
+};
+
+#ifdef CONFIG_PM
+/* policy for WoWLAN attributes */
+static const struct nla_policy
+nl80211_wowlan_policy[NUM_NL80211_WOWLAN_TRIG] = {
+ [NL80211_WOWLAN_TRIG_ANY] = { .type = NLA_FLAG },
+ [NL80211_WOWLAN_TRIG_DISCONNECT] = { .type = NLA_FLAG },
+ [NL80211_WOWLAN_TRIG_MAGIC_PKT] = { .type = NLA_FLAG },
+ [NL80211_WOWLAN_TRIG_PKT_PATTERN] = { .type = NLA_NESTED },
+ [NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE] = { .type = NLA_FLAG },
+ [NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST] = { .type = NLA_FLAG },
+ [NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE] = { .type = NLA_FLAG },
+ [NL80211_WOWLAN_TRIG_RFKILL_RELEASE] = { .type = NLA_FLAG },
+ [NL80211_WOWLAN_TRIG_TCP_CONNECTION] = { .type = NLA_NESTED },
+ [NL80211_WOWLAN_TRIG_NET_DETECT] = { .type = NLA_NESTED },
+};
+
+static const struct nla_policy
+nl80211_wowlan_tcp_policy[NUM_NL80211_WOWLAN_TCP] = {
+ [NL80211_WOWLAN_TCP_SRC_IPV4] = { .type = NLA_U32 },
+ [NL80211_WOWLAN_TCP_DST_IPV4] = { .type = NLA_U32 },
+ [NL80211_WOWLAN_TCP_DST_MAC] = { .len = ETH_ALEN },
+ [NL80211_WOWLAN_TCP_SRC_PORT] = { .type = NLA_U16 },
+ [NL80211_WOWLAN_TCP_DST_PORT] = { .type = NLA_U16 },
+ [NL80211_WOWLAN_TCP_DATA_PAYLOAD] = { .len = 1 },
+ [NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ] = {
+ .len = sizeof(struct nl80211_wowlan_tcp_data_seq)
+ },
+ [NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN] = {
+ .len = sizeof(struct nl80211_wowlan_tcp_data_token)
+ },
+ [NL80211_WOWLAN_TCP_DATA_INTERVAL] = { .type = NLA_U32 },
+ [NL80211_WOWLAN_TCP_WAKE_PAYLOAD] = { .len = 1 },
+ [NL80211_WOWLAN_TCP_WAKE_MASK] = { .len = 1 },
+};
+#endif /* CONFIG_PM */
+
+/* policy for coalesce rule attributes */
+static const struct nla_policy
+nl80211_coalesce_policy[NUM_NL80211_ATTR_COALESCE_RULE] = {
+ [NL80211_ATTR_COALESCE_RULE_DELAY] = { .type = NLA_U32 },
+ [NL80211_ATTR_COALESCE_RULE_CONDITION] = { .type = NLA_U32 },
+ [NL80211_ATTR_COALESCE_RULE_PKT_PATTERN] = { .type = NLA_NESTED },
+};
+
+/* policy for GTK rekey offload attributes */
+static const struct nla_policy
+nl80211_rekey_policy[NUM_NL80211_REKEY_DATA] = {
+ [NL80211_REKEY_DATA_KEK] = { .len = NL80211_KEK_LEN },
+ [NL80211_REKEY_DATA_KCK] = { .len = NL80211_KCK_LEN },
+ [NL80211_REKEY_DATA_REPLAY_CTR] = { .len = NL80211_REPLAY_CTR_LEN },
+};
+
+static const struct nla_policy
+nl80211_match_policy[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1] = {
+ [NL80211_SCHED_SCAN_MATCH_ATTR_SSID] = { .type = NLA_BINARY,
+ .len = IEEE80211_MAX_SSID_LEN },
+ [NL80211_SCHED_SCAN_MATCH_ATTR_BSSID] = { .len = ETH_ALEN },
+ [NL80211_SCHED_SCAN_MATCH_ATTR_RSSI] = { .type = NLA_U32 },
+};
+
+static const struct nla_policy
+nl80211_plan_policy[NL80211_SCHED_SCAN_PLAN_MAX + 1] = {
+ [NL80211_SCHED_SCAN_PLAN_INTERVAL] = { .type = NLA_U32 },
+ [NL80211_SCHED_SCAN_PLAN_ITERATIONS] = { .type = NLA_U32 },
+};
+
+static const struct nla_policy
+nl80211_bss_select_policy[NL80211_BSS_SELECT_ATTR_MAX + 1] = {
+ [NL80211_BSS_SELECT_ATTR_RSSI] = { .type = NLA_FLAG },
+ [NL80211_BSS_SELECT_ATTR_BAND_PREF] = { .type = NLA_U32 },
+ [NL80211_BSS_SELECT_ATTR_RSSI_ADJUST] = {
+ .len = sizeof(struct nl80211_bss_select_rssi_adjust)
+ },
+};
+
+/* policy for NAN function attributes */
+static const struct nla_policy
+nl80211_nan_func_policy[NL80211_NAN_FUNC_ATTR_MAX + 1] = {
+ [NL80211_NAN_FUNC_TYPE] = { .type = NLA_U8 },
+ [NL80211_NAN_FUNC_SERVICE_ID] = { .type = NLA_BINARY,
+ .len = NL80211_NAN_FUNC_SERVICE_ID_LEN },
+ [NL80211_NAN_FUNC_PUBLISH_TYPE] = { .type = NLA_U8 },
+ [NL80211_NAN_FUNC_PUBLISH_BCAST] = { .type = NLA_FLAG },
+ [NL80211_NAN_FUNC_SUBSCRIBE_ACTIVE] = { .type = NLA_FLAG },
+ [NL80211_NAN_FUNC_FOLLOW_UP_ID] = { .type = NLA_U8 },
+ [NL80211_NAN_FUNC_FOLLOW_UP_REQ_ID] = { .type = NLA_U8 },
+ [NL80211_NAN_FUNC_FOLLOW_UP_DEST] = { .len = ETH_ALEN },
+ [NL80211_NAN_FUNC_CLOSE_RANGE] = { .type = NLA_FLAG },
+ [NL80211_NAN_FUNC_TTL] = { .type = NLA_U32 },
+ [NL80211_NAN_FUNC_SERVICE_INFO] = { .type = NLA_BINARY,
+ .len = NL80211_NAN_FUNC_SERVICE_SPEC_INFO_MAX_LEN },
+ [NL80211_NAN_FUNC_SRF] = { .type = NLA_NESTED },
+ [NL80211_NAN_FUNC_RX_MATCH_FILTER] = { .type = NLA_NESTED },
+ [NL80211_NAN_FUNC_TX_MATCH_FILTER] = { .type = NLA_NESTED },
+ [NL80211_NAN_FUNC_INSTANCE_ID] = { .type = NLA_U8 },
+ [NL80211_NAN_FUNC_TERM_REASON] = { .type = NLA_U8 },
+};
+
+/* policy for Service Response Filter attributes */
+static const struct nla_policy
+nl80211_nan_srf_policy[NL80211_NAN_SRF_ATTR_MAX + 1] = {
+ [NL80211_NAN_SRF_INCLUDE] = { .type = NLA_FLAG },
+ [NL80211_NAN_SRF_BF] = { .type = NLA_BINARY,
+ .len = NL80211_NAN_FUNC_SRF_MAX_LEN },
+ [NL80211_NAN_SRF_BF_IDX] = { .type = NLA_U8 },
+ [NL80211_NAN_SRF_MAC_ADDRS] = { .type = NLA_NESTED },
+};
+
+static int nl80211_prepare_wdev_dump(struct sk_buff *skb,
+ struct netlink_callback *cb,
+ struct cfg80211_registered_device **rdev,
+ struct wireless_dev **wdev)
+{
+ int err;
+
+ if (!cb->args[0]) {
+ err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
+ genl_family_attrbuf(&nl80211_fam),
+ nl80211_fam.maxattr, nl80211_policy, NULL);
+ if (err)
+ return err;
+
+ *wdev = __cfg80211_wdev_from_attrs(
+ sock_net(skb->sk),
+ genl_family_attrbuf(&nl80211_fam));
+ if (IS_ERR(*wdev))
+ return PTR_ERR(*wdev);
+ *rdev = wiphy_to_rdev((*wdev)->wiphy);
+ /* 0 is the first index - add 1 to parse only once */
+ cb->args[0] = (*rdev)->wiphy_idx + 1;
+ cb->args[1] = (*wdev)->identifier;
+ } else {
+ /* subtract the 1 again here */
+ struct wiphy *wiphy = wiphy_idx_to_wiphy(cb->args[0] - 1);
+ struct wireless_dev *tmp;
+
+ if (!wiphy)
+ return -ENODEV;
+ *rdev = wiphy_to_rdev(wiphy);
+ *wdev = NULL;
+
+ list_for_each_entry(tmp, &(*rdev)->wiphy.wdev_list, list) {
+ if (tmp->identifier == cb->args[1]) {
+ *wdev = tmp;
+ break;
+ }
+ }
+
+ if (!*wdev)
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+/* IE validation */
+static bool is_valid_ie_attr(const struct nlattr *attr)
+{
+ const u8 *pos;
+ int len;
+
+ if (!attr)
+ return true;
+
+ pos = nla_data(attr);
+ len = nla_len(attr);
+
+ while (len) {
+ u8 elemlen;
+
+ if (len < 2)
+ return false;
+ len -= 2;
+
+ elemlen = pos[1];
+ if (elemlen > len)
+ return false;
+
+ len -= elemlen;
+ pos += 2 + elemlen;
+ }
+
+ return true;
+}
+
+/* message building helper */
+static inline void *nl80211hdr_put(struct sk_buff *skb, u32 portid, u32 seq,
+ int flags, u8 cmd)
+{
+ /* since there is no private header just add the generic one */
+ return genlmsg_put(skb, portid, seq, &nl80211_fam, flags, cmd);
+}
+
+static int nl80211_msg_put_channel(struct sk_buff *msg,
+ struct ieee80211_channel *chan,
+ bool large)
+{
+ /* Some channels must be completely excluded from the
+ * list to protect old user-space tools from breaking
+ */
+ if (!large && chan->flags &
+ (IEEE80211_CHAN_NO_10MHZ | IEEE80211_CHAN_NO_20MHZ))
+ return 0;
+
+ if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_FREQ,
+ chan->center_freq))
+ goto nla_put_failure;
+
+ if ((chan->flags & IEEE80211_CHAN_DISABLED) &&
+ nla_put_flag(msg, NL80211_FREQUENCY_ATTR_DISABLED))
+ goto nla_put_failure;
+ if (chan->flags & IEEE80211_CHAN_NO_IR) {
+ if (nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_IR))
+ goto nla_put_failure;
+ if (nla_put_flag(msg, __NL80211_FREQUENCY_ATTR_NO_IBSS))
+ goto nla_put_failure;
+ }
+ if (chan->flags & IEEE80211_CHAN_RADAR) {
+ if (nla_put_flag(msg, NL80211_FREQUENCY_ATTR_RADAR))
+ goto nla_put_failure;
+ if (large) {
+ u32 time;
+
+ time = elapsed_jiffies_msecs(chan->dfs_state_entered);
+
+ if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_DFS_STATE,
+ chan->dfs_state))
+ goto nla_put_failure;
+ if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_DFS_TIME,
+ time))
+ goto nla_put_failure;
+ if (nla_put_u32(msg,
+ NL80211_FREQUENCY_ATTR_DFS_CAC_TIME,
+ chan->dfs_cac_ms))
+ goto nla_put_failure;
+ }
+ }
+
+ if (large) {
+ if ((chan->flags & IEEE80211_CHAN_NO_HT40MINUS) &&
+ nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_HT40_MINUS))
+ goto nla_put_failure;
+ if ((chan->flags & IEEE80211_CHAN_NO_HT40PLUS) &&
+ nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_HT40_PLUS))
+ goto nla_put_failure;
+ if ((chan->flags & IEEE80211_CHAN_NO_80MHZ) &&
+ nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_80MHZ))
+ goto nla_put_failure;
+ if ((chan->flags & IEEE80211_CHAN_NO_160MHZ) &&
+ nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_160MHZ))
+ goto nla_put_failure;
+ if ((chan->flags & IEEE80211_CHAN_INDOOR_ONLY) &&
+ nla_put_flag(msg, NL80211_FREQUENCY_ATTR_INDOOR_ONLY))
+ goto nla_put_failure;
+ if ((chan->flags & IEEE80211_CHAN_IR_CONCURRENT) &&
+ nla_put_flag(msg, NL80211_FREQUENCY_ATTR_IR_CONCURRENT))
+ goto nla_put_failure;
+ if ((chan->flags & IEEE80211_CHAN_NO_20MHZ) &&
+ nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_20MHZ))
+ goto nla_put_failure;
+ if ((chan->flags & IEEE80211_CHAN_NO_10MHZ) &&
+ nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_10MHZ))
+ goto nla_put_failure;
+ }
+
+ if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
+ DBM_TO_MBM(chan->max_power)))
+ goto nla_put_failure;
+
+ return 0;
+
+ nla_put_failure:
+ return -ENOBUFS;
+}
+
+/* netlink command implementations */
+
+struct key_parse {
+ struct key_params p;
+ int idx;
+ int type;
+ bool def, defmgmt;
+ bool def_uni, def_multi;
+};
+
+static int nl80211_parse_key_new(struct nlattr *key, struct key_parse *k)
+{
+ struct nlattr *tb[NL80211_KEY_MAX + 1];
+ int err = nla_parse_nested(tb, NL80211_KEY_MAX, key,
+ nl80211_key_policy, NULL);
+ if (err)
+ return err;
+
+ k->def = !!tb[NL80211_KEY_DEFAULT];
+ k->defmgmt = !!tb[NL80211_KEY_DEFAULT_MGMT];
+
+ if (k->def) {
+ k->def_uni = true;
+ k->def_multi = true;
+ }
+ if (k->defmgmt)
+ k->def_multi = true;
+
+ if (tb[NL80211_KEY_IDX])
+ k->idx = nla_get_u8(tb[NL80211_KEY_IDX]);
+
+ if (tb[NL80211_KEY_DATA]) {
+ k->p.key = nla_data(tb[NL80211_KEY_DATA]);
+ k->p.key_len = nla_len(tb[NL80211_KEY_DATA]);
+ }
+
+ if (tb[NL80211_KEY_SEQ]) {
+ k->p.seq = nla_data(tb[NL80211_KEY_SEQ]);
+ k->p.seq_len = nla_len(tb[NL80211_KEY_SEQ]);
+ }
+
+ if (tb[NL80211_KEY_CIPHER])
+ k->p.cipher = nla_get_u32(tb[NL80211_KEY_CIPHER]);
+
+ if (tb[NL80211_KEY_TYPE]) {
+ k->type = nla_get_u32(tb[NL80211_KEY_TYPE]);
+ if (k->type < 0 || k->type >= NUM_NL80211_KEYTYPES)
+ return -EINVAL;
+ }
+
+ if (tb[NL80211_KEY_DEFAULT_TYPES]) {
+ struct nlattr *kdt[NUM_NL80211_KEY_DEFAULT_TYPES];
+
+ err = nla_parse_nested(kdt, NUM_NL80211_KEY_DEFAULT_TYPES - 1,
+ tb[NL80211_KEY_DEFAULT_TYPES],
+ nl80211_key_default_policy, NULL);
+ if (err)
+ return err;
+
+ k->def_uni = kdt[NL80211_KEY_DEFAULT_TYPE_UNICAST];
+ k->def_multi = kdt[NL80211_KEY_DEFAULT_TYPE_MULTICAST];
+ }
+
+ return 0;
+}
+
+static int nl80211_parse_key_old(struct genl_info *info, struct key_parse *k)
+{
+ if (info->attrs[NL80211_ATTR_KEY_DATA]) {
+ k->p.key = nla_data(info->attrs[NL80211_ATTR_KEY_DATA]);
+ k->p.key_len = nla_len(info->attrs[NL80211_ATTR_KEY_DATA]);
+ }
+
+ if (info->attrs[NL80211_ATTR_KEY_SEQ]) {
+ k->p.seq = nla_data(info->attrs[NL80211_ATTR_KEY_SEQ]);
+ k->p.seq_len = nla_len(info->attrs[NL80211_ATTR_KEY_SEQ]);
+ }
+
+ if (info->attrs[NL80211_ATTR_KEY_IDX])
+ k->idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
+
+ if (info->attrs[NL80211_ATTR_KEY_CIPHER])
+ k->p.cipher = nla_get_u32(info->attrs[NL80211_ATTR_KEY_CIPHER]);
+
+ k->def = !!info->attrs[NL80211_ATTR_KEY_DEFAULT];
+ k->defmgmt = !!info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT];
+
+ if (k->def) {
+ k->def_uni = true;
+ k->def_multi = true;
+ }
+ if (k->defmgmt)
+ k->def_multi = true;
+
+ if (info->attrs[NL80211_ATTR_KEY_TYPE]) {
+ k->type = nla_get_u32(info->attrs[NL80211_ATTR_KEY_TYPE]);
+ if (k->type < 0 || k->type >= NUM_NL80211_KEYTYPES)
+ return -EINVAL;
+ }
+
+ if (info->attrs[NL80211_ATTR_KEY_DEFAULT_TYPES]) {
+ struct nlattr *kdt[NUM_NL80211_KEY_DEFAULT_TYPES];
+ int err = nla_parse_nested(kdt,
+ NUM_NL80211_KEY_DEFAULT_TYPES - 1,
+ info->attrs[NL80211_ATTR_KEY_DEFAULT_TYPES],
+ nl80211_key_default_policy,
+ genl_info_extack(info));
+ if (err)
+ return err;
+
+ k->def_uni = kdt[NL80211_KEY_DEFAULT_TYPE_UNICAST];
+ k->def_multi = kdt[NL80211_KEY_DEFAULT_TYPE_MULTICAST];
+ }
+
+ return 0;
+}
+
+static int nl80211_parse_key(struct genl_info *info, struct key_parse *k)
+{
+ int err;
+
+ memset(k, 0, sizeof(*k));
+ k->idx = -1;
+ k->type = -1;
+
+ if (info->attrs[NL80211_ATTR_KEY])
+ err = nl80211_parse_key_new(info->attrs[NL80211_ATTR_KEY], k);
+ else
+ err = nl80211_parse_key_old(info, k);
+
+ if (err)
+ return err;
+
+ if (k->def && k->defmgmt)
+ return -EINVAL;
+
+ if (k->defmgmt) {
+ if (k->def_uni || !k->def_multi)
+ return -EINVAL;
+ }
+
+ if (k->idx != -1) {
+ if (k->defmgmt) {
+ if (k->idx < 4 || k->idx > 5)
+ return -EINVAL;
+ } else if (k->def) {
+ if (k->idx < 0 || k->idx > 3)
+ return -EINVAL;
+ } else {
+ if (k->idx < 0 || k->idx > 5)
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static struct cfg80211_cached_keys *
+nl80211_parse_connkeys(struct cfg80211_registered_device *rdev,
+ struct nlattr *keys, bool *no_ht)
+{
+ struct key_parse parse;
+ struct nlattr *key;
+ struct cfg80211_cached_keys *result;
+ int rem, err, def = 0;
+ bool have_key = false;
+
+ nla_for_each_nested(key, keys, rem) {
+ have_key = true;
+ break;
+ }
+
+ if (!have_key)
+ return NULL;
+
+ result = kzalloc(sizeof(*result), GFP_KERNEL);
+ if (!result)
+ return ERR_PTR(-ENOMEM);
+
+ result->def = -1;
+
+ nla_for_each_nested(key, keys, rem) {
+ memset(&parse, 0, sizeof(parse));
+ parse.idx = -1;
+
+ err = nl80211_parse_key_new(key, &parse);
+ if (err)
+ goto error;
+ err = -EINVAL;
+ if (!parse.p.key)
+ goto error;
+ if (parse.idx < 0 || parse.idx > 3)
+ goto error;
+ if (parse.def) {
+ if (def)
+ goto error;
+ def = 1;
+ result->def = parse.idx;
+ if (!parse.def_uni || !parse.def_multi)
+ goto error;
+ } else if (parse.defmgmt)
+ goto error;
+ err = cfg80211_validate_key_settings(rdev, &parse.p,
+ parse.idx, false, NULL);
+ if (err)
+ goto error;
+ if (parse.p.cipher != WLAN_CIPHER_SUITE_WEP40 &&
+ parse.p.cipher != WLAN_CIPHER_SUITE_WEP104) {
+ err = -EINVAL;
+ goto error;
+ }
+ result->params[parse.idx].cipher = parse.p.cipher;
+ result->params[parse.idx].key_len = parse.p.key_len;
+ result->params[parse.idx].key = result->data[parse.idx];
+ memcpy(result->data[parse.idx], parse.p.key, parse.p.key_len);
+
+ /* must be WEP key if we got here */
+ if (no_ht)
+ *no_ht = true;
+ }
+
+ if (result->def < 0) {
+ err = -EINVAL;
+ goto error;
+ }
+
+ return result;
+ error:
+ kfree(result);
+ return ERR_PTR(err);
+}
+
+static int nl80211_key_allowed(struct wireless_dev *wdev)
+{
+ ASSERT_WDEV_LOCK(wdev);
+
+ switch (wdev->iftype) {
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_AP_VLAN:
+ case NL80211_IFTYPE_P2P_GO:
+ case NL80211_IFTYPE_MESH_POINT:
+ break;
+ case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_P2P_CLIENT:
+ if (!wdev->current_bss)
+ return -ENOLINK;
+ break;
+ case NL80211_IFTYPE_UNSPECIFIED:
+ case NL80211_IFTYPE_OCB:
+ case NL80211_IFTYPE_MONITOR:
+ case NL80211_IFTYPE_NAN:
+ case NL80211_IFTYPE_P2P_DEVICE:
+ case NL80211_IFTYPE_WDS:
+ case NUM_NL80211_IFTYPES:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static struct ieee80211_channel *nl80211_get_valid_chan(struct wiphy *wiphy,
+ struct nlattr *tb)
+{
+ struct ieee80211_channel *chan;
+
+ if (tb == NULL)
+ return NULL;
+ chan = ieee80211_get_channel(wiphy, nla_get_u32(tb));
+ if (!chan || chan->flags & IEEE80211_CHAN_DISABLED)
+ return NULL;
+ return chan;
+}
+
+static int nl80211_put_iftypes(struct sk_buff *msg, u32 attr, u16 ifmodes)
+{
+ struct nlattr *nl_modes = nla_nest_start(msg, attr);
+ int i;
+
+ if (!nl_modes)
+ goto nla_put_failure;
+
+ i = 0;
+ while (ifmodes) {
+ if ((ifmodes & 1) && nla_put_flag(msg, i))
+ goto nla_put_failure;
+ ifmodes >>= 1;
+ i++;
+ }
+
+ nla_nest_end(msg, nl_modes);
+ return 0;
+
+nla_put_failure:
+ return -ENOBUFS;
+}
+
+static int nl80211_put_iface_combinations(struct wiphy *wiphy,
+ struct sk_buff *msg,
+ bool large)
+{
+ struct nlattr *nl_combis;
+ int i, j;
+
+ nl_combis = nla_nest_start(msg,
+ NL80211_ATTR_INTERFACE_COMBINATIONS);
+ if (!nl_combis)
+ goto nla_put_failure;
+
+ for (i = 0; i < wiphy->n_iface_combinations; i++) {
+ const struct ieee80211_iface_combination *c;
+ struct nlattr *nl_combi, *nl_limits;
+
+ c = &wiphy->iface_combinations[i];
+
+ nl_combi = nla_nest_start(msg, i + 1);
+ if (!nl_combi)
+ goto nla_put_failure;
+
+ nl_limits = nla_nest_start(msg, NL80211_IFACE_COMB_LIMITS);
+ if (!nl_limits)
+ goto nla_put_failure;
+
+ for (j = 0; j < c->n_limits; j++) {
+ struct nlattr *nl_limit;
+
+ nl_limit = nla_nest_start(msg, j + 1);
+ if (!nl_limit)
+ goto nla_put_failure;
+ if (nla_put_u32(msg, NL80211_IFACE_LIMIT_MAX,
+ c->limits[j].max))
+ goto nla_put_failure;
+ if (nl80211_put_iftypes(msg, NL80211_IFACE_LIMIT_TYPES,
+ c->limits[j].types))
+ goto nla_put_failure;
+ nla_nest_end(msg, nl_limit);
+ }
+
+ nla_nest_end(msg, nl_limits);
+
+ if (c->beacon_int_infra_match &&
+ nla_put_flag(msg, NL80211_IFACE_COMB_STA_AP_BI_MATCH))
+ goto nla_put_failure;
+ if (nla_put_u32(msg, NL80211_IFACE_COMB_NUM_CHANNELS,
+ c->num_different_channels) ||
+ nla_put_u32(msg, NL80211_IFACE_COMB_MAXNUM,
+ c->max_interfaces))
+ goto nla_put_failure;
+ if (large &&
+ (nla_put_u32(msg, NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS,
+ c->radar_detect_widths) ||
+ nla_put_u32(msg, NL80211_IFACE_COMB_RADAR_DETECT_REGIONS,
+ c->radar_detect_regions)))
+ goto nla_put_failure;
+ if (c->beacon_int_min_gcd &&
+ nla_put_u32(msg, NL80211_IFACE_COMB_BI_MIN_GCD,
+ c->beacon_int_min_gcd))
+ goto nla_put_failure;
+
+ nla_nest_end(msg, nl_combi);
+ }
+
+ nla_nest_end(msg, nl_combis);
+
+ return 0;
+nla_put_failure:
+ return -ENOBUFS;
+}
+
+#ifdef CONFIG_PM
+static int nl80211_send_wowlan_tcp_caps(struct cfg80211_registered_device *rdev,
+ struct sk_buff *msg)
+{
+ const struct wiphy_wowlan_tcp_support *tcp = rdev->wiphy.wowlan->tcp;
+ struct nlattr *nl_tcp;
+
+ if (!tcp)
+ return 0;
+
+ nl_tcp = nla_nest_start(msg, NL80211_WOWLAN_TRIG_TCP_CONNECTION);
+ if (!nl_tcp)
+ return -ENOBUFS;
+
+ if (nla_put_u32(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD,
+ tcp->data_payload_max))
+ return -ENOBUFS;
+
+ if (nla_put_u32(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD,
+ tcp->data_payload_max))
+ return -ENOBUFS;
+
+ if (tcp->seq && nla_put_flag(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ))
+ return -ENOBUFS;
+
+ if (tcp->tok && nla_put(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN,
+ sizeof(*tcp->tok), tcp->tok))
+ return -ENOBUFS;
+
+ if (nla_put_u32(msg, NL80211_WOWLAN_TCP_DATA_INTERVAL,
+ tcp->data_interval_max))
+ return -ENOBUFS;
+
+ if (nla_put_u32(msg, NL80211_WOWLAN_TCP_WAKE_PAYLOAD,
+ tcp->wake_payload_max))
+ return -ENOBUFS;
+
+ nla_nest_end(msg, nl_tcp);
+ return 0;
+}
+
+static int nl80211_send_wowlan(struct sk_buff *msg,
+ struct cfg80211_registered_device *rdev,
+ bool large)
+{
+ struct nlattr *nl_wowlan;
+
+ if (!rdev->wiphy.wowlan)
+ return 0;
+
+ nl_wowlan = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED);
+ if (!nl_wowlan)
+ return -ENOBUFS;
+
+ if (((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_ANY) &&
+ nla_put_flag(msg, NL80211_WOWLAN_TRIG_ANY)) ||
+ ((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_DISCONNECT) &&
+ nla_put_flag(msg, NL80211_WOWLAN_TRIG_DISCONNECT)) ||
+ ((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_MAGIC_PKT) &&
+ nla_put_flag(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT)) ||
+ ((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY) &&
+ nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED)) ||
+ ((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) &&
+ nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE)) ||
+ ((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ) &&
+ nla_put_flag(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST)) ||
+ ((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_4WAY_HANDSHAKE) &&
+ nla_put_flag(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE)) ||
+ ((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_RFKILL_RELEASE) &&
+ nla_put_flag(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE)))
+ return -ENOBUFS;
+
+ if (rdev->wiphy.wowlan->n_patterns) {
+ struct nl80211_pattern_support pat = {
+ .max_patterns = rdev->wiphy.wowlan->n_patterns,
+ .min_pattern_len = rdev->wiphy.wowlan->pattern_min_len,
+ .max_pattern_len = rdev->wiphy.wowlan->pattern_max_len,
+ .max_pkt_offset = rdev->wiphy.wowlan->max_pkt_offset,
+ };
+
+ if (nla_put(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN,
+ sizeof(pat), &pat))
+ return -ENOBUFS;
+ }
+
+ if ((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_NET_DETECT) &&
+ nla_put_u32(msg, NL80211_WOWLAN_TRIG_NET_DETECT,
+ rdev->wiphy.wowlan->max_nd_match_sets))
+ return -ENOBUFS;
+
+ if (large && nl80211_send_wowlan_tcp_caps(rdev, msg))
+ return -ENOBUFS;
+
+ nla_nest_end(msg, nl_wowlan);
+
+ return 0;
+}
+#endif
+
+static int nl80211_send_coalesce(struct sk_buff *msg,
+ struct cfg80211_registered_device *rdev)
+{
+ struct nl80211_coalesce_rule_support rule;
+
+ if (!rdev->wiphy.coalesce)
+ return 0;
+
+ rule.max_rules = rdev->wiphy.coalesce->n_rules;
+ rule.max_delay = rdev->wiphy.coalesce->max_delay;
+ rule.pat.max_patterns = rdev->wiphy.coalesce->n_patterns;
+ rule.pat.min_pattern_len = rdev->wiphy.coalesce->pattern_min_len;
+ rule.pat.max_pattern_len = rdev->wiphy.coalesce->pattern_max_len;
+ rule.pat.max_pkt_offset = rdev->wiphy.coalesce->max_pkt_offset;
+
+ if (nla_put(msg, NL80211_ATTR_COALESCE_RULE, sizeof(rule), &rule))
+ return -ENOBUFS;
+
+ return 0;
+}
+
+static int nl80211_send_band_rateinfo(struct sk_buff *msg,
+ struct ieee80211_supported_band *sband)
+{
+ struct nlattr *nl_rates, *nl_rate;
+ struct ieee80211_rate *rate;
+ int i;
+
+ /* add HT info */
+ if (sband->ht_cap.ht_supported &&
+ (nla_put(msg, NL80211_BAND_ATTR_HT_MCS_SET,
+ sizeof(sband->ht_cap.mcs),
+ &sband->ht_cap.mcs) ||
+ nla_put_u16(msg, NL80211_BAND_ATTR_HT_CAPA,
+ sband->ht_cap.cap) ||
+ nla_put_u8(msg, NL80211_BAND_ATTR_HT_AMPDU_FACTOR,
+ sband->ht_cap.ampdu_factor) ||
+ nla_put_u8(msg, NL80211_BAND_ATTR_HT_AMPDU_DENSITY,
+ sband->ht_cap.ampdu_density)))
+ return -ENOBUFS;
+
+ /* add VHT info */
+ if (sband->vht_cap.vht_supported &&
+ (nla_put(msg, NL80211_BAND_ATTR_VHT_MCS_SET,
+ sizeof(sband->vht_cap.vht_mcs),
+ &sband->vht_cap.vht_mcs) ||
+ nla_put_u32(msg, NL80211_BAND_ATTR_VHT_CAPA,
+ sband->vht_cap.cap)))
+ return -ENOBUFS;
+
+ /* add bitrates */
+ nl_rates = nla_nest_start(msg, NL80211_BAND_ATTR_RATES);
+ if (!nl_rates)
+ return -ENOBUFS;
+
+ for (i = 0; i < sband->n_bitrates; i++) {
+ nl_rate = nla_nest_start(msg, i);
+ if (!nl_rate)
+ return -ENOBUFS;
+
+ rate = &sband->bitrates[i];
+ if (nla_put_u32(msg, NL80211_BITRATE_ATTR_RATE,
+ rate->bitrate))
+ return -ENOBUFS;
+ if ((rate->flags & IEEE80211_RATE_SHORT_PREAMBLE) &&
+ nla_put_flag(msg,
+ NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE))
+ return -ENOBUFS;
+
+ nla_nest_end(msg, nl_rate);
+ }
+
+ nla_nest_end(msg, nl_rates);
+
+ return 0;
+}
+
+static int
+nl80211_send_mgmt_stypes(struct sk_buff *msg,
+ const struct ieee80211_txrx_stypes *mgmt_stypes)
+{
+ u16 stypes;
+ struct nlattr *nl_ftypes, *nl_ifs;
+ enum nl80211_iftype ift;
+ int i;
+
+ if (!mgmt_stypes)
+ return 0;
+
+ nl_ifs = nla_nest_start(msg, NL80211_ATTR_TX_FRAME_TYPES);
+ if (!nl_ifs)
+ return -ENOBUFS;
+
+ for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) {
+ nl_ftypes = nla_nest_start(msg, ift);
+ if (!nl_ftypes)
+ return -ENOBUFS;
+ i = 0;
+ stypes = mgmt_stypes[ift].tx;
+ while (stypes) {
+ if ((stypes & 1) &&
+ nla_put_u16(msg, NL80211_ATTR_FRAME_TYPE,
+ (i << 4) | IEEE80211_FTYPE_MGMT))
+ return -ENOBUFS;
+ stypes >>= 1;
+ i++;
+ }
+ nla_nest_end(msg, nl_ftypes);
+ }
+
+ nla_nest_end(msg, nl_ifs);
+
+ nl_ifs = nla_nest_start(msg, NL80211_ATTR_RX_FRAME_TYPES);
+ if (!nl_ifs)
+ return -ENOBUFS;
+
+ for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) {
+ nl_ftypes = nla_nest_start(msg, ift);
+ if (!nl_ftypes)
+ return -ENOBUFS;
+ i = 0;
+ stypes = mgmt_stypes[ift].rx;
+ while (stypes) {
+ if ((stypes & 1) &&
+ nla_put_u16(msg, NL80211_ATTR_FRAME_TYPE,
+ (i << 4) | IEEE80211_FTYPE_MGMT))
+ return -ENOBUFS;
+ stypes >>= 1;
+ i++;
+ }
+ nla_nest_end(msg, nl_ftypes);
+ }
+ nla_nest_end(msg, nl_ifs);
+
+ return 0;
+}
+
+#define CMD(op, n) \
+ do { \
+ if (rdev->ops->op) { \
+ i++; \
+ if (nla_put_u32(msg, i, NL80211_CMD_ ## n)) \
+ goto nla_put_failure; \
+ } \
+ } while (0)
+
+static int nl80211_add_commands_unsplit(struct cfg80211_registered_device *rdev,
+ struct sk_buff *msg)
+{
+ int i = 0;
+
+ /*
+ * do *NOT* add anything into this function, new things need to be
+ * advertised only to new versions of userspace that can deal with
+ * the split (and they can't possibly care about new features...
+ */
+ CMD(add_virtual_intf, NEW_INTERFACE);
+ CMD(change_virtual_intf, SET_INTERFACE);
+ CMD(add_key, NEW_KEY);
+ CMD(start_ap, START_AP);
+ CMD(add_station, NEW_STATION);
+ CMD(add_mpath, NEW_MPATH);
+ CMD(update_mesh_config, SET_MESH_CONFIG);
+ CMD(change_bss, SET_BSS);
+ CMD(auth, AUTHENTICATE);
+ CMD(assoc, ASSOCIATE);
+ CMD(deauth, DEAUTHENTICATE);
+ CMD(disassoc, DISASSOCIATE);
+ CMD(join_ibss, JOIN_IBSS);
+ CMD(join_mesh, JOIN_MESH);
+ CMD(set_pmksa, SET_PMKSA);
+ CMD(del_pmksa, DEL_PMKSA);
+ CMD(flush_pmksa, FLUSH_PMKSA);
+ if (rdev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL)
+ CMD(remain_on_channel, REMAIN_ON_CHANNEL);
+ CMD(set_bitrate_mask, SET_TX_BITRATE_MASK);
+ CMD(mgmt_tx, FRAME);
+ CMD(mgmt_tx_cancel_wait, FRAME_WAIT_CANCEL);
+ if (rdev->wiphy.flags & WIPHY_FLAG_NETNS_OK) {
+ i++;
+ if (nla_put_u32(msg, i, NL80211_CMD_SET_WIPHY_NETNS))
+ goto nla_put_failure;
+ }
+ if (rdev->ops->set_monitor_channel || rdev->ops->start_ap ||
+ rdev->ops->join_mesh) {
+ i++;
+ if (nla_put_u32(msg, i, NL80211_CMD_SET_CHANNEL))
+ goto nla_put_failure;
+ }
+ CMD(set_wds_peer, SET_WDS_PEER);
+ if (rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) {
+ CMD(tdls_mgmt, TDLS_MGMT);
+ CMD(tdls_oper, TDLS_OPER);
+ }
+ if (rdev->wiphy.max_sched_scan_reqs)
+ CMD(sched_scan_start, START_SCHED_SCAN);
+ CMD(probe_client, PROBE_CLIENT);
+ CMD(set_noack_map, SET_NOACK_MAP);
+ if (rdev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS) {
+ i++;
+ if (nla_put_u32(msg, i, NL80211_CMD_REGISTER_BEACONS))
+ goto nla_put_failure;
+ }
+ CMD(start_p2p_device, START_P2P_DEVICE);
+ CMD(set_mcast_rate, SET_MCAST_RATE);
+#ifdef CPTCFG_NL80211_TESTMODE
+ CMD(testmode_cmd, TESTMODE);
+#endif
+
+ if (rdev->ops->connect || rdev->ops->auth) {
+ i++;
+ if (nla_put_u32(msg, i, NL80211_CMD_CONNECT))
+ goto nla_put_failure;
+ }
+
+ if (rdev->ops->disconnect || rdev->ops->deauth) {
+ i++;
+ if (nla_put_u32(msg, i, NL80211_CMD_DISCONNECT))
+ goto nla_put_failure;
+ }
+
+ return i;
+ nla_put_failure:
+ return -ENOBUFS;
+}
+
+struct nl80211_dump_wiphy_state {
+ s64 filter_wiphy;
+ long start;
+ long split_start, band_start, chan_start, capa_start;
+ bool split;
+};
+
+static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
+ enum nl80211_commands cmd,
+ struct sk_buff *msg, u32 portid, u32 seq,
+ int flags, struct nl80211_dump_wiphy_state *state)
+{
+ void *hdr;
+ struct nlattr *nl_bands, *nl_band;
+ struct nlattr *nl_freqs, *nl_freq;
+ struct nlattr *nl_cmds;
+ enum nl80211_band band;
+ struct ieee80211_channel *chan;
+ int i;
+ const struct ieee80211_txrx_stypes *mgmt_stypes =
+ rdev->wiphy.mgmt_stypes;
+ u32 features;
+
+ hdr = nl80211hdr_put(msg, portid, seq, flags, cmd);
+ if (!hdr)
+ return -ENOBUFS;
+
+ if (WARN_ON(!state))
+ return -EINVAL;
+
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
+ nla_put_string(msg, NL80211_ATTR_WIPHY_NAME,
+ wiphy_name(&rdev->wiphy)) ||
+ nla_put_u32(msg, NL80211_ATTR_GENERATION,
+ cfg80211_rdev_list_generation))
+ goto nla_put_failure;
+
+ if (cmd != NL80211_CMD_NEW_WIPHY)
+ goto finish;
+
+ switch (state->split_start) {
+ case 0:
+ if (nla_put_u8(msg, NL80211_ATTR_WIPHY_RETRY_SHORT,
+ rdev->wiphy.retry_short) ||
+ nla_put_u8(msg, NL80211_ATTR_WIPHY_RETRY_LONG,
+ rdev->wiphy.retry_long) ||
+ nla_put_u32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD,
+ rdev->wiphy.frag_threshold) ||
+ nla_put_u32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD,
+ rdev->wiphy.rts_threshold) ||
+ nla_put_u8(msg, NL80211_ATTR_WIPHY_COVERAGE_CLASS,
+ rdev->wiphy.coverage_class) ||
+ nla_put_u8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS,
+ rdev->wiphy.max_scan_ssids) ||
+ nla_put_u8(msg, NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS,
+ rdev->wiphy.max_sched_scan_ssids) ||
+ nla_put_u16(msg, NL80211_ATTR_MAX_SCAN_IE_LEN,
+ rdev->wiphy.max_scan_ie_len) ||
+ nla_put_u16(msg, NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN,
+ rdev->wiphy.max_sched_scan_ie_len) ||
+ nla_put_u8(msg, NL80211_ATTR_MAX_MATCH_SETS,
+ rdev->wiphy.max_match_sets) ||
+ nla_put_u32(msg, NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS,
+ rdev->wiphy.max_sched_scan_plans) ||
+ nla_put_u32(msg, NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL,
+ rdev->wiphy.max_sched_scan_plan_interval) ||
+ nla_put_u32(msg, NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS,
+ rdev->wiphy.max_sched_scan_plan_iterations))
+ goto nla_put_failure;
+
+ if ((rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN) &&
+ nla_put_flag(msg, NL80211_ATTR_SUPPORT_IBSS_RSN))
+ goto nla_put_failure;
+ if ((rdev->wiphy.flags & WIPHY_FLAG_MESH_AUTH) &&
+ nla_put_flag(msg, NL80211_ATTR_SUPPORT_MESH_AUTH))
+ goto nla_put_failure;
+ if ((rdev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) &&
+ nla_put_flag(msg, NL80211_ATTR_SUPPORT_AP_UAPSD))
+ goto nla_put_failure;
+ if ((rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_FW_ROAM) &&
+ nla_put_flag(msg, NL80211_ATTR_ROAM_SUPPORT))
+ goto nla_put_failure;
+ if ((rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) &&
+ nla_put_flag(msg, NL80211_ATTR_TDLS_SUPPORT))
+ goto nla_put_failure;
+ if ((rdev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP) &&
+ nla_put_flag(msg, NL80211_ATTR_TDLS_EXTERNAL_SETUP))
+ goto nla_put_failure;
+ state->split_start++;
+ if (state->split)
+ break;
+ case 1:
+ if (nla_put(msg, NL80211_ATTR_CIPHER_SUITES,
+ sizeof(u32) * rdev->wiphy.n_cipher_suites,
+ rdev->wiphy.cipher_suites))
+ goto nla_put_failure;
+
+ if (nla_put_u8(msg, NL80211_ATTR_MAX_NUM_PMKIDS,
+ rdev->wiphy.max_num_pmkids))
+ goto nla_put_failure;
+
+ if ((rdev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL) &&
+ nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE))
+ goto nla_put_failure;
+
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX,
+ rdev->wiphy.available_antennas_tx) ||
+ nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX,
+ rdev->wiphy.available_antennas_rx))
+ goto nla_put_failure;
+
+ if ((rdev->wiphy.flags & WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD) &&
+ nla_put_u32(msg, NL80211_ATTR_PROBE_RESP_OFFLOAD,
+ rdev->wiphy.probe_resp_offload))
+ goto nla_put_failure;
+
+ if ((rdev->wiphy.available_antennas_tx ||
+ rdev->wiphy.available_antennas_rx) &&
+ rdev->ops->get_antenna) {
+ u32 tx_ant = 0, rx_ant = 0;
+ int res;
+
+ res = rdev_get_antenna(rdev, &tx_ant, &rx_ant);
+ if (!res) {
+ if (nla_put_u32(msg,
+ NL80211_ATTR_WIPHY_ANTENNA_TX,
+ tx_ant) ||
+ nla_put_u32(msg,
+ NL80211_ATTR_WIPHY_ANTENNA_RX,
+ rx_ant))
+ goto nla_put_failure;
+ }
+ }
+
+ state->split_start++;
+ if (state->split)
+ break;
+ case 2:
+ if (nl80211_put_iftypes(msg, NL80211_ATTR_SUPPORTED_IFTYPES,
+ rdev->wiphy.interface_modes))
+ goto nla_put_failure;
+ state->split_start++;
+ if (state->split)
+ break;
+ case 3:
+ nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS);
+ if (!nl_bands)
+ goto nla_put_failure;
+
+ for (band = state->band_start;
+ band < NUM_NL80211_BANDS; band++) {
+ struct ieee80211_supported_band *sband;
+
+ sband = rdev->wiphy.bands[band];
+
+ if (!sband)
+ continue;
+
+ nl_band = nla_nest_start(msg, band);
+ if (!nl_band)
+ goto nla_put_failure;
+
+ switch (state->chan_start) {
+ case 0:
+ if (nl80211_send_band_rateinfo(msg, sband))
+ goto nla_put_failure;
+ state->chan_start++;
+ if (state->split)
+ break;
+ default:
+ /* add frequencies */
+ nl_freqs = nla_nest_start(
+ msg, NL80211_BAND_ATTR_FREQS);
+ if (!nl_freqs)
+ goto nla_put_failure;
+
+ for (i = state->chan_start - 1;
+ i < sband->n_channels;
+ i++) {
+ nl_freq = nla_nest_start(msg, i);
+ if (!nl_freq)
+ goto nla_put_failure;
+
+ chan = &sband->channels[i];
+
+ if (nl80211_msg_put_channel(
+ msg, chan,
+ state->split))
+ goto nla_put_failure;
+
+ nla_nest_end(msg, nl_freq);
+ if (state->split)
+ break;
+ }
+ if (i < sband->n_channels)
+ state->chan_start = i + 2;
+ else
+ state->chan_start = 0;
+ nla_nest_end(msg, nl_freqs);
+ }
+
+ nla_nest_end(msg, nl_band);
+
+ if (state->split) {
+ /* start again here */
+ if (state->chan_start)
+ band--;
+ break;
+ }
+ }
+ nla_nest_end(msg, nl_bands);
+
+ if (band < NUM_NL80211_BANDS)
+ state->band_start = band + 1;
+ else
+ state->band_start = 0;
+
+ /* if bands & channels are done, continue outside */
+ if (state->band_start == 0 && state->chan_start == 0)
+ state->split_start++;
+ if (state->split)
+ break;
+ case 4:
+ nl_cmds = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_COMMANDS);
+ if (!nl_cmds)
+ goto nla_put_failure;
+
+ i = nl80211_add_commands_unsplit(rdev, msg);
+ if (i < 0)
+ goto nla_put_failure;
+ if (state->split) {
+ CMD(crit_proto_start, CRIT_PROTOCOL_START);
+ CMD(crit_proto_stop, CRIT_PROTOCOL_STOP);
+ if (rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH)
+ CMD(channel_switch, CHANNEL_SWITCH);
+ CMD(set_qos_map, SET_QOS_MAP);
+ if (rdev->wiphy.features &
+ NL80211_FEATURE_SUPPORTS_WMM_ADMISSION)
+ CMD(add_tx_ts, ADD_TX_TS);
+ CMD(set_multicast_to_unicast, SET_MULTICAST_TO_UNICAST);
+ CMD(update_connect_params, UPDATE_CONNECT_PARAMS);
+ }
+#undef CMD
+
+ nla_nest_end(msg, nl_cmds);
+ state->split_start++;
+ if (state->split)
+ break;
+ case 5:
+ if (rdev->ops->remain_on_channel &&
+ (rdev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL) &&
+ nla_put_u32(msg,
+ NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION,
+ rdev->wiphy.max_remain_on_channel_duration))
+ goto nla_put_failure;
+
+ if ((rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX) &&
+ nla_put_flag(msg, NL80211_ATTR_OFFCHANNEL_TX_OK))
+ goto nla_put_failure;
+
+ if (nl80211_send_mgmt_stypes(msg, mgmt_stypes))
+ goto nla_put_failure;
+ state->split_start++;
+ if (state->split)
+ break;
+ case 6:
+#ifdef CONFIG_PM
+ if (nl80211_send_wowlan(msg, rdev, state->split))
+ goto nla_put_failure;
+ state->split_start++;
+ if (state->split)
+ break;
+#else
+ state->split_start++;
+#endif
+ case 7:
+ if (nl80211_put_iftypes(msg, NL80211_ATTR_SOFTWARE_IFTYPES,
+ rdev->wiphy.software_iftypes))
+ goto nla_put_failure;
+
+ if (nl80211_put_iface_combinations(&rdev->wiphy, msg,
+ state->split))
+ goto nla_put_failure;
+
+ state->split_start++;
+ if (state->split)
+ break;
+ case 8:
+ if ((rdev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME) &&
+ nla_put_u32(msg, NL80211_ATTR_DEVICE_AP_SME,
+ rdev->wiphy.ap_sme_capa))
+ goto nla_put_failure;
+
+ features = rdev->wiphy.features;
+ /*
+ * We can only add the per-channel limit information if the
+ * dump is split, otherwise it makes it too big. Therefore
+ * only advertise it in that case.
+ */
+ if (state->split)
+ features |= NL80211_FEATURE_ADVERTISE_CHAN_LIMITS;
+ if (nla_put_u32(msg, NL80211_ATTR_FEATURE_FLAGS, features))
+ goto nla_put_failure;
+
+ if (rdev->wiphy.ht_capa_mod_mask &&
+ nla_put(msg, NL80211_ATTR_HT_CAPABILITY_MASK,
+ sizeof(*rdev->wiphy.ht_capa_mod_mask),
+ rdev->wiphy.ht_capa_mod_mask))
+ goto nla_put_failure;
+
+ if (rdev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME &&
+ rdev->wiphy.max_acl_mac_addrs &&
+ nla_put_u32(msg, NL80211_ATTR_MAC_ACL_MAX,
+ rdev->wiphy.max_acl_mac_addrs))
+ goto nla_put_failure;
+
+ /*
+ * Any information below this point is only available to
+ * applications that can deal with it being split. This
+ * helps ensure that newly added capabilities don't break
+ * older tools by overrunning their buffers.
+ *
+ * We still increment split_start so that in the split
+ * case we'll continue with more data in the next round,
+ * but break unconditionally so unsplit data stops here.
+ */
+ state->split_start++;
+ break;
+ case 9:
+ if (rdev->wiphy.extended_capabilities &&
+ (nla_put(msg, NL80211_ATTR_EXT_CAPA,
+ rdev->wiphy.extended_capabilities_len,
+ rdev->wiphy.extended_capabilities) ||
+ nla_put(msg, NL80211_ATTR_EXT_CAPA_MASK,
+ rdev->wiphy.extended_capabilities_len,
+ rdev->wiphy.extended_capabilities_mask)))
+ goto nla_put_failure;
+
+ if (rdev->wiphy.vht_capa_mod_mask &&
+ nla_put(msg, NL80211_ATTR_VHT_CAPABILITY_MASK,
+ sizeof(*rdev->wiphy.vht_capa_mod_mask),
+ rdev->wiphy.vht_capa_mod_mask))
+ goto nla_put_failure;
+
+ state->split_start++;
+ break;
+ case 10:
+ if (nl80211_send_coalesce(msg, rdev))
+ goto nla_put_failure;
+
+ if ((rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_5_10_MHZ) &&
+ (nla_put_flag(msg, NL80211_ATTR_SUPPORT_5_MHZ) ||
+ nla_put_flag(msg, NL80211_ATTR_SUPPORT_10_MHZ)))
+ goto nla_put_failure;
+
+ if (rdev->wiphy.max_ap_assoc_sta &&
+ nla_put_u32(msg, NL80211_ATTR_MAX_AP_ASSOC_STA,
+ rdev->wiphy.max_ap_assoc_sta))
+ goto nla_put_failure;
+
+ state->split_start++;
+ break;
+ case 11:
+ if (rdev->wiphy.n_vendor_commands) {
+ const struct nl80211_vendor_cmd_info *info;
+ struct nlattr *nested;
+
+ nested = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+ if (!nested)
+ goto nla_put_failure;
+
+ for (i = 0; i < rdev->wiphy.n_vendor_commands; i++) {
+ info = &rdev->wiphy.vendor_commands[i].info;
+ if (nla_put(msg, i + 1, sizeof(*info), info))
+ goto nla_put_failure;
+ }
+ nla_nest_end(msg, nested);
+ }
+
+ if (rdev->wiphy.n_vendor_events) {
+ const struct nl80211_vendor_cmd_info *info;
+ struct nlattr *nested;
+
+ nested = nla_nest_start(msg,
+ NL80211_ATTR_VENDOR_EVENTS);
+ if (!nested)
+ goto nla_put_failure;
+
+ for (i = 0; i < rdev->wiphy.n_vendor_events; i++) {
+ info = &rdev->wiphy.vendor_events[i];
+ if (nla_put(msg, i + 1, sizeof(*info), info))
+ goto nla_put_failure;
+ }
+ nla_nest_end(msg, nested);
+ }
+ state->split_start++;
+ break;
+ case 12:
+ if (rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH &&
+ nla_put_u8(msg, NL80211_ATTR_MAX_CSA_COUNTERS,
+ rdev->wiphy.max_num_csa_counters))
+ goto nla_put_failure;
+
+ if (rdev->wiphy.regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED &&
+ nla_put_flag(msg, NL80211_ATTR_WIPHY_SELF_MANAGED_REG))
+ goto nla_put_failure;
+
+ if (rdev->wiphy.max_sched_scan_reqs &&
+ nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_MAX_REQS,
+ rdev->wiphy.max_sched_scan_reqs))
+ goto nla_put_failure;
+
+ if (nla_put(msg, NL80211_ATTR_EXT_FEATURES,
+ sizeof(rdev->wiphy.ext_features),
+ rdev->wiphy.ext_features))
+ goto nla_put_failure;
+
+ if (rdev->wiphy.bss_select_support) {
+ struct nlattr *nested;
+ u32 bss_select_support = rdev->wiphy.bss_select_support;
+
+ nested = nla_nest_start(msg, NL80211_ATTR_BSS_SELECT);
+ if (!nested)
+ goto nla_put_failure;
+
+ i = 0;
+ while (bss_select_support) {
+ if ((bss_select_support & 1) &&
+ nla_put_flag(msg, i))
+ goto nla_put_failure;
+ i++;
+ bss_select_support >>= 1;
+ }
+ nla_nest_end(msg, nested);
+ }
+
+ state->split_start++;
+ break;
+ case 13:
+ if (rdev->wiphy.num_iftype_ext_capab &&
+ rdev->wiphy.iftype_ext_capab) {
+ struct nlattr *nested_ext_capab, *nested;
+
+ nested = nla_nest_start(msg,
+ NL80211_ATTR_IFTYPE_EXT_CAPA);
+ if (!nested)
+ goto nla_put_failure;
+
+ for (i = state->capa_start;
+ i < rdev->wiphy.num_iftype_ext_capab; i++) {
+ const struct wiphy_iftype_ext_capab *capab;
+
+ capab = &rdev->wiphy.iftype_ext_capab[i];
+
+ nested_ext_capab = nla_nest_start(msg, i);
+ if (!nested_ext_capab ||
+ nla_put_u32(msg, NL80211_ATTR_IFTYPE,
+ capab->iftype) ||
+ nla_put(msg, NL80211_ATTR_EXT_CAPA,
+ capab->extended_capabilities_len,
+ capab->extended_capabilities) ||
+ nla_put(msg, NL80211_ATTR_EXT_CAPA_MASK,
+ capab->extended_capabilities_len,
+ capab->extended_capabilities_mask))
+ goto nla_put_failure;
+
+ nla_nest_end(msg, nested_ext_capab);
+ if (state->split)
+ break;
+ }
+ nla_nest_end(msg, nested);
+ if (i < rdev->wiphy.num_iftype_ext_capab) {
+ state->capa_start = i + 1;
+ break;
+ }
+ }
+
+ if (nla_put_u32(msg, NL80211_ATTR_BANDS,
+ rdev->wiphy.nan_supported_bands))
+ goto nla_put_failure;
+
+ /* done */
+ state->split_start = 0;
+ break;
+ }
+ finish:
+ genlmsg_end(msg, hdr);
+ return 0;
+
+ nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ return -EMSGSIZE;
+}
+
+static int nl80211_dump_wiphy_parse(struct sk_buff *skb,
+ struct netlink_callback *cb,
+ struct nl80211_dump_wiphy_state *state)
+{
+ struct nlattr **tb = genl_family_attrbuf(&nl80211_fam);
+ int ret = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize, tb,
+ nl80211_fam.maxattr, nl80211_policy, NULL);
+ /* ignore parse errors for backward compatibility */
+ if (ret)
+ return 0;
+
+ state->split = tb[NL80211_ATTR_SPLIT_WIPHY_DUMP];
+ if (tb[NL80211_ATTR_WIPHY])
+ state->filter_wiphy = nla_get_u32(tb[NL80211_ATTR_WIPHY]);
+ if (tb[NL80211_ATTR_WDEV])
+ state->filter_wiphy = nla_get_u64(tb[NL80211_ATTR_WDEV]) >> 32;
+ if (tb[NL80211_ATTR_IFINDEX]) {
+ struct net_device *netdev;
+ struct cfg80211_registered_device *rdev;
+ int ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]);
+
+ netdev = __dev_get_by_index(sock_net(skb->sk), ifidx);
+ if (!netdev)
+ return -ENODEV;
+ if (netdev->ieee80211_ptr) {
+ rdev = wiphy_to_rdev(
+ netdev->ieee80211_ptr->wiphy);
+ state->filter_wiphy = rdev->wiphy_idx;
+ }
+ }
+
+ return 0;
+}
+
+static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ int idx = 0, ret;
+ struct nl80211_dump_wiphy_state *state = (void *)cb->args[0];
+ struct cfg80211_registered_device *rdev;
+
+ rtnl_lock();
+ if (!state) {
+ state = kzalloc(sizeof(*state), GFP_KERNEL);
+ if (!state) {
+ rtnl_unlock();
+ return -ENOMEM;
+ }
+ state->filter_wiphy = -1;
+ ret = nl80211_dump_wiphy_parse(skb, cb, state);
+ if (ret) {
+ kfree(state);
+ rtnl_unlock();
+ return ret;
+ }
+ cb->args[0] = (long)state;
+ }
+
+ list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
+ if (!net_eq(wiphy_net(&rdev->wiphy), sock_net(skb->sk)))
+ continue;
+ if (++idx <= state->start)
+ continue;
+ if (state->filter_wiphy != -1 &&
+ state->filter_wiphy != rdev->wiphy_idx)
+ continue;
+ /* attempt to fit multiple wiphy data chunks into the skb */
+ do {
+ ret = nl80211_send_wiphy(rdev, NL80211_CMD_NEW_WIPHY,
+ skb,
+ NETLINK_CB_PORTID(cb->skb),
+ cb->nlh->nlmsg_seq,
+ NLM_F_MULTI, state);
+ if (ret < 0) {
+#if LINUX_VERSION_IS_GEQ(3,1,0)
+ /*
+ * If sending the wiphy data didn't fit (ENOBUFS
+ * or EMSGSIZE returned), this SKB is still
+ * empty (so it's not too big because another
+ * wiphy dataset is already in the skb) and
+ * we've not tried to adjust the dump allocation
+ * yet ... then adjust the alloc size to be
+ * bigger, and return 1 but with the empty skb.
+ * This results in an empty message being RX'ed
+ * in userspace, but that is ignored.
+ *
+ * We can then retry with the larger buffer.
+ */
+ if ((ret == -ENOBUFS || ret == -EMSGSIZE) &&
+ !skb->len && !state->split &&
+ cb->min_dump_alloc < 4096) {
+ cb->min_dump_alloc = 4096;
+ state->split_start = 0;
+ rtnl_unlock();
+ return 1;
+ }
+#endif
+ idx--;
+ break;
+ }
+ } while (state->split_start > 0);
+ break;
+ }
+ rtnl_unlock();
+
+ state->start = idx;
+
+ return skb->len;
+}
+
+static int nl80211_dump_wiphy_done(struct netlink_callback *cb)
+{
+ kfree((void *)cb->args[0]);
+ return 0;
+}
+
+static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info)
+{
+ struct sk_buff *msg;
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct nl80211_dump_wiphy_state state = {};
+
+ msg = nlmsg_new(4096, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ if (nl80211_send_wiphy(rdev, NL80211_CMD_NEW_WIPHY, msg,
+ genl_info_snd_portid(info), info->snd_seq, 0,
+ &state) < 0) {
+ nlmsg_free(msg);
+ return -ENOBUFS;
+ }
+
+ return genlmsg_reply(msg, info);
+}
+
+static const struct nla_policy txq_params_policy[NL80211_TXQ_ATTR_MAX + 1] = {
+ [NL80211_TXQ_ATTR_QUEUE] = { .type = NLA_U8 },
+ [NL80211_TXQ_ATTR_TXOP] = { .type = NLA_U16 },
+ [NL80211_TXQ_ATTR_CWMIN] = { .type = NLA_U16 },
+ [NL80211_TXQ_ATTR_CWMAX] = { .type = NLA_U16 },
+ [NL80211_TXQ_ATTR_AIFS] = { .type = NLA_U8 },
+};
+
+static int parse_txq_params(struct nlattr *tb[],
+ struct ieee80211_txq_params *txq_params)
+{
+ if (!tb[NL80211_TXQ_ATTR_AC] || !tb[NL80211_TXQ_ATTR_TXOP] ||
+ !tb[NL80211_TXQ_ATTR_CWMIN] || !tb[NL80211_TXQ_ATTR_CWMAX] ||
+ !tb[NL80211_TXQ_ATTR_AIFS])
+ return -EINVAL;
+
+ txq_params->ac = nla_get_u8(tb[NL80211_TXQ_ATTR_AC]);
+ txq_params->txop = nla_get_u16(tb[NL80211_TXQ_ATTR_TXOP]);
+ txq_params->cwmin = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMIN]);
+ txq_params->cwmax = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMAX]);
+ txq_params->aifs = nla_get_u8(tb[NL80211_TXQ_ATTR_AIFS]);
+
+ if (txq_params->ac >= NL80211_NUM_ACS)
+ return -EINVAL;
+
+ return 0;
+}
+
+static bool nl80211_can_set_dev_channel(struct wireless_dev *wdev)
+{
+ /*
+ * You can only set the channel explicitly for WDS interfaces,
+ * all others have their channel managed via their respective
+ * "establish a connection" command (connect, join, ...)
+ *
+ * For AP/GO and mesh mode, the channel can be set with the
+ * channel userspace API, but is only stored and passed to the
+ * low-level driver when the AP starts or the mesh is joined.
+ * This is for backward compatibility, userspace can also give
+ * the channel in the start-ap or join-mesh commands instead.
+ *
+ * Monitors are special as they are normally slaved to
+ * whatever else is going on, so they have their own special
+ * operation to set the monitor channel if possible.
+ */
+ return !wdev ||
+ wdev->iftype == NL80211_IFTYPE_AP ||
+ wdev->iftype == NL80211_IFTYPE_MESH_POINT ||
+ wdev->iftype == NL80211_IFTYPE_MONITOR ||
+ wdev->iftype == NL80211_IFTYPE_P2P_GO;
+}
+
+static int nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
+ struct genl_info *info,
+ struct cfg80211_chan_def *chandef)
+{
+ u32 control_freq;
+
+ if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
+ return -EINVAL;
+
+ control_freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
+
+ chandef->chan = ieee80211_get_channel(&rdev->wiphy, control_freq);
+ chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
+ chandef->center_freq1 = control_freq;
+ chandef->center_freq2 = 0;
+
+ /* Primary channel not allowed */
+ if (!chandef->chan || chandef->chan->flags & IEEE80211_CHAN_DISABLED)
+ return -EINVAL;
+
+ if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
+ enum nl80211_channel_type chantype;
+
+ chantype = nla_get_u32(
+ info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
+
+ switch (chantype) {
+ case NL80211_CHAN_NO_HT:
+ case NL80211_CHAN_HT20:
+ case NL80211_CHAN_HT40PLUS:
+ case NL80211_CHAN_HT40MINUS:
+ cfg80211_chandef_create(chandef, chandef->chan,
+ chantype);
+ break;
+ default:
+ return -EINVAL;
+ }
+ } else if (info->attrs[NL80211_ATTR_CHANNEL_WIDTH]) {
+ chandef->width =
+ nla_get_u32(info->attrs[NL80211_ATTR_CHANNEL_WIDTH]);
+ if (info->attrs[NL80211_ATTR_CENTER_FREQ1])
+ chandef->center_freq1 =
+ nla_get_u32(
+ info->attrs[NL80211_ATTR_CENTER_FREQ1]);
+ if (info->attrs[NL80211_ATTR_CENTER_FREQ2])
+ chandef->center_freq2 =
+ nla_get_u32(
+ info->attrs[NL80211_ATTR_CENTER_FREQ2]);
+ }
+
+ if (!cfg80211_chandef_valid(chandef))
+ return -EINVAL;
+
+ if (!cfg80211_chandef_usable(&rdev->wiphy, chandef,
+ IEEE80211_CHAN_DISABLED))
+ return -EINVAL;
+
+ if ((chandef->width == NL80211_CHAN_WIDTH_5 ||
+ chandef->width == NL80211_CHAN_WIDTH_10) &&
+ !(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_5_10_MHZ))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int __nl80211_set_channel(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct genl_info *info)
+{
+ struct cfg80211_chan_def chandef;
+ int result;
+ enum nl80211_iftype iftype = NL80211_IFTYPE_MONITOR;
+ struct wireless_dev *wdev = NULL;
+
+ if (dev)
+ wdev = dev->ieee80211_ptr;
+ if (!nl80211_can_set_dev_channel(wdev))
+ return -EOPNOTSUPP;
+ if (wdev)
+ iftype = wdev->iftype;
+
+ result = nl80211_parse_chandef(rdev, info, &chandef);
+ if (result)
+ return result;
+
+ switch (iftype) {
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_P2P_GO:
+ if (!cfg80211_reg_can_beacon_relax(&rdev->wiphy, &chandef,
+ iftype)) {
+ result = -EINVAL;
+ break;
+ }
+ if (wdev->beacon_interval) {
+ if (!dev || !rdev->ops->set_ap_chanwidth ||
+ !(rdev->wiphy.features &
+ NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE)) {
+ result = -EBUSY;
+ break;
+ }
+
+ /* Only allow dynamic channel width changes */
+ if (chandef.chan != wdev->preset_chandef.chan) {
+ result = -EBUSY;
+ break;
+ }
+ result = rdev_set_ap_chanwidth(rdev, dev, &chandef);
+ if (result)
+ break;
+ }
+ wdev->preset_chandef = chandef;
+ result = 0;
+ break;
+ case NL80211_IFTYPE_MESH_POINT:
+ result = cfg80211_set_mesh_channel(rdev, wdev, &chandef);
+ break;
+ case NL80211_IFTYPE_MONITOR:
+ result = cfg80211_set_monitor_channel(rdev, &chandef);
+ break;
+ default:
+ result = -EINVAL;
+ }
+
+ return result;
+}
+
+static int nl80211_set_channel(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *netdev = info->user_ptr[1];
+
+ return __nl80211_set_channel(rdev, netdev, info);
+}
+
+static int nl80211_set_wds_peer(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ const u8 *bssid;
+
+ if (!info->attrs[NL80211_ATTR_MAC])
+ return -EINVAL;
+
+ if (netif_running(dev))
+ return -EBUSY;
+
+ if (!rdev->ops->set_wds_peer)
+ return -EOPNOTSUPP;
+
+ if (wdev->iftype != NL80211_IFTYPE_WDS)
+ return -EOPNOTSUPP;
+
+ bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
+ return rdev_set_wds_peer(rdev, dev, bssid);
+}
+
+static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev;
+ struct net_device *netdev = NULL;
+ struct wireless_dev *wdev;
+ int result = 0, rem_txq_params = 0;
+ struct nlattr *nl_txq_params;
+ u32 changed;
+ u8 retry_short = 0, retry_long = 0;
+ u32 frag_threshold = 0, rts_threshold = 0;
+ u8 coverage_class = 0;
+
+ ASSERT_RTNL();
+
+ /*
+ * Try to find the wiphy and netdev. Normally this
+ * function shouldn't need the netdev, but this is
+ * done for backward compatibility -- previously
+ * setting the channel was done per wiphy, but now
+ * it is per netdev. Previous userland like hostapd
+ * also passed a netdev to set_wiphy, so that it is
+ * possible to let that go to the right netdev!
+ */
+
+ if (info->attrs[NL80211_ATTR_IFINDEX]) {
+ int ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]);
+
+ netdev = __dev_get_by_index(genl_info_net(info), ifindex);
+ if (netdev && netdev->ieee80211_ptr)
+ rdev = wiphy_to_rdev(netdev->ieee80211_ptr->wiphy);
+ else
+ netdev = NULL;
+ }
+
+ if (!netdev) {
+ rdev = __cfg80211_rdev_from_attrs(genl_info_net(info),
+ info->attrs);
+ if (IS_ERR(rdev))
+ return PTR_ERR(rdev);
+ wdev = NULL;
+ netdev = NULL;
+ result = 0;
+ } else
+ wdev = netdev->ieee80211_ptr;
+
+ /*
+ * end workaround code, by now the rdev is available
+ * and locked, and wdev may or may not be NULL.
+ */
+
+ if (info->attrs[NL80211_ATTR_WIPHY_NAME])
+ result = cfg80211_dev_rename(
+ rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));
+
+ if (result)
+ return result;
+
+ if (info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS]) {
+ struct ieee80211_txq_params txq_params;
+ struct nlattr *tb[NL80211_TXQ_ATTR_MAX + 1];
+
+ if (!rdev->ops->set_txq_params)
+ return -EOPNOTSUPP;
+
+ if (!netdev)
+ return -EINVAL;
+
+ if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
+ netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
+ return -EINVAL;
+
+ if (!netif_running(netdev))
+ return -ENETDOWN;
+
+ nla_for_each_nested(nl_txq_params,
+ info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS],
+ rem_txq_params) {
+ result = nla_parse_nested(tb, NL80211_TXQ_ATTR_MAX,
+ nl_txq_params,
+ txq_params_policy,
+ genl_info_extack(info));
+ if (result)
+ return result;
+ result = parse_txq_params(tb, &txq_params);
+ if (result)
+ return result;
+
+ result = rdev_set_txq_params(rdev, netdev,
+ &txq_params);
+ if (result)
+ return result;
+ }
+ }
+
+ if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
+ result = __nl80211_set_channel(
+ rdev,
+ nl80211_can_set_dev_channel(wdev) ? netdev : NULL,
+ info);
+ if (result)
+ return result;
+ }
+
+ if (info->attrs[NL80211_ATTR_WIPHY_TX_POWER_SETTING]) {
+ struct wireless_dev *txp_wdev = wdev;
+ enum nl80211_tx_power_setting type;
+ int idx, mbm = 0;
+
+ if (!(rdev->wiphy.features & NL80211_FEATURE_VIF_TXPOWER))
+ txp_wdev = NULL;
+
+ if (!rdev->ops->set_tx_power)
+ return -EOPNOTSUPP;
+
+ idx = NL80211_ATTR_WIPHY_TX_POWER_SETTING;
+ type = nla_get_u32(info->attrs[idx]);
+
+ if (!info->attrs[NL80211_ATTR_WIPHY_TX_POWER_LEVEL] &&
+ (type != NL80211_TX_POWER_AUTOMATIC))
+ return -EINVAL;
+
+ if (type != NL80211_TX_POWER_AUTOMATIC) {
+ idx = NL80211_ATTR_WIPHY_TX_POWER_LEVEL;
+ mbm = nla_get_u32(info->attrs[idx]);
+ }
+
+ result = rdev_set_tx_power(rdev, txp_wdev, type, mbm);
+ if (result)
+ return result;
+ }
+
+ if (info->attrs[NL80211_ATTR_WIPHY_ANTENNA_TX] &&
+ info->attrs[NL80211_ATTR_WIPHY_ANTENNA_RX]) {
+ u32 tx_ant, rx_ant;
+
+ if ((!rdev->wiphy.available_antennas_tx &&
+ !rdev->wiphy.available_antennas_rx) ||
+ !rdev->ops->set_antenna)
+ return -EOPNOTSUPP;
+
+ tx_ant = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_ANTENNA_TX]);
+ rx_ant = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_ANTENNA_RX]);
+
+ /* reject antenna configurations which don't match the
+ * available antenna masks, except for the "all" mask */
+ if ((~tx_ant && (tx_ant & ~rdev->wiphy.available_antennas_tx)) ||
+ (~rx_ant && (rx_ant & ~rdev->wiphy.available_antennas_rx)))
+ return -EINVAL;
+
+ tx_ant = tx_ant & rdev->wiphy.available_antennas_tx;
+ rx_ant = rx_ant & rdev->wiphy.available_antennas_rx;
+
+ result = rdev_set_antenna(rdev, tx_ant, rx_ant);
+ if (result)
+ return result;
+ }
+
+ changed = 0;
+
+ if (info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]) {
+ retry_short = nla_get_u8(
+ info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]);
+ if (retry_short == 0)
+ return -EINVAL;
+
+ changed |= WIPHY_PARAM_RETRY_SHORT;
+ }
+
+ if (info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]) {
+ retry_long = nla_get_u8(
+ info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]);
+ if (retry_long == 0)
+ return -EINVAL;
+
+ changed |= WIPHY_PARAM_RETRY_LONG;
+ }
+
+ if (info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]) {
+ frag_threshold = nla_get_u32(
+ info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]);
+ if (frag_threshold < 256)
+ return -EINVAL;
+
+ if (frag_threshold != (u32) -1) {
+ /*
+ * Fragments (apart from the last one) are required to
+ * have even length. Make the fragmentation code
+ * simpler by stripping LSB should someone try to use
+ * odd threshold value.
+ */
+ frag_threshold &= ~0x1;
+ }
+ changed |= WIPHY_PARAM_FRAG_THRESHOLD;
+ }
+
+ if (info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]) {
+ rts_threshold = nla_get_u32(
+ info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]);
+ changed |= WIPHY_PARAM_RTS_THRESHOLD;
+ }
+
+ if (info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]) {
+ if (info->attrs[NL80211_ATTR_WIPHY_DYN_ACK])
+ return -EINVAL;
+
+ coverage_class = nla_get_u8(
+ info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]);
+ changed |= WIPHY_PARAM_COVERAGE_CLASS;
+ }
+
+ if (info->attrs[NL80211_ATTR_WIPHY_DYN_ACK]) {
+ if (!(rdev->wiphy.features & NL80211_FEATURE_ACKTO_ESTIMATION))
+ return -EOPNOTSUPP;
+
+ changed |= WIPHY_PARAM_DYN_ACK;
+ }
+
+ if (changed) {
+ u8 old_retry_short, old_retry_long;
+ u32 old_frag_threshold, old_rts_threshold;
+ u8 old_coverage_class;
+
+ if (!rdev->ops->set_wiphy_params)
+ return -EOPNOTSUPP;
+
+ old_retry_short = rdev->wiphy.retry_short;
+ old_retry_long = rdev->wiphy.retry_long;
+ old_frag_threshold = rdev->wiphy.frag_threshold;
+ old_rts_threshold = rdev->wiphy.rts_threshold;
+ old_coverage_class = rdev->wiphy.coverage_class;
+
+ if (changed & WIPHY_PARAM_RETRY_SHORT)
+ rdev->wiphy.retry_short = retry_short;
+ if (changed & WIPHY_PARAM_RETRY_LONG)
+ rdev->wiphy.retry_long = retry_long;
+ if (changed & WIPHY_PARAM_FRAG_THRESHOLD)
+ rdev->wiphy.frag_threshold = frag_threshold;
+ if (changed & WIPHY_PARAM_RTS_THRESHOLD)
+ rdev->wiphy.rts_threshold = rts_threshold;
+ if (changed & WIPHY_PARAM_COVERAGE_CLASS)
+ rdev->wiphy.coverage_class = coverage_class;
+
+ result = rdev_set_wiphy_params(rdev, changed);
+ if (result) {
+ rdev->wiphy.retry_short = old_retry_short;
+ rdev->wiphy.retry_long = old_retry_long;
+ rdev->wiphy.frag_threshold = old_frag_threshold;
+ rdev->wiphy.rts_threshold = old_rts_threshold;
+ rdev->wiphy.coverage_class = old_coverage_class;
+ return result;
+ }
+ }
+ return 0;
+}
+
+static inline u64 wdev_id(struct wireless_dev *wdev)
+{
+ return (u64)wdev->identifier |
+ ((u64)wiphy_to_rdev(wdev->wiphy)->wiphy_idx << 32);
+}
+
+static int nl80211_send_chandef(struct sk_buff *msg,
+ const struct cfg80211_chan_def *chandef)
+{
+ if (WARN_ON(!cfg80211_chandef_valid(chandef)))
+ return -EINVAL;
+
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ,
+ chandef->chan->center_freq))
+ return -ENOBUFS;
+ switch (chandef->width) {
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ case NL80211_CHAN_WIDTH_20:
+ case NL80211_CHAN_WIDTH_40:
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE,
+ cfg80211_get_chandef_type(chandef)))
+ return -ENOBUFS;
+ break;
+ default:
+ break;
+ }
+ if (nla_put_u32(msg, NL80211_ATTR_CHANNEL_WIDTH, chandef->width))
+ return -ENOBUFS;
+ if (nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ1, chandef->center_freq1))
+ return -ENOBUFS;
+ if (chandef->center_freq2 &&
+ nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ2, chandef->center_freq2))
+ return -ENOBUFS;
+ return 0;
+}
+
+static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flags,
+ struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev, bool removal)
+{
+ struct net_device *dev = wdev->netdev;
+ u8 cmd = NL80211_CMD_NEW_INTERFACE;
+ void *hdr;
+
+ if (removal)
+ cmd = NL80211_CMD_DEL_INTERFACE;
+
+ hdr = nl80211hdr_put(msg, portid, seq, flags, cmd);
+ if (!hdr)
+ return -1;
+
+ if (dev &&
+ (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
+ nla_put_string(msg, NL80211_ATTR_IFNAME, dev->name)))
+ goto nla_put_failure;
+
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
+ nla_put_u32(msg, NL80211_ATTR_IFTYPE, wdev->iftype) ||
+ nla_put_u64_64bit(msg, NL80211_ATTR_WDEV, wdev_id(wdev),
+ NL80211_ATTR_PAD) ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, wdev_address(wdev)) ||
+ nla_put_u32(msg, NL80211_ATTR_GENERATION,
+ rdev->devlist_generation ^
+ (cfg80211_rdev_list_generation << 2)))
+ goto nla_put_failure;
+
+ if (rdev->ops->get_channel) {
+ int ret;
+ struct cfg80211_chan_def chandef;
+
+ ret = rdev_get_channel(rdev, wdev, &chandef);
+ if (ret == 0) {
+ if (nl80211_send_chandef(msg, &chandef))
+ goto nla_put_failure;
+ }
+ }
+
+ if (rdev->ops->get_tx_power) {
+ int dbm, ret;
+
+ ret = rdev_get_tx_power(rdev, wdev, &dbm);
+ if (ret == 0 &&
+ nla_put_u32(msg, NL80211_ATTR_WIPHY_TX_POWER_LEVEL,
+ DBM_TO_MBM(dbm)))
+ goto nla_put_failure;
+ }
+
+ if (wdev->ssid_len) {
+ if (nla_put(msg, NL80211_ATTR_SSID, wdev->ssid_len, wdev->ssid))
+ goto nla_put_failure;
+ }
+
+ genlmsg_end(msg, hdr);
+ return 0;
+
+ nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ return -EMSGSIZE;
+}
+
+static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ int wp_idx = 0;
+ int if_idx = 0;
+ int wp_start = cb->args[0];
+ int if_start = cb->args[1];
+ int filter_wiphy = -1;
+ struct cfg80211_registered_device *rdev;
+ struct wireless_dev *wdev;
+ int ret;
+
+ rtnl_lock();
+ if (!cb->args[2]) {
+ struct nl80211_dump_wiphy_state state = {
+ .filter_wiphy = -1,
+ };
+
+ ret = nl80211_dump_wiphy_parse(skb, cb, &state);
+ if (ret)
+ goto out_unlock;
+
+ filter_wiphy = state.filter_wiphy;
+
+ /*
+ * if filtering, set cb->args[2] to +1 since 0 is the default
+ * value needed to determine that parsing is necessary.
+ */
+ if (filter_wiphy >= 0)
+ cb->args[2] = filter_wiphy + 1;
+ else
+ cb->args[2] = -1;
+ } else if (cb->args[2] > 0) {
+ filter_wiphy = cb->args[2] - 1;
+ }
+
+ list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
+ if (!net_eq(wiphy_net(&rdev->wiphy), sock_net(skb->sk)))
+ continue;
+ if (wp_idx < wp_start) {
+ wp_idx++;
+ continue;
+ }
+
+ if (filter_wiphy >= 0 && filter_wiphy != rdev->wiphy_idx)
+ continue;
+
+ if_idx = 0;
+
+ list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
+ if (if_idx < if_start) {
+ if_idx++;
+ continue;
+ }
+ if (nl80211_send_iface(skb, NETLINK_CB_PORTID(cb->skb),
+ cb->nlh->nlmsg_seq, NLM_F_MULTI,
+ rdev, wdev, false) < 0) {
+ goto out;
+ }
+ if_idx++;
+ }
+
+ wp_idx++;
+ }
+ out:
+ cb->args[0] = wp_idx;
+ cb->args[1] = if_idx;
+
+ ret = skb->len;
+ out_unlock:
+ rtnl_unlock();
+
+ return ret;
+}
+
+static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info)
+{
+ struct sk_buff *msg;
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct wireless_dev *wdev = info->user_ptr[1];
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ if (nl80211_send_iface(msg, genl_info_snd_portid(info), info->snd_seq, 0,
+ rdev, wdev, false) < 0) {
+ nlmsg_free(msg);
+ return -ENOBUFS;
+ }
+
+ return genlmsg_reply(msg, info);
+}
+
+static const struct nla_policy mntr_flags_policy[NL80211_MNTR_FLAG_MAX + 1] = {
+ [NL80211_MNTR_FLAG_FCSFAIL] = { .type = NLA_FLAG },
+ [NL80211_MNTR_FLAG_PLCPFAIL] = { .type = NLA_FLAG },
+ [NL80211_MNTR_FLAG_CONTROL] = { .type = NLA_FLAG },
+ [NL80211_MNTR_FLAG_OTHER_BSS] = { .type = NLA_FLAG },
+ [NL80211_MNTR_FLAG_COOK_FRAMES] = { .type = NLA_FLAG },
+ [NL80211_MNTR_FLAG_ACTIVE] = { .type = NLA_FLAG },
+};
+
+static int parse_monitor_flags(struct nlattr *nla, u32 *mntrflags)
+{
+ struct nlattr *flags[NL80211_MNTR_FLAG_MAX + 1];
+ int flag;
+
+ *mntrflags = 0;
+
+ if (!nla)
+ return -EINVAL;
+
+ if (nla_parse_nested(flags, NL80211_MNTR_FLAG_MAX, nla,
+ mntr_flags_policy, NULL))
+ return -EINVAL;
+
+ for (flag = 1; flag <= NL80211_MNTR_FLAG_MAX; flag++)
+ if (flags[flag])
+ *mntrflags |= (1<<flag);
+
+ *mntrflags |= MONITOR_FLAG_CHANGED;
+
+ return 0;
+}
+
+static int nl80211_parse_mon_options(struct cfg80211_registered_device *rdev,
+ enum nl80211_iftype type,
+ struct genl_info *info,
+ struct vif_params *params)
+{
+ bool change = false;
+ int err;
+
+ if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) {
+ if (type != NL80211_IFTYPE_MONITOR)
+ return -EINVAL;
+
+ err = parse_monitor_flags(info->attrs[NL80211_ATTR_MNTR_FLAGS],
+ ¶ms->flags);
+ if (err)
+ return err;
+
+ change = true;
+ }
+
+ if (params->flags & MONITOR_FLAG_ACTIVE &&
+ !(rdev->wiphy.features & NL80211_FEATURE_ACTIVE_MONITOR))
+ return -EOPNOTSUPP;
+
+ if (info->attrs[NL80211_ATTR_MU_MIMO_GROUP_DATA]) {
+ const u8 *mumimo_groups;
+ u32 cap_flag = NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER;
+
+ if (type != NL80211_IFTYPE_MONITOR)
+ return -EINVAL;
+
+ if (!wiphy_ext_feature_isset(&rdev->wiphy, cap_flag))
+ return -EOPNOTSUPP;
+
+ mumimo_groups =
+ nla_data(info->attrs[NL80211_ATTR_MU_MIMO_GROUP_DATA]);
+
+ /* bits 0 and 63 are reserved and must be zero */
+ if ((mumimo_groups[0] & BIT(0)) ||
+ (mumimo_groups[VHT_MUMIMO_GROUPS_DATA_LEN - 1] & BIT(7)))
+ return -EINVAL;
+
+ params->vht_mumimo_groups = mumimo_groups;
+ change = true;
+ }
+
+ if (info->attrs[NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR]) {
+ u32 cap_flag = NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER;
+
+ if (type != NL80211_IFTYPE_MONITOR)
+ return -EINVAL;
+
+ if (!wiphy_ext_feature_isset(&rdev->wiphy, cap_flag))
+ return -EOPNOTSUPP;
+
+ params->vht_mumimo_follow_addr =
+ nla_data(info->attrs[NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR]);
+ change = true;
+ }
+
+ return change ? 1 : 0;
+}
+
+static int nl80211_valid_4addr(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev, u8 use_4addr,
+ enum nl80211_iftype iftype)
+{
+ if (!use_4addr) {
+ if (netdev && (netdev->priv_flags & IFF_BRIDGE_PORT))
+ return -EBUSY;
+ return 0;
+ }
+
+ switch (iftype) {
+ case NL80211_IFTYPE_AP_VLAN:
+ if (rdev->wiphy.flags & WIPHY_FLAG_4ADDR_AP)
+ return 0;
+ break;
+ case NL80211_IFTYPE_STATION:
+ if (rdev->wiphy.flags & WIPHY_FLAG_4ADDR_STATION)
+ return 0;
+ break;
+ default:
+ break;
+ }
+
+ return -EOPNOTSUPP;
+}
+
+static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct vif_params params;
+ int err;
+ enum nl80211_iftype otype, ntype;
+ struct net_device *dev = info->user_ptr[1];
+ bool change = false;
+
+ memset(¶ms, 0, sizeof(params));
+
+ otype = ntype = dev->ieee80211_ptr->iftype;
+
+ if (info->attrs[NL80211_ATTR_IFTYPE]) {
+ ntype = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
+ if (otype != ntype)
+ change = true;
+ if (ntype > NL80211_IFTYPE_MAX)
+ return -EINVAL;
+ }
+
+ if (info->attrs[NL80211_ATTR_MESH_ID]) {
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+
+ if (ntype != NL80211_IFTYPE_MESH_POINT)
+ return -EINVAL;
+ if (netif_running(dev))
+ return -EBUSY;
+
+ wdev_lock(wdev);
+ BUILD_BUG_ON(IEEE80211_MAX_SSID_LEN !=
+ IEEE80211_MAX_MESH_ID_LEN);
+ wdev->mesh_id_up_len =
+ nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
+ memcpy(wdev->ssid, nla_data(info->attrs[NL80211_ATTR_MESH_ID]),
+ wdev->mesh_id_up_len);
+ wdev_unlock(wdev);
+ }
+
+ if (info->attrs[NL80211_ATTR_4ADDR]) {
+ params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]);
+ change = true;
+ err = nl80211_valid_4addr(rdev, dev, params.use_4addr, ntype);
+ if (err)
+ return err;
+ } else {
+ params.use_4addr = -1;
+ }
+
+ err = nl80211_parse_mon_options(rdev, ntype, info, ¶ms);
+ if (err < 0)
+ return err;
+ if (err > 0)
+ change = true;
+
+ if (change)
+ err = cfg80211_change_iface(rdev, dev, ntype, ¶ms);
+ else
+ err = 0;
+
+ if (!err && params.use_4addr != -1)
+ dev->ieee80211_ptr->use_4addr = params.use_4addr;
+
+ return err;
+}
+
+static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct vif_params params;
+ struct wireless_dev *wdev;
+ struct sk_buff *msg;
+ int err;
+ enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED;
+
+ /* to avoid failing a new interface creation due to pending removal */
+ cfg80211_destroy_ifaces(rdev);
+
+ memset(¶ms, 0, sizeof(params));
+
+ if (!info->attrs[NL80211_ATTR_IFNAME])
+ return -EINVAL;
+
+ if (info->attrs[NL80211_ATTR_IFTYPE]) {
+ type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
+ if (type > NL80211_IFTYPE_MAX)
+ return -EINVAL;
+ }
+
+ if (!rdev->ops->add_virtual_intf ||
+ !(rdev->wiphy.interface_modes & (1 << type)))
+ return -EOPNOTSUPP;
+
+ if ((type == NL80211_IFTYPE_P2P_DEVICE || type == NL80211_IFTYPE_NAN ||
+ rdev->wiphy.features & NL80211_FEATURE_MAC_ON_CREATE) &&
+ info->attrs[NL80211_ATTR_MAC]) {
+ nla_memcpy(params.macaddr, info->attrs[NL80211_ATTR_MAC],
+ ETH_ALEN);
+ if (!is_valid_ether_addr(params.macaddr))
+ return -EADDRNOTAVAIL;
+ }
+
+ if (info->attrs[NL80211_ATTR_4ADDR]) {
+ params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]);
+ err = nl80211_valid_4addr(rdev, NULL, params.use_4addr, type);
+ if (err)
+ return err;
+ }
+
+ err = nl80211_parse_mon_options(rdev, type, info, ¶ms);
+ if (err < 0)
+ return err;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ wdev = rdev_add_virtual_intf(rdev,
+ nla_data(info->attrs[NL80211_ATTR_IFNAME]),
+ NET_NAME_USER, type, ¶ms);
+ if (WARN_ON(!wdev)) {
+ nlmsg_free(msg);
+ return -EPROTO;
+ } else if (IS_ERR(wdev)) {
+ nlmsg_free(msg);
+ return PTR_ERR(wdev);
+ }
+
+ if (info->attrs[NL80211_ATTR_SOCKET_OWNER])
+ wdev->owner_nlportid = genl_info_snd_portid(info);
+
+ switch (type) {
+ case NL80211_IFTYPE_MESH_POINT:
+ if (!info->attrs[NL80211_ATTR_MESH_ID])
+ break;
+ wdev_lock(wdev);
+ BUILD_BUG_ON(IEEE80211_MAX_SSID_LEN !=
+ IEEE80211_MAX_MESH_ID_LEN);
+ wdev->mesh_id_up_len =
+ nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
+ memcpy(wdev->ssid, nla_data(info->attrs[NL80211_ATTR_MESH_ID]),
+ wdev->mesh_id_up_len);
+ wdev_unlock(wdev);
+ break;
+ case NL80211_IFTYPE_NAN:
+ case NL80211_IFTYPE_P2P_DEVICE:
+ /*
+ * P2P Device and NAN do not have a netdev, so don't go
+ * through the netdev notifier and must be added here
+ */
+ mutex_init(&wdev->mtx);
+ INIT_LIST_HEAD(&wdev->event_list);
+ spin_lock_init(&wdev->event_lock);
+ INIT_LIST_HEAD(&wdev->mgmt_registrations);
+ spin_lock_init(&wdev->mgmt_registrations_lock);
+
+ wdev->identifier = ++rdev->wdev_id;
+ list_add_rcu(&wdev->list, &rdev->wiphy.wdev_list);
+ rdev->devlist_generation++;
+ break;
+ default:
+ break;
+ }
+
+ if (nl80211_send_iface(msg, genl_info_snd_portid(info), info->snd_seq, 0,
+ rdev, wdev, false) < 0) {
+ nlmsg_free(msg);
+ return -ENOBUFS;
+ }
+
+ /*
+ * For wdevs which have no associated netdev object (e.g. of type
+ * NL80211_IFTYPE_P2P_DEVICE), emit the NEW_INTERFACE event here.
+ * For all other types, the event will be generated from the
+ * netdev notifier
+ */
+ if (!wdev->netdev)
+ nl80211_notify_iface(rdev, wdev, NL80211_CMD_NEW_INTERFACE);
+
+ return genlmsg_reply(msg, info);
+}
+
+static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct wireless_dev *wdev = info->user_ptr[1];
+
+ if (!rdev->ops->del_virtual_intf)
+ return -EOPNOTSUPP;
+
+ /*
+ * If we remove a wireless device without a netdev then clear
+ * user_ptr[1] so that nl80211_post_doit won't dereference it
+ * to check if it needs to do dev_put(). Otherwise it crashes
+ * since the wdev has been freed, unlike with a netdev where
+ * we need the dev_put() for the netdev to really be freed.
+ */
+ if (!wdev->netdev)
+ info->user_ptr[1] = NULL;
+
+ return rdev_del_virtual_intf(rdev, wdev);
+}
+
+static int nl80211_set_noack_map(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ u16 noack_map;
+
+ if (!info->attrs[NL80211_ATTR_NOACK_MAP])
+ return -EINVAL;
+
+ if (!rdev->ops->set_noack_map)
+ return -EOPNOTSUPP;
+
+ noack_map = nla_get_u16(info->attrs[NL80211_ATTR_NOACK_MAP]);
+
+ return rdev_set_noack_map(rdev, dev, noack_map);
+}
+
+struct get_key_cookie {
+ struct sk_buff *msg;
+ int error;
+ int idx;
+};
+
+static void get_key_callback(void *c, struct key_params *params)
+{
+ struct nlattr *key;
+ struct get_key_cookie *cookie = c;
+
+ if ((params->key &&
+ nla_put(cookie->msg, NL80211_ATTR_KEY_DATA,
+ params->key_len, params->key)) ||
+ (params->seq &&
+ nla_put(cookie->msg, NL80211_ATTR_KEY_SEQ,
+ params->seq_len, params->seq)) ||
+ (params->cipher &&
+ nla_put_u32(cookie->msg, NL80211_ATTR_KEY_CIPHER,
+ params->cipher)))
+ goto nla_put_failure;
+
+ key = nla_nest_start(cookie->msg, NL80211_ATTR_KEY);
+ if (!key)
+ goto nla_put_failure;
+
+ if ((params->key &&
+ nla_put(cookie->msg, NL80211_KEY_DATA,
+ params->key_len, params->key)) ||
+ (params->seq &&
+ nla_put(cookie->msg, NL80211_KEY_SEQ,
+ params->seq_len, params->seq)) ||
+ (params->cipher &&
+ nla_put_u32(cookie->msg, NL80211_KEY_CIPHER,
+ params->cipher)))
+ goto nla_put_failure;
+
+ if (nla_put_u8(cookie->msg, NL80211_ATTR_KEY_IDX, cookie->idx))
+ goto nla_put_failure;
+
+ nla_nest_end(cookie->msg, key);
+
+ return;
+ nla_put_failure:
+ cookie->error = 1;
+}
+
+static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ int err;
+ struct net_device *dev = info->user_ptr[1];
+ u8 key_idx = 0;
+ const u8 *mac_addr = NULL;
+ bool pairwise;
+ struct get_key_cookie cookie = {
+ .error = 0,
+ };
+ void *hdr;
+ struct sk_buff *msg;
+
+ if (info->attrs[NL80211_ATTR_KEY_IDX])
+ key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
+
+ if (key_idx > 5)
+ return -EINVAL;
+
+ if (info->attrs[NL80211_ATTR_MAC])
+ mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
+
+ pairwise = !!mac_addr;
+ if (info->attrs[NL80211_ATTR_KEY_TYPE]) {
+ u32 kt = nla_get_u32(info->attrs[NL80211_ATTR_KEY_TYPE]);
+
+ if (kt >= NUM_NL80211_KEYTYPES)
+ return -EINVAL;
+ if (kt != NL80211_KEYTYPE_GROUP &&
+ kt != NL80211_KEYTYPE_PAIRWISE)
+ return -EINVAL;
+ pairwise = kt == NL80211_KEYTYPE_PAIRWISE;
+ }
+
+ if (!rdev->ops->get_key)
+ return -EOPNOTSUPP;
+
+ if (!pairwise && mac_addr && !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN))
+ return -ENOENT;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ hdr = nl80211hdr_put(msg, genl_info_snd_portid(info), info->snd_seq,
+ 0,
+ NL80211_CMD_NEW_KEY);
+ if (!hdr)
+ goto nla_put_failure;
+
+ cookie.msg = msg;
+ cookie.idx = key_idx;
+
+ if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
+ nla_put_u8(msg, NL80211_ATTR_KEY_IDX, key_idx))
+ goto nla_put_failure;
+ if (mac_addr &&
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr))
+ goto nla_put_failure;
+
+ err = rdev_get_key(rdev, dev, key_idx, pairwise, mac_addr, &cookie,
+ get_key_callback);
+
+ if (err)
+ goto free_msg;
+
+ if (cookie.error)
+ goto nla_put_failure;
+
+ genlmsg_end(msg, hdr);
+ return genlmsg_reply(msg, info);
+
+ nla_put_failure:
+ err = -ENOBUFS;
+ free_msg:
+ nlmsg_free(msg);
+ return err;
+}
+
+static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct key_parse key;
+ int err;
+ struct net_device *dev = info->user_ptr[1];
+
+ err = nl80211_parse_key(info, &key);
+ if (err)
+ return err;
+
+ if (key.idx < 0)
+ return -EINVAL;
+
+ /* only support setting default key */
+ if (!key.def && !key.defmgmt)
+ return -EINVAL;
+
+ wdev_lock(dev->ieee80211_ptr);
+
+ if (key.def) {
+ if (!rdev->ops->set_default_key) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ err = nl80211_key_allowed(dev->ieee80211_ptr);
+ if (err)
+ goto out;
+
+ err = rdev_set_default_key(rdev, dev, key.idx,
+ key.def_uni, key.def_multi);
+
+ if (err)
+ goto out;
+
+#ifdef CPTCFG_CFG80211_WEXT
+ dev->ieee80211_ptr->wext.default_key = key.idx;
+#endif
+ } else {
+ if (key.def_uni || !key.def_multi) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ if (!rdev->ops->set_default_mgmt_key) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ err = nl80211_key_allowed(dev->ieee80211_ptr);
+ if (err)
+ goto out;
+
+ err = rdev_set_default_mgmt_key(rdev, dev, key.idx);
+ if (err)
+ goto out;
+
+#ifdef CPTCFG_CFG80211_WEXT
+ dev->ieee80211_ptr->wext.default_mgmt_key = key.idx;
+#endif
+ }
+
+ out:
+ wdev_unlock(dev->ieee80211_ptr);
+
+ return err;
+}
+
+static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ int err;
+ struct net_device *dev = info->user_ptr[1];
+ struct key_parse key;
+ const u8 *mac_addr = NULL;
+
+ err = nl80211_parse_key(info, &key);
+ if (err)
+ return err;
+
+ if (!key.p.key)
+ return -EINVAL;
+
+ if (info->attrs[NL80211_ATTR_MAC])
+ mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
+
+ if (key.type == -1) {
+ if (mac_addr)
+ key.type = NL80211_KEYTYPE_PAIRWISE;
+ else
+ key.type = NL80211_KEYTYPE_GROUP;
+ }
+
+ /* for now */
+ if (key.type != NL80211_KEYTYPE_PAIRWISE &&
+ key.type != NL80211_KEYTYPE_GROUP)
+ return -EINVAL;
+
+ if (!rdev->ops->add_key)
+ return -EOPNOTSUPP;
+
+ if (cfg80211_validate_key_settings(rdev, &key.p, key.idx,
+ key.type == NL80211_KEYTYPE_PAIRWISE,
+ mac_addr))
+ return -EINVAL;
+
+ wdev_lock(dev->ieee80211_ptr);
+ err = nl80211_key_allowed(dev->ieee80211_ptr);
+ if (!err)
+ err = rdev_add_key(rdev, dev, key.idx,
+ key.type == NL80211_KEYTYPE_PAIRWISE,
+ mac_addr, &key.p);
+ wdev_unlock(dev->ieee80211_ptr);
+
+ return err;
+}
+
+static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ int err;
+ struct net_device *dev = info->user_ptr[1];
+ u8 *mac_addr = NULL;
+ struct key_parse key;
+
+ err = nl80211_parse_key(info, &key);
+ if (err)
+ return err;
+
+ if (info->attrs[NL80211_ATTR_MAC])
+ mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
+
+ if (key.type == -1) {
+ if (mac_addr)
+ key.type = NL80211_KEYTYPE_PAIRWISE;
+ else
+ key.type = NL80211_KEYTYPE_GROUP;
+ }
+
+ /* for now */
+ if (key.type != NL80211_KEYTYPE_PAIRWISE &&
+ key.type != NL80211_KEYTYPE_GROUP)
+ return -EINVAL;
+
+ if (!rdev->ops->del_key)
+ return -EOPNOTSUPP;
+
+ wdev_lock(dev->ieee80211_ptr);
+ err = nl80211_key_allowed(dev->ieee80211_ptr);
+
+ if (key.type == NL80211_KEYTYPE_GROUP && mac_addr &&
+ !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN))
+ err = -ENOENT;
+
+ if (!err)
+ err = rdev_del_key(rdev, dev, key.idx,
+ key.type == NL80211_KEYTYPE_PAIRWISE,
+ mac_addr);
+
+#ifdef CPTCFG_CFG80211_WEXT
+ if (!err) {
+ if (key.idx == dev->ieee80211_ptr->wext.default_key)
+ dev->ieee80211_ptr->wext.default_key = -1;
+ else if (key.idx == dev->ieee80211_ptr->wext.default_mgmt_key)
+ dev->ieee80211_ptr->wext.default_mgmt_key = -1;
+ }
+#endif
+ wdev_unlock(dev->ieee80211_ptr);
+
+ return err;
+}
+
+/* This function returns an error or the number of nested attributes */
+static int validate_acl_mac_addrs(struct nlattr *nl_attr)
+{
+ struct nlattr *attr;
+ int n_entries = 0, tmp;
+
+ nla_for_each_nested(attr, nl_attr, tmp) {
+ if (nla_len(attr) != ETH_ALEN)
+ return -EINVAL;
+
+ n_entries++;
+ }
+
+ return n_entries;
+}
+
+/*
+ * This function parses ACL information and allocates memory for ACL data.
+ * On successful return, the calling function is responsible to free the
+ * ACL buffer returned by this function.
+ */
+static struct cfg80211_acl_data *parse_acl_data(struct wiphy *wiphy,
+ struct genl_info *info)
+{
+ enum nl80211_acl_policy acl_policy;
+ struct nlattr *attr;
+ struct cfg80211_acl_data *acl;
+ int i = 0, n_entries, tmp;
+
+ if (!wiphy->max_acl_mac_addrs)
+ return ERR_PTR(-EOPNOTSUPP);
+
+ if (!info->attrs[NL80211_ATTR_ACL_POLICY])
+ return ERR_PTR(-EINVAL);
+
+ acl_policy = nla_get_u32(info->attrs[NL80211_ATTR_ACL_POLICY]);
+ if (acl_policy != NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED &&
+ acl_policy != NL80211_ACL_POLICY_DENY_UNLESS_LISTED)
+ return ERR_PTR(-EINVAL);
+
+ if (!info->attrs[NL80211_ATTR_MAC_ADDRS])
+ return ERR_PTR(-EINVAL);
+
+ n_entries = validate_acl_mac_addrs(info->attrs[NL80211_ATTR_MAC_ADDRS]);
+ if (n_entries < 0)
+ return ERR_PTR(n_entries);
+
+ if (n_entries > wiphy->max_acl_mac_addrs)
+ return ERR_PTR(-ENOTSUPP);
+
+ acl = kzalloc(sizeof(*acl) + (sizeof(struct mac_address) * n_entries),
+ GFP_KERNEL);
+ if (!acl)
+ return ERR_PTR(-ENOMEM);
+
+ nla_for_each_nested(attr, info->attrs[NL80211_ATTR_MAC_ADDRS], tmp) {
+ memcpy(acl->mac_addrs[i].addr, nla_data(attr), ETH_ALEN);
+ i++;
+ }
+
+ acl->n_acl_entries = n_entries;
+ acl->acl_policy = acl_policy;
+
+ return acl;
+}
+
+static int nl80211_set_mac_acl(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct cfg80211_acl_data *acl;
+ int err;
+
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
+ dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
+ return -EOPNOTSUPP;
+
+ if (!dev->ieee80211_ptr->beacon_interval)
+ return -EINVAL;
+
+ acl = parse_acl_data(&rdev->wiphy, info);
+ if (IS_ERR(acl))
+ return PTR_ERR(acl);
+
+ err = rdev_set_mac_acl(rdev, dev, acl);
+
+ kfree(acl);
+
+ return err;
+}
+
+static u32 rateset_to_mask(struct ieee80211_supported_band *sband,
+ u8 *rates, u8 rates_len)
+{
+ u8 i;
+ u32 mask = 0;
+
+ for (i = 0; i < rates_len; i++) {
+ int rate = (rates[i] & 0x7f) * 5;
+ int ridx;
+
+ for (ridx = 0; ridx < sband->n_bitrates; ridx++) {
+ struct ieee80211_rate *srate =
+ &sband->bitrates[ridx];
+ if (rate == srate->bitrate) {
+ mask |= 1 << ridx;
+ break;
+ }
+ }
+ if (ridx == sband->n_bitrates)
+ return 0; /* rate not found */
+ }
+
+ return mask;
+}
+
+static bool ht_rateset_to_mask(struct ieee80211_supported_band *sband,
+ u8 *rates, u8 rates_len,
+ u8 mcs[IEEE80211_HT_MCS_MASK_LEN])
+{
+ u8 i;
+
+ memset(mcs, 0, IEEE80211_HT_MCS_MASK_LEN);
+
+ for (i = 0; i < rates_len; i++) {
+ int ridx, rbit;
+
+ ridx = rates[i] / 8;
+ rbit = BIT(rates[i] % 8);
+
+ /* check validity */
+ if ((ridx < 0) || (ridx >= IEEE80211_HT_MCS_MASK_LEN))
+ return false;
+
+ /* check availability */
+ if (sband->ht_cap.mcs.rx_mask[ridx] & rbit)
+ mcs[ridx] |= rbit;
+ else
+ return false;
+ }
+
+ return true;
+}
+
+static u16 vht_mcs_map_to_mcs_mask(u8 vht_mcs_map)
+{
+ u16 mcs_mask = 0;
+
+ switch (vht_mcs_map) {
+ case IEEE80211_VHT_MCS_NOT_SUPPORTED:
+ break;
+ case IEEE80211_VHT_MCS_SUPPORT_0_7:
+ mcs_mask = 0x00FF;
+ break;
+ case IEEE80211_VHT_MCS_SUPPORT_0_8:
+ mcs_mask = 0x01FF;
+ break;
+ case IEEE80211_VHT_MCS_SUPPORT_0_9:
+ mcs_mask = 0x03FF;
+ break;
+ default:
+ break;
+ }
+
+ return mcs_mask;
+}
+
+static void vht_build_mcs_mask(u16 vht_mcs_map,
+ u16 vht_mcs_mask[NL80211_VHT_NSS_MAX])
+{
+ u8 nss;
+
+ for (nss = 0; nss < NL80211_VHT_NSS_MAX; nss++) {
+ vht_mcs_mask[nss] = vht_mcs_map_to_mcs_mask(vht_mcs_map & 0x03);
+ vht_mcs_map >>= 2;
+ }
+}
+
+static bool vht_set_mcs_mask(struct ieee80211_supported_band *sband,
+ struct nl80211_txrate_vht *txrate,
+ u16 mcs[NL80211_VHT_NSS_MAX])
+{
+ u16 tx_mcs_map = le16_to_cpu(sband->vht_cap.vht_mcs.tx_mcs_map);
+ u16 tx_mcs_mask[NL80211_VHT_NSS_MAX] = {};
+ u8 i;
+
+ if (!sband->vht_cap.vht_supported)
+ return false;
+
+ memset(mcs, 0, sizeof(u16) * NL80211_VHT_NSS_MAX);
+
+ /* Build vht_mcs_mask from VHT capabilities */
+ vht_build_mcs_mask(tx_mcs_map, tx_mcs_mask);
+
+ for (i = 0; i < NL80211_VHT_NSS_MAX; i++) {
+ if ((tx_mcs_mask[i] & txrate->mcs[i]) == txrate->mcs[i])
+ mcs[i] = txrate->mcs[i];
+ else
+ return false;
+ }
+
+ return true;
+}
+
+static const struct nla_policy nl80211_txattr_policy[NL80211_TXRATE_MAX + 1] = {
+ [NL80211_TXRATE_LEGACY] = { .type = NLA_BINARY,
+ .len = NL80211_MAX_SUPP_RATES },
+ [NL80211_TXRATE_HT] = { .type = NLA_BINARY,
+ .len = NL80211_MAX_SUPP_HT_RATES },
+ [NL80211_TXRATE_VHT] = { .len = sizeof(struct nl80211_txrate_vht)},
+ [NL80211_TXRATE_GI] = { .type = NLA_U8 },
+};
+
+static int nl80211_parse_tx_bitrate_mask(struct genl_info *info,
+ struct cfg80211_bitrate_mask *mask)
+{
+ struct nlattr *tb[NL80211_TXRATE_MAX + 1];
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ int rem, i;
+ struct nlattr *tx_rates;
+ struct ieee80211_supported_band *sband;
+ u16 vht_tx_mcs_map;
+
+ memset(mask, 0, sizeof(*mask));
+ /* Default to all rates enabled */
+ for (i = 0; i < NUM_NL80211_BANDS; i++) {
+ sband = rdev->wiphy.bands[i];
+
+ if (!sband)
+ continue;
+
+ mask->control[i].legacy = (1 << sband->n_bitrates) - 1;
+ memcpy(mask->control[i].ht_mcs,
+ sband->ht_cap.mcs.rx_mask,
+ sizeof(mask->control[i].ht_mcs));
+
+ if (!sband->vht_cap.vht_supported)
+ continue;
+
+ vht_tx_mcs_map = le16_to_cpu(sband->vht_cap.vht_mcs.tx_mcs_map);
+ vht_build_mcs_mask(vht_tx_mcs_map, mask->control[i].vht_mcs);
+ }
+
+ /* if no rates are given set it back to the defaults */
+ if (!info->attrs[NL80211_ATTR_TX_RATES])
+ goto out;
+
+ /* The nested attribute uses enum nl80211_band as the index. This maps
+ * directly to the enum nl80211_band values used in cfg80211.
+ */
+ BUILD_BUG_ON(NL80211_MAX_SUPP_HT_RATES > IEEE80211_HT_MCS_MASK_LEN * 8);
+ nla_for_each_nested(tx_rates, info->attrs[NL80211_ATTR_TX_RATES], rem) {
+ enum nl80211_band band = nla_type(tx_rates);
+ int err;
+
+ if (band < 0 || band >= NUM_NL80211_BANDS)
+ return -EINVAL;
+ sband = rdev->wiphy.bands[band];
+ if (sband == NULL)
+ return -EINVAL;
+ err = nla_parse_nested(tb, NL80211_TXRATE_MAX, tx_rates,
+ nl80211_txattr_policy,
+ genl_info_extack(info));
+ if (err)
+ return err;
+ if (tb[NL80211_TXRATE_LEGACY]) {
+ mask->control[band].legacy = rateset_to_mask(
+ sband,
+ nla_data(tb[NL80211_TXRATE_LEGACY]),
+ nla_len(tb[NL80211_TXRATE_LEGACY]));
+ if ((mask->control[band].legacy == 0) &&
+ nla_len(tb[NL80211_TXRATE_LEGACY]))
+ return -EINVAL;
+ }
+ if (tb[NL80211_TXRATE_HT]) {
+ if (!ht_rateset_to_mask(
+ sband,
+ nla_data(tb[NL80211_TXRATE_HT]),
+ nla_len(tb[NL80211_TXRATE_HT]),
+ mask->control[band].ht_mcs))
+ return -EINVAL;
+ }
+ if (tb[NL80211_TXRATE_VHT]) {
+ if (!vht_set_mcs_mask(
+ sband,
+ nla_data(tb[NL80211_TXRATE_VHT]),
+ mask->control[band].vht_mcs))
+ return -EINVAL;
+ }
+ if (tb[NL80211_TXRATE_GI]) {
+ mask->control[band].gi =
+ nla_get_u8(tb[NL80211_TXRATE_GI]);
+ if (mask->control[band].gi > NL80211_TXRATE_FORCE_LGI)
+ return -EINVAL;
+ }
+
+ if (mask->control[band].legacy == 0) {
+ /* don't allow empty legacy rates if HT or VHT
+ * are not even supported.
+ */
+ if (!(rdev->wiphy.bands[band]->ht_cap.ht_supported ||
+ rdev->wiphy.bands[band]->vht_cap.vht_supported))
+ return -EINVAL;
+
+ for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++)
+ if (mask->control[band].ht_mcs[i])
+ goto out;
+
+ for (i = 0; i < NL80211_VHT_NSS_MAX; i++)
+ if (mask->control[band].vht_mcs[i])
+ goto out;
+
+ /* legacy and mcs rates may not be both empty */
+ return -EINVAL;
+ }
+ }
+
+out:
+ return 0;
+}
+
+static int validate_beacon_tx_rate(struct cfg80211_registered_device *rdev,
+ enum nl80211_band band,
+ struct cfg80211_bitrate_mask *beacon_rate)
+{
+ u32 count_ht, count_vht, i;
+ u32 rate = beacon_rate->control[band].legacy;
+
+ /* Allow only one rate */
+ if (hweight32(rate) > 1)
+ return -EINVAL;
+
+ count_ht = 0;
+ for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) {
+ if (hweight8(beacon_rate->control[band].ht_mcs[i]) > 1) {
+ return -EINVAL;
+ } else if (beacon_rate->control[band].ht_mcs[i]) {
+ count_ht++;
+ if (count_ht > 1)
+ return -EINVAL;
+ }
+ if (count_ht && rate)
+ return -EINVAL;
+ }
+
+ count_vht = 0;
+ for (i = 0; i < NL80211_VHT_NSS_MAX; i++) {
+ if (hweight16(beacon_rate->control[band].vht_mcs[i]) > 1) {
+ return -EINVAL;
+ } else if (beacon_rate->control[band].vht_mcs[i]) {
+ count_vht++;
+ if (count_vht > 1)
+ return -EINVAL;
+ }
+ if (count_vht && rate)
+ return -EINVAL;
+ }
+
+ if ((count_ht && count_vht) || (!rate && !count_ht && !count_vht))
+ return -EINVAL;
+
+ if (rate &&
+ !wiphy_ext_feature_isset(&rdev->wiphy,
+ NL80211_EXT_FEATURE_BEACON_RATE_LEGACY))
+ return -EINVAL;
+ if (count_ht &&
+ !wiphy_ext_feature_isset(&rdev->wiphy,
+ NL80211_EXT_FEATURE_BEACON_RATE_HT))
+ return -EINVAL;
+ if (count_vht &&
+ !wiphy_ext_feature_isset(&rdev->wiphy,
+ NL80211_EXT_FEATURE_BEACON_RATE_VHT))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int nl80211_parse_beacon(struct nlattr *attrs[],
+ struct cfg80211_beacon_data *bcn)
+{
+ bool haveinfo = false;
+
+ if (!is_valid_ie_attr(attrs[NL80211_ATTR_BEACON_TAIL]) ||
+ !is_valid_ie_attr(attrs[NL80211_ATTR_IE]) ||
+ !is_valid_ie_attr(attrs[NL80211_ATTR_IE_PROBE_RESP]) ||
+ !is_valid_ie_attr(attrs[NL80211_ATTR_IE_ASSOC_RESP]))
+ return -EINVAL;
+
+ memset(bcn, 0, sizeof(*bcn));
+
+ if (attrs[NL80211_ATTR_BEACON_HEAD]) {
+ bcn->head = nla_data(attrs[NL80211_ATTR_BEACON_HEAD]);
+ bcn->head_len = nla_len(attrs[NL80211_ATTR_BEACON_HEAD]);
+ if (!bcn->head_len)
+ return -EINVAL;
+ haveinfo = true;
+ }
+
+ if (attrs[NL80211_ATTR_BEACON_TAIL]) {
+ bcn->tail = nla_data(attrs[NL80211_ATTR_BEACON_TAIL]);
+ bcn->tail_len = nla_len(attrs[NL80211_ATTR_BEACON_TAIL]);
+ haveinfo = true;
+ }
+
+ if (!haveinfo)
+ return -EINVAL;
+
+ if (attrs[NL80211_ATTR_IE]) {
+ bcn->beacon_ies = nla_data(attrs[NL80211_ATTR_IE]);
+ bcn->beacon_ies_len = nla_len(attrs[NL80211_ATTR_IE]);
+ }
+
+ if (attrs[NL80211_ATTR_IE_PROBE_RESP]) {
+ bcn->proberesp_ies =
+ nla_data(attrs[NL80211_ATTR_IE_PROBE_RESP]);
+ bcn->proberesp_ies_len =
+ nla_len(attrs[NL80211_ATTR_IE_PROBE_RESP]);
+ }
+
+ if (attrs[NL80211_ATTR_IE_ASSOC_RESP]) {
+ bcn->assocresp_ies =
+ nla_data(attrs[NL80211_ATTR_IE_ASSOC_RESP]);
+ bcn->assocresp_ies_len =
+ nla_len(attrs[NL80211_ATTR_IE_ASSOC_RESP]);
+ }
+
+ if (attrs[NL80211_ATTR_PROBE_RESP]) {
+ bcn->probe_resp = nla_data(attrs[NL80211_ATTR_PROBE_RESP]);
+ bcn->probe_resp_len = nla_len(attrs[NL80211_ATTR_PROBE_RESP]);
+ }
+
+ return 0;
+}
+
+static void nl80211_check_ap_rate_selectors(struct cfg80211_ap_settings *params,
+ const u8 *rates)
+{
+ int i;
+
+ if (!rates)
+ return;
+
+ for (i = 0; i < rates[1]; i++) {
+ if (rates[2 + i] == BSS_MEMBERSHIP_SELECTOR_HT_PHY)
+ params->ht_required = true;
+ if (rates[2 + i] == BSS_MEMBERSHIP_SELECTOR_VHT_PHY)
+ params->vht_required = true;
+ }
+}
+
+/*
+ * Since the nl80211 API didn't include, from the beginning, attributes about
+ * HT/VHT requirements/capabilities, we parse them out of the IEs for the
+ * benefit of drivers that rebuild IEs in the firmware.
+ */
+static void nl80211_calculate_ap_params(struct cfg80211_ap_settings *params)
+{
+ const struct cfg80211_beacon_data *bcn = ¶ms->beacon;
+ size_t ies_len = bcn->beacon_ies_len;
+ const u8 *ies = bcn->beacon_ies;
+ const u8 *rates;
+ const u8 *cap;
+
+ rates = cfg80211_find_ie(WLAN_EID_SUPP_RATES, ies, ies_len);
+ nl80211_check_ap_rate_selectors(params, rates);
+
+ rates = cfg80211_find_ie(WLAN_EID_EXT_SUPP_RATES, ies, ies_len);
+ nl80211_check_ap_rate_selectors(params, rates);
+
+ cap = cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, ies, ies_len);
+ if (cap && cap[1] >= sizeof(*params->ht_cap))
+ params->ht_cap = (void *)(cap + 2);
+ cap = cfg80211_find_ie(WLAN_EID_VHT_CAPABILITY, ies, ies_len);
+ if (cap && cap[1] >= sizeof(*params->vht_cap))
+ params->vht_cap = (void *)(cap + 2);
+}
+
+static bool nl80211_get_ap_channel(struct cfg80211_registered_device *rdev,
+ struct cfg80211_ap_settings *params)
+{
+ struct wireless_dev *wdev;
+ bool ret = false;
+
+ list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
+ if (wdev->iftype != NL80211_IFTYPE_AP &&
+ wdev->iftype != NL80211_IFTYPE_P2P_GO)
+ continue;
+
+ if (!wdev->preset_chandef.chan)
+ continue;
+
+ params->chandef = wdev->preset_chandef;
+ ret = true;
+ break;
+ }
+
+ return ret;
+}
+
+static bool nl80211_valid_auth_type(struct cfg80211_registered_device *rdev,
+ enum nl80211_auth_type auth_type,
+ enum nl80211_commands cmd)
+{
+ if (auth_type > NL80211_AUTHTYPE_MAX)
+ return false;
+
+ switch (cmd) {
+ case NL80211_CMD_AUTHENTICATE:
+ if (!(rdev->wiphy.features & NL80211_FEATURE_SAE) &&
+ auth_type == NL80211_AUTHTYPE_SAE)
+ return false;
+ if (!wiphy_ext_feature_isset(&rdev->wiphy,
+ NL80211_EXT_FEATURE_FILS_STA) &&
+ (auth_type == NL80211_AUTHTYPE_FILS_SK ||
+ auth_type == NL80211_AUTHTYPE_FILS_SK_PFS ||
+ auth_type == NL80211_AUTHTYPE_FILS_PK))
+ return false;
+ return true;
+ case NL80211_CMD_CONNECT:
+ /* SAE not supported yet */
+ if (auth_type == NL80211_AUTHTYPE_SAE)
+ return false;
+ /* FILS with SK PFS or PK not supported yet */
+ if (auth_type == NL80211_AUTHTYPE_FILS_SK_PFS ||
+ auth_type == NL80211_AUTHTYPE_FILS_PK)
+ return false;
+ if (!wiphy_ext_feature_isset(
+ &rdev->wiphy,
+ NL80211_EXT_FEATURE_FILS_SK_OFFLOAD) &&
+ auth_type == NL80211_AUTHTYPE_FILS_SK)
+ return false;
+ return true;
+ case NL80211_CMD_START_AP:
+ /* SAE not supported yet */
+ if (auth_type == NL80211_AUTHTYPE_SAE)
+ return false;
+ /* FILS not supported yet */
+ if (auth_type == NL80211_AUTHTYPE_FILS_SK ||
+ auth_type == NL80211_AUTHTYPE_FILS_SK_PFS ||
+ auth_type == NL80211_AUTHTYPE_FILS_PK)
+ return false;
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_ap_settings params;
+ int err;
+
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
+ dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
+ return -EOPNOTSUPP;
+
+ if (!rdev->ops->start_ap)
+ return -EOPNOTSUPP;
+
+ if (wdev->beacon_interval)
+ return -EALREADY;
+
+ memset(¶ms, 0, sizeof(params));
+
+ /* these are required for START_AP */
+ if (!info->attrs[NL80211_ATTR_BEACON_INTERVAL] ||
+ !info->attrs[NL80211_ATTR_DTIM_PERIOD] ||
+ !info->attrs[NL80211_ATTR_BEACON_HEAD])
+ return -EINVAL;
+
+ err = nl80211_parse_beacon(info->attrs, ¶ms.beacon);
+ if (err)
+ return err;
+
+ params.beacon_interval =
+ nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
+ params.dtim_period =
+ nla_get_u32(info->attrs[NL80211_ATTR_DTIM_PERIOD]);
+
+ err = cfg80211_validate_beacon_int(rdev, dev->ieee80211_ptr->iftype,
+ params.beacon_interval);
+ if (err)
+ return err;
+
+ /*
+ * In theory, some of these attributes should be required here
+ * but since they were not used when the command was originally
+ * added, keep them optional for old user space programs to let
+ * them continue to work with drivers that do not need the
+ * additional information -- drivers must check!
+ */
+ if (info->attrs[NL80211_ATTR_SSID]) {
+ params.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
+ params.ssid_len =
+ nla_len(info->attrs[NL80211_ATTR_SSID]);
+ if (params.ssid_len == 0 ||
+ params.ssid_len > IEEE80211_MAX_SSID_LEN)
+ return -EINVAL;
+ }
+
+ if (info->attrs[NL80211_ATTR_HIDDEN_SSID]) {
+ params.hidden_ssid = nla_get_u32(
+ info->attrs[NL80211_ATTR_HIDDEN_SSID]);
+ if (params.hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE &&
+ params.hidden_ssid != NL80211_HIDDEN_SSID_ZERO_LEN &&
+ params.hidden_ssid != NL80211_HIDDEN_SSID_ZERO_CONTENTS)
+ return -EINVAL;
+ }
+
+ params.privacy = !!info->attrs[NL80211_ATTR_PRIVACY];
+
+ if (info->attrs[NL80211_ATTR_AUTH_TYPE]) {
+ params.auth_type = nla_get_u32(
+ info->attrs[NL80211_ATTR_AUTH_TYPE]);
+ if (!nl80211_valid_auth_type(rdev, params.auth_type,
+ NL80211_CMD_START_AP))
+ return -EINVAL;
+ } else
+ params.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
+
+ err = nl80211_crypto_settings(rdev, info, ¶ms.crypto,
+ NL80211_MAX_NR_CIPHER_SUITES);
+ if (err)
+ return err;
+
+ if (info->attrs[NL80211_ATTR_INACTIVITY_TIMEOUT]) {
+ if (!(rdev->wiphy.features & NL80211_FEATURE_INACTIVITY_TIMER))
+ return -EOPNOTSUPP;
+ params.inactivity_timeout = nla_get_u16(
+ info->attrs[NL80211_ATTR_INACTIVITY_TIMEOUT]);
+ }
+
+ if (info->attrs[NL80211_ATTR_P2P_CTWINDOW]) {
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
+ return -EINVAL;
+ params.p2p_ctwindow =
+ nla_get_u8(info->attrs[NL80211_ATTR_P2P_CTWINDOW]);
+ if (params.p2p_ctwindow > 127)
+ return -EINVAL;
+ if (params.p2p_ctwindow != 0 &&
+ !(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_CTWIN))
+ return -EINVAL;
+ }
+
+ if (info->attrs[NL80211_ATTR_P2P_OPPPS]) {
+ u8 tmp;
+
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
+ return -EINVAL;
+ tmp = nla_get_u8(info->attrs[NL80211_ATTR_P2P_OPPPS]);
+ if (tmp > 1)
+ return -EINVAL;
+ params.p2p_opp_ps = tmp;
+ if (params.p2p_opp_ps != 0 &&
+ !(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_OPPPS))
+ return -EINVAL;
+ }
+
+ if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
+ err = nl80211_parse_chandef(rdev, info, ¶ms.chandef);
+ if (err)
+ return err;
+ } else if (wdev->preset_chandef.chan) {
+ params.chandef = wdev->preset_chandef;
+ } else if (!nl80211_get_ap_channel(rdev, ¶ms))
+ return -EINVAL;
+
+ if (!cfg80211_reg_can_beacon_relax(&rdev->wiphy, ¶ms.chandef,
+ wdev->iftype))
+ return -EINVAL;
+
+ if (info->attrs[NL80211_ATTR_TX_RATES]) {
+ err = nl80211_parse_tx_bitrate_mask(info, ¶ms.beacon_rate);
+ if (err)
+ return err;
+
+ err = validate_beacon_tx_rate(rdev, params.chandef.chan->band,
+ ¶ms.beacon_rate);
+ if (err)
+ return err;
+ }
+
+ if (info->attrs[NL80211_ATTR_SMPS_MODE]) {
+ params.smps_mode =
+ nla_get_u8(info->attrs[NL80211_ATTR_SMPS_MODE]);
+ switch (params.smps_mode) {
+ case NL80211_SMPS_OFF:
+ break;
+ case NL80211_SMPS_STATIC:
+ if (!(rdev->wiphy.features &
+ NL80211_FEATURE_STATIC_SMPS))
+ return -EINVAL;
+ break;
+ case NL80211_SMPS_DYNAMIC:
+ if (!(rdev->wiphy.features &
+ NL80211_FEATURE_DYNAMIC_SMPS))
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+ } else {
+ params.smps_mode = NL80211_SMPS_OFF;
+ }
+
+ params.pbss = nla_get_flag(info->attrs[NL80211_ATTR_PBSS]);
+ if (params.pbss && !rdev->wiphy.bands[NL80211_BAND_60GHZ])
+ return -EOPNOTSUPP;
+
+ if (info->attrs[NL80211_ATTR_ACL_POLICY]) {
+ params.acl = parse_acl_data(&rdev->wiphy, info);
+ if (IS_ERR(params.acl))
+ return PTR_ERR(params.acl);
+ }
+
+ nl80211_calculate_ap_params(¶ms);
+
+ wdev_lock(wdev);
+ err = rdev_start_ap(rdev, dev, ¶ms);
+ if (!err) {
+ wdev->preset_chandef = params.chandef;
+ wdev->beacon_interval = params.beacon_interval;
+ wdev->chandef = params.chandef;
+ wdev->ssid_len = params.ssid_len;
+ memcpy(wdev->ssid, params.ssid, wdev->ssid_len);
+ }
+ wdev_unlock(wdev);
+
+ kfree(params.acl);
+
+ return err;
+}
+
+static int nl80211_set_beacon(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_beacon_data params;
+ int err;
+
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
+ dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
+ return -EOPNOTSUPP;
+
+ if (!rdev->ops->change_beacon)
+ return -EOPNOTSUPP;
+
+ if (!wdev->beacon_interval)
+ return -EINVAL;
+
+ err = nl80211_parse_beacon(info->attrs, ¶ms);
+ if (err)
+ return err;
+
+ wdev_lock(wdev);
+ err = rdev_change_beacon(rdev, dev, ¶ms);
+ wdev_unlock(wdev);
+
+ return err;
+}
+
+static int nl80211_stop_ap(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+
+ return cfg80211_stop_ap(rdev, dev, false);
+}
+
+static const struct nla_policy sta_flags_policy[NL80211_STA_FLAG_MAX + 1] = {
+ [NL80211_STA_FLAG_AUTHORIZED] = { .type = NLA_FLAG },
+ [NL80211_STA_FLAG_SHORT_PREAMBLE] = { .type = NLA_FLAG },
+ [NL80211_STA_FLAG_WME] = { .type = NLA_FLAG },
+ [NL80211_STA_FLAG_MFP] = { .type = NLA_FLAG },
+ [NL80211_STA_FLAG_AUTHENTICATED] = { .type = NLA_FLAG },
+ [NL80211_STA_FLAG_TDLS_PEER] = { .type = NLA_FLAG },
+};
+
+static int parse_station_flags(struct genl_info *info,
+ enum nl80211_iftype iftype,
+ struct station_parameters *params)
+{
+ struct nlattr *flags[NL80211_STA_FLAG_MAX + 1];
+ struct nlattr *nla;
+ int flag;
+
+ /*
+ * Try parsing the new attribute first so userspace
+ * can specify both for older kernels.
+ */
+ nla = info->attrs[NL80211_ATTR_STA_FLAGS2];
+ if (nla) {
+ struct nl80211_sta_flag_update *sta_flags;
+
+ sta_flags = nla_data(nla);
+ params->sta_flags_mask = sta_flags->mask;
+ params->sta_flags_set = sta_flags->set;
+ params->sta_flags_set &= params->sta_flags_mask;
+ if ((params->sta_flags_mask |
+ params->sta_flags_set) & BIT(__NL80211_STA_FLAG_INVALID))
+ return -EINVAL;
+ return 0;
+ }
+
+ /* if present, parse the old attribute */
+
+ nla = info->attrs[NL80211_ATTR_STA_FLAGS];
+ if (!nla)
+ return 0;
+
+ if (nla_parse_nested(flags, NL80211_STA_FLAG_MAX, nla,
+ sta_flags_policy, genl_info_extack(info)))
+ return -EINVAL;
+
+ /*
+ * Only allow certain flags for interface types so that
+ * other attributes are silently ignored. Remember that
+ * this is backward compatibility code with old userspace
+ * and shouldn't be hit in other cases anyway.
+ */
+ switch (iftype) {
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_AP_VLAN:
+ case NL80211_IFTYPE_P2P_GO:
+ params->sta_flags_mask = BIT(NL80211_STA_FLAG_AUTHORIZED) |
+ BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) |
+ BIT(NL80211_STA_FLAG_WME) |
+ BIT(NL80211_STA_FLAG_MFP);
+ break;
+ case NL80211_IFTYPE_P2P_CLIENT:
+ case NL80211_IFTYPE_STATION:
+ params->sta_flags_mask = BIT(NL80211_STA_FLAG_AUTHORIZED) |
+ BIT(NL80211_STA_FLAG_TDLS_PEER);
+ break;
+ case NL80211_IFTYPE_MESH_POINT:
+ params->sta_flags_mask = BIT(NL80211_STA_FLAG_AUTHENTICATED) |
+ BIT(NL80211_STA_FLAG_MFP) |
+ BIT(NL80211_STA_FLAG_AUTHORIZED);
+ default:
+ return -EINVAL;
+ }
+
+ for (flag = 1; flag <= NL80211_STA_FLAG_MAX; flag++) {
+ if (flags[flag]) {
+ params->sta_flags_set |= (1<<flag);
+
+ /* no longer support new API additions in old API */
+ if (flag > NL80211_STA_FLAG_MAX_OLD_API)
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info,
+ int attr)
+{
+ struct nlattr *rate;
+ u32 bitrate;
+ u16 bitrate_compat;
+ enum nl80211_rate_info rate_flg;
+
+ rate = nla_nest_start(msg, attr);
+ if (!rate)
+ return false;
+
+ /* cfg80211_calculate_bitrate will return 0 for mcs >= 32 */
+ bitrate = cfg80211_calculate_bitrate(info);
+ /* report 16-bit bitrate only if we can */
+ bitrate_compat = bitrate < (1UL << 16) ? bitrate : 0;
+ if (bitrate > 0 &&
+ nla_put_u32(msg, NL80211_RATE_INFO_BITRATE32, bitrate))
+ return false;
+ if (bitrate_compat > 0 &&
+ nla_put_u16(msg, NL80211_RATE_INFO_BITRATE, bitrate_compat))
+ return false;
+
+ switch (info->bw) {
+ case RATE_INFO_BW_5:
+ rate_flg = NL80211_RATE_INFO_5_MHZ_WIDTH;
+ break;
+ case RATE_INFO_BW_10:
+ rate_flg = NL80211_RATE_INFO_10_MHZ_WIDTH;
+ break;
+ default:
+ WARN_ON(1);
+ /* fall through */
+ case RATE_INFO_BW_20:
+ rate_flg = 0;
+ break;
+ case RATE_INFO_BW_40:
+ rate_flg = NL80211_RATE_INFO_40_MHZ_WIDTH;
+ break;
+ case RATE_INFO_BW_80:
+ rate_flg = NL80211_RATE_INFO_80_MHZ_WIDTH;
+ break;
+ case RATE_INFO_BW_160:
+ rate_flg = NL80211_RATE_INFO_160_MHZ_WIDTH;
+ break;
+ }
+
+ if (rate_flg && nla_put_flag(msg, rate_flg))
+ return false;
+
+ if (info->flags & RATE_INFO_FLAGS_MCS) {
+ if (nla_put_u8(msg, NL80211_RATE_INFO_MCS, info->mcs))
+ return false;
+ if (info->flags & RATE_INFO_FLAGS_SHORT_GI &&
+ nla_put_flag(msg, NL80211_RATE_INFO_SHORT_GI))
+ return false;
+ } else if (info->flags & RATE_INFO_FLAGS_VHT_MCS) {
+ if (nla_put_u8(msg, NL80211_RATE_INFO_VHT_MCS, info->mcs))
+ return false;
+ if (nla_put_u8(msg, NL80211_RATE_INFO_VHT_NSS, info->nss))
+ return false;
+ if (info->flags & RATE_INFO_FLAGS_SHORT_GI &&
+ nla_put_flag(msg, NL80211_RATE_INFO_SHORT_GI))
+ return false;
+ }
+
+ nla_nest_end(msg, rate);
+ return true;
+}
+
+static bool nl80211_put_signal(struct sk_buff *msg, u8 mask, s8 *signal,
+ int id)
+{
+ void *attr;
+ int i = 0;
+
+ if (!mask)
+ return true;
+
+ attr = nla_nest_start(msg, id);
+ if (!attr)
+ return false;
+
+ for (i = 0; i < IEEE80211_MAX_CHAINS; i++) {
+ if (!(mask & BIT(i)))
+ continue;
+
+ if (nla_put_u8(msg, i, signal[i]))
+ return false;
+ }
+
+ nla_nest_end(msg, attr);
+
+ return true;
+}
+
+static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid,
+ u32 seq, int flags,
+ struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ const u8 *mac_addr, struct station_info *sinfo)
+{
+ void *hdr;
+ struct nlattr *sinfoattr, *bss_param;
+
+ hdr = nl80211hdr_put(msg, portid, seq, flags, cmd);
+ if (!hdr)
+ return -1;
+
+ if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr) ||
+ nla_put_u32(msg, NL80211_ATTR_GENERATION, sinfo->generation))
+ goto nla_put_failure;
+
+ sinfoattr = nla_nest_start(msg, NL80211_ATTR_STA_INFO);
+ if (!sinfoattr)
+ goto nla_put_failure;
+
+#define PUT_SINFO(attr, memb, type) do { \
+ BUILD_BUG_ON(sizeof(type) == sizeof(u64)); \
+ if (sinfo->filled & (1ULL << NL80211_STA_INFO_ ## attr) && \
+ nla_put_ ## type(msg, NL80211_STA_INFO_ ## attr, \
+ sinfo->memb)) \
+ goto nla_put_failure; \
+ } while (0)
+#define PUT_SINFO_U64(attr, memb) do { \
+ if (sinfo->filled & (1ULL << NL80211_STA_INFO_ ## attr) && \
+ nla_put_u64_64bit(msg, NL80211_STA_INFO_ ## attr, \
+ sinfo->memb, NL80211_STA_INFO_PAD)) \
+ goto nla_put_failure; \
+ } while (0)
+
+ PUT_SINFO(CONNECTED_TIME, connected_time, u32);
+ PUT_SINFO(INACTIVE_TIME, inactive_time, u32);
+
+ if (sinfo->filled & (BIT(NL80211_STA_INFO_RX_BYTES) |
+ BIT(NL80211_STA_INFO_RX_BYTES64)) &&
+ nla_put_u32(msg, NL80211_STA_INFO_RX_BYTES,
+ (u32)sinfo->rx_bytes))
+ goto nla_put_failure;
+
+ if (sinfo->filled & (BIT(NL80211_STA_INFO_TX_BYTES) |
+ BIT(NL80211_STA_INFO_TX_BYTES64)) &&
+ nla_put_u32(msg, NL80211_STA_INFO_TX_BYTES,
+ (u32)sinfo->tx_bytes))
+ goto nla_put_failure;
+
+ PUT_SINFO_U64(RX_BYTES64, rx_bytes);
+ PUT_SINFO_U64(TX_BYTES64, tx_bytes);
+ PUT_SINFO(LLID, llid, u16);
+ PUT_SINFO(PLID, plid, u16);
+ PUT_SINFO(PLINK_STATE, plink_state, u8);
+ PUT_SINFO_U64(RX_DURATION, rx_duration);
+
+ switch (rdev->wiphy.signal_type) {
+ case CFG80211_SIGNAL_TYPE_MBM:
+ PUT_SINFO(SIGNAL, signal, u8);
+ PUT_SINFO(SIGNAL_AVG, signal_avg, u8);
+ break;
+ default:
+ break;
+ }
+ if (sinfo->filled & BIT(NL80211_STA_INFO_CHAIN_SIGNAL)) {
+ if (!nl80211_put_signal(msg, sinfo->chains,
+ sinfo->chain_signal,
+ NL80211_STA_INFO_CHAIN_SIGNAL))
+ goto nla_put_failure;
+ }
+ if (sinfo->filled & BIT(NL80211_STA_INFO_CHAIN_SIGNAL_AVG)) {
+ if (!nl80211_put_signal(msg, sinfo->chains,
+ sinfo->chain_signal_avg,
+ NL80211_STA_INFO_CHAIN_SIGNAL_AVG))
+ goto nla_put_failure;
+ }
+ if (sinfo->filled & BIT(NL80211_STA_INFO_TX_BITRATE)) {
+ if (!nl80211_put_sta_rate(msg, &sinfo->txrate,
+ NL80211_STA_INFO_TX_BITRATE))
+ goto nla_put_failure;
+ }
+ if (sinfo->filled & BIT(NL80211_STA_INFO_RX_BITRATE)) {
+ if (!nl80211_put_sta_rate(msg, &sinfo->rxrate,
+ NL80211_STA_INFO_RX_BITRATE))
+ goto nla_put_failure;
+ }
+
+ PUT_SINFO(RX_PACKETS, rx_packets, u32);
+ PUT_SINFO(TX_PACKETS, tx_packets, u32);
+ PUT_SINFO(TX_RETRIES, tx_retries, u32);
+ PUT_SINFO(TX_FAILED, tx_failed, u32);
+ PUT_SINFO(EXPECTED_THROUGHPUT, expected_throughput, u32);
+ PUT_SINFO(BEACON_LOSS, beacon_loss_count, u32);
+ PUT_SINFO(LOCAL_PM, local_pm, u32);
+ PUT_SINFO(PEER_PM, peer_pm, u32);
+ PUT_SINFO(NONPEER_PM, nonpeer_pm, u32);
+
+ if (sinfo->filled & BIT(NL80211_STA_INFO_BSS_PARAM)) {
+ bss_param = nla_nest_start(msg, NL80211_STA_INFO_BSS_PARAM);
+ if (!bss_param)
+ goto nla_put_failure;
+
+ if (((sinfo->bss_param.flags & BSS_PARAM_FLAGS_CTS_PROT) &&
+ nla_put_flag(msg, NL80211_STA_BSS_PARAM_CTS_PROT)) ||
+ ((sinfo->bss_param.flags & BSS_PARAM_FLAGS_SHORT_PREAMBLE) &&
+ nla_put_flag(msg, NL80211_STA_BSS_PARAM_SHORT_PREAMBLE)) ||
+ ((sinfo->bss_param.flags & BSS_PARAM_FLAGS_SHORT_SLOT_TIME) &&
+ nla_put_flag(msg, NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME)) ||
+ nla_put_u8(msg, NL80211_STA_BSS_PARAM_DTIM_PERIOD,
+ sinfo->bss_param.dtim_period) ||
+ nla_put_u16(msg, NL80211_STA_BSS_PARAM_BEACON_INTERVAL,
+ sinfo->bss_param.beacon_interval))
+ goto nla_put_failure;
+
+ nla_nest_end(msg, bss_param);
+ }
+ if ((sinfo->filled & BIT(NL80211_STA_INFO_STA_FLAGS)) &&
+ nla_put(msg, NL80211_STA_INFO_STA_FLAGS,
+ sizeof(struct nl80211_sta_flag_update),
+ &sinfo->sta_flags))
+ goto nla_put_failure;
+
+ PUT_SINFO_U64(T_OFFSET, t_offset);
+ PUT_SINFO_U64(RX_DROP_MISC, rx_dropped_misc);
+ PUT_SINFO_U64(BEACON_RX, rx_beacon);
+ PUT_SINFO(BEACON_SIGNAL_AVG, rx_beacon_signal_avg, u8);
+
+#undef PUT_SINFO
+#undef PUT_SINFO_U64
+
+ if (sinfo->filled & BIT(NL80211_STA_INFO_TID_STATS)) {
+ struct nlattr *tidsattr;
+ int tid;
+
+ tidsattr = nla_nest_start(msg, NL80211_STA_INFO_TID_STATS);
+ if (!tidsattr)
+ goto nla_put_failure;
+
+ for (tid = 0; tid < IEEE80211_NUM_TIDS + 1; tid++) {
+ struct cfg80211_tid_stats *tidstats;
+ struct nlattr *tidattr;
+
+ tidstats = &sinfo->pertid[tid];
+
+ if (!tidstats->filled)
+ continue;
+
+ tidattr = nla_nest_start(msg, tid + 1);
+ if (!tidattr)
+ goto nla_put_failure;
+
+#define PUT_TIDVAL_U64(attr, memb) do { \
+ if (tidstats->filled & BIT(NL80211_TID_STATS_ ## attr) && \
+ nla_put_u64_64bit(msg, NL80211_TID_STATS_ ## attr, \
+ tidstats->memb, NL80211_TID_STATS_PAD)) \
+ goto nla_put_failure; \
+ } while (0)
+
+ PUT_TIDVAL_U64(RX_MSDU, rx_msdu);
+ PUT_TIDVAL_U64(TX_MSDU, tx_msdu);
+ PUT_TIDVAL_U64(TX_MSDU_RETRIES, tx_msdu_retries);
+ PUT_TIDVAL_U64(TX_MSDU_FAILED, tx_msdu_failed);
+
+#undef PUT_TIDVAL_U64
+ nla_nest_end(msg, tidattr);
+ }
+
+ nla_nest_end(msg, tidsattr);
+ }
+
+ nla_nest_end(msg, sinfoattr);
+
+ if (sinfo->assoc_req_ies_len &&
+ nla_put(msg, NL80211_ATTR_IE, sinfo->assoc_req_ies_len,
+ sinfo->assoc_req_ies))
+ goto nla_put_failure;
+
+ genlmsg_end(msg, hdr);
+ return 0;
+
+ nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ return -EMSGSIZE;
+}
+
+static int nl80211_dump_station(struct sk_buff *skb,
+ struct netlink_callback *cb)
+{
+ struct station_info sinfo;
+ struct cfg80211_registered_device *rdev;
+ struct wireless_dev *wdev;
+ u8 mac_addr[ETH_ALEN];
+ int sta_idx = cb->args[2];
+ int err;
+
+ rtnl_lock();
+ err = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev);
+ if (err)
+ goto out_err;
+
+ if (!wdev->netdev) {
+ err = -EINVAL;
+ goto out_err;
+ }
+
+ if (!rdev->ops->dump_station) {
+ err = -EOPNOTSUPP;
+ goto out_err;
+ }
+
+ while (1) {
+ memset(&sinfo, 0, sizeof(sinfo));
+ err = rdev_dump_station(rdev, wdev->netdev, sta_idx,
+ mac_addr, &sinfo);
+ if (err == -ENOENT)
+ break;
+ if (err)
+ goto out_err;
+
+ if (nl80211_send_station(skb, NL80211_CMD_NEW_STATION,
+ NETLINK_CB_PORTID(cb->skb),
+ cb->nlh->nlmsg_seq, NLM_F_MULTI,
+ rdev, wdev->netdev, mac_addr,
+ &sinfo) < 0)
+ goto out;
+
+ sta_idx++;
+ }
+
+ out:
+ cb->args[2] = sta_idx;
+ err = skb->len;
+ out_err:
+ rtnl_unlock();
+
+ return err;
+}
+
+static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct station_info sinfo;
+ struct sk_buff *msg;
+ u8 *mac_addr = NULL;
+ int err;
+
+ memset(&sinfo, 0, sizeof(sinfo));
+
+ if (!info->attrs[NL80211_ATTR_MAC])
+ return -EINVAL;
+
+ mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
+
+ if (!rdev->ops->get_station)
+ return -EOPNOTSUPP;
+
+ err = rdev_get_station(rdev, dev, mac_addr, &sinfo);
+ if (err)
+ return err;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ if (nl80211_send_station(msg, NL80211_CMD_NEW_STATION,
+ genl_info_snd_portid(info), info->snd_seq, 0,
+ rdev, dev, mac_addr, &sinfo) < 0) {
+ nlmsg_free(msg);
+ return -ENOBUFS;
+ }
+
+ return genlmsg_reply(msg, info);
+}
+
+int cfg80211_check_station_change(struct wiphy *wiphy,
+ struct station_parameters *params,
+ enum cfg80211_station_type statype)
+{
+ if (params->listen_interval != -1 &&
+ statype != CFG80211_STA_AP_CLIENT_UNASSOC)
+ return -EINVAL;
+
+ if (params->support_p2p_ps != -1 &&
+ statype != CFG80211_STA_AP_CLIENT_UNASSOC)
+ return -EINVAL;
+
+ if (params->aid &&
+ !(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) &&
+ statype != CFG80211_STA_AP_CLIENT_UNASSOC)
+ return -EINVAL;
+
+ /* When you run into this, adjust the code below for the new flag */
+ BUILD_BUG_ON(NL80211_STA_FLAG_MAX != 7);
+
+ switch (statype) {
+ case CFG80211_STA_MESH_PEER_KERNEL:
+ case CFG80211_STA_MESH_PEER_USER:
+ /*
+ * No ignoring the TDLS flag here -- the userspace mesh
+ * code doesn't have the bug of including TDLS in the
+ * mask everywhere.
+ */
+ if (params->sta_flags_mask &
+ ~(BIT(NL80211_STA_FLAG_AUTHENTICATED) |
+ BIT(NL80211_STA_FLAG_MFP) |
+ BIT(NL80211_STA_FLAG_AUTHORIZED)))
+ return -EINVAL;
+ break;
+ case CFG80211_STA_TDLS_PEER_SETUP:
+ case CFG80211_STA_TDLS_PEER_ACTIVE:
+ if (!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)))
+ return -EINVAL;
+ /* ignore since it can't change */
+ params->sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
+ break;
+ default:
+ /* disallow mesh-specific things */
+ if (params->plink_action != NL80211_PLINK_ACTION_NO_ACTION)
+ return -EINVAL;
+ if (params->local_pm)
+ return -EINVAL;
+ if (params->sta_modify_mask & STATION_PARAM_APPLY_PLINK_STATE)
+ return -EINVAL;
+ }
+
+ if (statype != CFG80211_STA_TDLS_PEER_SETUP &&
+ statype != CFG80211_STA_TDLS_PEER_ACTIVE) {
+ /* TDLS can't be set, ... */
+ if (params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
+ return -EINVAL;
+ /*
+ * ... but don't bother the driver with it. This works around
+ * a hostapd/wpa_supplicant issue -- it always includes the
+ * TLDS_PEER flag in the mask even for AP mode.
+ */
+ params->sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
+ }
+
+ if (statype != CFG80211_STA_TDLS_PEER_SETUP &&
+ statype != CFG80211_STA_AP_CLIENT_UNASSOC) {
+ /* reject other things that can't change */
+ if (params->sta_modify_mask & STATION_PARAM_APPLY_UAPSD)
+ return -EINVAL;
+ if (params->sta_modify_mask & STATION_PARAM_APPLY_CAPABILITY)
+ return -EINVAL;
+ if (params->supported_rates)
+ return -EINVAL;
+ if (params->ext_capab || params->ht_capa || params->vht_capa)
+ return -EINVAL;
+ }
+
+ if (statype != CFG80211_STA_AP_CLIENT &&
+ statype != CFG80211_STA_AP_CLIENT_UNASSOC) {
+ if (params->vlan)
+ return -EINVAL;
+ }
+
+ switch (statype) {
+ case CFG80211_STA_AP_MLME_CLIENT:
+ /* Use this only for authorizing/unauthorizing a station */
+ if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))
+ return -EOPNOTSUPP;
+ break;
+ case CFG80211_STA_AP_CLIENT:
+ case CFG80211_STA_AP_CLIENT_UNASSOC:
+ /* accept only the listed bits */
+ if (params->sta_flags_mask &
+ ~(BIT(NL80211_STA_FLAG_AUTHORIZED) |
+ BIT(NL80211_STA_FLAG_AUTHENTICATED) |
+ BIT(NL80211_STA_FLAG_ASSOCIATED) |
+ BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) |
+ BIT(NL80211_STA_FLAG_WME) |
+ BIT(NL80211_STA_FLAG_MFP)))
+ return -EINVAL;
+
+ /* but authenticated/associated only if driver handles it */
+ if (!(wiphy->features & NL80211_FEATURE_FULL_AP_CLIENT_STATE) &&
+ params->sta_flags_mask &
+ (BIT(NL80211_STA_FLAG_AUTHENTICATED) |
+ BIT(NL80211_STA_FLAG_ASSOCIATED)))
+ return -EINVAL;
+ break;
+ case CFG80211_STA_IBSS:
+ case CFG80211_STA_AP_STA:
+ /* reject any changes other than AUTHORIZED */
+ if (params->sta_flags_mask & ~BIT(NL80211_STA_FLAG_AUTHORIZED))
+ return -EINVAL;
+ break;
+ case CFG80211_STA_TDLS_PEER_SETUP:
+ /* reject any changes other than AUTHORIZED or WME */
+ if (params->sta_flags_mask & ~(BIT(NL80211_STA_FLAG_AUTHORIZED) |
+ BIT(NL80211_STA_FLAG_WME)))
+ return -EINVAL;
+ /* force (at least) rates when authorizing */
+ if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED) &&
+ !params->supported_rates)
+ return -EINVAL;
+ break;
+ case CFG80211_STA_TDLS_PEER_ACTIVE:
+ /* reject any changes */
+ return -EINVAL;
+ case CFG80211_STA_MESH_PEER_KERNEL:
+ if (params->sta_modify_mask & STATION_PARAM_APPLY_PLINK_STATE)
+ return -EINVAL;
+ break;
+ case CFG80211_STA_MESH_PEER_USER:
+ if (params->plink_action != NL80211_PLINK_ACTION_NO_ACTION &&
+ params->plink_action != NL80211_PLINK_ACTION_BLOCK)
+ return -EINVAL;
+ break;
+ }
+
+ /*
+ * Older kernel versions ignored this attribute entirely, so don't
+ * reject attempts to update it but mark it as unused instead so the
+ * driver won't look at the data.
+ */
+ if (statype != CFG80211_STA_AP_CLIENT_UNASSOC &&
+ statype != CFG80211_STA_TDLS_PEER_SETUP)
+ params->opmode_notif_used = false;
+
+ return 0;
+}
+EXPORT_SYMBOL(cfg80211_check_station_change);
+
+/*
+ * Get vlan interface making sure it is running and on the right wiphy.
+ */
+static struct net_device *get_vlan(struct genl_info *info,
+ struct cfg80211_registered_device *rdev)
+{
+ struct nlattr *vlanattr = info->attrs[NL80211_ATTR_STA_VLAN];
+ struct net_device *v;
+ int ret;
+
+ if (!vlanattr)
+ return NULL;
+
+ v = dev_get_by_index(genl_info_net(info), nla_get_u32(vlanattr));
+ if (!v)
+ return ERR_PTR(-ENODEV);
+
+ if (!v->ieee80211_ptr || v->ieee80211_ptr->wiphy != &rdev->wiphy) {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ if (v->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
+ v->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
+ v->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ if (!netif_running(v)) {
+ ret = -ENETDOWN;
+ goto error;
+ }
+
+ return v;
+ error:
+ dev_put(v);
+ return ERR_PTR(ret);
+}
+
+static const struct nla_policy
+nl80211_sta_wme_policy[NL80211_STA_WME_MAX + 1] = {
+ [NL80211_STA_WME_UAPSD_QUEUES] = { .type = NLA_U8 },
+ [NL80211_STA_WME_MAX_SP] = { .type = NLA_U8 },
+};
+
+static int nl80211_parse_sta_wme(struct genl_info *info,
+ struct station_parameters *params)
+{
+ struct nlattr *tb[NL80211_STA_WME_MAX + 1];
+ struct nlattr *nla;
+ int err;
+
+ /* parse WME attributes if present */
+ if (!info->attrs[NL80211_ATTR_STA_WME])
+ return 0;
+
+ nla = info->attrs[NL80211_ATTR_STA_WME];
+ err = nla_parse_nested(tb, NL80211_STA_WME_MAX, nla,
+ nl80211_sta_wme_policy, genl_info_extack(info));
+ if (err)
+ return err;
+
+ if (tb[NL80211_STA_WME_UAPSD_QUEUES])
+ params->uapsd_queues = nla_get_u8(
+ tb[NL80211_STA_WME_UAPSD_QUEUES]);
+ if (params->uapsd_queues & ~IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK)
+ return -EINVAL;
+
+ if (tb[NL80211_STA_WME_MAX_SP])
+ params->max_sp = nla_get_u8(tb[NL80211_STA_WME_MAX_SP]);
+
+ if (params->max_sp & ~IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK)
+ return -EINVAL;
+
+ params->sta_modify_mask |= STATION_PARAM_APPLY_UAPSD;
+
+ return 0;
+}
+
+static int nl80211_parse_sta_channel_info(struct genl_info *info,
+ struct station_parameters *params)
+{
+ if (info->attrs[NL80211_ATTR_STA_SUPPORTED_CHANNELS]) {
+ params->supported_channels =
+ nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_CHANNELS]);
+ params->supported_channels_len =
+ nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_CHANNELS]);
+ /*
+ * Need to include at least one (first channel, number of
+ * channels) tuple for each subband, and must have proper
+ * tuples for the rest of the data as well.
+ */
+ if (params->supported_channels_len < 2)
+ return -EINVAL;
+ if (params->supported_channels_len % 2)
+ return -EINVAL;
+ }
+
+ if (info->attrs[NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES]) {
+ params->supported_oper_classes =
+ nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES]);
+ params->supported_oper_classes_len =
+ nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES]);
+ /*
+ * The value of the Length field of the Supported Operating
+ * Classes element is between 2 and 253.
+ */
+ if (params->supported_oper_classes_len < 2 ||
+ params->supported_oper_classes_len > 253)
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int nl80211_set_station_tdls(struct genl_info *info,
+ struct station_parameters *params)
+{
+ int err;
+ /* Dummy STA entry gets updated once the peer capabilities are known */
+ if (info->attrs[NL80211_ATTR_PEER_AID])
+ params->aid = nla_get_u16(info->attrs[NL80211_ATTR_PEER_AID]);
+ if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
+ params->ht_capa =
+ nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
+ if (info->attrs[NL80211_ATTR_VHT_CAPABILITY])
+ params->vht_capa =
+ nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]);
+
+ err = nl80211_parse_sta_channel_info(info, params);
+ if (err)
+ return err;
+
+ return nl80211_parse_sta_wme(info, params);
+}
+
+static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct station_parameters params;
+ u8 *mac_addr;
+ int err;
+
+ memset(¶ms, 0, sizeof(params));
+
+ if (!rdev->ops->change_station)
+ return -EOPNOTSUPP;
+
+ /*
+ * AID and listen_interval properties can be set only for unassociated
+ * station. Include these parameters here and will check them in
+ * cfg80211_check_station_change().
+ */
+ if (info->attrs[NL80211_ATTR_STA_AID])
+ params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]);
+
+ if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
+ params.listen_interval =
+ nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
+ else
+ params.listen_interval = -1;
+
+ if (info->attrs[NL80211_ATTR_STA_SUPPORT_P2P_PS]) {
+ u8 tmp;
+
+ tmp = nla_get_u8(info->attrs[NL80211_ATTR_STA_SUPPORT_P2P_PS]);
+ if (tmp >= NUM_NL80211_P2P_PS_STATUS)
+ return -EINVAL;
+
+ params.support_p2p_ps = tmp;
+ } else {
+ params.support_p2p_ps = -1;
+ }
+
+ if (!info->attrs[NL80211_ATTR_MAC])
+ return -EINVAL;
+
+ mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
+
+ if (info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) {
+ params.supported_rates =
+ nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
+ params.supported_rates_len =
+ nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
+ }
+
+ if (info->attrs[NL80211_ATTR_STA_CAPABILITY]) {
+ params.capability =
+ nla_get_u16(info->attrs[NL80211_ATTR_STA_CAPABILITY]);
+ params.sta_modify_mask |= STATION_PARAM_APPLY_CAPABILITY;
+ }
+
+ if (info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]) {
+ params.ext_capab =
+ nla_data(info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]);
+ params.ext_capab_len =
+ nla_len(info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]);
+ }
+
+ if (parse_station_flags(info, dev->ieee80211_ptr->iftype, ¶ms))
+ return -EINVAL;
+
+ if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION]) {
+ params.plink_action =
+ nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
+ if (params.plink_action >= NUM_NL80211_PLINK_ACTIONS)
+ return -EINVAL;
+ }
+
+ if (info->attrs[NL80211_ATTR_STA_PLINK_STATE]) {
+ params.plink_state =
+ nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_STATE]);
+ if (params.plink_state >= NUM_NL80211_PLINK_STATES)
+ return -EINVAL;
+ if (info->attrs[NL80211_ATTR_MESH_PEER_AID]) {
+ params.peer_aid = nla_get_u16(
+ info->attrs[NL80211_ATTR_MESH_PEER_AID]);
+ if (params.peer_aid > IEEE80211_MAX_AID)
+ return -EINVAL;
+ }
+ params.sta_modify_mask |= STATION_PARAM_APPLY_PLINK_STATE;
+ }
+
+ if (info->attrs[NL80211_ATTR_LOCAL_MESH_POWER_MODE]) {
+ enum nl80211_mesh_power_mode pm = nla_get_u32(
+ info->attrs[NL80211_ATTR_LOCAL_MESH_POWER_MODE]);
+
+ if (pm <= NL80211_MESH_POWER_UNKNOWN ||
+ pm > NL80211_MESH_POWER_MAX)
+ return -EINVAL;
+
+ params.local_pm = pm;
+ }
+
+ if (info->attrs[NL80211_ATTR_OPMODE_NOTIF]) {
+ params.opmode_notif_used = true;
+ params.opmode_notif =
+ nla_get_u8(info->attrs[NL80211_ATTR_OPMODE_NOTIF]);
+ }
+
+ /* Include parameters for TDLS peer (will check later) */
+ err = nl80211_set_station_tdls(info, ¶ms);
+ if (err)
+ return err;
+
+ params.vlan = get_vlan(info, rdev);
+ if (IS_ERR(params.vlan))
+ return PTR_ERR(params.vlan);
+
+ switch (dev->ieee80211_ptr->iftype) {
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_AP_VLAN:
+ case NL80211_IFTYPE_P2P_GO:
+ case NL80211_IFTYPE_P2P_CLIENT:
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_MESH_POINT:
+ break;
+ default:
+ err = -EOPNOTSUPP;
+ goto out_put_vlan;
+ }
+
+ /* driver will call cfg80211_check_station_change() */
+ err = rdev_change_station(rdev, dev, mac_addr, ¶ms);
+
+ out_put_vlan:
+ if (params.vlan)
+ dev_put(params.vlan);
+
+ return err;
+}
+
+static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ int err;
+ struct net_device *dev = info->user_ptr[1];
+ struct station_parameters params;
+ u8 *mac_addr = NULL;
+ u32 auth_assoc = BIT(NL80211_STA_FLAG_AUTHENTICATED) |
+ BIT(NL80211_STA_FLAG_ASSOCIATED);
+
+ memset(¶ms, 0, sizeof(params));
+
+ if (!rdev->ops->add_station)
+ return -EOPNOTSUPP;
+
+ if (!info->attrs[NL80211_ATTR_MAC])
+ return -EINVAL;
+
+ if (!info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
+ return -EINVAL;
+
+ if (!info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES])
+ return -EINVAL;
+
+ if (!info->attrs[NL80211_ATTR_STA_AID] &&
+ !info->attrs[NL80211_ATTR_PEER_AID])
+ return -EINVAL;
+
+ mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
+ params.supported_rates =
+ nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
+ params.supported_rates_len =
+ nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
+ params.listen_interval =
+ nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
+
+ if (info->attrs[NL80211_ATTR_STA_SUPPORT_P2P_PS]) {
+ u8 tmp;
+
+ tmp = nla_get_u8(info->attrs[NL80211_ATTR_STA_SUPPORT_P2P_PS]);
+ if (tmp >= NUM_NL80211_P2P_PS_STATUS)
+ return -EINVAL;
+
+ params.support_p2p_ps = tmp;
+ } else {
+ /*
+ * if not specified, assume it's supported for P2P GO interface,
+ * and is NOT supported for AP interface
+ */
+ params.support_p2p_ps =
+ dev->ieee80211_ptr->iftype == NL80211_IFTYPE_P2P_GO;
+ }
+
+ if (info->attrs[NL80211_ATTR_PEER_AID])
+ params.aid = nla_get_u16(info->attrs[NL80211_ATTR_PEER_AID]);
+ else
+ params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]);
+ if (!params.aid || params.aid > IEEE80211_MAX_AID)
+ return -EINVAL;
+
+ if (info->attrs[NL80211_ATTR_STA_CAPABILITY]) {
+ params.capability =
+ nla_get_u16(info->attrs[NL80211_ATTR_STA_CAPABILITY]);
+ params.sta_modify_mask |= STATION_PARAM_APPLY_CAPABILITY;
+ }
+
+ if (info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]) {
+ params.ext_capab =
+ nla_data(info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]);
+ params.ext_capab_len =
+ nla_len(info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]);
+ }
+
+ if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
+ params.ht_capa =
+ nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
+
+ if (info->attrs[NL80211_ATTR_VHT_CAPABILITY])
+ params.vht_capa =
+ nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]);
+
+ if (info->attrs[NL80211_ATTR_OPMODE_NOTIF]) {
+ params.opmode_notif_used = true;
+ params.opmode_notif =
+ nla_get_u8(info->attrs[NL80211_ATTR_OPMODE_NOTIF]);
+ }
+
+ if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION]) {
+ params.plink_action =
+ nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
+ if (params.plink_action >= NUM_NL80211_PLINK_ACTIONS)
+ return -EINVAL;
+ }
+
+ err = nl80211_parse_sta_channel_info(info, ¶ms);
+ if (err)
+ return err;
+
+ err = nl80211_parse_sta_wme(info, ¶ms);
+ if (err)
+ return err;
+
+ if (parse_station_flags(info, dev->ieee80211_ptr->iftype, ¶ms))
+ return -EINVAL;
+
+ /* HT/VHT requires QoS, but if we don't have that just ignore HT/VHT
+ * as userspace might just pass through the capabilities from the IEs
+ * directly, rather than enforcing this restriction and returning an
+ * error in this case.
+ */
+ if (!(params.sta_flags_set & BIT(NL80211_STA_FLAG_WME))) {
+ params.ht_capa = NULL;
+ params.vht_capa = NULL;
+ }
+
+ /* When you run into this, adjust the code below for the new flag */
+ BUILD_BUG_ON(NL80211_STA_FLAG_MAX != 7);
+
+ switch (dev->ieee80211_ptr->iftype) {
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_AP_VLAN:
+ case NL80211_IFTYPE_P2P_GO:
+ /* ignore WME attributes if iface/sta is not capable */
+ if (!(rdev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) ||
+ !(params.sta_flags_set & BIT(NL80211_STA_FLAG_WME)))
+ params.sta_modify_mask &= ~STATION_PARAM_APPLY_UAPSD;
+
+ /* TDLS peers cannot be added */
+ if ((params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) ||
+ info->attrs[NL80211_ATTR_PEER_AID])
+ return -EINVAL;
+ /* but don't bother the driver with it */
+ params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
+
+ /* allow authenticated/associated only if driver handles it */
+ if (!(rdev->wiphy.features &
+ NL80211_FEATURE_FULL_AP_CLIENT_STATE) &&
+ params.sta_flags_mask & auth_assoc)
+ return -EINVAL;
+
+ /* Older userspace, or userspace wanting to be compatible with
+ * !NL80211_FEATURE_FULL_AP_CLIENT_STATE, will not set the auth
+ * and assoc flags in the mask, but assumes the station will be
+ * added as associated anyway since this was the required driver
+ * behaviour before NL80211_FEATURE_FULL_AP_CLIENT_STATE was
+ * introduced.
+ * In order to not bother drivers with this quirk in the API
+ * set the flags in both the mask and set for new stations in
+ * this case.
+ */
+ if (!(params.sta_flags_mask & auth_assoc)) {
+ params.sta_flags_mask |= auth_assoc;
+ params.sta_flags_set |= auth_assoc;
+ }
+
+ /* must be last in here for error handling */
+ params.vlan = get_vlan(info, rdev);
+ if (IS_ERR(params.vlan))
+ return PTR_ERR(params.vlan);
+ break;
+ case NL80211_IFTYPE_MESH_POINT:
+ /* ignore uAPSD data */
+ params.sta_modify_mask &= ~STATION_PARAM_APPLY_UAPSD;
+
+ /* associated is disallowed */
+ if (params.sta_flags_mask & BIT(NL80211_STA_FLAG_ASSOCIATED))
+ return -EINVAL;
+ /* TDLS peers cannot be added */
+ if ((params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) ||
+ info->attrs[NL80211_ATTR_PEER_AID])
+ return -EINVAL;
+ break;
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_P2P_CLIENT:
+ /* ignore uAPSD data */
+ params.sta_modify_mask &= ~STATION_PARAM_APPLY_UAPSD;
+
+ /* these are disallowed */
+ if (params.sta_flags_mask &
+ (BIT(NL80211_STA_FLAG_ASSOCIATED) |
+ BIT(NL80211_STA_FLAG_AUTHENTICATED)))
+ return -EINVAL;
+ /* Only TDLS peers can be added */
+ if (!(params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)))
+ return -EINVAL;
+ /* Can only add if TDLS ... */
+ if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS))
+ return -EOPNOTSUPP;
+ /* ... with external setup is supported */
+ if (!(rdev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP))
+ return -EOPNOTSUPP;
+ /*
+ * Older wpa_supplicant versions always mark the TDLS peer
+ * as authorized, but it shouldn't yet be.
+ */
+ params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_AUTHORIZED);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ /* be aware of params.vlan when changing code here */
+
+ err = rdev_add_station(rdev, dev, mac_addr, ¶ms);
+
+ if (params.vlan)
+ dev_put(params.vlan);
+ return err;
+}
+
+static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct station_del_parameters params;
+
+ memset(¶ms, 0, sizeof(params));
+
+ if (info->attrs[NL80211_ATTR_MAC])
+ params.mac = nla_data(info->attrs[NL80211_ATTR_MAC]);
+
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
+ dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
+ dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT &&
+ dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
+ return -EINVAL;
+
+ if (!rdev->ops->del_station)
+ return -EOPNOTSUPP;
+
+ if (info->attrs[NL80211_ATTR_MGMT_SUBTYPE]) {
+ params.subtype =
+ nla_get_u8(info->attrs[NL80211_ATTR_MGMT_SUBTYPE]);
+ if (params.subtype != IEEE80211_STYPE_DISASSOC >> 4 &&
+ params.subtype != IEEE80211_STYPE_DEAUTH >> 4)
+ return -EINVAL;
+ } else {
+ /* Default to Deauthentication frame */
+ params.subtype = IEEE80211_STYPE_DEAUTH >> 4;
+ }
+
+ if (info->attrs[NL80211_ATTR_REASON_CODE]) {
+ params.reason_code =
+ nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
+ if (params.reason_code == 0)
+ return -EINVAL; /* 0 is reserved */
+ } else {
+ /* Default to reason code 2 */
+ params.reason_code = WLAN_REASON_PREV_AUTH_NOT_VALID;
+ }
+
+ return rdev_del_station(rdev, dev, ¶ms);
+}
+
+static int nl80211_send_mpath(struct sk_buff *msg, u32 portid, u32 seq,
+ int flags, struct net_device *dev,
+ u8 *dst, u8 *next_hop,
+ struct mpath_info *pinfo)
+{
+ void *hdr;
+ struct nlattr *pinfoattr;
+
+ hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_MPATH);
+ if (!hdr)
+ return -1;
+
+ if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, dst) ||
+ nla_put(msg, NL80211_ATTR_MPATH_NEXT_HOP, ETH_ALEN, next_hop) ||
+ nla_put_u32(msg, NL80211_ATTR_GENERATION, pinfo->generation))
+ goto nla_put_failure;
+
+ pinfoattr = nla_nest_start(msg, NL80211_ATTR_MPATH_INFO);
+ if (!pinfoattr)
+ goto nla_put_failure;
+ if ((pinfo->filled & MPATH_INFO_FRAME_QLEN) &&
+ nla_put_u32(msg, NL80211_MPATH_INFO_FRAME_QLEN,
+ pinfo->frame_qlen))
+ goto nla_put_failure;
+ if (((pinfo->filled & MPATH_INFO_SN) &&
+ nla_put_u32(msg, NL80211_MPATH_INFO_SN, pinfo->sn)) ||
+ ((pinfo->filled & MPATH_INFO_METRIC) &&
+ nla_put_u32(msg, NL80211_MPATH_INFO_METRIC,
+ pinfo->metric)) ||
+ ((pinfo->filled & MPATH_INFO_EXPTIME) &&
+ nla_put_u32(msg, NL80211_MPATH_INFO_EXPTIME,
+ pinfo->exptime)) ||
+ ((pinfo->filled & MPATH_INFO_FLAGS) &&
+ nla_put_u8(msg, NL80211_MPATH_INFO_FLAGS,
+ pinfo->flags)) ||
+ ((pinfo->filled & MPATH_INFO_DISCOVERY_TIMEOUT) &&
+ nla_put_u32(msg, NL80211_MPATH_INFO_DISCOVERY_TIMEOUT,
+ pinfo->discovery_timeout)) ||
+ ((pinfo->filled & MPATH_INFO_DISCOVERY_RETRIES) &&
+ nla_put_u8(msg, NL80211_MPATH_INFO_DISCOVERY_RETRIES,
+ pinfo->discovery_retries)))
+ goto nla_put_failure;
+
+ nla_nest_end(msg, pinfoattr);
+
+ genlmsg_end(msg, hdr);
+ return 0;
+
+ nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ return -EMSGSIZE;
+}
+
+static int nl80211_dump_mpath(struct sk_buff *skb,
+ struct netlink_callback *cb)
+{
+ struct mpath_info pinfo;
+ struct cfg80211_registered_device *rdev;
+ struct wireless_dev *wdev;
+ u8 dst[ETH_ALEN];
+ u8 next_hop[ETH_ALEN];
+ int path_idx = cb->args[2];
+ int err;
+
+ rtnl_lock();
+ err = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev);
+ if (err)
+ goto out_err;
+
+ if (!rdev->ops->dump_mpath) {
+ err = -EOPNOTSUPP;
+ goto out_err;
+ }
+
+ if (wdev->iftype != NL80211_IFTYPE_MESH_POINT) {
+ err = -EOPNOTSUPP;
+ goto out_err;
+ }
+
+ while (1) {
+ err = rdev_dump_mpath(rdev, wdev->netdev, path_idx, dst,
+ next_hop, &pinfo);
+ if (err == -ENOENT)
+ break;
+ if (err)
+ goto out_err;
+
+ if (nl80211_send_mpath(skb, NETLINK_CB_PORTID(cb->skb),
+ cb->nlh->nlmsg_seq, NLM_F_MULTI,
+ wdev->netdev, dst, next_hop,
+ &pinfo) < 0)
+ goto out;
+
+ path_idx++;
+ }
+
+ out:
+ cb->args[2] = path_idx;
+ err = skb->len;
+ out_err:
+ rtnl_unlock();
+ return err;
+}
+
+static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ int err;
+ struct net_device *dev = info->user_ptr[1];
+ struct mpath_info pinfo;
+ struct sk_buff *msg;
+ u8 *dst = NULL;
+ u8 next_hop[ETH_ALEN];
+
+ memset(&pinfo, 0, sizeof(pinfo));
+
+ if (!info->attrs[NL80211_ATTR_MAC])
+ return -EINVAL;
+
+ dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
+
+ if (!rdev->ops->get_mpath)
+ return -EOPNOTSUPP;
+
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
+ return -EOPNOTSUPP;
+
+ err = rdev_get_mpath(rdev, dev, dst, next_hop, &pinfo);
+ if (err)
+ return err;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ if (nl80211_send_mpath(msg, genl_info_snd_portid(info), info->snd_seq, 0,
+ dev, dst, next_hop, &pinfo) < 0) {
+ nlmsg_free(msg);
+ return -ENOBUFS;
+ }
+
+ return genlmsg_reply(msg, info);
+}
+
+static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ u8 *dst = NULL;
+ u8 *next_hop = NULL;
+
+ if (!info->attrs[NL80211_ATTR_MAC])
+ return -EINVAL;
+
+ if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP])
+ return -EINVAL;
+
+ dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
+ next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]);
+
+ if (!rdev->ops->change_mpath)
+ return -EOPNOTSUPP;
+
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
+ return -EOPNOTSUPP;
+
+ return rdev_change_mpath(rdev, dev, dst, next_hop);
+}
+
+static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ u8 *dst = NULL;
+ u8 *next_hop = NULL;
+
+ if (!info->attrs[NL80211_ATTR_MAC])
+ return -EINVAL;
+
+ if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP])
+ return -EINVAL;
+
+ dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
+ next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]);
+
+ if (!rdev->ops->add_mpath)
+ return -EOPNOTSUPP;
+
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
+ return -EOPNOTSUPP;
+
+ return rdev_add_mpath(rdev, dev, dst, next_hop);
+}
+
+static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ u8 *dst = NULL;
+
+ if (info->attrs[NL80211_ATTR_MAC])
+ dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
+
+ if (!rdev->ops->del_mpath)
+ return -EOPNOTSUPP;
+
+ return rdev_del_mpath(rdev, dev, dst);
+}
+
+static int nl80211_get_mpp(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ int err;
+ struct net_device *dev = info->user_ptr[1];
+ struct mpath_info pinfo;
+ struct sk_buff *msg;
+ u8 *dst = NULL;
+ u8 mpp[ETH_ALEN];
+
+ memset(&pinfo, 0, sizeof(pinfo));
+
+ if (!info->attrs[NL80211_ATTR_MAC])
+ return -EINVAL;
+
+ dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
+
+ if (!rdev->ops->get_mpp)
+ return -EOPNOTSUPP;
+
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
+ return -EOPNOTSUPP;
+
+ err = rdev_get_mpp(rdev, dev, dst, mpp, &pinfo);
+ if (err)
+ return err;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ if (nl80211_send_mpath(msg, genl_info_snd_portid(info), info->snd_seq, 0,
+ dev, dst, mpp, &pinfo) < 0) {
+ nlmsg_free(msg);
+ return -ENOBUFS;
+ }
+
+ return genlmsg_reply(msg, info);
+}
+
+static int nl80211_dump_mpp(struct sk_buff *skb,
+ struct netlink_callback *cb)
+{
+ struct mpath_info pinfo;
+ struct cfg80211_registered_device *rdev;
+ struct wireless_dev *wdev;
+ u8 dst[ETH_ALEN];
+ u8 mpp[ETH_ALEN];
+ int path_idx = cb->args[2];
+ int err;
+
+ rtnl_lock();
+ err = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev);
+ if (err)
+ goto out_err;
+
+ if (!rdev->ops->dump_mpp) {
+ err = -EOPNOTSUPP;
+ goto out_err;
+ }
+
+ if (wdev->iftype != NL80211_IFTYPE_MESH_POINT) {
+ err = -EOPNOTSUPP;
+ goto out_err;
+ }
+
+ while (1) {
+ err = rdev_dump_mpp(rdev, wdev->netdev, path_idx, dst,
+ mpp, &pinfo);
+ if (err == -ENOENT)
+ break;
+ if (err)
+ goto out_err;
+
+ if (nl80211_send_mpath(skb, NETLINK_CB_PORTID(cb->skb),
+ cb->nlh->nlmsg_seq, NLM_F_MULTI,
+ wdev->netdev, dst, mpp,
+ &pinfo) < 0)
+ goto out;
+
+ path_idx++;
+ }
+
+ out:
+ cb->args[2] = path_idx;
+ err = skb->len;
+ out_err:
+ rtnl_unlock();
+ return err;
+}
+
+static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct bss_parameters params;
+ int err;
+
+ memset(¶ms, 0, sizeof(params));
+ /* default to not changing parameters */
+ params.use_cts_prot = -1;
+ params.use_short_preamble = -1;
+ params.use_short_slot_time = -1;
+ params.ap_isolate = -1;
+ params.ht_opmode = -1;
+ params.p2p_ctwindow = -1;
+ params.p2p_opp_ps = -1;
+
+ if (info->attrs[NL80211_ATTR_BSS_CTS_PROT])
+ params.use_cts_prot =
+ nla_get_u8(info->attrs[NL80211_ATTR_BSS_CTS_PROT]);
+ if (info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE])
+ params.use_short_preamble =
+ nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE]);
+ if (info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME])
+ params.use_short_slot_time =
+ nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]);
+ if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) {
+ params.basic_rates =
+ nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
+ params.basic_rates_len =
+ nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
+ }
+ if (info->attrs[NL80211_ATTR_AP_ISOLATE])
+ params.ap_isolate = !!nla_get_u8(info->attrs[NL80211_ATTR_AP_ISOLATE]);
+ if (info->attrs[NL80211_ATTR_BSS_HT_OPMODE])
+ params.ht_opmode =
+ nla_get_u16(info->attrs[NL80211_ATTR_BSS_HT_OPMODE]);
+
+ if (info->attrs[NL80211_ATTR_P2P_CTWINDOW]) {
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
+ return -EINVAL;
+ params.p2p_ctwindow =
+ nla_get_s8(info->attrs[NL80211_ATTR_P2P_CTWINDOW]);
+ if (params.p2p_ctwindow < 0)
+ return -EINVAL;
+ if (params.p2p_ctwindow != 0 &&
+ !(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_CTWIN))
+ return -EINVAL;
+ }
+
+ if (info->attrs[NL80211_ATTR_P2P_OPPPS]) {
+ u8 tmp;
+
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
+ return -EINVAL;
+ tmp = nla_get_u8(info->attrs[NL80211_ATTR_P2P_OPPPS]);
+ if (tmp > 1)
+ return -EINVAL;
+ params.p2p_opp_ps = tmp;
+ if (params.p2p_opp_ps &&
+ !(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_OPPPS))
+ return -EINVAL;
+ }
+
+ if (!rdev->ops->change_bss)
+ return -EOPNOTSUPP;
+
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
+ dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
+ return -EOPNOTSUPP;
+
+ wdev_lock(wdev);
+ err = rdev_change_bss(rdev, dev, ¶ms);
+ wdev_unlock(wdev);
+
+ return err;
+}
+
+static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
+{
+ char *data = NULL;
+ bool is_indoor;
+ enum nl80211_user_reg_hint_type user_reg_hint_type;
+ u32 owner_nlportid;
+
+ /*
+ * You should only get this when cfg80211 hasn't yet initialized
+ * completely when built-in to the kernel right between the time
+ * window between nl80211_init() and regulatory_init(), if that is
+ * even possible.
+ */
+ if (unlikely(!rcu_access_pointer(cfg80211_regdomain)))
+ return -EINPROGRESS;
+
+ if (info->attrs[NL80211_ATTR_USER_REG_HINT_TYPE])
+ user_reg_hint_type =
+ nla_get_u32(info->attrs[NL80211_ATTR_USER_REG_HINT_TYPE]);
+ else
+ user_reg_hint_type = NL80211_USER_REG_HINT_USER;
+
+ switch (user_reg_hint_type) {
+ case NL80211_USER_REG_HINT_USER:
+ case NL80211_USER_REG_HINT_CELL_BASE:
+ if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
+ return -EINVAL;
+
+ data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
+ return regulatory_hint_user(data, user_reg_hint_type);
+ case NL80211_USER_REG_HINT_INDOOR:
+ if (info->attrs[NL80211_ATTR_SOCKET_OWNER]) {
+ owner_nlportid = genl_info_snd_portid(info);
+ is_indoor = !!info->attrs[NL80211_ATTR_REG_INDOOR];
+ } else {
+ owner_nlportid = 0;
+ is_indoor = true;
+ }
+
+ return regulatory_hint_indoor(is_indoor, owner_nlportid);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int nl80211_get_mesh_config(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct mesh_config cur_params;
+ int err = 0;
+ void *hdr;
+ struct nlattr *pinfoattr;
+ struct sk_buff *msg;
+
+ if (wdev->iftype != NL80211_IFTYPE_MESH_POINT)
+ return -EOPNOTSUPP;
+
+ if (!rdev->ops->get_mesh_config)
+ return -EOPNOTSUPP;
+
+ wdev_lock(wdev);
+ /* If not connected, get default parameters */
+ if (!wdev->mesh_id_len)
+ memcpy(&cur_params, &default_mesh_config, sizeof(cur_params));
+ else
+ err = rdev_get_mesh_config(rdev, dev, &cur_params);
+ wdev_unlock(wdev);
+
+ if (err)
+ return err;
+
+ /* Draw up a netlink message to send back */
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+ hdr = nl80211hdr_put(msg, genl_info_snd_portid(info), info->snd_seq,
+ 0,
+ NL80211_CMD_GET_MESH_CONFIG);
+ if (!hdr)
+ goto out;
+ pinfoattr = nla_nest_start(msg, NL80211_ATTR_MESH_CONFIG);
+ if (!pinfoattr)
+ goto nla_put_failure;
+ if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
+ nla_put_u16(msg, NL80211_MESHCONF_RETRY_TIMEOUT,
+ cur_params.dot11MeshRetryTimeout) ||
+ nla_put_u16(msg, NL80211_MESHCONF_CONFIRM_TIMEOUT,
+ cur_params.dot11MeshConfirmTimeout) ||
+ nla_put_u16(msg, NL80211_MESHCONF_HOLDING_TIMEOUT,
+ cur_params.dot11MeshHoldingTimeout) ||
+ nla_put_u16(msg, NL80211_MESHCONF_MAX_PEER_LINKS,
+ cur_params.dot11MeshMaxPeerLinks) ||
+ nla_put_u8(msg, NL80211_MESHCONF_MAX_RETRIES,
+ cur_params.dot11MeshMaxRetries) ||
+ nla_put_u8(msg, NL80211_MESHCONF_TTL,
+ cur_params.dot11MeshTTL) ||
+ nla_put_u8(msg, NL80211_MESHCONF_ELEMENT_TTL,
+ cur_params.element_ttl) ||
+ nla_put_u8(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS,
+ cur_params.auto_open_plinks) ||
+ nla_put_u32(msg, NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR,
+ cur_params.dot11MeshNbrOffsetMaxNeighbor) ||
+ nla_put_u8(msg, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
+ cur_params.dot11MeshHWMPmaxPREQretries) ||
+ nla_put_u32(msg, NL80211_MESHCONF_PATH_REFRESH_TIME,
+ cur_params.path_refresh_time) ||
+ nla_put_u16(msg, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
+ cur_params.min_discovery_timeout) ||
+ nla_put_u32(msg, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
+ cur_params.dot11MeshHWMPactivePathTimeout) ||
+ nla_put_u16(msg, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
+ cur_params.dot11MeshHWMPpreqMinInterval) ||
+ nla_put_u16(msg, NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL,
+ cur_params.dot11MeshHWMPperrMinInterval) ||
+ nla_put_u16(msg, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
+ cur_params.dot11MeshHWMPnetDiameterTraversalTime) ||
+ nla_put_u8(msg, NL80211_MESHCONF_HWMP_ROOTMODE,
+ cur_params.dot11MeshHWMPRootMode) ||
+ nla_put_u16(msg, NL80211_MESHCONF_HWMP_RANN_INTERVAL,
+ cur_params.dot11MeshHWMPRannInterval) ||
+ nla_put_u8(msg, NL80211_MESHCONF_GATE_ANNOUNCEMENTS,
+ cur_params.dot11MeshGateAnnouncementProtocol) ||
+ nla_put_u8(msg, NL80211_MESHCONF_FORWARDING,
+ cur_params.dot11MeshForwarding) ||
+ nla_put_s32(msg, NL80211_MESHCONF_RSSI_THRESHOLD,
+ cur_params.rssi_threshold) ||
+ nla_put_u32(msg, NL80211_MESHCONF_HT_OPMODE,
+ cur_params.ht_opmode) ||
+ nla_put_u32(msg, NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT,
+ cur_params.dot11MeshHWMPactivePathToRootTimeout) ||
+ nla_put_u16(msg, NL80211_MESHCONF_HWMP_ROOT_INTERVAL,
+ cur_params.dot11MeshHWMProotInterval) ||
+ nla_put_u16(msg, NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL,
+ cur_params.dot11MeshHWMPconfirmationInterval) ||
+ nla_put_u32(msg, NL80211_MESHCONF_POWER_MODE,
+ cur_params.power_mode) ||
+ nla_put_u16(msg, NL80211_MESHCONF_AWAKE_WINDOW,
+ cur_params.dot11MeshAwakeWindowDuration) ||
+ nla_put_u32(msg, NL80211_MESHCONF_PLINK_TIMEOUT,
+ cur_params.plink_timeout))
+ goto nla_put_failure;
+ nla_nest_end(msg, pinfoattr);
+ genlmsg_end(msg, hdr);
+ return genlmsg_reply(msg, info);
+
+ nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ out:
+ nlmsg_free(msg);
+ return -ENOBUFS;
+}
+
+static const struct nla_policy nl80211_meshconf_params_policy[NL80211_MESHCONF_ATTR_MAX+1] = {
+ [NL80211_MESHCONF_RETRY_TIMEOUT] = { .type = NLA_U16 },
+ [NL80211_MESHCONF_CONFIRM_TIMEOUT] = { .type = NLA_U16 },
+ [NL80211_MESHCONF_HOLDING_TIMEOUT] = { .type = NLA_U16 },
+ [NL80211_MESHCONF_MAX_PEER_LINKS] = { .type = NLA_U16 },
+ [NL80211_MESHCONF_MAX_RETRIES] = { .type = NLA_U8 },
+ [NL80211_MESHCONF_TTL] = { .type = NLA_U8 },
+ [NL80211_MESHCONF_ELEMENT_TTL] = { .type = NLA_U8 },
+ [NL80211_MESHCONF_AUTO_OPEN_PLINKS] = { .type = NLA_U8 },
+ [NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR] = { .type = NLA_U32 },
+ [NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES] = { .type = NLA_U8 },
+ [NL80211_MESHCONF_PATH_REFRESH_TIME] = { .type = NLA_U32 },
+ [NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT] = { .type = NLA_U16 },
+ [NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT] = { .type = NLA_U32 },
+ [NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL] = { .type = NLA_U16 },
+ [NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL] = { .type = NLA_U16 },
+ [NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME] = { .type = NLA_U16 },
+ [NL80211_MESHCONF_HWMP_ROOTMODE] = { .type = NLA_U8 },
+ [NL80211_MESHCONF_HWMP_RANN_INTERVAL] = { .type = NLA_U16 },
+ [NL80211_MESHCONF_GATE_ANNOUNCEMENTS] = { .type = NLA_U8 },
+ [NL80211_MESHCONF_FORWARDING] = { .type = NLA_U8 },
+ [NL80211_MESHCONF_RSSI_THRESHOLD] = { .type = NLA_U32 },
+ [NL80211_MESHCONF_HT_OPMODE] = { .type = NLA_U16 },
+ [NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT] = { .type = NLA_U32 },
+ [NL80211_MESHCONF_HWMP_ROOT_INTERVAL] = { .type = NLA_U16 },
+ [NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL] = { .type = NLA_U16 },
+ [NL80211_MESHCONF_POWER_MODE] = { .type = NLA_U32 },
+ [NL80211_MESHCONF_AWAKE_WINDOW] = { .type = NLA_U16 },
+ [NL80211_MESHCONF_PLINK_TIMEOUT] = { .type = NLA_U32 },
+};
+
+static const struct nla_policy
+ nl80211_mesh_setup_params_policy[NL80211_MESH_SETUP_ATTR_MAX+1] = {
+ [NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC] = { .type = NLA_U8 },
+ [NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL] = { .type = NLA_U8 },
+ [NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC] = { .type = NLA_U8 },
+ [NL80211_MESH_SETUP_USERSPACE_AUTH] = { .type = NLA_FLAG },
+ [NL80211_MESH_SETUP_AUTH_PROTOCOL] = { .type = NLA_U8 },
+ [NL80211_MESH_SETUP_USERSPACE_MPM] = { .type = NLA_FLAG },
+ [NL80211_MESH_SETUP_IE] = { .type = NLA_BINARY,
+ .len = IEEE80211_MAX_DATA_LEN },
+ [NL80211_MESH_SETUP_USERSPACE_AMPE] = { .type = NLA_FLAG },
+};
+
+static int nl80211_check_bool(const struct nlattr *nla, u8 min, u8 max, bool *out)
+{
+ u8 val = nla_get_u8(nla);
+ if (val < min || val > max)
+ return -EINVAL;
+ *out = val;
+ return 0;
+}
+
+static int nl80211_check_u8(const struct nlattr *nla, u8 min, u8 max, u8 *out)
+{
+ u8 val = nla_get_u8(nla);
+ if (val < min || val > max)
+ return -EINVAL;
+ *out = val;
+ return 0;
+}
+
+static int nl80211_check_u16(const struct nlattr *nla, u16 min, u16 max, u16 *out)
+{
+ u16 val = nla_get_u16(nla);
+ if (val < min || val > max)
+ return -EINVAL;
+ *out = val;
+ return 0;
+}
+
+static int nl80211_check_u32(const struct nlattr *nla, u32 min, u32 max, u32 *out)
+{
+ u32 val = nla_get_u32(nla);
+ if (val < min || val > max)
+ return -EINVAL;
+ *out = val;
+ return 0;
+}
+
+static int nl80211_check_s32(const struct nlattr *nla, s32 min, s32 max, s32 *out)
+{
+ s32 val = nla_get_s32(nla);
+ if (val < min || val > max)
+ return -EINVAL;
+ *out = val;
+ return 0;
+}
+
+static int nl80211_check_power_mode(const struct nlattr *nla,
+ enum nl80211_mesh_power_mode min,
+ enum nl80211_mesh_power_mode max,
+ enum nl80211_mesh_power_mode *out)
+{
+ u32 val = nla_get_u32(nla);
+ if (val < min || val > max)
+ return -EINVAL;
+ *out = val;
+ return 0;
+}
+
+static int nl80211_parse_mesh_config(struct genl_info *info,
+ struct mesh_config *cfg,
+ u32 *mask_out)
+{
+ struct nlattr *tb[NL80211_MESHCONF_ATTR_MAX + 1];
+ u32 mask = 0;
+ u16 ht_opmode;
+
+#define FILL_IN_MESH_PARAM_IF_SET(tb, cfg, param, min, max, mask, attr, fn) \
+do { \
+ if (tb[attr]) { \
+ if (fn(tb[attr], min, max, &cfg->param)) \
+ return -EINVAL; \
+ mask |= (1 << (attr - 1)); \
+ } \
+} while (0)
+
+ if (!info->attrs[NL80211_ATTR_MESH_CONFIG])
+ return -EINVAL;
+ if (nla_parse_nested(tb, NL80211_MESHCONF_ATTR_MAX,
+ info->attrs[NL80211_ATTR_MESH_CONFIG],
+ nl80211_meshconf_params_policy, genl_info_extack(info)))
+ return -EINVAL;
+
+ /* This makes sure that there aren't more than 32 mesh config
+ * parameters (otherwise our bitfield scheme would not work.) */
+ BUILD_BUG_ON(NL80211_MESHCONF_ATTR_MAX > 32);
+
+ /* Fill in the params struct */
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshRetryTimeout, 1, 255,
+ mask, NL80211_MESHCONF_RETRY_TIMEOUT,
+ nl80211_check_u16);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshConfirmTimeout, 1, 255,
+ mask, NL80211_MESHCONF_CONFIRM_TIMEOUT,
+ nl80211_check_u16);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHoldingTimeout, 1, 255,
+ mask, NL80211_MESHCONF_HOLDING_TIMEOUT,
+ nl80211_check_u16);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxPeerLinks, 0, 255,
+ mask, NL80211_MESHCONF_MAX_PEER_LINKS,
+ nl80211_check_u16);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxRetries, 0, 16,
+ mask, NL80211_MESHCONF_MAX_RETRIES,
+ nl80211_check_u8);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshTTL, 1, 255,
+ mask, NL80211_MESHCONF_TTL, nl80211_check_u8);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, element_ttl, 1, 255,
+ mask, NL80211_MESHCONF_ELEMENT_TTL,
+ nl80211_check_u8);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, auto_open_plinks, 0, 1,
+ mask, NL80211_MESHCONF_AUTO_OPEN_PLINKS,
+ nl80211_check_bool);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshNbrOffsetMaxNeighbor,
+ 1, 255, mask,
+ NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR,
+ nl80211_check_u32);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPmaxPREQretries, 0, 255,
+ mask, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
+ nl80211_check_u8);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, path_refresh_time, 1, 65535,
+ mask, NL80211_MESHCONF_PATH_REFRESH_TIME,
+ nl80211_check_u32);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, min_discovery_timeout, 1, 65535,
+ mask, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
+ nl80211_check_u16);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathTimeout,
+ 1, 65535, mask,
+ NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
+ nl80211_check_u32);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPpreqMinInterval,
+ 1, 65535, mask,
+ NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
+ nl80211_check_u16);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPperrMinInterval,
+ 1, 65535, mask,
+ NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL,
+ nl80211_check_u16);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
+ dot11MeshHWMPnetDiameterTraversalTime,
+ 1, 65535, mask,
+ NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
+ nl80211_check_u16);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPRootMode, 0, 4,
+ mask, NL80211_MESHCONF_HWMP_ROOTMODE,
+ nl80211_check_u8);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPRannInterval, 1, 65535,
+ mask, NL80211_MESHCONF_HWMP_RANN_INTERVAL,
+ nl80211_check_u16);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
+ dot11MeshGateAnnouncementProtocol, 0, 1,
+ mask, NL80211_MESHCONF_GATE_ANNOUNCEMENTS,
+ nl80211_check_bool);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshForwarding, 0, 1,
+ mask, NL80211_MESHCONF_FORWARDING,
+ nl80211_check_bool);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, rssi_threshold, -255, 0,
+ mask, NL80211_MESHCONF_RSSI_THRESHOLD,
+ nl80211_check_s32);
+ /*
+ * Check HT operation mode based on
+ * IEEE 802.11 2012 8.4.2.59 HT Operation element.
+ */
+ if (tb[NL80211_MESHCONF_HT_OPMODE]) {
+ ht_opmode = nla_get_u16(tb[NL80211_MESHCONF_HT_OPMODE]);
+
+ if (ht_opmode & ~(IEEE80211_HT_OP_MODE_PROTECTION |
+ IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT |
+ IEEE80211_HT_OP_MODE_NON_HT_STA_PRSNT))
+ return -EINVAL;
+
+ if ((ht_opmode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT) &&
+ (ht_opmode & IEEE80211_HT_OP_MODE_NON_HT_STA_PRSNT))
+ return -EINVAL;
+
+ switch (ht_opmode & IEEE80211_HT_OP_MODE_PROTECTION) {
+ case IEEE80211_HT_OP_MODE_PROTECTION_NONE:
+ case IEEE80211_HT_OP_MODE_PROTECTION_20MHZ:
+ if (ht_opmode & IEEE80211_HT_OP_MODE_NON_HT_STA_PRSNT)
+ return -EINVAL;
+ break;
+ case IEEE80211_HT_OP_MODE_PROTECTION_NONMEMBER:
+ case IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED:
+ if (!(ht_opmode & IEEE80211_HT_OP_MODE_NON_HT_STA_PRSNT))
+ return -EINVAL;
+ break;
+ }
+ cfg->ht_opmode = ht_opmode;
+ mask |= (1 << (NL80211_MESHCONF_HT_OPMODE - 1));
+ }
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathToRootTimeout,
+ 1, 65535, mask,
+ NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT,
+ nl80211_check_u32);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMProotInterval, 1, 65535,
+ mask, NL80211_MESHCONF_HWMP_ROOT_INTERVAL,
+ nl80211_check_u16);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
+ dot11MeshHWMPconfirmationInterval,
+ 1, 65535, mask,
+ NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL,
+ nl80211_check_u16);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, power_mode,
+ NL80211_MESH_POWER_ACTIVE,
+ NL80211_MESH_POWER_MAX,
+ mask, NL80211_MESHCONF_POWER_MODE,
+ nl80211_check_power_mode);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshAwakeWindowDuration,
+ 0, 65535, mask,
+ NL80211_MESHCONF_AWAKE_WINDOW, nl80211_check_u16);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, plink_timeout, 0, 0xffffffff,
+ mask, NL80211_MESHCONF_PLINK_TIMEOUT,
+ nl80211_check_u32);
+ if (mask_out)
+ *mask_out = mask;
+
+ return 0;
+
+#undef FILL_IN_MESH_PARAM_IF_SET
+}
+
+static int nl80211_parse_mesh_setup(struct genl_info *info,
+ struct mesh_setup *setup)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct nlattr *tb[NL80211_MESH_SETUP_ATTR_MAX + 1];
+
+ if (!info->attrs[NL80211_ATTR_MESH_SETUP])
+ return -EINVAL;
+ if (nla_parse_nested(tb, NL80211_MESH_SETUP_ATTR_MAX,
+ info->attrs[NL80211_ATTR_MESH_SETUP],
+ nl80211_mesh_setup_params_policy, genl_info_extack(info)))
+ return -EINVAL;
+
+ if (tb[NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC])
+ setup->sync_method =
+ (nla_get_u8(tb[NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC])) ?
+ IEEE80211_SYNC_METHOD_VENDOR :
+ IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET;
+
+ if (tb[NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL])
+ setup->path_sel_proto =
+ (nla_get_u8(tb[NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL])) ?
+ IEEE80211_PATH_PROTOCOL_VENDOR :
+ IEEE80211_PATH_PROTOCOL_HWMP;
+
+ if (tb[NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC])
+ setup->path_metric =
+ (nla_get_u8(tb[NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC])) ?
+ IEEE80211_PATH_METRIC_VENDOR :
+ IEEE80211_PATH_METRIC_AIRTIME;
+
+ if (tb[NL80211_MESH_SETUP_IE]) {
+ struct nlattr *ieattr =
+ tb[NL80211_MESH_SETUP_IE];
+ if (!is_valid_ie_attr(ieattr))
+ return -EINVAL;
+ setup->ie = nla_data(ieattr);
+ setup->ie_len = nla_len(ieattr);
+ }
+ if (tb[NL80211_MESH_SETUP_USERSPACE_MPM] &&
+ !(rdev->wiphy.features & NL80211_FEATURE_USERSPACE_MPM))
+ return -EINVAL;
+ setup->user_mpm = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_MPM]);
+ setup->is_authenticated = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_AUTH]);
+ setup->is_secure = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_AMPE]);
+ if (setup->is_secure)
+ setup->user_mpm = true;
+
+ if (tb[NL80211_MESH_SETUP_AUTH_PROTOCOL]) {
+ if (!setup->user_mpm)
+ return -EINVAL;
+ setup->auth_id =
+ nla_get_u8(tb[NL80211_MESH_SETUP_AUTH_PROTOCOL]);
+ }
+
+ return 0;
+}
+
+static int nl80211_update_mesh_config(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct mesh_config cfg;
+ u32 mask;
+ int err;
+
+ if (wdev->iftype != NL80211_IFTYPE_MESH_POINT)
+ return -EOPNOTSUPP;
+
+ if (!rdev->ops->update_mesh_config)
+ return -EOPNOTSUPP;
+
+ err = nl80211_parse_mesh_config(info, &cfg, &mask);
+ if (err)
+ return err;
+
+ wdev_lock(wdev);
+ if (!wdev->mesh_id_len)
+ err = -ENOLINK;
+
+ if (!err)
+ err = rdev_update_mesh_config(rdev, dev, mask, &cfg);
+
+ wdev_unlock(wdev);
+
+ return err;
+}
+
+static int nl80211_put_regdom(const struct ieee80211_regdomain *regdom,
+ struct sk_buff *msg)
+{
+ struct nlattr *nl_reg_rules;
+ unsigned int i;
+
+ if (nla_put_string(msg, NL80211_ATTR_REG_ALPHA2, regdom->alpha2) ||
+ (regdom->dfs_region &&
+ nla_put_u8(msg, NL80211_ATTR_DFS_REGION, regdom->dfs_region)))
+ goto nla_put_failure;
+
+ nl_reg_rules = nla_nest_start(msg, NL80211_ATTR_REG_RULES);
+ if (!nl_reg_rules)
+ goto nla_put_failure;
+
+ for (i = 0; i < regdom->n_reg_rules; i++) {
+ struct nlattr *nl_reg_rule;
+ const struct ieee80211_reg_rule *reg_rule;
+ const struct ieee80211_freq_range *freq_range;
+ const struct ieee80211_power_rule *power_rule;
+ unsigned int max_bandwidth_khz;
+
+ reg_rule = ®dom->reg_rules[i];
+ freq_range = ®_rule->freq_range;
+ power_rule = ®_rule->power_rule;
+
+ nl_reg_rule = nla_nest_start(msg, i);
+ if (!nl_reg_rule)
+ goto nla_put_failure;
+
+ max_bandwidth_khz = freq_range->max_bandwidth_khz;
+ if (!max_bandwidth_khz)
+ max_bandwidth_khz = reg_get_max_bandwidth(regdom,
+ reg_rule);
+
+ if (nla_put_u32(msg, NL80211_ATTR_REG_RULE_FLAGS,
+ reg_rule->flags) ||
+ nla_put_u32(msg, NL80211_ATTR_FREQ_RANGE_START,
+ freq_range->start_freq_khz) ||
+ nla_put_u32(msg, NL80211_ATTR_FREQ_RANGE_END,
+ freq_range->end_freq_khz) ||
+ nla_put_u32(msg, NL80211_ATTR_FREQ_RANGE_MAX_BW,
+ max_bandwidth_khz) ||
+ nla_put_u32(msg, NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN,
+ power_rule->max_antenna_gain) ||
+ nla_put_u32(msg, NL80211_ATTR_POWER_RULE_MAX_EIRP,
+ power_rule->max_eirp) ||
+ nla_put_u32(msg, NL80211_ATTR_DFS_CAC_TIME,
+ reg_rule->dfs_cac_ms))
+ goto nla_put_failure;
+
+ nla_nest_end(msg, nl_reg_rule);
+ }
+
+ nla_nest_end(msg, nl_reg_rules);
+ return 0;
+
+nla_put_failure:
+ return -EMSGSIZE;
+}
+
+static int nl80211_get_reg_do(struct sk_buff *skb, struct genl_info *info)
+{
+ const struct ieee80211_regdomain *regdom = NULL;
+ struct cfg80211_registered_device *rdev;
+ struct wiphy *wiphy = NULL;
+ struct sk_buff *msg;
+ void *hdr;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg)
+ return -ENOBUFS;
+
+ hdr = nl80211hdr_put(msg, genl_info_snd_portid(info), info->snd_seq,
+ 0,
+ NL80211_CMD_GET_REG);
+ if (!hdr)
+ goto put_failure;
+
+ if (info->attrs[NL80211_ATTR_WIPHY]) {
+ bool self_managed;
+
+ rdev = cfg80211_get_dev_from_info(genl_info_net(info), info);
+ if (IS_ERR(rdev)) {
+ nlmsg_free(msg);
+ return PTR_ERR(rdev);
+ }
+
+ wiphy = &rdev->wiphy;
+ self_managed = wiphy->regulatory_flags &
+ REGULATORY_WIPHY_SELF_MANAGED;
+ regdom = get_wiphy_regdom(wiphy);
+
+ /* a self-managed-reg device must have a private regdom */
+ if (WARN_ON(!regdom && self_managed)) {
+ nlmsg_free(msg);
+ return -EINVAL;
+ }
+
+ if (regdom &&
+ nla_put_u32(msg, NL80211_ATTR_WIPHY, get_wiphy_idx(wiphy)))
+ goto nla_put_failure;
+ }
+
+ if (!wiphy && reg_last_request_cell_base() &&
+ nla_put_u32(msg, NL80211_ATTR_USER_REG_HINT_TYPE,
+ NL80211_USER_REG_HINT_CELL_BASE))
+ goto nla_put_failure;
+
+ rcu_read_lock();
+
+ if (!regdom)
+ regdom = rcu_dereference(cfg80211_regdomain);
+
+ if (nl80211_put_regdom(regdom, msg))
+ goto nla_put_failure_rcu;
+
+ rcu_read_unlock();
+
+ genlmsg_end(msg, hdr);
+ return genlmsg_reply(msg, info);
+
+nla_put_failure_rcu:
+ rcu_read_unlock();
+nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+put_failure:
+ nlmsg_free(msg);
+ return -EMSGSIZE;
+}
+
+static int nl80211_send_regdom(struct sk_buff *msg, struct netlink_callback *cb,
+ u32 seq, int flags, struct wiphy *wiphy,
+ const struct ieee80211_regdomain *regdom)
+{
+ void *hdr = nl80211hdr_put(msg, NETLINK_CB_PORTID(cb->skb), seq,
+ flags,
+ NL80211_CMD_GET_REG);
+
+ if (!hdr)
+ return -1;
+
+ genl_dump_check_consistent(cb, hdr, &nl80211_fam);
+
+ if (nl80211_put_regdom(regdom, msg))
+ goto nla_put_failure;
+
+ if (!wiphy && reg_last_request_cell_base() &&
+ nla_put_u32(msg, NL80211_ATTR_USER_REG_HINT_TYPE,
+ NL80211_USER_REG_HINT_CELL_BASE))
+ goto nla_put_failure;
+
+ if (wiphy &&
+ nla_put_u32(msg, NL80211_ATTR_WIPHY, get_wiphy_idx(wiphy)))
+ goto nla_put_failure;
+
+ if (wiphy && wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED &&
+ nla_put_flag(msg, NL80211_ATTR_WIPHY_SELF_MANAGED_REG))
+ goto nla_put_failure;
+
+ genlmsg_end(msg, hdr);
+ return 0;
+
+nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ return -EMSGSIZE;
+}
+
+static int nl80211_get_reg_dump(struct sk_buff *skb,
+ struct netlink_callback *cb)
+{
+ const struct ieee80211_regdomain *regdom = NULL;
+ struct cfg80211_registered_device *rdev;
+ int err, reg_idx, start = cb->args[2];
+
+ rtnl_lock();
+
+ if (cfg80211_regdomain && start == 0) {
+ err = nl80211_send_regdom(skb, cb, cb->nlh->nlmsg_seq,
+ NLM_F_MULTI, NULL,
+ rtnl_dereference(cfg80211_regdomain));
+ if (err < 0)
+ goto out_err;
+ }
+
+ /* the global regdom is idx 0 */
+ reg_idx = 1;
+ list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
+ regdom = get_wiphy_regdom(&rdev->wiphy);
+ if (!regdom)
+ continue;
+
+ if (++reg_idx <= start)
+ continue;
+
+ err = nl80211_send_regdom(skb, cb, cb->nlh->nlmsg_seq,
+ NLM_F_MULTI, &rdev->wiphy, regdom);
+ if (err < 0) {
+ reg_idx--;
+ break;
+ }
+ }
+
+ cb->args[2] = reg_idx;
+ err = skb->len;
+out_err:
+ rtnl_unlock();
+ return err;
+}
+
+#ifdef CPTCFG_CFG80211_CRDA_SUPPORT
+static const struct nla_policy reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] = {
+ [NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 },
+ [NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 },
+ [NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 },
+ [NL80211_ATTR_FREQ_RANGE_MAX_BW] = { .type = NLA_U32 },
+ [NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN] = { .type = NLA_U32 },
+ [NL80211_ATTR_POWER_RULE_MAX_EIRP] = { .type = NLA_U32 },
+ [NL80211_ATTR_DFS_CAC_TIME] = { .type = NLA_U32 },
+};
+
+static int parse_reg_rule(struct nlattr *tb[],
+ struct ieee80211_reg_rule *reg_rule)
+{
+ struct ieee80211_freq_range *freq_range = ®_rule->freq_range;
+ struct ieee80211_power_rule *power_rule = ®_rule->power_rule;
+
+ if (!tb[NL80211_ATTR_REG_RULE_FLAGS])
+ return -EINVAL;
+ if (!tb[NL80211_ATTR_FREQ_RANGE_START])
+ return -EINVAL;
+ if (!tb[NL80211_ATTR_FREQ_RANGE_END])
+ return -EINVAL;
+ if (!tb[NL80211_ATTR_FREQ_RANGE_MAX_BW])
+ return -EINVAL;
+ if (!tb[NL80211_ATTR_POWER_RULE_MAX_EIRP])
+ return -EINVAL;
+
+ reg_rule->flags = nla_get_u32(tb[NL80211_ATTR_REG_RULE_FLAGS]);
+
+ freq_range->start_freq_khz =
+ nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]);
+ freq_range->end_freq_khz =
+ nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]);
+ freq_range->max_bandwidth_khz =
+ nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]);
+
+ power_rule->max_eirp =
+ nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_EIRP]);
+
+ if (tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN])
+ power_rule->max_antenna_gain =
+ nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN]);
+
+ if (tb[NL80211_ATTR_DFS_CAC_TIME])
+ reg_rule->dfs_cac_ms =
+ nla_get_u32(tb[NL80211_ATTR_DFS_CAC_TIME]);
+
+ return 0;
+}
+
+static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
+{
+ struct nlattr *tb[NL80211_REG_RULE_ATTR_MAX + 1];
+ struct nlattr *nl_reg_rule;
+ char *alpha2;
+ int rem_reg_rules, r;
+ u32 num_rules = 0, rule_idx = 0, size_of_regd;
+ enum nl80211_dfs_regions dfs_region = NL80211_DFS_UNSET;
+ struct ieee80211_regdomain *rd;
+
+ if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
+ return -EINVAL;
+
+ if (!info->attrs[NL80211_ATTR_REG_RULES])
+ return -EINVAL;
+
+ alpha2 = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
+
+ if (info->attrs[NL80211_ATTR_DFS_REGION])
+ dfs_region = nla_get_u8(info->attrs[NL80211_ATTR_DFS_REGION]);
+
+ nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
+ rem_reg_rules) {
+ num_rules++;
+ if (num_rules > NL80211_MAX_SUPP_REG_RULES)
+ return -EINVAL;
+ }
+
+ if (!reg_is_valid_request(alpha2))
+ return -EINVAL;
+
+ size_of_regd = sizeof(struct ieee80211_regdomain) +
+ num_rules * sizeof(struct ieee80211_reg_rule);
+
+ rd = kzalloc(size_of_regd, GFP_KERNEL);
+ if (!rd)
+ return -ENOMEM;
+
+ rd->n_reg_rules = num_rules;
+ rd->alpha2[0] = alpha2[0];
+ rd->alpha2[1] = alpha2[1];
+
+ /*
+ * Disable DFS master mode if the DFS region was
+ * not supported or known on this kernel.
+ */
+ if (reg_supported_dfs_region(dfs_region))
+ rd->dfs_region = dfs_region;
+
+ nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
+ rem_reg_rules) {
+ r = nla_parse_nested(tb, NL80211_REG_RULE_ATTR_MAX,
+ nl_reg_rule, reg_rule_policy,
+ genl_info_extack(info));
+ if (r)
+ goto bad_reg;
+ r = parse_reg_rule(tb, &rd->reg_rules[rule_idx]);
+ if (r)
+ goto bad_reg;
+
+ rule_idx++;
+
+ if (rule_idx > NL80211_MAX_SUPP_REG_RULES) {
+ r = -EINVAL;
+ goto bad_reg;
+ }
+ }
+
+ /* set_regdom takes ownership of rd */
+ return set_regdom(rd, REGD_SOURCE_CRDA);
+ bad_reg:
+ kfree(rd);
+ return r;
+}
+#endif /* CPTCFG_CFG80211_CRDA_SUPPORT */
+
+static int validate_scan_freqs(struct nlattr *freqs)
+{
+ struct nlattr *attr1, *attr2;
+ int n_channels = 0, tmp1, tmp2;
+
+ nla_for_each_nested(attr1, freqs, tmp1) {
+ n_channels++;
+ /*
+ * Some hardware has a limited channel list for
+ * scanning, and it is pretty much nonsensical
+ * to scan for a channel twice, so disallow that
+ * and don't require drivers to check that the
+ * channel list they get isn't longer than what
+ * they can scan, as long as they can scan all
+ * the channels they registered at once.
+ */
+ nla_for_each_nested(attr2, freqs, tmp2)
+ if (attr1 != attr2 &&
+ nla_get_u32(attr1) == nla_get_u32(attr2))
+ return 0;
+ }
+
+ return n_channels;
+}
+
+static bool is_band_valid(struct wiphy *wiphy, enum nl80211_band b)
+{
+ return b < NUM_NL80211_BANDS && wiphy->bands[b];
+}
+
+static int parse_bss_select(struct nlattr *nla, struct wiphy *wiphy,
+ struct cfg80211_bss_selection *bss_select)
+{
+ struct nlattr *attr[NL80211_BSS_SELECT_ATTR_MAX + 1];
+ struct nlattr *nest;
+ int err;
+ bool found = false;
+ int i;
+
+ /* only process one nested attribute */
+ nest = nla_data(nla);
+ if (!nla_ok(nest, nla_len(nest)))
+ return -EINVAL;
+
+ err = nla_parse_nested(attr, NL80211_BSS_SELECT_ATTR_MAX, nest,
+ nl80211_bss_select_policy, NULL);
+ if (err)
+ return err;
+
+ /* only one attribute may be given */
+ for (i = 0; i <= NL80211_BSS_SELECT_ATTR_MAX; i++) {
+ if (attr[i]) {
+ if (found)
+ return -EINVAL;
+ found = true;
+ }
+ }
+
+ bss_select->behaviour = __NL80211_BSS_SELECT_ATTR_INVALID;
+
+ if (attr[NL80211_BSS_SELECT_ATTR_RSSI])
+ bss_select->behaviour = NL80211_BSS_SELECT_ATTR_RSSI;
+
+ if (attr[NL80211_BSS_SELECT_ATTR_BAND_PREF]) {
+ bss_select->behaviour = NL80211_BSS_SELECT_ATTR_BAND_PREF;
+ bss_select->param.band_pref =
+ nla_get_u32(attr[NL80211_BSS_SELECT_ATTR_BAND_PREF]);
+ if (!is_band_valid(wiphy, bss_select->param.band_pref))
+ return -EINVAL;
+ }
+
+ if (attr[NL80211_BSS_SELECT_ATTR_RSSI_ADJUST]) {
+ struct nl80211_bss_select_rssi_adjust *adj_param;
+
+ adj_param = nla_data(attr[NL80211_BSS_SELECT_ATTR_RSSI_ADJUST]);
+ bss_select->behaviour = NL80211_BSS_SELECT_ATTR_RSSI_ADJUST;
+ bss_select->param.adjust.band = adj_param->band;
+ bss_select->param.adjust.delta = adj_param->delta;
+ if (!is_band_valid(wiphy, bss_select->param.adjust.band))
+ return -EINVAL;
+ }
+
+ /* user-space did not provide behaviour attribute */
+ if (bss_select->behaviour == __NL80211_BSS_SELECT_ATTR_INVALID)
+ return -EINVAL;
+
+ if (!(wiphy->bss_select_support & BIT(bss_select->behaviour)))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int nl80211_parse_random_mac(struct nlattr **attrs,
+ u8 *mac_addr, u8 *mac_addr_mask)
+{
+ int i;
+
+ if (!attrs[NL80211_ATTR_MAC] && !attrs[NL80211_ATTR_MAC_MASK]) {
+ eth_zero_addr(mac_addr);
+ eth_zero_addr(mac_addr_mask);
+ mac_addr[0] = 0x2;
+ mac_addr_mask[0] = 0x3;
+
+ return 0;
+ }
+
+ /* need both or none */
+ if (!attrs[NL80211_ATTR_MAC] || !attrs[NL80211_ATTR_MAC_MASK])
+ return -EINVAL;
+
+ memcpy(mac_addr, nla_data(attrs[NL80211_ATTR_MAC]), ETH_ALEN);
+ memcpy(mac_addr_mask, nla_data(attrs[NL80211_ATTR_MAC_MASK]), ETH_ALEN);
+
+ /* don't allow or configure an mcast address */
+ if (!is_multicast_ether_addr(mac_addr_mask) ||
+ is_multicast_ether_addr(mac_addr))
+ return -EINVAL;
+
+ /*
+ * allow users to pass a MAC address that has bits set outside
+ * of the mask, but don't bother drivers with having to deal
+ * with such bits
+ */
+ for (i = 0; i < ETH_ALEN; i++)
+ mac_addr[i] &= mac_addr_mask[i];
+
+ return 0;
+}
+
+static bool cfg80211_off_channel_oper_allowed(struct wireless_dev *wdev)
+{
+ ASSERT_WDEV_LOCK(wdev);
+
+ if (!cfg80211_beaconing_iface_active(wdev))
+ return true;
+
+ if (!(wdev->chandef.chan->flags & IEEE80211_CHAN_RADAR))
+ return true;
+
+ return regulatory_pre_cac_allowed(wdev->wiphy);
+}
+
+static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct wireless_dev *wdev = info->user_ptr[1];
+ struct cfg80211_scan_request *request;
+ struct nlattr *attr;
+ struct wiphy *wiphy;
+ int err, tmp, n_ssids = 0, n_channels, i;
+ size_t ie_len;
+
+ if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
+ return -EINVAL;
+
+ wiphy = &rdev->wiphy;
+
+ if (wdev->iftype == NL80211_IFTYPE_NAN)
+ return -EOPNOTSUPP;
+
+ if (!rdev->ops->scan)
+ return -EOPNOTSUPP;
+
+ if (rdev->scan_req || rdev->scan_msg) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
+ n_channels = validate_scan_freqs(
+ info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]);
+ if (!n_channels) {
+ err = -EINVAL;
+ goto unlock;
+ }
+ } else {
+ n_channels = ieee80211_get_num_supported_channels(wiphy);
+ }
+
+ if (info->attrs[NL80211_ATTR_SCAN_SSIDS])
+ nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp)
+ n_ssids++;
+
+ if (n_ssids > wiphy->max_scan_ssids) {
+ err = -EINVAL;
+ goto unlock;
+ }
+
+ if (info->attrs[NL80211_ATTR_IE])
+ ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+ else
+ ie_len = 0;
+
+ if (ie_len > wiphy->max_scan_ie_len) {
+ err = -EINVAL;
+ goto unlock;
+ }
+
+ request = kzalloc(sizeof(*request)
+ + sizeof(*request->ssids) * n_ssids
+ + sizeof(*request->channels) * n_channels
+ + ie_len, GFP_KERNEL);
+ if (!request) {
+ err = -ENOMEM;
+ goto unlock;
+ }
+
+ if (n_ssids)
+ request->ssids = (void *)&request->channels[n_channels];
+ request->n_ssids = n_ssids;
+ if (ie_len) {
+ if (n_ssids)
+ request->ie = (void *)(request->ssids + n_ssids);
+ else
+ request->ie = (void *)(request->channels + n_channels);
+ }
+
+ i = 0;
+ if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
+ /* user specified, bail out if channel not found */
+ nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], tmp) {
+ struct ieee80211_channel *chan;
+
+ chan = ieee80211_get_channel(wiphy, nla_get_u32(attr));
+
+ if (!chan) {
+ err = -EINVAL;
+ goto out_free;
+ }
+
+ /* ignore disabled channels */
+ if (chan->flags & IEEE80211_CHAN_DISABLED)
+ continue;
+
+ request->channels[i] = chan;
+ i++;
+ }
+ } else {
+ enum nl80211_band band;
+
+ /* all channels */
+ for (band = 0; band < NUM_NL80211_BANDS; band++) {
+ int j;
+
+ if (!wiphy->bands[band])
+ continue;
+ for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
+ struct ieee80211_channel *chan;
+
+ chan = &wiphy->bands[band]->channels[j];
+
+ if (chan->flags & IEEE80211_CHAN_DISABLED)
+ continue;
+
+ request->channels[i] = chan;
+ i++;
+ }
+ }
+ }
+
+ if (!i) {
+ err = -EINVAL;
+ goto out_free;
+ }
+
+ request->n_channels = i;
+
+ wdev_lock(wdev);
+ if (!cfg80211_off_channel_oper_allowed(wdev)) {
+ struct ieee80211_channel *chan;
+
+ if (request->n_channels != 1) {
+ wdev_unlock(wdev);
+ err = -EBUSY;
+ goto out_free;
+ }
+
+ chan = request->channels[0];
+ if (chan->center_freq != wdev->chandef.chan->center_freq) {
+ wdev_unlock(wdev);
+ err = -EBUSY;
+ goto out_free;
+ }
+ }
+ wdev_unlock(wdev);
+
+ i = 0;
+ if (n_ssids) {
+ nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) {
+ if (nla_len(attr) > IEEE80211_MAX_SSID_LEN) {
+ err = -EINVAL;
+ goto out_free;
+ }
+ request->ssids[i].ssid_len = nla_len(attr);
+ memcpy(request->ssids[i].ssid, nla_data(attr), nla_len(attr));
+ i++;
+ }
+ }
+
+ if (info->attrs[NL80211_ATTR_IE]) {
+ request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+ memcpy((void *)request->ie,
+ nla_data(info->attrs[NL80211_ATTR_IE]),
+ request->ie_len);
+ }
+
+ for (i = 0; i < NUM_NL80211_BANDS; i++)
+ if (wiphy->bands[i])
+ request->rates[i] =
+ (1 << wiphy->bands[i]->n_bitrates) - 1;
+
+ if (info->attrs[NL80211_ATTR_SCAN_SUPP_RATES]) {
+ nla_for_each_nested(attr,
+ info->attrs[NL80211_ATTR_SCAN_SUPP_RATES],
+ tmp) {
+ enum nl80211_band band = nla_type(attr);
+
+ if (band < 0 || band >= NUM_NL80211_BANDS) {
+ err = -EINVAL;
+ goto out_free;
+ }
+
+ if (!wiphy->bands[band])
+ continue;
+
+ err = ieee80211_get_ratemask(wiphy->bands[band],
+ nla_data(attr),
+ nla_len(attr),
+ &request->rates[band]);
+ if (err)
+ goto out_free;
+ }
+ }
+
+ if (info->attrs[NL80211_ATTR_MEASUREMENT_DURATION]) {
+ if (!wiphy_ext_feature_isset(wiphy,
+ NL80211_EXT_FEATURE_SET_SCAN_DWELL)) {
+ err = -EOPNOTSUPP;
+ goto out_free;
+ }
+
+ request->duration =
+ nla_get_u16(info->attrs[NL80211_ATTR_MEASUREMENT_DURATION]);
+ request->duration_mandatory =
+ nla_get_flag(info->attrs[NL80211_ATTR_MEASUREMENT_DURATION_MANDATORY]);
+ }
+
+ if (info->attrs[NL80211_ATTR_SCAN_FLAGS]) {
+ request->flags = nla_get_u32(
+ info->attrs[NL80211_ATTR_SCAN_FLAGS]);
+ if ((request->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) &&
+ !(wiphy->features & NL80211_FEATURE_LOW_PRIORITY_SCAN)) {
+ err = -EOPNOTSUPP;
+ goto out_free;
+ }
+
+ if (request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) {
+ if (!(wiphy->features &
+ NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR)) {
+ err = -EOPNOTSUPP;
+ goto out_free;
+ }
+
+ if (wdev->current_bss) {
+ err = -EOPNOTSUPP;
+ goto out_free;
+ }
+
+ err = nl80211_parse_random_mac(info->attrs,
+ request->mac_addr,
+ request->mac_addr_mask);
+ if (err)
+ goto out_free;
+ }
+ }
+
+ request->no_cck =
+ nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]);
+
+ /* Initial implementation used NL80211_ATTR_MAC to set the specific
+ * BSSID to scan for. This was problematic because that same attribute
+ * was already used for another purpose (local random MAC address). The
+ * NL80211_ATTR_BSSID attribute was added to fix this. For backwards
+ * compatibility with older userspace components, also use the
+ * NL80211_ATTR_MAC value here if it can be determined to be used for
+ * the specific BSSID use case instead of the random MAC address
+ * (NL80211_ATTR_SCAN_FLAGS is used to enable random MAC address use).
+ */
+ if (info->attrs[NL80211_ATTR_BSSID])
+ memcpy(request->bssid,
+ nla_data(info->attrs[NL80211_ATTR_BSSID]), ETH_ALEN);
+ else if (!(request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) &&
+ info->attrs[NL80211_ATTR_MAC])
+ memcpy(request->bssid, nla_data(info->attrs[NL80211_ATTR_MAC]),
+ ETH_ALEN);
+ else
+ eth_broadcast_addr(request->bssid);
+
+ request->wdev = wdev;
+ request->wiphy = &rdev->wiphy;
+ request->scan_start = jiffies;
+
+ rdev->scan_req = request;
+ err = rdev_scan(rdev, request);
+
+ if (!err) {
+ nl80211_send_scan_start(rdev, wdev);
+ if (wdev->netdev)
+ dev_hold(wdev->netdev);
+ } else {
+ out_free:
+ rdev->scan_req = NULL;
+ kfree(request);
+ }
+
+ unlock:
+ return err;
+}
+
+static int nl80211_abort_scan(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct wireless_dev *wdev = info->user_ptr[1];
+
+ if (!rdev->ops->abort_scan)
+ return -EOPNOTSUPP;
+
+ if (rdev->scan_msg)
+ return 0;
+
+ if (!rdev->scan_req)
+ return -ENOENT;
+
+ rdev_abort_scan(rdev, wdev);
+ return 0;
+}
+
+static int
+nl80211_parse_sched_scan_plans(struct wiphy *wiphy, int n_plans,
+ struct cfg80211_sched_scan_request *request,
+ struct nlattr **attrs)
+{
+ int tmp, err, i = 0;
+ struct nlattr *attr;
+
+ if (!attrs[NL80211_ATTR_SCHED_SCAN_PLANS]) {
+ u32 interval;
+
+ /*
+ * If scan plans are not specified,
+ * %NL80211_ATTR_SCHED_SCAN_INTERVAL will be specified. In this
+ * case one scan plan will be set with the specified scan
+ * interval and infinite number of iterations.
+ */
+ interval = nla_get_u32(attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL]);
+ if (!interval)
+ return -EINVAL;
+
+ request->scan_plans[0].interval =
+ DIV_ROUND_UP(interval, MSEC_PER_SEC);
+ if (!request->scan_plans[0].interval)
+ return -EINVAL;
+
+ if (request->scan_plans[0].interval >
+ wiphy->max_sched_scan_plan_interval)
+ request->scan_plans[0].interval =
+ wiphy->max_sched_scan_plan_interval;
+
+ return 0;
+ }
+
+ nla_for_each_nested(attr, attrs[NL80211_ATTR_SCHED_SCAN_PLANS], tmp) {
+ struct nlattr *plan[NL80211_SCHED_SCAN_PLAN_MAX + 1];
+
+ if (WARN_ON(i >= n_plans))
+ return -EINVAL;
+
+ err = nla_parse_nested(plan, NL80211_SCHED_SCAN_PLAN_MAX,
+ attr, nl80211_plan_policy, NULL);
+ if (err)
+ return err;
+
+ if (!plan[NL80211_SCHED_SCAN_PLAN_INTERVAL])
+ return -EINVAL;
+
+ request->scan_plans[i].interval =
+ nla_get_u32(plan[NL80211_SCHED_SCAN_PLAN_INTERVAL]);
+ if (!request->scan_plans[i].interval ||
+ request->scan_plans[i].interval >
+ wiphy->max_sched_scan_plan_interval)
+ return -EINVAL;
+
+ if (plan[NL80211_SCHED_SCAN_PLAN_ITERATIONS]) {
+ request->scan_plans[i].iterations =
+ nla_get_u32(plan[NL80211_SCHED_SCAN_PLAN_ITERATIONS]);
+ if (!request->scan_plans[i].iterations ||
+ (request->scan_plans[i].iterations >
+ wiphy->max_sched_scan_plan_iterations))
+ return -EINVAL;
+ } else if (i < n_plans - 1) {
+ /*
+ * All scan plans but the last one must specify
+ * a finite number of iterations
+ */
+ return -EINVAL;
+ }
+
+ i++;
+ }
+
+ /*
+ * The last scan plan must not specify the number of
+ * iterations, it is supposed to run infinitely
+ */
+ if (request->scan_plans[n_plans - 1].iterations)
+ return -EINVAL;
+
+ return 0;
+}
+
+static struct cfg80211_sched_scan_request *
+nl80211_parse_sched_scan(struct wiphy *wiphy, struct wireless_dev *wdev,
+ struct nlattr **attrs, int max_match_sets)
+{
+ struct cfg80211_sched_scan_request *request;
+ struct nlattr *attr;
+ int err, tmp, n_ssids = 0, n_match_sets = 0, n_channels, i, n_plans = 0;
+ enum nl80211_band band;
+ size_t ie_len;
+ struct nlattr *tb[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1];
+ s32 default_match_rssi = NL80211_SCAN_RSSI_THOLD_OFF;
+
+ if (!is_valid_ie_attr(attrs[NL80211_ATTR_IE]))
+ return ERR_PTR(-EINVAL);
+
+ if (attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
+ n_channels = validate_scan_freqs(
+ attrs[NL80211_ATTR_SCAN_FREQUENCIES]);
+ if (!n_channels)
+ return ERR_PTR(-EINVAL);
+ } else {
+ n_channels = ieee80211_get_num_supported_channels(wiphy);
+ }
+
+ if (attrs[NL80211_ATTR_SCAN_SSIDS])
+ nla_for_each_nested(attr, attrs[NL80211_ATTR_SCAN_SSIDS],
+ tmp)
+ n_ssids++;
+
+ if (n_ssids > wiphy->max_sched_scan_ssids)
+ return ERR_PTR(-EINVAL);
+
+ /*
+ * First, count the number of 'real' matchsets. Due to an issue with
+ * the old implementation, matchsets containing only the RSSI attribute
+ * (NL80211_SCHED_SCAN_MATCH_ATTR_RSSI) are considered as the 'default'
+ * RSSI for all matchsets, rather than their own matchset for reporting
+ * all APs with a strong RSSI. This is needed to be compatible with
+ * older userspace that treated a matchset with only the RSSI as the
+ * global RSSI for all other matchsets - if there are other matchsets.
+ */
+ if (attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) {
+ nla_for_each_nested(attr,
+ attrs[NL80211_ATTR_SCHED_SCAN_MATCH],
+ tmp) {
+ struct nlattr *rssi;
+
+ err = nla_parse_nested(tb,
+ NL80211_SCHED_SCAN_MATCH_ATTR_MAX,
+ attr, nl80211_match_policy,
+ NULL);
+ if (err)
+ return ERR_PTR(err);
+
+ /* SSID and BSSID are mutually exclusive */
+ if (tb[NL80211_SCHED_SCAN_MATCH_ATTR_SSID] &&
+ tb[NL80211_SCHED_SCAN_MATCH_ATTR_BSSID])
+ return ERR_PTR(-EINVAL);
+
+ /* add other standalone attributes here */
+ if (tb[NL80211_SCHED_SCAN_MATCH_ATTR_SSID] ||
+ tb[NL80211_SCHED_SCAN_MATCH_ATTR_BSSID]) {
+ n_match_sets++;
+ continue;
+ }
+ rssi = tb[NL80211_SCHED_SCAN_MATCH_ATTR_RSSI];
+ if (rssi)
+ default_match_rssi = nla_get_s32(rssi);
+ }
+ }
+
+ /* However, if there's no other matchset, add the RSSI one */
+ if (!n_match_sets && default_match_rssi != NL80211_SCAN_RSSI_THOLD_OFF)
+ n_match_sets = 1;
+
+ if (n_match_sets > max_match_sets)
+ return ERR_PTR(-EINVAL);
+
+ if (attrs[NL80211_ATTR_IE])
+ ie_len = nla_len(attrs[NL80211_ATTR_IE]);
+ else
+ ie_len = 0;
+
+ if (ie_len > wiphy->max_sched_scan_ie_len)
+ return ERR_PTR(-EINVAL);
+
+ if (attrs[NL80211_ATTR_SCHED_SCAN_PLANS]) {
+ /*
+ * NL80211_ATTR_SCHED_SCAN_INTERVAL must not be specified since
+ * each scan plan already specifies its own interval
+ */
+ if (attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL])
+ return ERR_PTR(-EINVAL);
+
+ nla_for_each_nested(attr,
+ attrs[NL80211_ATTR_SCHED_SCAN_PLANS], tmp)
+ n_plans++;
+ } else {
+ /*
+ * The scan interval attribute is kept for backward
+ * compatibility. If no scan plans are specified and sched scan
+ * interval is specified, one scan plan will be set with this
+ * scan interval and infinite number of iterations.
+ */
+ if (!attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL])
+ return ERR_PTR(-EINVAL);
+
+ n_plans = 1;
+ }
+
+ if (!n_plans || n_plans > wiphy->max_sched_scan_plans)
+ return ERR_PTR(-EINVAL);
+
+ if (!wiphy_ext_feature_isset(
+ wiphy, NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI) &&
+ (attrs[NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI] ||
+ attrs[NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST]))
+ return ERR_PTR(-EINVAL);
+
+ request = kzalloc(sizeof(*request)
+ + sizeof(*request->ssids) * n_ssids
+ + sizeof(*request->match_sets) * n_match_sets
+ + sizeof(*request->scan_plans) * n_plans
+ + sizeof(*request->channels) * n_channels
+ + ie_len, GFP_KERNEL);
+ if (!request)
+ return ERR_PTR(-ENOMEM);
+
+ if (n_ssids)
+ request->ssids = (void *)&request->channels[n_channels];
+ request->n_ssids = n_ssids;
+ if (ie_len) {
+ if (n_ssids)
+ request->ie = (void *)(request->ssids + n_ssids);
+ else
+ request->ie = (void *)(request->channels + n_channels);
+ }
+
+ if (n_match_sets) {
+ if (request->ie)
+ request->match_sets = (void *)(request->ie + ie_len);
+ else if (n_ssids)
+ request->match_sets =
+ (void *)(request->ssids + n_ssids);
+ else
+ request->match_sets =
+ (void *)(request->channels + n_channels);
+ }
+ request->n_match_sets = n_match_sets;
+
+ if (n_match_sets)
+ request->scan_plans = (void *)(request->match_sets +
+ n_match_sets);
+ else if (request->ie)
+ request->scan_plans = (void *)(request->ie + ie_len);
+ else if (n_ssids)
+ request->scan_plans = (void *)(request->ssids + n_ssids);
+ else
+ request->scan_plans = (void *)(request->channels + n_channels);
+
+ request->n_scan_plans = n_plans;
+
+ i = 0;
+ if (attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
+ /* user specified, bail out if channel not found */
+ nla_for_each_nested(attr,
+ attrs[NL80211_ATTR_SCAN_FREQUENCIES],
+ tmp) {
+ struct ieee80211_channel *chan;
+
+ chan = ieee80211_get_channel(wiphy, nla_get_u32(attr));
+
+ if (!chan) {
+ err = -EINVAL;
+ goto out_free;
+ }
+
+ /* ignore disabled channels */
+ if (chan->flags & IEEE80211_CHAN_DISABLED)
+ continue;
+
+ request->channels[i] = chan;
+ i++;
+ }
+ } else {
+ /* all channels */
+ for (band = 0; band < NUM_NL80211_BANDS; band++) {
+ int j;
+
+ if (!wiphy->bands[band])
+ continue;
+ for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
+ struct ieee80211_channel *chan;
+
+ chan = &wiphy->bands[band]->channels[j];
+
+ if (chan->flags & IEEE80211_CHAN_DISABLED)
+ continue;
+
+ request->channels[i] = chan;
+ i++;
+ }
+ }
+ }
+
+ if (!i) {
+ err = -EINVAL;
+ goto out_free;
+ }
+
+ request->n_channels = i;
+
+ i = 0;
+ if (n_ssids) {
+ nla_for_each_nested(attr, attrs[NL80211_ATTR_SCAN_SSIDS],
+ tmp) {
+ if (nla_len(attr) > IEEE80211_MAX_SSID_LEN) {
+ err = -EINVAL;
+ goto out_free;
+ }
+ request->ssids[i].ssid_len = nla_len(attr);
+ memcpy(request->ssids[i].ssid, nla_data(attr),
+ nla_len(attr));
+ i++;
+ }
+ }
+
+ i = 0;
+ if (attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) {
+ nla_for_each_nested(attr,
+ attrs[NL80211_ATTR_SCHED_SCAN_MATCH],
+ tmp) {
+ struct nlattr *ssid, *bssid, *rssi;
+
+ err = nla_parse_nested(tb,
+ NL80211_SCHED_SCAN_MATCH_ATTR_MAX,
+ attr, nl80211_match_policy,
+ NULL);
+ if (err)
+ goto out_free;
+ ssid = tb[NL80211_SCHED_SCAN_MATCH_ATTR_SSID];
+ bssid = tb[NL80211_SCHED_SCAN_MATCH_ATTR_BSSID];
+ if (ssid || bssid) {
+ if (WARN_ON(i >= n_match_sets)) {
+ /* this indicates a programming error,
+ * the loop above should have verified
+ * things properly
+ */
+ err = -EINVAL;
+ goto out_free;
+ }
+
+ if (ssid) {
+ if (nla_len(ssid) > IEEE80211_MAX_SSID_LEN) {
+ err = -EINVAL;
+ goto out_free;
+ }
+ memcpy(request->match_sets[i].ssid.ssid,
+ nla_data(ssid), nla_len(ssid));
+ request->match_sets[i].ssid.ssid_len =
+ nla_len(ssid);
+ }
+ if (bssid) {
+ if (nla_len(bssid) != ETH_ALEN) {
+ err = -EINVAL;
+ goto out_free;
+ }
+ memcpy(request->match_sets[i].bssid,
+ nla_data(bssid), ETH_ALEN);
+ }
+
+ /* special attribute - old implementation w/a */
+ request->match_sets[i].rssi_thold =
+ default_match_rssi;
+ rssi = tb[NL80211_SCHED_SCAN_MATCH_ATTR_RSSI];
+ if (rssi)
+ request->match_sets[i].rssi_thold =
+ nla_get_s32(rssi);
+ }
+ i++;
+ }
+
+ /* there was no other matchset, so the RSSI one is alone */
+ if (i == 0 && n_match_sets)
+ request->match_sets[0].rssi_thold = default_match_rssi;
+
+ request->min_rssi_thold = INT_MAX;
+ for (i = 0; i < n_match_sets; i++)
+ request->min_rssi_thold =
+ min(request->match_sets[i].rssi_thold,
+ request->min_rssi_thold);
+ } else {
+ request->min_rssi_thold = NL80211_SCAN_RSSI_THOLD_OFF;
+ }
+
+ if (ie_len) {
+ request->ie_len = ie_len;
+ memcpy((void *)request->ie,
+ nla_data(attrs[NL80211_ATTR_IE]),
+ request->ie_len);
+ }
+
+ if (attrs[NL80211_ATTR_SCAN_FLAGS]) {
+ request->flags = nla_get_u32(
+ attrs[NL80211_ATTR_SCAN_FLAGS]);
+ if ((request->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) &&
+ !(wiphy->features & NL80211_FEATURE_LOW_PRIORITY_SCAN)) {
+ err = -EOPNOTSUPP;
+ goto out_free;
+ }
+
+ if (request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) {
+ u32 flg = NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR;
+
+ if (!wdev) /* must be net-detect */
+ flg = NL80211_FEATURE_ND_RANDOM_MAC_ADDR;
+
+ if (!(wiphy->features & flg)) {
+ err = -EOPNOTSUPP;
+ goto out_free;
+ }
+
+ if (wdev && wdev->current_bss) {
+ err = -EOPNOTSUPP;
+ goto out_free;
+ }
+
+ err = nl80211_parse_random_mac(attrs, request->mac_addr,
+ request->mac_addr_mask);
+ if (err)
+ goto out_free;
+ }
+ }
+
+ if (attrs[NL80211_ATTR_SCHED_SCAN_DELAY])
+ request->delay =
+ nla_get_u32(attrs[NL80211_ATTR_SCHED_SCAN_DELAY]);
+
+ if (attrs[NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI]) {
+ request->relative_rssi = nla_get_s8(
+ attrs[NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI]);
+ request->relative_rssi_set = true;
+ }
+
+ if (request->relative_rssi_set &&
+ attrs[NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST]) {
+ struct nl80211_bss_select_rssi_adjust *rssi_adjust;
+
+ rssi_adjust = nla_data(
+ attrs[NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST]);
+ request->rssi_adjust.band = rssi_adjust->band;
+ request->rssi_adjust.delta = rssi_adjust->delta;
+ if (!is_band_valid(wiphy, request->rssi_adjust.band)) {
+ err = -EINVAL;
+ goto out_free;
+ }
+ }
+
+ err = nl80211_parse_sched_scan_plans(wiphy, n_plans, request, attrs);
+ if (err)
+ goto out_free;
+
+ request->scan_start = jiffies;
+
+ return request;
+
+out_free:
+ kfree(request);
+ return ERR_PTR(err);
+}
+
+static int nl80211_start_sched_scan(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_sched_scan_request *sched_scan_req;
+ bool want_multi;
+ int err;
+
+ if (!rdev->wiphy.max_sched_scan_reqs || !rdev->ops->sched_scan_start)
+ return -EOPNOTSUPP;
+
+ want_multi = info->attrs[NL80211_ATTR_SCHED_SCAN_MULTI];
+ err = cfg80211_sched_scan_req_possible(rdev, want_multi);
+ if (err)
+ return err;
+
+ sched_scan_req = nl80211_parse_sched_scan(&rdev->wiphy, wdev,
+ info->attrs,
+ rdev->wiphy.max_match_sets);
+
+ err = PTR_ERR_OR_ZERO(sched_scan_req);
+ if (err)
+ goto out_err;
+
+ /* leave request id zero for legacy request
+ * or if driver does not support multi-scheduled scan
+ */
+ if (want_multi && rdev->wiphy.max_sched_scan_reqs > 1) {
+ while (!sched_scan_req->reqid)
+ sched_scan_req->reqid = rdev->wiphy.cookie_counter++;
+ }
+
+ err = rdev_sched_scan_start(rdev, dev, sched_scan_req);
+ if (err)
+ goto out_free;
+
+ sched_scan_req->dev = dev;
+ sched_scan_req->wiphy = &rdev->wiphy;
+
+ if (info->attrs[NL80211_ATTR_SOCKET_OWNER])
+ sched_scan_req->owner_nlportid = genl_info_snd_portid(info);
+
+ cfg80211_add_sched_scan_req(rdev, sched_scan_req);
+
+ nl80211_send_sched_scan(sched_scan_req, NL80211_CMD_START_SCHED_SCAN);
+ return 0;
+
+out_free:
+ kfree(sched_scan_req);
+out_err:
+ return err;
+}
+
+static int nl80211_stop_sched_scan(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct cfg80211_sched_scan_request *req;
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ u64 cookie;
+
+ if (!rdev->wiphy.max_sched_scan_reqs || !rdev->ops->sched_scan_stop)
+ return -EOPNOTSUPP;
+
+ if (info->attrs[NL80211_ATTR_COOKIE]) {
+ cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]);
+ return __cfg80211_stop_sched_scan(rdev, cookie, false);
+ }
+
+ req = list_first_or_null_rcu(&rdev->sched_scan_req_list,
+ struct cfg80211_sched_scan_request,
+ list);
+ if (!req || req->reqid ||
+ (req->owner_nlportid &&
+ req->owner_nlportid != genl_info_snd_portid(info)))
+ return -ENOENT;
+
+ return cfg80211_stop_sched_scan_req(rdev, req, false);
+}
+
+static int nl80211_start_radar_detection(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_chan_def chandef;
+ enum nl80211_dfs_regions dfs_region;
+ unsigned int cac_time_ms;
+ int err;
+
+ dfs_region = reg_get_dfs_region(wdev->wiphy);
+ if (dfs_region == NL80211_DFS_UNSET)
+ return -EINVAL;
+
+ err = nl80211_parse_chandef(rdev, info, &chandef);
+ if (err)
+ return err;
+
+ if (netif_carrier_ok(dev))
+ return -EBUSY;
+
+ if (wdev->cac_started)
+ return -EBUSY;
+
+ err = cfg80211_chandef_dfs_required(wdev->wiphy, &chandef,
+ wdev->iftype);
+ if (err < 0)
+ return err;
+
+ if (err == 0)
+ return -EINVAL;
+
+ if (!cfg80211_chandef_dfs_usable(wdev->wiphy, &chandef))
+ return -EINVAL;
+
+ if (!rdev->ops->start_radar_detection)
+ return -EOPNOTSUPP;
+
+ cac_time_ms = cfg80211_chandef_dfs_cac_time(&rdev->wiphy, &chandef);
+ if (WARN_ON(!cac_time_ms))
+ cac_time_ms = IEEE80211_DFS_MIN_CAC_TIME_MS;
+
+ err = rdev_start_radar_detection(rdev, dev, &chandef, cac_time_ms);
+ if (!err) {
+ wdev->chandef = chandef;
+ wdev->cac_started = true;
+ wdev->cac_start_time = jiffies;
+ wdev->cac_time_ms = cac_time_ms;
+ }
+ return err;
+}
+
+static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_csa_settings params;
+ /* csa_attrs is defined static to avoid waste of stack size - this
+ * function is called under RTNL lock, so this should not be a problem.
+ */
+ static struct nlattr *csa_attrs[NL80211_ATTR_MAX+1];
+ int err;
+ bool need_new_beacon = false;
+ int len, i;
+ u32 cs_count;
+
+ if (!rdev->ops->channel_switch ||
+ !(rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH))
+ return -EOPNOTSUPP;
+
+ switch (dev->ieee80211_ptr->iftype) {
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_P2P_GO:
+ need_new_beacon = true;
+
+ /* useless if AP is not running */
+ if (!wdev->beacon_interval)
+ return -ENOTCONN;
+ break;
+ case NL80211_IFTYPE_ADHOC:
+ if (!wdev->ssid_len)
+ return -ENOTCONN;
+ break;
+ case NL80211_IFTYPE_MESH_POINT:
+ if (!wdev->mesh_id_len)
+ return -ENOTCONN;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ memset(¶ms, 0, sizeof(params));
+
+ if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] ||
+ !info->attrs[NL80211_ATTR_CH_SWITCH_COUNT])
+ return -EINVAL;
+
+ /* only important for AP, IBSS and mesh create IEs internally */
+ if (need_new_beacon && !info->attrs[NL80211_ATTR_CSA_IES])
+ return -EINVAL;
+
+ /* Even though the attribute is u32, the specification says
+ * u8, so let's make sure we don't overflow.
+ */
+ cs_count = nla_get_u32(info->attrs[NL80211_ATTR_CH_SWITCH_COUNT]);
+ if (cs_count > 255)
+ return -EINVAL;
+
+ params.count = cs_count;
+
+ if (!need_new_beacon)
+ goto skip_beacons;
+
+ err = nl80211_parse_beacon(info->attrs, ¶ms.beacon_after);
+ if (err)
+ return err;
+
+ err = nla_parse_nested(csa_attrs, NL80211_ATTR_MAX,
+ info->attrs[NL80211_ATTR_CSA_IES],
+ nl80211_policy, genl_info_extack(info));
+ if (err)
+ return err;
+
+ err = nl80211_parse_beacon(csa_attrs, ¶ms.beacon_csa);
+ if (err)
+ return err;
+
+ if (!csa_attrs[NL80211_ATTR_CSA_C_OFF_BEACON])
+ return -EINVAL;
+
+ len = nla_len(csa_attrs[NL80211_ATTR_CSA_C_OFF_BEACON]);
+ if (!len || (len % sizeof(u16)))
+ return -EINVAL;
+
+ params.n_counter_offsets_beacon = len / sizeof(u16);
+ if (rdev->wiphy.max_num_csa_counters &&
+ (params.n_counter_offsets_beacon >
+ rdev->wiphy.max_num_csa_counters))
+ return -EINVAL;
+
+ params.counter_offsets_beacon =
+ nla_data(csa_attrs[NL80211_ATTR_CSA_C_OFF_BEACON]);
+
+ /* sanity checks - counters should fit and be the same */
+ for (i = 0; i < params.n_counter_offsets_beacon; i++) {
+ u16 offset = params.counter_offsets_beacon[i];
+
+ if (offset >= params.beacon_csa.tail_len)
+ return -EINVAL;
+
+ if (params.beacon_csa.tail[offset] != params.count)
+ return -EINVAL;
+ }
+
+ if (csa_attrs[NL80211_ATTR_CSA_C_OFF_PRESP]) {
+ len = nla_len(csa_attrs[NL80211_ATTR_CSA_C_OFF_PRESP]);
+ if (!len || (len % sizeof(u16)))
+ return -EINVAL;
+
+ params.n_counter_offsets_presp = len / sizeof(u16);
+ if (rdev->wiphy.max_num_csa_counters &&
+ (params.n_counter_offsets_presp >
+ rdev->wiphy.max_num_csa_counters))
+ return -EINVAL;
+
+ params.counter_offsets_presp =
+ nla_data(csa_attrs[NL80211_ATTR_CSA_C_OFF_PRESP]);
+
+ /* sanity checks - counters should fit and be the same */
+ for (i = 0; i < params.n_counter_offsets_presp; i++) {
+ u16 offset = params.counter_offsets_presp[i];
+
+ if (offset >= params.beacon_csa.probe_resp_len)
+ return -EINVAL;
+
+ if (params.beacon_csa.probe_resp[offset] !=
+ params.count)
+ return -EINVAL;
+ }
+ }
+
+skip_beacons:
+ err = nl80211_parse_chandef(rdev, info, ¶ms.chandef);
+ if (err)
+ return err;
+
+ if (!cfg80211_reg_can_beacon_relax(&rdev->wiphy, ¶ms.chandef,
+ wdev->iftype))
+ return -EINVAL;
+
+ err = cfg80211_chandef_dfs_required(wdev->wiphy,
+ ¶ms.chandef,
+ wdev->iftype);
+ if (err < 0)
+ return err;
+
+ if (err > 0)
+ params.radar_required = true;
+
+ if (info->attrs[NL80211_ATTR_CH_SWITCH_BLOCK_TX])
+ params.block_tx = true;
+
+ wdev_lock(wdev);
+ err = rdev_channel_switch(rdev, dev, ¶ms);
+ wdev_unlock(wdev);
+
+ return err;
+}
+
+static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb,
+ u32 seq, int flags,
+ struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev,
+ struct cfg80211_internal_bss *intbss)
+{
+ struct cfg80211_bss *res = &intbss->pub;
+ const struct cfg80211_bss_ies *ies;
+ void *hdr;
+ struct nlattr *bss;
+
+ ASSERT_WDEV_LOCK(wdev);
+
+ hdr = nl80211hdr_put(msg, NETLINK_CB_PORTID(cb->skb), seq, flags,
+ NL80211_CMD_NEW_SCAN_RESULTS);
+ if (!hdr)
+ return -1;
+
+ genl_dump_check_consistent(cb, hdr, &nl80211_fam);
+
+ if (nla_put_u32(msg, NL80211_ATTR_GENERATION, rdev->bss_generation))
+ goto nla_put_failure;
+ if (wdev->netdev &&
+ nla_put_u32(msg, NL80211_ATTR_IFINDEX, wdev->netdev->ifindex))
+ goto nla_put_failure;
+ if (nla_put_u64_64bit(msg, NL80211_ATTR_WDEV, wdev_id(wdev),
+ NL80211_ATTR_PAD))
+ goto nla_put_failure;
+
+ bss = nla_nest_start(msg, NL80211_ATTR_BSS);
+ if (!bss)
+ goto nla_put_failure;
+ if ((!is_zero_ether_addr(res->bssid) &&
+ nla_put(msg, NL80211_BSS_BSSID, ETH_ALEN, res->bssid)))
+ goto nla_put_failure;
+
+ rcu_read_lock();
+ /* indicate whether we have probe response data or not */
+ if (rcu_access_pointer(res->proberesp_ies) &&
+ nla_put_flag(msg, NL80211_BSS_PRESP_DATA))
+ goto fail_unlock_rcu;
+
+ /* this pointer prefers to be pointed to probe response data
+ * but is always valid
+ */
+ ies = rcu_dereference(res->ies);
+ if (ies) {
+ if (nla_put_u64_64bit(msg, NL80211_BSS_TSF, ies->tsf,
+ NL80211_BSS_PAD))
+ goto fail_unlock_rcu;
+ if (ies->len && nla_put(msg, NL80211_BSS_INFORMATION_ELEMENTS,
+ ies->len, ies->data))
+ goto fail_unlock_rcu;
+ }
+
+ /* and this pointer is always (unless driver didn't know) beacon data */
+ ies = rcu_dereference(res->beacon_ies);
+ if (ies && ies->from_beacon) {
+ if (nla_put_u64_64bit(msg, NL80211_BSS_BEACON_TSF, ies->tsf,
+ NL80211_BSS_PAD))
+ goto fail_unlock_rcu;
+ if (ies->len && nla_put(msg, NL80211_BSS_BEACON_IES,
+ ies->len, ies->data))
+ goto fail_unlock_rcu;
+ }
+ rcu_read_unlock();
+
+ if (res->beacon_interval &&
+ nla_put_u16(msg, NL80211_BSS_BEACON_INTERVAL, res->beacon_interval))
+ goto nla_put_failure;
+ if (nla_put_u16(msg, NL80211_BSS_CAPABILITY, res->capability) ||
+ nla_put_u32(msg, NL80211_BSS_FREQUENCY, res->channel->center_freq) ||
+ nla_put_u32(msg, NL80211_BSS_CHAN_WIDTH, res->scan_width) ||
+ nla_put_u32(msg, NL80211_BSS_SEEN_MS_AGO,
+ jiffies_to_msecs(jiffies - intbss->ts)))
+ goto nla_put_failure;
+
+ if (intbss->parent_tsf &&
+ (nla_put_u64_64bit(msg, NL80211_BSS_PARENT_TSF,
+ intbss->parent_tsf, NL80211_BSS_PAD) ||
+ nla_put(msg, NL80211_BSS_PARENT_BSSID, ETH_ALEN,
+ intbss->parent_bssid)))
+ goto nla_put_failure;
+
+ if (intbss->ts_boottime &&
+ nla_put_u64_64bit(msg, NL80211_BSS_LAST_SEEN_BOOTTIME,
+ intbss->ts_boottime, NL80211_BSS_PAD))
+ goto nla_put_failure;
+
+ switch (rdev->wiphy.signal_type) {
+ case CFG80211_SIGNAL_TYPE_MBM:
+ if (nla_put_u32(msg, NL80211_BSS_SIGNAL_MBM, res->signal))
+ goto nla_put_failure;
+ break;
+ case CFG80211_SIGNAL_TYPE_UNSPEC:
+ if (nla_put_u8(msg, NL80211_BSS_SIGNAL_UNSPEC, res->signal))
+ goto nla_put_failure;
+ break;
+ default:
+ break;
+ }
+
+ switch (wdev->iftype) {
+ case NL80211_IFTYPE_P2P_CLIENT:
+ case NL80211_IFTYPE_STATION:
+ if (intbss == wdev->current_bss &&
+ nla_put_u32(msg, NL80211_BSS_STATUS,
+ NL80211_BSS_STATUS_ASSOCIATED))
+ goto nla_put_failure;
+ break;
+ case NL80211_IFTYPE_ADHOC:
+ if (intbss == wdev->current_bss &&
+ nla_put_u32(msg, NL80211_BSS_STATUS,
+ NL80211_BSS_STATUS_IBSS_JOINED))
+ goto nla_put_failure;
+ break;
+ default:
+ break;
+ }
+
+ nla_nest_end(msg, bss);
+
+ genlmsg_end(msg, hdr);
+ return 0;
+
+ fail_unlock_rcu:
+ rcu_read_unlock();
+ nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ return -EMSGSIZE;
+}
+
+static int nl80211_dump_scan(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ struct cfg80211_registered_device *rdev;
+ struct cfg80211_internal_bss *scan;
+ struct wireless_dev *wdev;
+ int start = cb->args[2], idx = 0;
+ int err;
+
+ rtnl_lock();
+ err = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev);
+ if (err) {
+ rtnl_unlock();
+ return err;
+ }
+
+ wdev_lock(wdev);
+ spin_lock_bh(&rdev->bss_lock);
+ cfg80211_bss_expire(rdev);
+
+#if LINUX_VERSION_IS_GEQ(3,1,0)
+ cb->seq = rdev->bss_generation;
+#endif
+
+ list_for_each_entry(scan, &rdev->bss_list, list) {
+ if (++idx <= start)
+ continue;
+ if (nl80211_send_bss(skb, cb,
+ cb->nlh->nlmsg_seq, NLM_F_MULTI,
+ rdev, wdev, scan) < 0) {
+ idx--;
+ break;
+ }
+ }
+
+ spin_unlock_bh(&rdev->bss_lock);
+ wdev_unlock(wdev);
+
+ cb->args[2] = idx;
+ rtnl_unlock();
+
+ return skb->len;
+}
+
+static int nl80211_send_survey(struct sk_buff *msg, u32 portid, u32 seq,
+ int flags, struct net_device *dev,
+ bool allow_radio_stats,
+ struct survey_info *survey)
+{
+ void *hdr;
+ struct nlattr *infoattr;
+
+ /* skip radio stats if userspace didn't request them */
+ if (!survey->channel && !allow_radio_stats)
+ return 0;
+
+ hdr = nl80211hdr_put(msg, portid, seq, flags,
+ NL80211_CMD_NEW_SURVEY_RESULTS);
+ if (!hdr)
+ return -ENOMEM;
+
+ if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex))
+ goto nla_put_failure;
+
+ infoattr = nla_nest_start(msg, NL80211_ATTR_SURVEY_INFO);
+ if (!infoattr)
+ goto nla_put_failure;
+
+ if (survey->channel &&
+ nla_put_u32(msg, NL80211_SURVEY_INFO_FREQUENCY,
+ survey->channel->center_freq))
+ goto nla_put_failure;
+
+ if ((survey->filled & SURVEY_INFO_NOISE_DBM) &&
+ nla_put_u8(msg, NL80211_SURVEY_INFO_NOISE, survey->noise))
+ goto nla_put_failure;
+ if ((survey->filled & SURVEY_INFO_IN_USE) &&
+ nla_put_flag(msg, NL80211_SURVEY_INFO_IN_USE))
+ goto nla_put_failure;
+ if ((survey->filled & SURVEY_INFO_TIME) &&
+ nla_put_u64_64bit(msg, NL80211_SURVEY_INFO_TIME,
+ survey->time, NL80211_SURVEY_INFO_PAD))
+ goto nla_put_failure;
+ if ((survey->filled & SURVEY_INFO_TIME_BUSY) &&
+ nla_put_u64_64bit(msg, NL80211_SURVEY_INFO_TIME_BUSY,
+ survey->time_busy, NL80211_SURVEY_INFO_PAD))
+ goto nla_put_failure;
+ if ((survey->filled & SURVEY_INFO_TIME_EXT_BUSY) &&
+ nla_put_u64_64bit(msg, NL80211_SURVEY_INFO_TIME_EXT_BUSY,
+ survey->time_ext_busy, NL80211_SURVEY_INFO_PAD))
+ goto nla_put_failure;
+ if ((survey->filled & SURVEY_INFO_TIME_RX) &&
+ nla_put_u64_64bit(msg, NL80211_SURVEY_INFO_TIME_RX,
+ survey->time_rx, NL80211_SURVEY_INFO_PAD))
+ goto nla_put_failure;
+ if ((survey->filled & SURVEY_INFO_TIME_TX) &&
+ nla_put_u64_64bit(msg, NL80211_SURVEY_INFO_TIME_TX,
+ survey->time_tx, NL80211_SURVEY_INFO_PAD))
+ goto nla_put_failure;
+ if ((survey->filled & SURVEY_INFO_TIME_SCAN) &&
+ nla_put_u64_64bit(msg, NL80211_SURVEY_INFO_TIME_SCAN,
+ survey->time_scan, NL80211_SURVEY_INFO_PAD))
+ goto nla_put_failure;
+
+ nla_nest_end(msg, infoattr);
+
+ genlmsg_end(msg, hdr);
+ return 0;
+
+ nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ return -EMSGSIZE;
+}
+
+static int nl80211_dump_survey(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ struct nlattr **attrbuf = genl_family_attrbuf(&nl80211_fam);
+ struct survey_info survey;
+ struct cfg80211_registered_device *rdev;
+ struct wireless_dev *wdev;
+ int survey_idx = cb->args[2];
+ int res;
+ bool radio_stats;
+
+ rtnl_lock();
+ res = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev);
+ if (res)
+ goto out_err;
+
+ /* prepare_wdev_dump parsed the attributes */
+ radio_stats = attrbuf[NL80211_ATTR_SURVEY_RADIO_STATS];
+
+ if (!wdev->netdev) {
+ res = -EINVAL;
+ goto out_err;
+ }
+
+ if (!rdev->ops->dump_survey) {
+ res = -EOPNOTSUPP;
+ goto out_err;
+ }
+
+ while (1) {
+ res = rdev_dump_survey(rdev, wdev->netdev, survey_idx, &survey);
+ if (res == -ENOENT)
+ break;
+ if (res)
+ goto out_err;
+
+ /* don't send disabled channels, but do send non-channel data */
+ if (survey.channel &&
+ survey.channel->flags & IEEE80211_CHAN_DISABLED) {
+ survey_idx++;
+ continue;
+ }
+
+ if (nl80211_send_survey(skb,
+ NETLINK_CB_PORTID(cb->skb),
+ cb->nlh->nlmsg_seq, NLM_F_MULTI,
+ wdev->netdev, radio_stats, &survey) < 0)
+ goto out;
+ survey_idx++;
+ }
+
+ out:
+ cb->args[2] = survey_idx;
+ res = skb->len;
+ out_err:
+ rtnl_unlock();
+ return res;
+}
+
+static bool nl80211_valid_wpa_versions(u32 wpa_versions)
+{
+ return !(wpa_versions & ~(NL80211_WPA_VERSION_1 |
+ NL80211_WPA_VERSION_2));
+}
+
+static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct ieee80211_channel *chan;
+ const u8 *bssid, *ssid, *ie = NULL, *auth_data = NULL;
+ int err, ssid_len, ie_len = 0, auth_data_len = 0;
+ enum nl80211_auth_type auth_type;
+ struct key_parse key;
+ bool local_state_change;
+
+ if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
+ return -EINVAL;
+
+ if (!info->attrs[NL80211_ATTR_MAC])
+ return -EINVAL;
+
+ if (!info->attrs[NL80211_ATTR_AUTH_TYPE])
+ return -EINVAL;
+
+ if (!info->attrs[NL80211_ATTR_SSID])
+ return -EINVAL;
+
+ if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
+ return -EINVAL;
+
+ err = nl80211_parse_key(info, &key);
+ if (err)
+ return err;
+
+ if (key.idx >= 0) {
+ if (key.type != -1 && key.type != NL80211_KEYTYPE_GROUP)
+ return -EINVAL;
+ if (!key.p.key || !key.p.key_len)
+ return -EINVAL;
+ if ((key.p.cipher != WLAN_CIPHER_SUITE_WEP40 ||
+ key.p.key_len != WLAN_KEY_LEN_WEP40) &&
+ (key.p.cipher != WLAN_CIPHER_SUITE_WEP104 ||
+ key.p.key_len != WLAN_KEY_LEN_WEP104))
+ return -EINVAL;
+ if (key.idx > 3)
+ return -EINVAL;
+ } else {
+ key.p.key_len = 0;
+ key.p.key = NULL;
+ }
+
+ if (key.idx >= 0) {
+ int i;
+ bool ok = false;
+
+ for (i = 0; i < rdev->wiphy.n_cipher_suites; i++) {
+ if (key.p.cipher == rdev->wiphy.cipher_suites[i]) {
+ ok = true;
+ break;
+ }
+ }
+ if (!ok)
+ return -EINVAL;
+ }
+
+ if (!rdev->ops->auth)
+ return -EOPNOTSUPP;
+
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
+ dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
+ return -EOPNOTSUPP;
+
+ bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
+ chan = nl80211_get_valid_chan(&rdev->wiphy,
+ info->attrs[NL80211_ATTR_WIPHY_FREQ]);
+ if (!chan)
+ return -EINVAL;
+
+ ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
+ ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
+
+ if (info->attrs[NL80211_ATTR_IE]) {
+ ie = nla_data(info->attrs[NL80211_ATTR_IE]);
+ ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+ }
+
+ auth_type = nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]);
+ if (!nl80211_valid_auth_type(rdev, auth_type, NL80211_CMD_AUTHENTICATE))
+ return -EINVAL;
+
+ if ((auth_type == NL80211_AUTHTYPE_SAE ||
+ auth_type == NL80211_AUTHTYPE_FILS_SK ||
+ auth_type == NL80211_AUTHTYPE_FILS_SK_PFS ||
+ auth_type == NL80211_AUTHTYPE_FILS_PK) &&
+ !info->attrs[NL80211_ATTR_AUTH_DATA])
+ return -EINVAL;
+
+ if (info->attrs[NL80211_ATTR_AUTH_DATA]) {
+ if (auth_type != NL80211_AUTHTYPE_SAE &&
+ auth_type != NL80211_AUTHTYPE_FILS_SK &&
+ auth_type != NL80211_AUTHTYPE_FILS_SK_PFS &&
+ auth_type != NL80211_AUTHTYPE_FILS_PK)
+ return -EINVAL;
+ auth_data = nla_data(info->attrs[NL80211_ATTR_AUTH_DATA]);
+ auth_data_len = nla_len(info->attrs[NL80211_ATTR_AUTH_DATA]);
+ /* need to include at least Auth Transaction and Status Code */
+ if (auth_data_len < 4)
+ return -EINVAL;
+ }
+
+ local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
+
+ /*
+ * Since we no longer track auth state, ignore
+ * requests to only change local state.
+ */
+ if (local_state_change)
+ return 0;
+
+ wdev_lock(dev->ieee80211_ptr);
+ err = cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid,
+ ssid, ssid_len, ie, ie_len,
+ key.p.key, key.p.key_len, key.idx,
+ auth_data, auth_data_len);
+ wdev_unlock(dev->ieee80211_ptr);
+ return err;
+}
+
+static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev,
+ struct genl_info *info,
+ struct cfg80211_crypto_settings *settings,
+ int cipher_limit)
+{
+ memset(settings, 0, sizeof(*settings));
+
+ settings->control_port = info->attrs[NL80211_ATTR_CONTROL_PORT];
+
+ if (info->attrs[NL80211_ATTR_CONTROL_PORT_ETHERTYPE]) {
+ u16 proto;
+
+ proto = nla_get_u16(
+ info->attrs[NL80211_ATTR_CONTROL_PORT_ETHERTYPE]);
+ settings->control_port_ethertype = cpu_to_be16(proto);
+ if (!(rdev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL) &&
+ proto != ETH_P_PAE)
+ return -EINVAL;
+ if (info->attrs[NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT])
+ settings->control_port_no_encrypt = true;
+ } else
+ settings->control_port_ethertype = cpu_to_be16(ETH_P_PAE);
+
+ if (info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]) {
+ void *data;
+ int len, i;
+
+ data = nla_data(info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]);
+ len = nla_len(info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]);
+ settings->n_ciphers_pairwise = len / sizeof(u32);
+
+ if (len % sizeof(u32))
+ return -EINVAL;
+
+ if (settings->n_ciphers_pairwise > cipher_limit)
+ return -EINVAL;
+
+ memcpy(settings->ciphers_pairwise, data, len);
+
+ for (i = 0; i < settings->n_ciphers_pairwise; i++)
+ if (!cfg80211_supported_cipher_suite(
+ &rdev->wiphy,
+ settings->ciphers_pairwise[i]))
+ return -EINVAL;
+ }
+
+ if (info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]) {
+ settings->cipher_group =
+ nla_get_u32(info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]);
+ if (!cfg80211_supported_cipher_suite(&rdev->wiphy,
+ settings->cipher_group))
+ return -EINVAL;
+ }
+
+ if (info->attrs[NL80211_ATTR_WPA_VERSIONS]) {
+ settings->wpa_versions =
+ nla_get_u32(info->attrs[NL80211_ATTR_WPA_VERSIONS]);
+ if (!nl80211_valid_wpa_versions(settings->wpa_versions))
+ return -EINVAL;
+ }
+
+ if (info->attrs[NL80211_ATTR_AKM_SUITES]) {
+ void *data;
+ int len;
+
+ data = nla_data(info->attrs[NL80211_ATTR_AKM_SUITES]);
+ len = nla_len(info->attrs[NL80211_ATTR_AKM_SUITES]);
+ settings->n_akm_suites = len / sizeof(u32);
+
+ if (len % sizeof(u32))
+ return -EINVAL;
+
+ if (settings->n_akm_suites > NL80211_MAX_NR_AKM_SUITES)
+ return -EINVAL;
+
+ memcpy(settings->akm_suites, data, len);
+ }
+
+ if (info->attrs[NL80211_ATTR_PMK]) {
+ if (nla_len(info->attrs[NL80211_ATTR_PMK]) != WLAN_PMK_LEN)
+ return -EINVAL;
+ if (!wiphy_ext_feature_isset(&rdev->wiphy,
+ NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK))
+ return -EINVAL;
+ settings->psk = nla_data(info->attrs[NL80211_ATTR_PMK]);
+ }
+
+ return 0;
+}
+
+static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct ieee80211_channel *chan;
+ struct cfg80211_assoc_request req = {};
+ const u8 *bssid, *ssid;
+ int err, ssid_len = 0;
+
+ if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
+ return -EINVAL;
+
+ if (!info->attrs[NL80211_ATTR_MAC] ||
+ !info->attrs[NL80211_ATTR_SSID] ||
+ !info->attrs[NL80211_ATTR_WIPHY_FREQ])
+ return -EINVAL;
+
+ if (!rdev->ops->assoc)
+ return -EOPNOTSUPP;
+
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
+ dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
+ return -EOPNOTSUPP;
+
+ bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
+
+ chan = nl80211_get_valid_chan(&rdev->wiphy,
+ info->attrs[NL80211_ATTR_WIPHY_FREQ]);
+ if (!chan)
+ return -EINVAL;
+
+ ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
+ ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
+
+ if (info->attrs[NL80211_ATTR_IE]) {
+ req.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
+ req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+ }
+
+ if (info->attrs[NL80211_ATTR_USE_MFP]) {
+ enum nl80211_mfp mfp =
+ nla_get_u32(info->attrs[NL80211_ATTR_USE_MFP]);
+ if (mfp == NL80211_MFP_REQUIRED)
+ req.use_mfp = true;
+ else if (mfp != NL80211_MFP_NO)
+ return -EINVAL;
+ }
+
+ if (info->attrs[NL80211_ATTR_PREV_BSSID])
+ req.prev_bssid = nla_data(info->attrs[NL80211_ATTR_PREV_BSSID]);
+
+ if (nla_get_flag(info->attrs[NL80211_ATTR_DISABLE_HT]))
+ req.flags |= ASSOC_REQ_DISABLE_HT;
+
+ if (info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
+ memcpy(&req.ht_capa_mask,
+ nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]),
+ sizeof(req.ht_capa_mask));
+
+ if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) {
+ if (!info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
+ return -EINVAL;
+ memcpy(&req.ht_capa,
+ nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]),
+ sizeof(req.ht_capa));
+ }
+
+ if (nla_get_flag(info->attrs[NL80211_ATTR_DISABLE_VHT]))
+ req.flags |= ASSOC_REQ_DISABLE_VHT;
+
+ if (info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK])
+ memcpy(&req.vht_capa_mask,
+ nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK]),
+ sizeof(req.vht_capa_mask));
+
+ if (info->attrs[NL80211_ATTR_VHT_CAPABILITY]) {
+ if (!info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK])
+ return -EINVAL;
+ memcpy(&req.vht_capa,
+ nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]),
+ sizeof(req.vht_capa));
+ }
+
+ if (nla_get_flag(info->attrs[NL80211_ATTR_USE_RRM])) {
+ if (!((rdev->wiphy.features &
+ NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES) &&
+ (rdev->wiphy.features & NL80211_FEATURE_QUIET)) &&
+ !wiphy_ext_feature_isset(&rdev->wiphy,
+ NL80211_EXT_FEATURE_RRM))
+ return -EINVAL;
+ req.flags |= ASSOC_REQ_USE_RRM;
+ }
+
+ if (info->attrs[NL80211_ATTR_FILS_KEK]) {
+ req.fils_kek = nla_data(info->attrs[NL80211_ATTR_FILS_KEK]);
+ req.fils_kek_len = nla_len(info->attrs[NL80211_ATTR_FILS_KEK]);
+ if (!info->attrs[NL80211_ATTR_FILS_NONCES])
+ return -EINVAL;
+ req.fils_nonces =
+ nla_data(info->attrs[NL80211_ATTR_FILS_NONCES]);
+ }
+
+ err = nl80211_crypto_settings(rdev, info, &req.crypto, 1);
+ if (!err) {
+ wdev_lock(dev->ieee80211_ptr);
+
+ err = cfg80211_mlme_assoc(rdev, dev, chan, bssid,
+ ssid, ssid_len, &req);
+
+ if (!err && info->attrs[NL80211_ATTR_SOCKET_OWNER]) {
+ dev->ieee80211_ptr->conn_owner_nlportid =
+ genl_info_snd_portid(info);
+ memcpy(dev->ieee80211_ptr->disconnect_bssid,
+ bssid, ETH_ALEN);
+ }
+
+ wdev_unlock(dev->ieee80211_ptr);
+ }
+
+ return err;
+}
+
+static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ const u8 *ie = NULL, *bssid;
+ int ie_len = 0, err;
+ u16 reason_code;
+ bool local_state_change;
+
+ if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
+ return -EINVAL;
+
+ if (!info->attrs[NL80211_ATTR_MAC])
+ return -EINVAL;
+
+ if (!info->attrs[NL80211_ATTR_REASON_CODE])
+ return -EINVAL;
+
+ if (!rdev->ops->deauth)
+ return -EOPNOTSUPP;
+
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
+ dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
+ return -EOPNOTSUPP;
+
+ bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
+
+ reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
+ if (reason_code == 0) {
+ /* Reason Code 0 is reserved */
+ return -EINVAL;
+ }
+
+ if (info->attrs[NL80211_ATTR_IE]) {
+ ie = nla_data(info->attrs[NL80211_ATTR_IE]);
+ ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+ }
+
+ local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
+
+ wdev_lock(dev->ieee80211_ptr);
+ err = cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason_code,
+ local_state_change);
+ wdev_unlock(dev->ieee80211_ptr);
+ return err;
+}
+
+static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ const u8 *ie = NULL, *bssid;
+ int ie_len = 0, err;
+ u16 reason_code;
+ bool local_state_change;
+
+ if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
+ return -EINVAL;
+
+ if (!info->attrs[NL80211_ATTR_MAC])
+ return -EINVAL;
+
+ if (!info->attrs[NL80211_ATTR_REASON_CODE])
+ return -EINVAL;
+
+ if (!rdev->ops->disassoc)
+ return -EOPNOTSUPP;
+
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
+ dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
+ return -EOPNOTSUPP;
+
+ bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
+
+ reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
+ if (reason_code == 0) {
+ /* Reason Code 0 is reserved */
+ return -EINVAL;
+ }
+
+ if (info->attrs[NL80211_ATTR_IE]) {
+ ie = nla_data(info->attrs[NL80211_ATTR_IE]);
+ ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+ }
+
+ local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
+
+ wdev_lock(dev->ieee80211_ptr);
+ err = cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason_code,
+ local_state_change);
+ wdev_unlock(dev->ieee80211_ptr);
+ return err;
+}
+
+static bool
+nl80211_parse_mcast_rate(struct cfg80211_registered_device *rdev,
+ int mcast_rate[NUM_NL80211_BANDS],
+ int rateval)
+{
+ struct wiphy *wiphy = &rdev->wiphy;
+ bool found = false;
+ int band, i;
+
+ for (band = 0; band < NUM_NL80211_BANDS; band++) {
+ struct ieee80211_supported_band *sband;
+
+ sband = wiphy->bands[band];
+ if (!sband)
+ continue;
+
+ for (i = 0; i < sband->n_bitrates; i++) {
+ if (sband->bitrates[i].bitrate == rateval) {
+ mcast_rate[band] = i + 1;
+ found = true;
+ break;
+ }
+ }
+ }
+
+ return found;
+}
+
+static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct cfg80211_ibss_params ibss;
+ struct wiphy *wiphy;
+ struct cfg80211_cached_keys *connkeys = NULL;
+ int err;
+
+ memset(&ibss, 0, sizeof(ibss));
+
+ if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
+ return -EINVAL;
+
+ if (!info->attrs[NL80211_ATTR_SSID] ||
+ !nla_len(info->attrs[NL80211_ATTR_SSID]))
+ return -EINVAL;
+
+ ibss.beacon_interval = 100;
+
+ if (info->attrs[NL80211_ATTR_BEACON_INTERVAL])
+ ibss.beacon_interval =
+ nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
+
+ err = cfg80211_validate_beacon_int(rdev, NL80211_IFTYPE_ADHOC,
+ ibss.beacon_interval);
+ if (err)
+ return err;
+
+ if (!rdev->ops->join_ibss)
+ return -EOPNOTSUPP;
+
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC)
+ return -EOPNOTSUPP;
+
+ wiphy = &rdev->wiphy;
+
+ if (info->attrs[NL80211_ATTR_MAC]) {
+ ibss.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
+
+ if (!is_valid_ether_addr(ibss.bssid))
+ return -EINVAL;
+ }
+ ibss.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
+ ibss.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
+
+ if (info->attrs[NL80211_ATTR_IE]) {
+ ibss.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
+ ibss.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+ }
+
+ err = nl80211_parse_chandef(rdev, info, &ibss.chandef);
+ if (err)
+ return err;
+
+ if (!cfg80211_reg_can_beacon(&rdev->wiphy, &ibss.chandef,
+ NL80211_IFTYPE_ADHOC))
+ return -EINVAL;
+
+ switch (ibss.chandef.width) {
+ case NL80211_CHAN_WIDTH_5:
+ case NL80211_CHAN_WIDTH_10:
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ break;
+ case NL80211_CHAN_WIDTH_20:
+ case NL80211_CHAN_WIDTH_40:
+ if (!(rdev->wiphy.features & NL80211_FEATURE_HT_IBSS))
+ return -EINVAL;
+ break;
+ case NL80211_CHAN_WIDTH_80:
+ case NL80211_CHAN_WIDTH_80P80:
+ case NL80211_CHAN_WIDTH_160:
+ if (!(rdev->wiphy.features & NL80211_FEATURE_HT_IBSS))
+ return -EINVAL;
+ if (!wiphy_ext_feature_isset(&rdev->wiphy,
+ NL80211_EXT_FEATURE_VHT_IBSS))
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED];
+ ibss.privacy = !!info->attrs[NL80211_ATTR_PRIVACY];
+
+ if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) {
+ u8 *rates =
+ nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
+ int n_rates =
+ nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
+ struct ieee80211_supported_band *sband =
+ wiphy->bands[ibss.chandef.chan->band];
+
+ err = ieee80211_get_ratemask(sband, rates, n_rates,
+ &ibss.basic_rates);
+ if (err)
+ return err;
+ }
+
+ if (info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
+ memcpy(&ibss.ht_capa_mask,
+ nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]),
+ sizeof(ibss.ht_capa_mask));
+
+ if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) {
+ if (!info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
+ return -EINVAL;
+ memcpy(&ibss.ht_capa,
+ nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]),
+ sizeof(ibss.ht_capa));
+ }
+
+ if (info->attrs[NL80211_ATTR_MCAST_RATE] &&
+ !nl80211_parse_mcast_rate(rdev, ibss.mcast_rate,
+ nla_get_u32(info->attrs[NL80211_ATTR_MCAST_RATE])))
+ return -EINVAL;
+
+ if (ibss.privacy && info->attrs[NL80211_ATTR_KEYS]) {
+ bool no_ht = false;
+
+ connkeys = nl80211_parse_connkeys(rdev,
+ info->attrs[NL80211_ATTR_KEYS],
+ &no_ht);
+ if (IS_ERR(connkeys))
+ return PTR_ERR(connkeys);
+
+ if ((ibss.chandef.width != NL80211_CHAN_WIDTH_20_NOHT) &&
+ no_ht) {
+ kzfree(connkeys);
+ return -EINVAL;
+ }
+ }
+
+ ibss.control_port =
+ nla_get_flag(info->attrs[NL80211_ATTR_CONTROL_PORT]);
+
+ ibss.userspace_handles_dfs =
+ nla_get_flag(info->attrs[NL80211_ATTR_HANDLE_DFS]);
+
+ err = cfg80211_join_ibss(rdev, dev, &ibss, connkeys);
+ if (err)
+ kzfree(connkeys);
+ return err;
+}
+
+static int nl80211_leave_ibss(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+
+ if (!rdev->ops->leave_ibss)
+ return -EOPNOTSUPP;
+
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC)
+ return -EOPNOTSUPP;
+
+ return cfg80211_leave_ibss(rdev, dev, false);
+}
+
+static int nl80211_set_mcast_rate(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ int mcast_rate[NUM_NL80211_BANDS];
+ u32 nla_rate;
+ int err;
+
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC &&
+ dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT &&
+ dev->ieee80211_ptr->iftype != NL80211_IFTYPE_OCB)
+ return -EOPNOTSUPP;
+
+ if (!rdev->ops->set_mcast_rate)
+ return -EOPNOTSUPP;
+
+ memset(mcast_rate, 0, sizeof(mcast_rate));
+
+ if (!info->attrs[NL80211_ATTR_MCAST_RATE])
+ return -EINVAL;
+
+ nla_rate = nla_get_u32(info->attrs[NL80211_ATTR_MCAST_RATE]);
+ if (!nl80211_parse_mcast_rate(rdev, mcast_rate, nla_rate))
+ return -EINVAL;
+
+ err = rdev_set_mcast_rate(rdev, dev, mcast_rate);
+
+ return err;
+}
+
+static struct sk_buff *
+__cfg80211_alloc_vendor_skb(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev, int approxlen,
+ u32 portid, u32 seq, enum nl80211_commands cmd,
+ enum nl80211_attrs attr,
+ const struct nl80211_vendor_cmd_info *info,
+ gfp_t gfp)
+{
+ struct sk_buff *skb;
+ void *hdr;
+ struct nlattr *data;
+
+ skb = nlmsg_new(approxlen + 100, gfp);
+ if (!skb)
+ return NULL;
+
+ hdr = nl80211hdr_put(skb, portid, seq, 0, cmd);
+ if (!hdr) {
+ kfree_skb(skb);
+ return NULL;
+ }
+
+ if (nla_put_u32(skb, NL80211_ATTR_WIPHY, rdev->wiphy_idx))
+ goto nla_put_failure;
+
+ if (info) {
+ if (nla_put_u32(skb, NL80211_ATTR_VENDOR_ID,
+ info->vendor_id))
+ goto nla_put_failure;
+ if (nla_put_u32(skb, NL80211_ATTR_VENDOR_SUBCMD,
+ info->subcmd))
+ goto nla_put_failure;
+ }
+
+ if (wdev) {
+ if (nla_put_u64_64bit(skb, NL80211_ATTR_WDEV,
+ wdev_id(wdev), NL80211_ATTR_PAD))
+ goto nla_put_failure;
+ if (wdev->netdev &&
+ nla_put_u32(skb, NL80211_ATTR_IFINDEX,
+ wdev->netdev->ifindex))
+ goto nla_put_failure;
+ }
+
+ data = nla_nest_start(skb, attr);
+ if (!data)
+ goto nla_put_failure;
+
+ ((void **)skb->cb)[0] = rdev;
+ ((void **)skb->cb)[1] = hdr;
+ ((void **)skb->cb)[2] = data;
+
+ return skb;
+
+ nla_put_failure:
+ kfree_skb(skb);
+ return NULL;
+}
+
+struct sk_buff *__cfg80211_alloc_event_skb(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ enum nl80211_commands cmd,
+ enum nl80211_attrs attr,
+ int vendor_event_idx,
+ int approxlen, gfp_t gfp)
+{
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+ const struct nl80211_vendor_cmd_info *info;
+
+ switch (cmd) {
+ case NL80211_CMD_TESTMODE:
+ if (WARN_ON(vendor_event_idx != -1))
+ return NULL;
+ info = NULL;
+ break;
+ case NL80211_CMD_VENDOR:
+ if (WARN_ON(vendor_event_idx < 0 ||
+ vendor_event_idx >= wiphy->n_vendor_events))
+ return NULL;
+ info = &wiphy->vendor_events[vendor_event_idx];
+ break;
+ default:
+ WARN_ON(1);
+ return NULL;
+ }
+
+ return __cfg80211_alloc_vendor_skb(rdev, wdev, approxlen, 0, 0,
+ cmd, attr, info, gfp);
+}
+EXPORT_SYMBOL(__cfg80211_alloc_event_skb);
+
+void __cfg80211_send_event_skb(struct sk_buff *skb, gfp_t gfp)
+{
+ struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0];
+ void *hdr = ((void **)skb->cb)[1];
+ struct nlattr *data = ((void **)skb->cb)[2];
+ enum nl80211_multicast_groups mcgrp = NL80211_MCGRP_TESTMODE;
+
+ /* clear CB data for netlink core to own from now on */
+ memset(skb->cb, 0, sizeof(skb->cb));
+
+ nla_nest_end(skb, data);
+ genlmsg_end(skb, hdr);
+
+ if (data->nla_type == NL80211_ATTR_VENDOR_DATA)
+ mcgrp = NL80211_MCGRP_VENDOR;
+
+ genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), skb, 0,
+ mcgrp, gfp);
+}
+EXPORT_SYMBOL(__cfg80211_send_event_skb);
+
+#ifdef CPTCFG_NL80211_TESTMODE
+static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct wireless_dev *wdev =
+ __cfg80211_wdev_from_attrs(genl_info_net(info), info->attrs);
+ int err;
+
+ if (!rdev->ops->testmode_cmd)
+ return -EOPNOTSUPP;
+
+ if (IS_ERR(wdev)) {
+ err = PTR_ERR(wdev);
+ if (err != -EINVAL)
+ return err;
+ wdev = NULL;
+ } else if (wdev->wiphy != &rdev->wiphy) {
+ return -EINVAL;
+ }
+
+ if (!info->attrs[NL80211_ATTR_TESTDATA])
+ return -EINVAL;
+
+ rdev->cur_cmd_info = info;
+ err = rdev_testmode_cmd(rdev, wdev,
+ nla_data(info->attrs[NL80211_ATTR_TESTDATA]),
+ nla_len(info->attrs[NL80211_ATTR_TESTDATA]));
+ rdev->cur_cmd_info = NULL;
+
+ return err;
+}
+
+static int nl80211_testmode_dump(struct sk_buff *skb,
+ struct netlink_callback *cb)
+{
+ struct cfg80211_registered_device *rdev;
+ int err;
+ long phy_idx;
+ void *data = NULL;
+ int data_len = 0;
+
+ rtnl_lock();
+
+ if (cb->args[0]) {
+ /*
+ * 0 is a valid index, but not valid for args[0],
+ * so we need to offset by 1.
+ */
+ phy_idx = cb->args[0] - 1;
+
+ rdev = cfg80211_rdev_by_wiphy_idx(phy_idx);
+ if (!rdev) {
+ err = -ENOENT;
+ goto out_err;
+ }
+ } else {
+ struct nlattr **attrbuf = genl_family_attrbuf(&nl80211_fam);
+
+ err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
+ attrbuf, nl80211_fam.maxattr,
+ nl80211_policy, NULL);
+ if (err)
+ goto out_err;
+
+ rdev = __cfg80211_rdev_from_attrs(sock_net(skb->sk), attrbuf);
+ if (IS_ERR(rdev)) {
+ err = PTR_ERR(rdev);
+ goto out_err;
+ }
+ phy_idx = rdev->wiphy_idx;
+
+ if (attrbuf[NL80211_ATTR_TESTDATA])
+ cb->args[1] = (long)attrbuf[NL80211_ATTR_TESTDATA];
+ }
+
+ if (cb->args[1]) {
+ data = nla_data((void *)cb->args[1]);
+ data_len = nla_len((void *)cb->args[1]);
+ }
+
+ if (!rdev->ops->testmode_dump) {
+ err = -EOPNOTSUPP;
+ goto out_err;
+ }
+
+ while (1) {
+ void *hdr = nl80211hdr_put(skb, NETLINK_CB_PORTID(cb->skb),
+ cb->nlh->nlmsg_seq, NLM_F_MULTI,
+ NL80211_CMD_TESTMODE);
+ struct nlattr *tmdata;
+
+ if (!hdr)
+ break;
+
+ if (nla_put_u32(skb, NL80211_ATTR_WIPHY, phy_idx)) {
+ genlmsg_cancel(skb, hdr);
+ break;
+ }
+
+ tmdata = nla_nest_start(skb, NL80211_ATTR_TESTDATA);
+ if (!tmdata) {
+ genlmsg_cancel(skb, hdr);
+ break;
+ }
+ err = rdev_testmode_dump(rdev, skb, cb, data, data_len);
+ nla_nest_end(skb, tmdata);
+
+ if (err == -ENOBUFS || err == -ENOENT) {
+ genlmsg_cancel(skb, hdr);
+ break;
+ } else if (err) {
+ genlmsg_cancel(skb, hdr);
+ goto out_err;
+ }
+
+ genlmsg_end(skb, hdr);
+ }
+
+ err = skb->len;
+ /* see above */
+ cb->args[0] = phy_idx + 1;
+ out_err:
+ rtnl_unlock();
+ return err;
+}
+#endif
+
+static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct cfg80211_connect_params connect;
+ struct wiphy *wiphy;
+ struct cfg80211_cached_keys *connkeys = NULL;
+ int err;
+
+ memset(&connect, 0, sizeof(connect));
+
+ if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
+ return -EINVAL;
+
+ if (!info->attrs[NL80211_ATTR_SSID] ||
+ !nla_len(info->attrs[NL80211_ATTR_SSID]))
+ return -EINVAL;
+
+ if (info->attrs[NL80211_ATTR_AUTH_TYPE]) {
+ connect.auth_type =
+ nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]);
+ if (!nl80211_valid_auth_type(rdev, connect.auth_type,
+ NL80211_CMD_CONNECT))
+ return -EINVAL;
+ } else
+ connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
+
+ connect.privacy = info->attrs[NL80211_ATTR_PRIVACY];
+
+ if (info->attrs[NL80211_ATTR_WANT_1X_4WAY_HS] &&
+ !wiphy_ext_feature_isset(&rdev->wiphy,
+ NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X))
+ return -EINVAL;
+ connect.want_1x = info->attrs[NL80211_ATTR_WANT_1X_4WAY_HS];
+
+ err = nl80211_crypto_settings(rdev, info, &connect.crypto,
+ NL80211_MAX_NR_CIPHER_SUITES);
+ if (err)
+ return err;
+
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
+ dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
+ return -EOPNOTSUPP;
+
+ wiphy = &rdev->wiphy;
+
+ connect.bg_scan_period = -1;
+ if (info->attrs[NL80211_ATTR_BG_SCAN_PERIOD] &&
+ (wiphy->flags & WIPHY_FLAG_SUPPORTS_FW_ROAM)) {
+ connect.bg_scan_period =
+ nla_get_u16(info->attrs[NL80211_ATTR_BG_SCAN_PERIOD]);
+ }
+
+ if (info->attrs[NL80211_ATTR_MAC])
+ connect.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
+ else if (info->attrs[NL80211_ATTR_MAC_HINT])
+ connect.bssid_hint =
+ nla_data(info->attrs[NL80211_ATTR_MAC_HINT]);
+ connect.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
+ connect.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
+
+ if (info->attrs[NL80211_ATTR_IE]) {
+ connect.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
+ connect.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+ }
+
+ if (info->attrs[NL80211_ATTR_USE_MFP]) {
+ connect.mfp = nla_get_u32(info->attrs[NL80211_ATTR_USE_MFP]);
+ if (connect.mfp != NL80211_MFP_REQUIRED &&
+ connect.mfp != NL80211_MFP_NO)
+ return -EINVAL;
+ } else {
+ connect.mfp = NL80211_MFP_NO;
+ }
+
+ if (info->attrs[NL80211_ATTR_PREV_BSSID])
+ connect.prev_bssid =
+ nla_data(info->attrs[NL80211_ATTR_PREV_BSSID]);
+
+ if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
+ connect.channel = nl80211_get_valid_chan(
+ wiphy, info->attrs[NL80211_ATTR_WIPHY_FREQ]);
+ if (!connect.channel)
+ return -EINVAL;
+ } else if (info->attrs[NL80211_ATTR_WIPHY_FREQ_HINT]) {
+ connect.channel_hint = nl80211_get_valid_chan(
+ wiphy, info->attrs[NL80211_ATTR_WIPHY_FREQ_HINT]);
+ if (!connect.channel_hint)
+ return -EINVAL;
+ }
+
+ if (connect.privacy && info->attrs[NL80211_ATTR_KEYS]) {
+ connkeys = nl80211_parse_connkeys(rdev,
+ info->attrs[NL80211_ATTR_KEYS], NULL);
+ if (IS_ERR(connkeys))
+ return PTR_ERR(connkeys);
+ }
+
+ if (nla_get_flag(info->attrs[NL80211_ATTR_DISABLE_HT]))
+ connect.flags |= ASSOC_REQ_DISABLE_HT;
+
+ if (info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
+ memcpy(&connect.ht_capa_mask,
+ nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]),
+ sizeof(connect.ht_capa_mask));
+
+ if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) {
+ if (!info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]) {
+ kzfree(connkeys);
+ return -EINVAL;
+ }
+ memcpy(&connect.ht_capa,
+ nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]),
+ sizeof(connect.ht_capa));
+ }
+
+ if (nla_get_flag(info->attrs[NL80211_ATTR_DISABLE_VHT]))
+ connect.flags |= ASSOC_REQ_DISABLE_VHT;
+
+ if (info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK])
+ memcpy(&connect.vht_capa_mask,
+ nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK]),
+ sizeof(connect.vht_capa_mask));
+
+ if (info->attrs[NL80211_ATTR_VHT_CAPABILITY]) {
+ if (!info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK]) {
+ kzfree(connkeys);
+ return -EINVAL;
+ }
+ memcpy(&connect.vht_capa,
+ nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]),
+ sizeof(connect.vht_capa));
+ }
+
+ if (nla_get_flag(info->attrs[NL80211_ATTR_USE_RRM])) {
+ if (!((rdev->wiphy.features &
+ NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES) &&
+ (rdev->wiphy.features & NL80211_FEATURE_QUIET)) &&
+ !wiphy_ext_feature_isset(&rdev->wiphy,
+ NL80211_EXT_FEATURE_RRM)) {
+ kzfree(connkeys);
+ return -EINVAL;
+ }
+ connect.flags |= ASSOC_REQ_USE_RRM;
+ }
+
+ connect.pbss = nla_get_flag(info->attrs[NL80211_ATTR_PBSS]);
+ if (connect.pbss && !rdev->wiphy.bands[NL80211_BAND_60GHZ]) {
+ kzfree(connkeys);
+ return -EOPNOTSUPP;
+ }
+
+ if (info->attrs[NL80211_ATTR_BSS_SELECT]) {
+ /* bss selection makes no sense if bssid is set */
+ if (connect.bssid) {
+ kzfree(connkeys);
+ return -EINVAL;
+ }
+
+ err = parse_bss_select(info->attrs[NL80211_ATTR_BSS_SELECT],
+ wiphy, &connect.bss_select);
+ if (err) {
+ kzfree(connkeys);
+ return err;
+ }
+ }
+
+ if (wiphy_ext_feature_isset(&rdev->wiphy,
+ NL80211_EXT_FEATURE_FILS_SK_OFFLOAD) &&
+ info->attrs[NL80211_ATTR_FILS_ERP_USERNAME] &&
+ info->attrs[NL80211_ATTR_FILS_ERP_REALM] &&
+ info->attrs[NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM] &&
+ info->attrs[NL80211_ATTR_FILS_ERP_RRK]) {
+ connect.fils_erp_username =
+ nla_data(info->attrs[NL80211_ATTR_FILS_ERP_USERNAME]);
+ connect.fils_erp_username_len =
+ nla_len(info->attrs[NL80211_ATTR_FILS_ERP_USERNAME]);
+ connect.fils_erp_realm =
+ nla_data(info->attrs[NL80211_ATTR_FILS_ERP_REALM]);
+ connect.fils_erp_realm_len =
+ nla_len(info->attrs[NL80211_ATTR_FILS_ERP_REALM]);
+ connect.fils_erp_next_seq_num =
+ nla_get_u16(
+ info->attrs[NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM]);
+ connect.fils_erp_rrk =
+ nla_data(info->attrs[NL80211_ATTR_FILS_ERP_RRK]);
+ connect.fils_erp_rrk_len =
+ nla_len(info->attrs[NL80211_ATTR_FILS_ERP_RRK]);
+ } else if (info->attrs[NL80211_ATTR_FILS_ERP_USERNAME] ||
+ info->attrs[NL80211_ATTR_FILS_ERP_REALM] ||
+ info->attrs[NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM] ||
+ info->attrs[NL80211_ATTR_FILS_ERP_RRK]) {
+ kzfree(connkeys);
+ return -EINVAL;
+ }
+
+ wdev_lock(dev->ieee80211_ptr);
+
+ err = cfg80211_connect(rdev, dev, &connect, connkeys,
+ connect.prev_bssid);
+ if (err)
+ kzfree(connkeys);
+
+ if (!err && info->attrs[NL80211_ATTR_SOCKET_OWNER]) {
+ dev->ieee80211_ptr->conn_owner_nlportid = genl_info_snd_portid(info);
+ if (connect.bssid)
+ memcpy(dev->ieee80211_ptr->disconnect_bssid,
+ connect.bssid, ETH_ALEN);
+ else
+ memset(dev->ieee80211_ptr->disconnect_bssid,
+ 0, ETH_ALEN);
+ }
+
+ wdev_unlock(dev->ieee80211_ptr);
+
+ return err;
+}
+
+static int nl80211_update_connect_params(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct cfg80211_connect_params connect = {};
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ u32 changed = 0;
+ int ret;
+
+ if (!rdev->ops->update_connect_params)
+ return -EOPNOTSUPP;
+
+ if (info->attrs[NL80211_ATTR_IE]) {
+ if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
+ return -EINVAL;
+ connect.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
+ connect.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+ changed |= UPDATE_ASSOC_IES;
+ }
+
+ wdev_lock(dev->ieee80211_ptr);
+ if (!wdev->current_bss)
+ ret = -ENOLINK;
+ else
+ ret = rdev_update_connect_params(rdev, dev, &connect, changed);
+ wdev_unlock(dev->ieee80211_ptr);
+
+ return ret;
+}
+
+static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ u16 reason;
+ int ret;
+
+ if (!info->attrs[NL80211_ATTR_REASON_CODE])
+ reason = WLAN_REASON_DEAUTH_LEAVING;
+ else
+ reason = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
+
+ if (reason == 0)
+ return -EINVAL;
+
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
+ dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
+ return -EOPNOTSUPP;
+
+ wdev_lock(dev->ieee80211_ptr);
+ ret = cfg80211_disconnect(rdev, dev, reason, true);
+ wdev_unlock(dev->ieee80211_ptr);
+ return ret;
+}
+
+static int nl80211_wiphy_netns(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net *net;
+ int err;
+
+ if (info->attrs[NL80211_ATTR_PID]) {
+ u32 pid = nla_get_u32(info->attrs[NL80211_ATTR_PID]);
+
+ net = get_net_ns_by_pid(pid);
+ } else if (info->attrs[NL80211_ATTR_NETNS_FD]) {
+ u32 fd = nla_get_u32(info->attrs[NL80211_ATTR_NETNS_FD]);
+
+ net = get_net_ns_by_fd(fd);
+ } else {
+ return -EINVAL;
+ }
+
+ if (IS_ERR(net))
+ return PTR_ERR(net);
+
+ err = 0;
+
+ /* check if anything to do */
+ if (!net_eq(wiphy_net(&rdev->wiphy), net))
+ err = cfg80211_switch_netns(rdev, net);
+
+ put_net(net);
+ return err;
+}
+
+static int nl80211_setdel_pmksa(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ int (*rdev_ops)(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_pmksa *pmksa) = NULL;
+ struct net_device *dev = info->user_ptr[1];
+ struct cfg80211_pmksa pmksa;
+
+ memset(&pmksa, 0, sizeof(struct cfg80211_pmksa));
+
+ if (!info->attrs[NL80211_ATTR_PMKID])
+ return -EINVAL;
+
+ pmksa.pmkid = nla_data(info->attrs[NL80211_ATTR_PMKID]);
+
+ if (info->attrs[NL80211_ATTR_MAC]) {
+ pmksa.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
+ } else if (info->attrs[NL80211_ATTR_SSID] &&
+ info->attrs[NL80211_ATTR_FILS_CACHE_ID] &&
+ (info->genlhdr->cmd == NL80211_CMD_DEL_PMKSA ||
+ info->attrs[NL80211_ATTR_PMK])) {
+ pmksa.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
+ pmksa.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
+ pmksa.cache_id =
+ nla_data(info->attrs[NL80211_ATTR_FILS_CACHE_ID]);
+ } else {
+ return -EINVAL;
+ }
+ if (info->attrs[NL80211_ATTR_PMK]) {
+ pmksa.pmk = nla_data(info->attrs[NL80211_ATTR_PMK]);
+ pmksa.pmk_len = nla_len(info->attrs[NL80211_ATTR_PMK]);
+ }
+
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
+ dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
+ return -EOPNOTSUPP;
+
+ switch (info->genlhdr->cmd) {
+ case NL80211_CMD_SET_PMKSA:
+ rdev_ops = rdev->ops->set_pmksa;
+ break;
+ case NL80211_CMD_DEL_PMKSA:
+ rdev_ops = rdev->ops->del_pmksa;
+ break;
+ default:
+ WARN_ON(1);
+ break;
+ }
+
+ if (!rdev_ops)
+ return -EOPNOTSUPP;
+
+ return rdev_ops(&rdev->wiphy, dev, &pmksa);
+}
+
+static int nl80211_flush_pmksa(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
+ dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
+ return -EOPNOTSUPP;
+
+ if (!rdev->ops->flush_pmksa)
+ return -EOPNOTSUPP;
+
+ return rdev_flush_pmksa(rdev, dev);
+}
+
+static int nl80211_tdls_mgmt(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ u8 action_code, dialog_token;
+ u32 peer_capability = 0;
+ u16 status_code;
+ u8 *peer;
+ bool initiator;
+
+ if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) ||
+ !rdev->ops->tdls_mgmt)
+ return -EOPNOTSUPP;
+
+ if (!info->attrs[NL80211_ATTR_TDLS_ACTION] ||
+ !info->attrs[NL80211_ATTR_STATUS_CODE] ||
+ !info->attrs[NL80211_ATTR_TDLS_DIALOG_TOKEN] ||
+ !info->attrs[NL80211_ATTR_IE] ||
+ !info->attrs[NL80211_ATTR_MAC])
+ return -EINVAL;
+
+ peer = nla_data(info->attrs[NL80211_ATTR_MAC]);
+ action_code = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_ACTION]);
+ status_code = nla_get_u16(info->attrs[NL80211_ATTR_STATUS_CODE]);
+ dialog_token = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_DIALOG_TOKEN]);
+ initiator = nla_get_flag(info->attrs[NL80211_ATTR_TDLS_INITIATOR]);
+ if (info->attrs[NL80211_ATTR_TDLS_PEER_CAPABILITY])
+ peer_capability =
+ nla_get_u32(info->attrs[NL80211_ATTR_TDLS_PEER_CAPABILITY]);
+
+ return rdev_tdls_mgmt(rdev, dev, peer, action_code,
+ dialog_token, status_code, peer_capability,
+ initiator,
+ nla_data(info->attrs[NL80211_ATTR_IE]),
+ nla_len(info->attrs[NL80211_ATTR_IE]));
+}
+
+static int nl80211_tdls_oper(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ enum nl80211_tdls_operation operation;
+ u8 *peer;
+
+ if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) ||
+ !rdev->ops->tdls_oper)
+ return -EOPNOTSUPP;
+
+ if (!info->attrs[NL80211_ATTR_TDLS_OPERATION] ||
+ !info->attrs[NL80211_ATTR_MAC])
+ return -EINVAL;
+
+ operation = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_OPERATION]);
+ peer = nla_data(info->attrs[NL80211_ATTR_MAC]);
+
+ return rdev_tdls_oper(rdev, dev, peer, operation);
+}
+
+static int nl80211_remain_on_channel(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct wireless_dev *wdev = info->user_ptr[1];
+ struct cfg80211_chan_def chandef;
+ const struct cfg80211_chan_def *compat_chandef;
+ struct sk_buff *msg;
+ void *hdr;
+ u64 cookie;
+ u32 duration;
+ int err;
+
+ if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] ||
+ !info->attrs[NL80211_ATTR_DURATION])
+ return -EINVAL;
+
+ duration = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]);
+
+ if (!rdev->ops->remain_on_channel ||
+ !(rdev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL))
+ return -EOPNOTSUPP;
+
+ /*
+ * We should be on that channel for at least a minimum amount of
+ * time (10ms) but no longer than the driver supports.
+ */
+ if (duration < NL80211_MIN_REMAIN_ON_CHANNEL_TIME ||
+ duration > rdev->wiphy.max_remain_on_channel_duration)
+ return -EINVAL;
+
+ err = nl80211_parse_chandef(rdev, info, &chandef);
+ if (err)
+ return err;
+
+ wdev_lock(wdev);
+ if (!cfg80211_off_channel_oper_allowed(wdev) &&
+ !cfg80211_chandef_identical(&wdev->chandef, &chandef)) {
+ compat_chandef = cfg80211_chandef_compatible(&wdev->chandef,
+ &chandef);
+ if (compat_chandef != &chandef) {
+ wdev_unlock(wdev);
+ return -EBUSY;
+ }
+ }
+ wdev_unlock(wdev);
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ hdr = nl80211hdr_put(msg, genl_info_snd_portid(info), info->snd_seq,
+ 0,
+ NL80211_CMD_REMAIN_ON_CHANNEL);
+ if (!hdr) {
+ err = -ENOBUFS;
+ goto free_msg;
+ }
+
+ err = rdev_remain_on_channel(rdev, wdev, chandef.chan,
+ duration, &cookie);
+
+ if (err)
+ goto free_msg;
+
+ if (nla_put_u64_64bit(msg, NL80211_ATTR_COOKIE, cookie,
+ NL80211_ATTR_PAD))
+ goto nla_put_failure;
+
+ genlmsg_end(msg, hdr);
+
+ return genlmsg_reply(msg, info);
+
+ nla_put_failure:
+ err = -ENOBUFS;
+ free_msg:
+ nlmsg_free(msg);
+ return err;
+}
+
+static int nl80211_cancel_remain_on_channel(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct wireless_dev *wdev = info->user_ptr[1];
+ u64 cookie;
+
+ if (!info->attrs[NL80211_ATTR_COOKIE])
+ return -EINVAL;
+
+ if (!rdev->ops->cancel_remain_on_channel)
+ return -EOPNOTSUPP;
+
+ cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]);
+
+ return rdev_cancel_remain_on_channel(rdev, wdev, cookie);
+}
+
+static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct cfg80211_bitrate_mask mask;
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ int err;
+
+ if (!rdev->ops->set_bitrate_mask)
+ return -EOPNOTSUPP;
+
+ err = nl80211_parse_tx_bitrate_mask(info, &mask);
+ if (err)
+ return err;
+
+ return rdev_set_bitrate_mask(rdev, dev, NULL, &mask);
+}
+
+static int nl80211_register_mgmt(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct wireless_dev *wdev = info->user_ptr[1];
+ u16 frame_type = IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION;
+
+ if (!info->attrs[NL80211_ATTR_FRAME_MATCH])
+ return -EINVAL;
+
+ if (info->attrs[NL80211_ATTR_FRAME_TYPE])
+ frame_type = nla_get_u16(info->attrs[NL80211_ATTR_FRAME_TYPE]);
+
+ switch (wdev->iftype) {
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_P2P_CLIENT:
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_AP_VLAN:
+ case NL80211_IFTYPE_MESH_POINT:
+ case NL80211_IFTYPE_P2P_GO:
+ case NL80211_IFTYPE_P2P_DEVICE:
+ break;
+ case NL80211_IFTYPE_NAN:
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ /* not much point in registering if we can't reply */
+ if (!rdev->ops->mgmt_tx)
+ return -EOPNOTSUPP;
+
+ return cfg80211_mlme_register_mgmt(wdev, genl_info_snd_portid(info),
+ frame_type,
+ nla_data(info->attrs[NL80211_ATTR_FRAME_MATCH]),
+ nla_len(info->attrs[NL80211_ATTR_FRAME_MATCH]));
+}
+
+static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct wireless_dev *wdev = info->user_ptr[1];
+ struct cfg80211_chan_def chandef;
+ int err;
+ void *hdr = NULL;
+ u64 cookie;
+ struct sk_buff *msg = NULL;
+ struct cfg80211_mgmt_tx_params params = {
+ .dont_wait_for_ack =
+ info->attrs[NL80211_ATTR_DONT_WAIT_FOR_ACK],
+ };
+
+ if (!info->attrs[NL80211_ATTR_FRAME])
+ return -EINVAL;
+
+ if (!rdev->ops->mgmt_tx)
+ return -EOPNOTSUPP;
+
+ switch (wdev->iftype) {
+ case NL80211_IFTYPE_P2P_DEVICE:
+ if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
+ return -EINVAL;
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_P2P_CLIENT:
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_AP_VLAN:
+ case NL80211_IFTYPE_MESH_POINT:
+ case NL80211_IFTYPE_P2P_GO:
+ break;
+ case NL80211_IFTYPE_NAN:
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ if (info->attrs[NL80211_ATTR_DURATION]) {
+ if (!(rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX))
+ return -EINVAL;
+ params.wait = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]);
+
+ /*
+ * We should wait on the channel for at least a minimum amount
+ * of time (10ms) but no longer than the driver supports.
+ */
+ if (params.wait < NL80211_MIN_REMAIN_ON_CHANNEL_TIME ||
+ params.wait > rdev->wiphy.max_remain_on_channel_duration)
+ return -EINVAL;
+ }
+
+ params.offchan = info->attrs[NL80211_ATTR_OFFCHANNEL_TX_OK];
+
+ if (params.offchan && !(rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX))
+ return -EINVAL;
+
+ params.no_cck = nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]);
+
+ /* get the channel if any has been specified, otherwise pass NULL to
+ * the driver. The latter will use the current one
+ */
+ chandef.chan = NULL;
+ if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
+ err = nl80211_parse_chandef(rdev, info, &chandef);
+ if (err)
+ return err;
+ }
+
+ if (!chandef.chan && params.offchan)
+ return -EINVAL;
+
+ wdev_lock(wdev);
+ if (params.offchan && !cfg80211_off_channel_oper_allowed(wdev)) {
+ wdev_unlock(wdev);
+ return -EBUSY;
+ }
+ wdev_unlock(wdev);
+
+ params.buf = nla_data(info->attrs[NL80211_ATTR_FRAME]);
+ params.len = nla_len(info->attrs[NL80211_ATTR_FRAME]);
+
+ if (info->attrs[NL80211_ATTR_CSA_C_OFFSETS_TX]) {
+ int len = nla_len(info->attrs[NL80211_ATTR_CSA_C_OFFSETS_TX]);
+ int i;
+
+ if (len % sizeof(u16))
+ return -EINVAL;
+
+ params.n_csa_offsets = len / sizeof(u16);
+ params.csa_offsets =
+ nla_data(info->attrs[NL80211_ATTR_CSA_C_OFFSETS_TX]);
+
+ /* check that all the offsets fit the frame */
+ for (i = 0; i < params.n_csa_offsets; i++) {
+ if (params.csa_offsets[i] >= params.len)
+ return -EINVAL;
+ }
+ }
+
+ if (!params.dont_wait_for_ack) {
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ hdr = nl80211hdr_put(msg, genl_info_snd_portid(info),
+ info->snd_seq, 0,
+ NL80211_CMD_FRAME);
+ if (!hdr) {
+ err = -ENOBUFS;
+ goto free_msg;
+ }
+ }
+
+ params.chan = chandef.chan;
+ err = cfg80211_mlme_mgmt_tx(rdev, wdev, ¶ms, &cookie);
+ if (err)
+ goto free_msg;
+
+ if (msg) {
+ if (nla_put_u64_64bit(msg, NL80211_ATTR_COOKIE, cookie,
+ NL80211_ATTR_PAD))
+ goto nla_put_failure;
+
+ genlmsg_end(msg, hdr);
+ return genlmsg_reply(msg, info);
+ }
+
+ return 0;
+
+ nla_put_failure:
+ err = -ENOBUFS;
+ free_msg:
+ nlmsg_free(msg);
+ return err;
+}
+
+static int nl80211_tx_mgmt_cancel_wait(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct wireless_dev *wdev = info->user_ptr[1];
+ u64 cookie;
+
+ if (!info->attrs[NL80211_ATTR_COOKIE])
+ return -EINVAL;
+
+ if (!rdev->ops->mgmt_tx_cancel_wait)
+ return -EOPNOTSUPP;
+
+ switch (wdev->iftype) {
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_P2P_CLIENT:
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_AP_VLAN:
+ case NL80211_IFTYPE_P2P_GO:
+ case NL80211_IFTYPE_P2P_DEVICE:
+ break;
+ case NL80211_IFTYPE_NAN:
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]);
+
+ return rdev_mgmt_tx_cancel_wait(rdev, wdev, cookie);
+}
+
+static int nl80211_set_power_save(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct wireless_dev *wdev;
+ struct net_device *dev = info->user_ptr[1];
+ u8 ps_state;
+ bool state;
+ int err;
+
+ if (!info->attrs[NL80211_ATTR_PS_STATE])
+ return -EINVAL;
+
+ ps_state = nla_get_u32(info->attrs[NL80211_ATTR_PS_STATE]);
+
+ if (ps_state != NL80211_PS_DISABLED && ps_state != NL80211_PS_ENABLED)
+ return -EINVAL;
+
+ wdev = dev->ieee80211_ptr;
+
+ if (!rdev->ops->set_power_mgmt)
+ return -EOPNOTSUPP;
+
+ state = (ps_state == NL80211_PS_ENABLED) ? true : false;
+
+ if (state == wdev->ps)
+ return 0;
+
+ err = rdev_set_power_mgmt(rdev, dev, state, wdev->ps_timeout);
+ if (!err)
+ wdev->ps = state;
+ return err;
+}
+
+static int nl80211_get_power_save(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ enum nl80211_ps_state ps_state;
+ struct wireless_dev *wdev;
+ struct net_device *dev = info->user_ptr[1];
+ struct sk_buff *msg;
+ void *hdr;
+ int err;
+
+ wdev = dev->ieee80211_ptr;
+
+ if (!rdev->ops->set_power_mgmt)
+ return -EOPNOTSUPP;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ hdr = nl80211hdr_put(msg, genl_info_snd_portid(info), info->snd_seq,
+ 0,
+ NL80211_CMD_GET_POWER_SAVE);
+ if (!hdr) {
+ err = -ENOBUFS;
+ goto free_msg;
+ }
+
+ if (wdev->ps)
+ ps_state = NL80211_PS_ENABLED;
+ else
+ ps_state = NL80211_PS_DISABLED;
+
+ if (nla_put_u32(msg, NL80211_ATTR_PS_STATE, ps_state))
+ goto nla_put_failure;
+
+ genlmsg_end(msg, hdr);
+ return genlmsg_reply(msg, info);
+
+ nla_put_failure:
+ err = -ENOBUFS;
+ free_msg:
+ nlmsg_free(msg);
+ return err;
+}
+
+static const struct nla_policy
+nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] = {
+ [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_BINARY },
+ [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U32 },
+ [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 },
+ [NL80211_ATTR_CQM_TXE_RATE] = { .type = NLA_U32 },
+ [NL80211_ATTR_CQM_TXE_PKTS] = { .type = NLA_U32 },
+ [NL80211_ATTR_CQM_TXE_INTVL] = { .type = NLA_U32 },
+ [NL80211_ATTR_CQM_RSSI_LEVEL] = { .type = NLA_S32 },
+};
+
+static int nl80211_set_cqm_txe(struct genl_info *info,
+ u32 rate, u32 pkts, u32 intvl)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+
+ if (rate > 100 || intvl > NL80211_CQM_TXE_MAX_INTVL)
+ return -EINVAL;
+
+ if (!rdev->ops->set_cqm_txe_config)
+ return -EOPNOTSUPP;
+
+ if (wdev->iftype != NL80211_IFTYPE_STATION &&
+ wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)
+ return -EOPNOTSUPP;
+
+ return rdev_set_cqm_txe_config(rdev, dev, rate, pkts, intvl);
+}
+
+static int cfg80211_cqm_rssi_update(struct cfg80211_registered_device *rdev,
+ struct net_device *dev)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ s32 last, low, high;
+ u32 hyst;
+ int i, n;
+ int err;
+
+ /* RSSI reporting disabled? */
+ if (!wdev->cqm_config)
+ return rdev_set_cqm_rssi_range_config(rdev, dev, 0, 0);
+
+ /*
+ * Obtain current RSSI value if possible, if not and no RSSI threshold
+ * event has been received yet, we should receive an event after a
+ * connection is established and enough beacons received to calculate
+ * the average.
+ */
+ if (!wdev->cqm_config->last_rssi_event_value && wdev->current_bss &&
+ rdev->ops->get_station) {
+ struct station_info sinfo;
+ u8 *mac_addr;
+
+ mac_addr = wdev->current_bss->pub.bssid;
+
+ err = rdev_get_station(rdev, dev, mac_addr, &sinfo);
+ if (err)
+ return err;
+
+ if (sinfo.filled & BIT(NL80211_STA_INFO_BEACON_SIGNAL_AVG))
+ wdev->cqm_config->last_rssi_event_value =
+ (s8) sinfo.rx_beacon_signal_avg;
+ }
+
+ last = wdev->cqm_config->last_rssi_event_value;
+ hyst = wdev->cqm_config->rssi_hyst;
+ n = wdev->cqm_config->n_rssi_thresholds;
+
+ for (i = 0; i < n; i++)
+ if (last < wdev->cqm_config->rssi_thresholds[i])
+ break;
+
+ low = i > 0 ?
+ (wdev->cqm_config->rssi_thresholds[i - 1] - hyst) : S32_MIN;
+ high = i < n ?
+ (wdev->cqm_config->rssi_thresholds[i] + hyst - 1) : S32_MAX;
+
+ return rdev_set_cqm_rssi_range_config(rdev, dev, low, high);
+}
+
+static int nl80211_set_cqm_rssi(struct genl_info *info,
+ const s32 *thresholds, int n_thresholds,
+ u32 hysteresis)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ int i, err;
+ s32 prev = S32_MIN;
+
+ /* Check all values negative and sorted */
+ for (i = 0; i < n_thresholds; i++) {
+ if (thresholds[i] > 0 || thresholds[i] <= prev)
+ return -EINVAL;
+
+ prev = thresholds[i];
+ }
+
+ if (wdev->iftype != NL80211_IFTYPE_STATION &&
+ wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)
+ return -EOPNOTSUPP;
+
+ wdev_lock(wdev);
+ cfg80211_cqm_config_free(wdev);
+ wdev_unlock(wdev);
+
+ if (n_thresholds <= 1 && rdev->ops->set_cqm_rssi_config) {
+ if (n_thresholds == 0 || thresholds[0] == 0) /* Disabling */
+ return rdev_set_cqm_rssi_config(rdev, dev, 0, 0);
+
+ return rdev_set_cqm_rssi_config(rdev, dev,
+ thresholds[0], hysteresis);
+ }
+
+ if (!wiphy_ext_feature_isset(&rdev->wiphy,
+ NL80211_EXT_FEATURE_CQM_RSSI_LIST))
+ return -EOPNOTSUPP;
+
+ if (n_thresholds == 1 && thresholds[0] == 0) /* Disabling */
+ n_thresholds = 0;
+
+ wdev_lock(wdev);
+ if (n_thresholds) {
+ struct cfg80211_cqm_config *cqm_config;
+
+ cqm_config = kzalloc(sizeof(struct cfg80211_cqm_config) +
+ n_thresholds * sizeof(s32), GFP_KERNEL);
+ if (!cqm_config) {
+ err = -ENOMEM;
+ goto unlock;
+ }
+
+ cqm_config->rssi_hyst = hysteresis;
+ cqm_config->n_rssi_thresholds = n_thresholds;
+ memcpy(cqm_config->rssi_thresholds, thresholds,
+ n_thresholds * sizeof(s32));
+
+ wdev->cqm_config = cqm_config;
+ }
+
+ err = cfg80211_cqm_rssi_update(rdev, dev);
+
+unlock:
+ wdev_unlock(wdev);
+
+ return err;
+}
+
+static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info)
+{
+ struct nlattr *attrs[NL80211_ATTR_CQM_MAX + 1];
+ struct nlattr *cqm;
+ int err;
+
+ cqm = info->attrs[NL80211_ATTR_CQM];
+ if (!cqm)
+ return -EINVAL;
+
+ err = nla_parse_nested(attrs, NL80211_ATTR_CQM_MAX, cqm,
+ nl80211_attr_cqm_policy,
+ genl_info_extack(info));
+ if (err)
+ return err;
+
+ if (attrs[NL80211_ATTR_CQM_RSSI_THOLD] &&
+ attrs[NL80211_ATTR_CQM_RSSI_HYST]) {
+ const s32 *thresholds =
+ nla_data(attrs[NL80211_ATTR_CQM_RSSI_THOLD]);
+ int len = nla_len(attrs[NL80211_ATTR_CQM_RSSI_THOLD]);
+ u32 hysteresis = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_HYST]);
+
+ if (len % 4)
+ return -EINVAL;
+
+ return nl80211_set_cqm_rssi(info, thresholds, len / 4,
+ hysteresis);
+ }
+
+ if (attrs[NL80211_ATTR_CQM_TXE_RATE] &&
+ attrs[NL80211_ATTR_CQM_TXE_PKTS] &&
+ attrs[NL80211_ATTR_CQM_TXE_INTVL]) {
+ u32 rate = nla_get_u32(attrs[NL80211_ATTR_CQM_TXE_RATE]);
+ u32 pkts = nla_get_u32(attrs[NL80211_ATTR_CQM_TXE_PKTS]);
+ u32 intvl = nla_get_u32(attrs[NL80211_ATTR_CQM_TXE_INTVL]);
+
+ return nl80211_set_cqm_txe(info, rate, pkts, intvl);
+ }
+
+ return -EINVAL;
+}
+
+static int nl80211_join_ocb(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct ocb_setup setup = {};
+ int err;
+
+ err = nl80211_parse_chandef(rdev, info, &setup.chandef);
+ if (err)
+ return err;
+
+ return cfg80211_join_ocb(rdev, dev, &setup);
+}
+
+static int nl80211_leave_ocb(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+
+ return cfg80211_leave_ocb(rdev, dev);
+}
+
+static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct mesh_config cfg;
+ struct mesh_setup setup;
+ int err;
+
+ /* start with default */
+ memcpy(&cfg, &default_mesh_config, sizeof(cfg));
+ memcpy(&setup, &default_mesh_setup, sizeof(setup));
+
+ if (info->attrs[NL80211_ATTR_MESH_CONFIG]) {
+ /* and parse parameters if given */
+ err = nl80211_parse_mesh_config(info, &cfg, NULL);
+ if (err)
+ return err;
+ }
+
+ if (!info->attrs[NL80211_ATTR_MESH_ID] ||
+ !nla_len(info->attrs[NL80211_ATTR_MESH_ID]))
+ return -EINVAL;
+
+ setup.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]);
+ setup.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
+
+ if (info->attrs[NL80211_ATTR_MCAST_RATE] &&
+ !nl80211_parse_mcast_rate(rdev, setup.mcast_rate,
+ nla_get_u32(info->attrs[NL80211_ATTR_MCAST_RATE])))
+ return -EINVAL;
+
+ if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) {
+ setup.beacon_interval =
+ nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
+
+ err = cfg80211_validate_beacon_int(rdev,
+ NL80211_IFTYPE_MESH_POINT,
+ setup.beacon_interval);
+ if (err)
+ return err;
+ }
+
+ if (info->attrs[NL80211_ATTR_DTIM_PERIOD]) {
+ setup.dtim_period =
+ nla_get_u32(info->attrs[NL80211_ATTR_DTIM_PERIOD]);
+ if (setup.dtim_period < 1 || setup.dtim_period > 100)
+ return -EINVAL;
+ }
+
+ if (info->attrs[NL80211_ATTR_MESH_SETUP]) {
+ /* parse additional setup parameters if given */
+ err = nl80211_parse_mesh_setup(info, &setup);
+ if (err)
+ return err;
+ }
+
+ if (setup.user_mpm)
+ cfg.auto_open_plinks = false;
+
+ if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
+ err = nl80211_parse_chandef(rdev, info, &setup.chandef);
+ if (err)
+ return err;
+ } else {
+ /* cfg80211_join_mesh() will sort it out */
+ setup.chandef.chan = NULL;
+ }
+
+ if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) {
+ u8 *rates = nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
+ int n_rates =
+ nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
+ struct ieee80211_supported_band *sband;
+
+ if (!setup.chandef.chan)
+ return -EINVAL;
+
+ sband = rdev->wiphy.bands[setup.chandef.chan->band];
+
+ err = ieee80211_get_ratemask(sband, rates, n_rates,
+ &setup.basic_rates);
+ if (err)
+ return err;
+ }
+
+ if (info->attrs[NL80211_ATTR_TX_RATES]) {
+ err = nl80211_parse_tx_bitrate_mask(info, &setup.beacon_rate);
+ if (err)
+ return err;
+
+ err = validate_beacon_tx_rate(rdev, setup.chandef.chan->band,
+ &setup.beacon_rate);
+ if (err)
+ return err;
+ }
+
+ return cfg80211_join_mesh(rdev, dev, &setup, &cfg);
+}
+
+static int nl80211_leave_mesh(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+
+ return cfg80211_leave_mesh(rdev, dev);
+}
+
+#ifdef CONFIG_PM
+static int nl80211_send_wowlan_patterns(struct sk_buff *msg,
+ struct cfg80211_registered_device *rdev)
+{
+ struct cfg80211_wowlan *wowlan = rdev->wiphy.wowlan_config;
+ struct nlattr *nl_pats, *nl_pat;
+ int i, pat_len;
+
+ if (!wowlan->n_patterns)
+ return 0;
+
+ nl_pats = nla_nest_start(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN);
+ if (!nl_pats)
+ return -ENOBUFS;
+
+ for (i = 0; i < wowlan->n_patterns; i++) {
+ nl_pat = nla_nest_start(msg, i + 1);
+ if (!nl_pat)
+ return -ENOBUFS;
+ pat_len = wowlan->patterns[i].pattern_len;
+ if (nla_put(msg, NL80211_PKTPAT_MASK, DIV_ROUND_UP(pat_len, 8),
+ wowlan->patterns[i].mask) ||
+ nla_put(msg, NL80211_PKTPAT_PATTERN, pat_len,
+ wowlan->patterns[i].pattern) ||
+ nla_put_u32(msg, NL80211_PKTPAT_OFFSET,
+ wowlan->patterns[i].pkt_offset) ||
+ nla_put_u8(msg, NL80211_PKTPAT_ACTION,
+ wowlan->patterns[i].action))
+ return -ENOBUFS;
+ nla_nest_end(msg, nl_pat);
+ }
+ nla_nest_end(msg, nl_pats);
+
+ return 0;
+}
+
+static int nl80211_send_wowlan_tcp(struct sk_buff *msg,
+ struct cfg80211_wowlan_tcp *tcp)
+{
+ struct nlattr *nl_tcp;
+
+ if (!tcp)
+ return 0;
+
+ nl_tcp = nla_nest_start(msg, NL80211_WOWLAN_TRIG_TCP_CONNECTION);
+ if (!nl_tcp)
+ return -ENOBUFS;
+
+ if (nla_put_in_addr(msg, NL80211_WOWLAN_TCP_SRC_IPV4, tcp->src) ||
+ nla_put_in_addr(msg, NL80211_WOWLAN_TCP_DST_IPV4, tcp->dst) ||
+ nla_put(msg, NL80211_WOWLAN_TCP_DST_MAC, ETH_ALEN, tcp->dst_mac) ||
+ nla_put_u16(msg, NL80211_WOWLAN_TCP_SRC_PORT, tcp->src_port) ||
+ nla_put_u16(msg, NL80211_WOWLAN_TCP_DST_PORT, tcp->dst_port) ||
+ nla_put(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD,
+ tcp->payload_len, tcp->payload) ||
+ nla_put_u32(msg, NL80211_WOWLAN_TCP_DATA_INTERVAL,
+ tcp->data_interval) ||
+ nla_put(msg, NL80211_WOWLAN_TCP_WAKE_PAYLOAD,
+ tcp->wake_len, tcp->wake_data) ||
+ nla_put(msg, NL80211_WOWLAN_TCP_WAKE_MASK,
+ DIV_ROUND_UP(tcp->wake_len, 8), tcp->wake_mask))
+ return -ENOBUFS;
+
+ if (tcp->payload_seq.len &&
+ nla_put(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ,
+ sizeof(tcp->payload_seq), &tcp->payload_seq))
+ return -ENOBUFS;
+
+ if (tcp->payload_tok.len &&
+ nla_put(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN,
+ sizeof(tcp->payload_tok) + tcp->tokens_size,
+ &tcp->payload_tok))
+ return -ENOBUFS;
+
+ nla_nest_end(msg, nl_tcp);
+
+ return 0;
+}
+
+static int nl80211_send_wowlan_nd(struct sk_buff *msg,
+ struct cfg80211_sched_scan_request *req)
+{
+ struct nlattr *nd, *freqs, *matches, *match, *scan_plans, *scan_plan;
+ int i;
+
+ if (!req)
+ return 0;
+
+ nd = nla_nest_start(msg, NL80211_WOWLAN_TRIG_NET_DETECT);
+ if (!nd)
+ return -ENOBUFS;
+
+ if (req->n_scan_plans == 1 &&
+ nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_INTERVAL,
+ req->scan_plans[0].interval * 1000))
+ return -ENOBUFS;
+
+ if (nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_DELAY, req->delay))
+ return -ENOBUFS;
+
+ if (req->relative_rssi_set) {
+ struct nl80211_bss_select_rssi_adjust rssi_adjust;
+
+ if (nla_put_s8(msg, NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI,
+ req->relative_rssi))
+ return -ENOBUFS;
+
+ rssi_adjust.band = req->rssi_adjust.band;
+ rssi_adjust.delta = req->rssi_adjust.delta;
+ if (nla_put(msg, NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST,
+ sizeof(rssi_adjust), &rssi_adjust))
+ return -ENOBUFS;
+ }
+
+ freqs = nla_nest_start(msg, NL80211_ATTR_SCAN_FREQUENCIES);
+ if (!freqs)
+ return -ENOBUFS;
+
+ for (i = 0; i < req->n_channels; i++) {
+ if (nla_put_u32(msg, i, req->channels[i]->center_freq))
+ return -ENOBUFS;
+ }
+
+ nla_nest_end(msg, freqs);
+
+ if (req->n_match_sets) {
+ matches = nla_nest_start(msg, NL80211_ATTR_SCHED_SCAN_MATCH);
+ if (!matches)
+ return -ENOBUFS;
+
+ for (i = 0; i < req->n_match_sets; i++) {
+ match = nla_nest_start(msg, i);
+ if (!match)
+ return -ENOBUFS;
+
+ if (nla_put(msg, NL80211_SCHED_SCAN_MATCH_ATTR_SSID,
+ req->match_sets[i].ssid.ssid_len,
+ req->match_sets[i].ssid.ssid))
+ return -ENOBUFS;
+ nla_nest_end(msg, match);
+ }
+ nla_nest_end(msg, matches);
+ }
+
+ scan_plans = nla_nest_start(msg, NL80211_ATTR_SCHED_SCAN_PLANS);
+ if (!scan_plans)
+ return -ENOBUFS;
+
+ for (i = 0; i < req->n_scan_plans; i++) {
+ scan_plan = nla_nest_start(msg, i + 1);
+ if (!scan_plan)
+ return -ENOBUFS;
+
+ if (!scan_plan ||
+ nla_put_u32(msg, NL80211_SCHED_SCAN_PLAN_INTERVAL,
+ req->scan_plans[i].interval) ||
+ (req->scan_plans[i].iterations &&
+ nla_put_u32(msg, NL80211_SCHED_SCAN_PLAN_ITERATIONS,
+ req->scan_plans[i].iterations)))
+ return -ENOBUFS;
+ nla_nest_end(msg, scan_plan);
+ }
+ nla_nest_end(msg, scan_plans);
+
+ nla_nest_end(msg, nd);
+
+ return 0;
+}
+
+static int nl80211_get_wowlan(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct sk_buff *msg;
+ void *hdr;
+ u32 size = NLMSG_DEFAULT_SIZE;
+
+ if (!rdev->wiphy.wowlan)
+ return -EOPNOTSUPP;
+
+ if (rdev->wiphy.wowlan_config && rdev->wiphy.wowlan_config->tcp) {
+ /* adjust size to have room for all the data */
+ size += rdev->wiphy.wowlan_config->tcp->tokens_size +
+ rdev->wiphy.wowlan_config->tcp->payload_len +
+ rdev->wiphy.wowlan_config->tcp->wake_len +
+ rdev->wiphy.wowlan_config->tcp->wake_len / 8;
+ }
+
+ msg = nlmsg_new(size, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ hdr = nl80211hdr_put(msg, genl_info_snd_portid(info), info->snd_seq,
+ 0,
+ NL80211_CMD_GET_WOWLAN);
+ if (!hdr)
+ goto nla_put_failure;
+
+ if (rdev->wiphy.wowlan_config) {
+ struct nlattr *nl_wowlan;
+
+ nl_wowlan = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS);
+ if (!nl_wowlan)
+ goto nla_put_failure;
+
+ if ((rdev->wiphy.wowlan_config->any &&
+ nla_put_flag(msg, NL80211_WOWLAN_TRIG_ANY)) ||
+ (rdev->wiphy.wowlan_config->disconnect &&
+ nla_put_flag(msg, NL80211_WOWLAN_TRIG_DISCONNECT)) ||
+ (rdev->wiphy.wowlan_config->magic_pkt &&
+ nla_put_flag(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT)) ||
+ (rdev->wiphy.wowlan_config->gtk_rekey_failure &&
+ nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE)) ||
+ (rdev->wiphy.wowlan_config->eap_identity_req &&
+ nla_put_flag(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST)) ||
+ (rdev->wiphy.wowlan_config->four_way_handshake &&
+ nla_put_flag(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE)) ||
+ (rdev->wiphy.wowlan_config->rfkill_release &&
+ nla_put_flag(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE)))
+ goto nla_put_failure;
+
+ if (nl80211_send_wowlan_patterns(msg, rdev))
+ goto nla_put_failure;
+
+ if (nl80211_send_wowlan_tcp(msg,
+ rdev->wiphy.wowlan_config->tcp))
+ goto nla_put_failure;
+
+ if (nl80211_send_wowlan_nd(
+ msg,
+ rdev->wiphy.wowlan_config->nd_config))
+ goto nla_put_failure;
+
+ nla_nest_end(msg, nl_wowlan);
+ }
+
+ genlmsg_end(msg, hdr);
+ return genlmsg_reply(msg, info);
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return -ENOBUFS;
+}
+
+static int nl80211_parse_wowlan_tcp(struct cfg80211_registered_device *rdev,
+ struct nlattr *attr,
+ struct cfg80211_wowlan *trig)
+{
+ struct nlattr *tb[NUM_NL80211_WOWLAN_TCP];
+ struct cfg80211_wowlan_tcp *cfg;
+ struct nl80211_wowlan_tcp_data_token *tok = NULL;
+ struct nl80211_wowlan_tcp_data_seq *seq = NULL;
+ u32 size;
+ u32 data_size, wake_size, tokens_size = 0, wake_mask_size;
+ int err, port;
+
+ if (!rdev->wiphy.wowlan->tcp)
+ return -EINVAL;
+
+ err = nla_parse_nested(tb, MAX_NL80211_WOWLAN_TCP, attr,
+ nl80211_wowlan_tcp_policy, NULL);
+ if (err)
+ return err;
+
+ if (!tb[NL80211_WOWLAN_TCP_SRC_IPV4] ||
+ !tb[NL80211_WOWLAN_TCP_DST_IPV4] ||
+ !tb[NL80211_WOWLAN_TCP_DST_MAC] ||
+ !tb[NL80211_WOWLAN_TCP_DST_PORT] ||
+ !tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD] ||
+ !tb[NL80211_WOWLAN_TCP_DATA_INTERVAL] ||
+ !tb[NL80211_WOWLAN_TCP_WAKE_PAYLOAD] ||
+ !tb[NL80211_WOWLAN_TCP_WAKE_MASK])
+ return -EINVAL;
+
+ data_size = nla_len(tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD]);
+ if (data_size > rdev->wiphy.wowlan->tcp->data_payload_max)
+ return -EINVAL;
+
+ if (nla_get_u32(tb[NL80211_WOWLAN_TCP_DATA_INTERVAL]) >
+ rdev->wiphy.wowlan->tcp->data_interval_max ||
+ nla_get_u32(tb[NL80211_WOWLAN_TCP_DATA_INTERVAL]) == 0)
+ return -EINVAL;
+
+ wake_size = nla_len(tb[NL80211_WOWLAN_TCP_WAKE_PAYLOAD]);
+ if (wake_size > rdev->wiphy.wowlan->tcp->wake_payload_max)
+ return -EINVAL;
+
+ wake_mask_size = nla_len(tb[NL80211_WOWLAN_TCP_WAKE_MASK]);
+ if (wake_mask_size != DIV_ROUND_UP(wake_size, 8))
+ return -EINVAL;
+
+ if (tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN]) {
+ u32 tokln = nla_len(tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN]);
+
+ tok = nla_data(tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN]);
+ tokens_size = tokln - sizeof(*tok);
+
+ if (!tok->len || tokens_size % tok->len)
+ return -EINVAL;
+ if (!rdev->wiphy.wowlan->tcp->tok)
+ return -EINVAL;
+ if (tok->len > rdev->wiphy.wowlan->tcp->tok->max_len)
+ return -EINVAL;
+ if (tok->len < rdev->wiphy.wowlan->tcp->tok->min_len)
+ return -EINVAL;
+ if (tokens_size > rdev->wiphy.wowlan->tcp->tok->bufsize)
+ return -EINVAL;
+ if (tok->offset + tok->len > data_size)
+ return -EINVAL;
+ }
+
+ if (tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ]) {
+ seq = nla_data(tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ]);
+ if (!rdev->wiphy.wowlan->tcp->seq)
+ return -EINVAL;
+ if (seq->len == 0 || seq->len > 4)
+ return -EINVAL;
+ if (seq->len + seq->offset > data_size)
+ return -EINVAL;
+ }
+
+ size = sizeof(*cfg);
+ size += data_size;
+ size += wake_size + wake_mask_size;
+ size += tokens_size;
+
+ cfg = kzalloc(size, GFP_KERNEL);
+ if (!cfg)
+ return -ENOMEM;
+ cfg->src = nla_get_in_addr(tb[NL80211_WOWLAN_TCP_SRC_IPV4]);
+ cfg->dst = nla_get_in_addr(tb[NL80211_WOWLAN_TCP_DST_IPV4]);
+ memcpy(cfg->dst_mac, nla_data(tb[NL80211_WOWLAN_TCP_DST_MAC]),
+ ETH_ALEN);
+ if (tb[NL80211_WOWLAN_TCP_SRC_PORT])
+ port = nla_get_u16(tb[NL80211_WOWLAN_TCP_SRC_PORT]);
+ else
+ port = 0;
+#ifdef CONFIG_INET
+ /* allocate a socket and port for it and use it */
+ err = __sock_create(wiphy_net(&rdev->wiphy), PF_INET, SOCK_STREAM,
+ IPPROTO_TCP, &cfg->sock, 1);
+ if (err) {
+ kfree(cfg);
+ return err;
+ }
+ if (inet_csk_get_port(cfg->sock->sk, port)) {
+ sock_release(cfg->sock);
+ kfree(cfg);
+ return -EADDRINUSE;
+ }
+ cfg->src_port = inet_sk(cfg->sock->sk)->inet_num;
+#else
+ if (!port) {
+ kfree(cfg);
+ return -EINVAL;
+ }
+ cfg->src_port = port;
+#endif
+
+ cfg->dst_port = nla_get_u16(tb[NL80211_WOWLAN_TCP_DST_PORT]);
+ cfg->payload_len = data_size;
+ cfg->payload = (u8 *)cfg + sizeof(*cfg) + tokens_size;
+ memcpy((void *)cfg->payload,
+ nla_data(tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD]),
+ data_size);
+ if (seq)
+ cfg->payload_seq = *seq;
+ cfg->data_interval = nla_get_u32(tb[NL80211_WOWLAN_TCP_DATA_INTERVAL]);
+ cfg->wake_len = wake_size;
+ cfg->wake_data = (u8 *)cfg + sizeof(*cfg) + tokens_size + data_size;
+ memcpy((void *)cfg->wake_data,
+ nla_data(tb[NL80211_WOWLAN_TCP_WAKE_PAYLOAD]),
+ wake_size);
+ cfg->wake_mask = (u8 *)cfg + sizeof(*cfg) + tokens_size +
+ data_size + wake_size;
+ memcpy((void *)cfg->wake_mask,
+ nla_data(tb[NL80211_WOWLAN_TCP_WAKE_MASK]),
+ wake_mask_size);
+ if (tok) {
+ cfg->tokens_size = tokens_size;
+ memcpy(&cfg->payload_tok, tok, sizeof(*tok) + tokens_size);
+ }
+
+ trig->tcp = cfg;
+
+ return 0;
+}
+
+static int nl80211_parse_wowlan_nd(struct cfg80211_registered_device *rdev,
+ const struct wiphy_wowlan_support *wowlan,
+ struct nlattr *attr,
+ struct cfg80211_wowlan *trig)
+{
+ struct nlattr **tb;
+ int err;
+
+ tb = kzalloc(NUM_NL80211_ATTR * sizeof(*tb), GFP_KERNEL);
+ if (!tb)
+ return -ENOMEM;
+
+ if (!(wowlan->flags & WIPHY_WOWLAN_NET_DETECT)) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ err = nla_parse_nested(tb, NL80211_ATTR_MAX, attr, nl80211_policy,
+ NULL);
+ if (err)
+ goto out;
+
+ trig->nd_config = nl80211_parse_sched_scan(&rdev->wiphy, NULL, tb,
+ wowlan->max_nd_match_sets);
+ err = PTR_ERR_OR_ZERO(trig->nd_config);
+ if (err)
+ trig->nd_config = NULL;
+
+out:
+ kfree(tb);
+ return err;
+}
+
+static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct nlattr *tb[NUM_NL80211_WOWLAN_TRIG];
+ struct cfg80211_wowlan new_triggers = {};
+ struct cfg80211_wowlan *ntrig;
+ const struct wiphy_wowlan_support *wowlan = rdev->wiphy.wowlan;
+ int err, i;
+ bool prev_enabled = rdev->wiphy.wowlan_config;
+ bool regular = false;
+
+ if (!wowlan)
+ return -EOPNOTSUPP;
+
+ if (!info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]) {
+ cfg80211_rdev_free_wowlan(rdev);
+ rdev->wiphy.wowlan_config = NULL;
+ goto set_wakeup;
+ }
+
+ err = nla_parse_nested(tb, MAX_NL80211_WOWLAN_TRIG,
+ info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS],
+ nl80211_wowlan_policy, genl_info_extack(info));
+ if (err)
+ return err;
+
+ if (tb[NL80211_WOWLAN_TRIG_ANY]) {
+ if (!(wowlan->flags & WIPHY_WOWLAN_ANY))
+ return -EINVAL;
+ new_triggers.any = true;
+ }
+
+ if (tb[NL80211_WOWLAN_TRIG_DISCONNECT]) {
+ if (!(wowlan->flags & WIPHY_WOWLAN_DISCONNECT))
+ return -EINVAL;
+ new_triggers.disconnect = true;
+ regular = true;
+ }
+
+ if (tb[NL80211_WOWLAN_TRIG_MAGIC_PKT]) {
+ if (!(wowlan->flags & WIPHY_WOWLAN_MAGIC_PKT))
+ return -EINVAL;
+ new_triggers.magic_pkt = true;
+ regular = true;
+ }
+
+ if (tb[NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED])
+ return -EINVAL;
+
+ if (tb[NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE]) {
+ if (!(wowlan->flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE))
+ return -EINVAL;
+ new_triggers.gtk_rekey_failure = true;
+ regular = true;
+ }
+
+ if (tb[NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST]) {
+ if (!(wowlan->flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ))
+ return -EINVAL;
+ new_triggers.eap_identity_req = true;
+ regular = true;
+ }
+
+ if (tb[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE]) {
+ if (!(wowlan->flags & WIPHY_WOWLAN_4WAY_HANDSHAKE))
+ return -EINVAL;
+ new_triggers.four_way_handshake = true;
+ regular = true;
+ }
+
+ if (tb[NL80211_WOWLAN_TRIG_RFKILL_RELEASE]) {
+ if (!(wowlan->flags & WIPHY_WOWLAN_RFKILL_RELEASE))
+ return -EINVAL;
+ new_triggers.rfkill_release = true;
+ regular = true;
+ }
+
+ if (tb[NL80211_WOWLAN_TRIG_PKT_PATTERN]) {
+ struct nlattr *pat;
+ int n_patterns = 0;
+ int rem, pat_len, mask_len, pkt_offset;
+ struct nlattr *pat_tb[NUM_NL80211_PKTPAT];
+ u8 action = NL80211_WOWLAN_ACTION_ALLOW;
+
+ regular = true;
+
+ nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN],
+ rem)
+ n_patterns++;
+ if (n_patterns > wowlan->n_patterns)
+ return -EINVAL;
+
+ new_triggers.patterns = kcalloc(n_patterns,
+ sizeof(new_triggers.patterns[0]),
+ GFP_KERNEL);
+ if (!new_triggers.patterns)
+ return -ENOMEM;
+
+ new_triggers.n_patterns = n_patterns;
+ i = 0;
+
+ nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN],
+ rem) {
+ u8 *mask_pat;
+
+ nla_parse_nested(pat_tb, MAX_NL80211_PKTPAT, pat,
+ NULL, genl_info_extack(info));
+ err = -EINVAL;
+ if (!pat_tb[NL80211_PKTPAT_MASK] ||
+ !pat_tb[NL80211_PKTPAT_PATTERN])
+ goto error;
+ pat_len = nla_len(pat_tb[NL80211_PKTPAT_PATTERN]);
+ mask_len = DIV_ROUND_UP(pat_len, 8);
+ if (nla_len(pat_tb[NL80211_PKTPAT_MASK]) != mask_len)
+ goto error;
+ if (pat_len > wowlan->pattern_max_len ||
+ pat_len < wowlan->pattern_min_len)
+ goto error;
+
+ if (!pat_tb[NL80211_PKTPAT_OFFSET])
+ pkt_offset = 0;
+ else
+ pkt_offset = nla_get_u32(
+ pat_tb[NL80211_PKTPAT_OFFSET]);
+ if (pkt_offset > wowlan->max_pkt_offset)
+ goto error;
+ new_triggers.patterns[i].pkt_offset = pkt_offset;
+
+ if (pat_tb[NL80211_PKTPAT_ACTION]) {
+ action = nla_get_u8(
+ pat_tb[NL80211_PKTPAT_ACTION]);
+ if (action > MAX_NL80211_WOWLAN_ACTION)
+ goto error;
+ }
+ mask_pat = kmalloc(mask_len + pat_len, GFP_KERNEL);
+ if (!mask_pat) {
+ err = -ENOMEM;
+ goto error;
+ }
+ new_triggers.patterns[i].action = action;
+ new_triggers.patterns[i].mask = mask_pat;
+ memcpy(mask_pat, nla_data(pat_tb[NL80211_PKTPAT_MASK]),
+ mask_len);
+ mask_pat += mask_len;
+ new_triggers.patterns[i].pattern = mask_pat;
+ new_triggers.patterns[i].pattern_len = pat_len;
+ memcpy(mask_pat,
+ nla_data(pat_tb[NL80211_PKTPAT_PATTERN]),
+ pat_len);
+ i++;
+ }
+ }
+
+ if (tb[NL80211_WOWLAN_TRIG_TCP_CONNECTION]) {
+ regular = true;
+ err = nl80211_parse_wowlan_tcp(
+ rdev, tb[NL80211_WOWLAN_TRIG_TCP_CONNECTION],
+ &new_triggers);
+ if (err)
+ goto error;
+ }
+
+ if (tb[NL80211_WOWLAN_TRIG_NET_DETECT]) {
+ regular = true;
+ err = nl80211_parse_wowlan_nd(
+ rdev, wowlan, tb[NL80211_WOWLAN_TRIG_NET_DETECT],
+ &new_triggers);
+ if (err)
+ goto error;
+ }
+
+ /* The 'any' trigger means the device continues operating more or less
+ * as in its normal operation mode and wakes up the host on most of the
+ * normal interrupts (like packet RX, ...)
+ * It therefore makes little sense to combine with the more constrained
+ * wakeup trigger modes.
+ */
+ if (new_triggers.any && regular) {
+ err = -EINVAL;
+ goto error;
+ }
+
+ ntrig = kmemdup(&new_triggers, sizeof(new_triggers), GFP_KERNEL);
+ if (!ntrig) {
+ err = -ENOMEM;
+ goto error;
+ }
+ cfg80211_rdev_free_wowlan(rdev);
+ rdev->wiphy.wowlan_config = ntrig;
+
+ set_wakeup:
+ if (rdev->ops->set_wakeup &&
+ prev_enabled != !!rdev->wiphy.wowlan_config)
+ rdev_set_wakeup(rdev, rdev->wiphy.wowlan_config);
+
+ return 0;
+ error:
+ for (i = 0; i < new_triggers.n_patterns; i++)
+ kfree(new_triggers.patterns[i].mask);
+ kfree(new_triggers.patterns);
+ if (new_triggers.tcp && new_triggers.tcp->sock)
+ sock_release(new_triggers.tcp->sock);
+ kfree(new_triggers.tcp);
+ kfree(new_triggers.nd_config);
+ return err;
+}
+#endif
+
+static int nl80211_send_coalesce_rules(struct sk_buff *msg,
+ struct cfg80211_registered_device *rdev)
+{
+ struct nlattr *nl_pats, *nl_pat, *nl_rule, *nl_rules;
+ int i, j, pat_len;
+ struct cfg80211_coalesce_rules *rule;
+
+ if (!rdev->coalesce->n_rules)
+ return 0;
+
+ nl_rules = nla_nest_start(msg, NL80211_ATTR_COALESCE_RULE);
+ if (!nl_rules)
+ return -ENOBUFS;
+
+ for (i = 0; i < rdev->coalesce->n_rules; i++) {
+ nl_rule = nla_nest_start(msg, i + 1);
+ if (!nl_rule)
+ return -ENOBUFS;
+
+ rule = &rdev->coalesce->rules[i];
+ if (nla_put_u32(msg, NL80211_ATTR_COALESCE_RULE_DELAY,
+ rule->delay))
+ return -ENOBUFS;
+
+ if (nla_put_u32(msg, NL80211_ATTR_COALESCE_RULE_CONDITION,
+ rule->condition))
+ return -ENOBUFS;
+
+ nl_pats = nla_nest_start(msg,
+ NL80211_ATTR_COALESCE_RULE_PKT_PATTERN);
+ if (!nl_pats)
+ return -ENOBUFS;
+
+ for (j = 0; j < rule->n_patterns; j++) {
+ nl_pat = nla_nest_start(msg, j + 1);
+ if (!nl_pat)
+ return -ENOBUFS;
+ pat_len = rule->patterns[j].pattern_len;
+ if (nla_put(msg, NL80211_PKTPAT_MASK,
+ DIV_ROUND_UP(pat_len, 8),
+ rule->patterns[j].mask) ||
+ nla_put(msg, NL80211_PKTPAT_PATTERN, pat_len,
+ rule->patterns[j].pattern) ||
+ nla_put_u32(msg, NL80211_PKTPAT_OFFSET,
+ rule->patterns[j].pkt_offset))
+ return -ENOBUFS;
+ nla_nest_end(msg, nl_pat);
+ }
+ nla_nest_end(msg, nl_pats);
+ nla_nest_end(msg, nl_rule);
+ }
+ nla_nest_end(msg, nl_rules);
+
+ return 0;
+}
+
+static int nl80211_get_coalesce(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct sk_buff *msg;
+ void *hdr;
+
+ if (!rdev->wiphy.coalesce)
+ return -EOPNOTSUPP;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ hdr = nl80211hdr_put(msg, genl_info_snd_portid(info), info->snd_seq,
+ 0,
+ NL80211_CMD_GET_COALESCE);
+ if (!hdr)
+ goto nla_put_failure;
+
+ if (rdev->coalesce && nl80211_send_coalesce_rules(msg, rdev))
+ goto nla_put_failure;
+
+ genlmsg_end(msg, hdr);
+ return genlmsg_reply(msg, info);
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return -ENOBUFS;
+}
+
+void cfg80211_rdev_free_coalesce(struct cfg80211_registered_device *rdev)
+{
+ struct cfg80211_coalesce *coalesce = rdev->coalesce;
+ int i, j;
+ struct cfg80211_coalesce_rules *rule;
+
+ if (!coalesce)
+ return;
+
+ for (i = 0; i < coalesce->n_rules; i++) {
+ rule = &coalesce->rules[i];
+ for (j = 0; j < rule->n_patterns; j++)
+ kfree(rule->patterns[j].mask);
+ kfree(rule->patterns);
+ }
+ kfree(coalesce->rules);
+ kfree(coalesce);
+ rdev->coalesce = NULL;
+}
+
+static int nl80211_parse_coalesce_rule(struct cfg80211_registered_device *rdev,
+ struct nlattr *rule,
+ struct cfg80211_coalesce_rules *new_rule)
+{
+ int err, i;
+ const struct wiphy_coalesce_support *coalesce = rdev->wiphy.coalesce;
+ struct nlattr *tb[NUM_NL80211_ATTR_COALESCE_RULE], *pat;
+ int rem, pat_len, mask_len, pkt_offset, n_patterns = 0;
+ struct nlattr *pat_tb[NUM_NL80211_PKTPAT];
+
+ err = nla_parse_nested(tb, NL80211_ATTR_COALESCE_RULE_MAX, rule,
+ nl80211_coalesce_policy, NULL);
+ if (err)
+ return err;
+
+ if (tb[NL80211_ATTR_COALESCE_RULE_DELAY])
+ new_rule->delay =
+ nla_get_u32(tb[NL80211_ATTR_COALESCE_RULE_DELAY]);
+ if (new_rule->delay > coalesce->max_delay)
+ return -EINVAL;
+
+ if (tb[NL80211_ATTR_COALESCE_RULE_CONDITION])
+ new_rule->condition =
+ nla_get_u32(tb[NL80211_ATTR_COALESCE_RULE_CONDITION]);
+ if (new_rule->condition != NL80211_COALESCE_CONDITION_MATCH &&
+ new_rule->condition != NL80211_COALESCE_CONDITION_NO_MATCH)
+ return -EINVAL;
+
+ if (!tb[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN])
+ return -EINVAL;
+
+ nla_for_each_nested(pat, tb[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN],
+ rem)
+ n_patterns++;
+ if (n_patterns > coalesce->n_patterns)
+ return -EINVAL;
+
+ new_rule->patterns = kcalloc(n_patterns, sizeof(new_rule->patterns[0]),
+ GFP_KERNEL);
+ if (!new_rule->patterns)
+ return -ENOMEM;
+
+ new_rule->n_patterns = n_patterns;
+ i = 0;
+
+ nla_for_each_nested(pat, tb[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN],
+ rem) {
+ u8 *mask_pat;
+
+ nla_parse_nested(pat_tb, MAX_NL80211_PKTPAT, pat, NULL, NULL);
+ if (!pat_tb[NL80211_PKTPAT_MASK] ||
+ !pat_tb[NL80211_PKTPAT_PATTERN])
+ return -EINVAL;
+ pat_len = nla_len(pat_tb[NL80211_PKTPAT_PATTERN]);
+ mask_len = DIV_ROUND_UP(pat_len, 8);
+ if (nla_len(pat_tb[NL80211_PKTPAT_MASK]) != mask_len)
+ return -EINVAL;
+ if (pat_len > coalesce->pattern_max_len ||
+ pat_len < coalesce->pattern_min_len)
+ return -EINVAL;
+
+ if (!pat_tb[NL80211_PKTPAT_OFFSET])
+ pkt_offset = 0;
+ else
+ pkt_offset = nla_get_u32(pat_tb[NL80211_PKTPAT_OFFSET]);
+ if (pkt_offset > coalesce->max_pkt_offset)
+ return -EINVAL;
+ new_rule->patterns[i].pkt_offset = pkt_offset;
+
+ mask_pat = kmalloc(mask_len + pat_len, GFP_KERNEL);
+ if (!mask_pat)
+ return -ENOMEM;
+
+ new_rule->patterns[i].mask = mask_pat;
+ memcpy(mask_pat, nla_data(pat_tb[NL80211_PKTPAT_MASK]),
+ mask_len);
+
+ mask_pat += mask_len;
+ new_rule->patterns[i].pattern = mask_pat;
+ new_rule->patterns[i].pattern_len = pat_len;
+ memcpy(mask_pat, nla_data(pat_tb[NL80211_PKTPAT_PATTERN]),
+ pat_len);
+ i++;
+ }
+
+ return 0;
+}
+
+static int nl80211_set_coalesce(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ const struct wiphy_coalesce_support *coalesce = rdev->wiphy.coalesce;
+ struct cfg80211_coalesce new_coalesce = {};
+ struct cfg80211_coalesce *n_coalesce;
+ int err, rem_rule, n_rules = 0, i, j;
+ struct nlattr *rule;
+ struct cfg80211_coalesce_rules *tmp_rule;
+
+ if (!rdev->wiphy.coalesce || !rdev->ops->set_coalesce)
+ return -EOPNOTSUPP;
+
+ if (!info->attrs[NL80211_ATTR_COALESCE_RULE]) {
+ cfg80211_rdev_free_coalesce(rdev);
+ rdev_set_coalesce(rdev, NULL);
+ return 0;
+ }
+
+ nla_for_each_nested(rule, info->attrs[NL80211_ATTR_COALESCE_RULE],
+ rem_rule)
+ n_rules++;
+ if (n_rules > coalesce->n_rules)
+ return -EINVAL;
+
+ new_coalesce.rules = kcalloc(n_rules, sizeof(new_coalesce.rules[0]),
+ GFP_KERNEL);
+ if (!new_coalesce.rules)
+ return -ENOMEM;
+
+ new_coalesce.n_rules = n_rules;
+ i = 0;
+
+ nla_for_each_nested(rule, info->attrs[NL80211_ATTR_COALESCE_RULE],
+ rem_rule) {
+ err = nl80211_parse_coalesce_rule(rdev, rule,
+ &new_coalesce.rules[i]);
+ if (err)
+ goto error;
+
+ i++;
+ }
+
+ err = rdev_set_coalesce(rdev, &new_coalesce);
+ if (err)
+ goto error;
+
+ n_coalesce = kmemdup(&new_coalesce, sizeof(new_coalesce), GFP_KERNEL);
+ if (!n_coalesce) {
+ err = -ENOMEM;
+ goto error;
+ }
+ cfg80211_rdev_free_coalesce(rdev);
+ rdev->coalesce = n_coalesce;
+
+ return 0;
+error:
+ for (i = 0; i < new_coalesce.n_rules; i++) {
+ tmp_rule = &new_coalesce.rules[i];
+ for (j = 0; j < tmp_rule->n_patterns; j++)
+ kfree(tmp_rule->patterns[j].mask);
+ kfree(tmp_rule->patterns);
+ }
+ kfree(new_coalesce.rules);
+
+ return err;
+}
+
+static int nl80211_set_rekey_data(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct nlattr *tb[NUM_NL80211_REKEY_DATA];
+ struct cfg80211_gtk_rekey_data rekey_data;
+ int err;
+
+ if (!info->attrs[NL80211_ATTR_REKEY_DATA])
+ return -EINVAL;
+
+ err = nla_parse_nested(tb, MAX_NL80211_REKEY_DATA,
+ info->attrs[NL80211_ATTR_REKEY_DATA],
+ nl80211_rekey_policy, genl_info_extack(info));
+ if (err)
+ return err;
+
+ if (nla_len(tb[NL80211_REKEY_DATA_REPLAY_CTR]) != NL80211_REPLAY_CTR_LEN)
+ return -ERANGE;
+ if (nla_len(tb[NL80211_REKEY_DATA_KEK]) != NL80211_KEK_LEN)
+ return -ERANGE;
+ if (nla_len(tb[NL80211_REKEY_DATA_KCK]) != NL80211_KCK_LEN)
+ return -ERANGE;
+
+ rekey_data.kek = nla_data(tb[NL80211_REKEY_DATA_KEK]);
+ rekey_data.kck = nla_data(tb[NL80211_REKEY_DATA_KCK]);
+ rekey_data.replay_ctr = nla_data(tb[NL80211_REKEY_DATA_REPLAY_CTR]);
+
+ wdev_lock(wdev);
+ if (!wdev->current_bss) {
+ err = -ENOTCONN;
+ goto out;
+ }
+
+ if (!rdev->ops->set_rekey_data) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ err = rdev_set_rekey_data(rdev, dev, &rekey_data);
+ out:
+ wdev_unlock(wdev);
+ return err;
+}
+
+static int nl80211_register_unexpected_frame(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct net_device *dev = info->user_ptr[1];
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+
+ if (wdev->iftype != NL80211_IFTYPE_AP &&
+ wdev->iftype != NL80211_IFTYPE_P2P_GO)
+ return -EINVAL;
+
+ if (wdev->ap_unexpected_nlportid)
+ return -EBUSY;
+
+ wdev->ap_unexpected_nlportid = genl_info_snd_portid(info);
+ return 0;
+}
+
+static int nl80211_probe_client(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct sk_buff *msg;
+ void *hdr;
+ const u8 *addr;
+ u64 cookie;
+ int err;
+
+ if (wdev->iftype != NL80211_IFTYPE_AP &&
+ wdev->iftype != NL80211_IFTYPE_P2P_GO)
+ return -EOPNOTSUPP;
+
+ if (!info->attrs[NL80211_ATTR_MAC])
+ return -EINVAL;
+
+ if (!rdev->ops->probe_client)
+ return -EOPNOTSUPP;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ hdr = nl80211hdr_put(msg, genl_info_snd_portid(info), info->snd_seq,
+ 0,
+ NL80211_CMD_PROBE_CLIENT);
+ if (!hdr) {
+ err = -ENOBUFS;
+ goto free_msg;
+ }
+
+ addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
+
+ err = rdev_probe_client(rdev, dev, addr, &cookie);
+ if (err)
+ goto free_msg;
+
+ if (nla_put_u64_64bit(msg, NL80211_ATTR_COOKIE, cookie,
+ NL80211_ATTR_PAD))
+ goto nla_put_failure;
+
+ genlmsg_end(msg, hdr);
+
+ return genlmsg_reply(msg, info);
+
+ nla_put_failure:
+ err = -ENOBUFS;
+ free_msg:
+ nlmsg_free(msg);
+ return err;
+}
+
+static int nl80211_register_beacons(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct cfg80211_beacon_registration *reg, *nreg;
+ int rv;
+
+ if (!(rdev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS))
+ return -EOPNOTSUPP;
+
+ nreg = kzalloc(sizeof(*nreg), GFP_KERNEL);
+ if (!nreg)
+ return -ENOMEM;
+
+ /* First, check if already registered. */
+ spin_lock_bh(&rdev->beacon_registrations_lock);
+ list_for_each_entry(reg, &rdev->beacon_registrations, list) {
+ if (reg->nlportid == genl_info_snd_portid(info)) {
+ rv = -EALREADY;
+ goto out_err;
+ }
+ }
+ /* Add it to the list */
+ nreg->nlportid = genl_info_snd_portid(info);
+ list_add(&nreg->list, &rdev->beacon_registrations);
+
+ spin_unlock_bh(&rdev->beacon_registrations_lock);
+
+ return 0;
+out_err:
+ spin_unlock_bh(&rdev->beacon_registrations_lock);
+ kfree(nreg);
+ return rv;
+}
+
+static int nl80211_start_p2p_device(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct wireless_dev *wdev = info->user_ptr[1];
+ int err;
+
+ if (!rdev->ops->start_p2p_device)
+ return -EOPNOTSUPP;
+
+ if (wdev->iftype != NL80211_IFTYPE_P2P_DEVICE)
+ return -EOPNOTSUPP;
+
+ if (wdev_running(wdev))
+ return 0;
+
+ if (rfkill_blocked(rdev->rfkill))
+ return -ERFKILL;
+
+ err = rdev_start_p2p_device(rdev, wdev);
+ if (err)
+ return err;
+
+ wdev->is_running = true;
+ rdev->opencount++;
+
+ return 0;
+}
+
+static int nl80211_stop_p2p_device(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct wireless_dev *wdev = info->user_ptr[1];
+
+ if (wdev->iftype != NL80211_IFTYPE_P2P_DEVICE)
+ return -EOPNOTSUPP;
+
+ if (!rdev->ops->stop_p2p_device)
+ return -EOPNOTSUPP;
+
+ cfg80211_stop_p2p_device(rdev, wdev);
+
+ return 0;
+}
+
+static int nl80211_start_nan(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct wireless_dev *wdev = info->user_ptr[1];
+ struct cfg80211_nan_conf conf = {};
+ int err;
+
+ if (wdev->iftype != NL80211_IFTYPE_NAN)
+ return -EOPNOTSUPP;
+
+ if (wdev_running(wdev))
+ return -EEXIST;
+
+ if (rfkill_blocked(rdev->rfkill))
+ return -ERFKILL;
+
+ if (!info->attrs[NL80211_ATTR_NAN_MASTER_PREF])
+ return -EINVAL;
+
+ conf.master_pref =
+ nla_get_u8(info->attrs[NL80211_ATTR_NAN_MASTER_PREF]);
+ if (!conf.master_pref)
+ return -EINVAL;
+
+ if (info->attrs[NL80211_ATTR_BANDS]) {
+ u32 bands = nla_get_u32(info->attrs[NL80211_ATTR_BANDS]);
+
+ if (bands & ~(u32)wdev->wiphy->nan_supported_bands)
+ return -EOPNOTSUPP;
+
+ if (bands && !(bands & BIT(NL80211_BAND_2GHZ)))
+ return -EINVAL;
+
+ conf.bands = bands;
+ }
+
+ err = rdev_start_nan(rdev, wdev, &conf);
+ if (err)
+ return err;
+
+ wdev->is_running = true;
+ rdev->opencount++;
+
+ return 0;
+}
+
+static int nl80211_stop_nan(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct wireless_dev *wdev = info->user_ptr[1];
+
+ if (wdev->iftype != NL80211_IFTYPE_NAN)
+ return -EOPNOTSUPP;
+
+ cfg80211_stop_nan(rdev, wdev);
+
+ return 0;
+}
+
+static int validate_nan_filter(struct nlattr *filter_attr)
+{
+ struct nlattr *attr;
+ int len = 0, n_entries = 0, rem;
+
+ nla_for_each_nested(attr, filter_attr, rem) {
+ len += nla_len(attr);
+ n_entries++;
+ }
+
+ if (len >= U8_MAX)
+ return -EINVAL;
+
+ return n_entries;
+}
+
+static int handle_nan_filter(struct nlattr *attr_filter,
+ struct cfg80211_nan_func *func,
+ bool tx)
+{
+ struct nlattr *attr;
+ int n_entries, rem, i;
+ struct cfg80211_nan_func_filter *filter;
+
+ n_entries = validate_nan_filter(attr_filter);
+ if (n_entries < 0)
+ return n_entries;
+
+ BUILD_BUG_ON(sizeof(*func->rx_filters) != sizeof(*func->tx_filters));
+
+ filter = kcalloc(n_entries, sizeof(*func->rx_filters), GFP_KERNEL);
+ if (!filter)
+ return -ENOMEM;
+
+ i = 0;
+ nla_for_each_nested(attr, attr_filter, rem) {
+ filter[i].filter = nla_memdup(attr, GFP_KERNEL);
+ filter[i].len = nla_len(attr);
+ i++;
+ }
+ if (tx) {
+ func->num_tx_filters = n_entries;
+ func->tx_filters = filter;
+ } else {
+ func->num_rx_filters = n_entries;
+ func->rx_filters = filter;
+ }
+
+ return 0;
+}
+
+static int nl80211_nan_add_func(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct wireless_dev *wdev = info->user_ptr[1];
+ struct nlattr *tb[NUM_NL80211_NAN_FUNC_ATTR], *func_attr;
+ struct cfg80211_nan_func *func;
+ struct sk_buff *msg = NULL;
+ void *hdr = NULL;
+ int err = 0;
+
+ if (wdev->iftype != NL80211_IFTYPE_NAN)
+ return -EOPNOTSUPP;
+
+ if (!wdev_running(wdev))
+ return -ENOTCONN;
+
+ if (!info->attrs[NL80211_ATTR_NAN_FUNC])
+ return -EINVAL;
+
+ if (wdev->owner_nlportid &&
+ wdev->owner_nlportid != genl_info_snd_portid(info))
+ return -ENOTCONN;
+
+ err = nla_parse_nested(tb, NL80211_NAN_FUNC_ATTR_MAX,
+ info->attrs[NL80211_ATTR_NAN_FUNC],
+ nl80211_nan_func_policy,
+ genl_info_extack(info));
+ if (err)
+ return err;
+
+ func = kzalloc(sizeof(*func), GFP_KERNEL);
+ if (!func)
+ return -ENOMEM;
+
+ func->cookie = wdev->wiphy->cookie_counter++;
+
+ if (!tb[NL80211_NAN_FUNC_TYPE] ||
+ nla_get_u8(tb[NL80211_NAN_FUNC_TYPE]) > NL80211_NAN_FUNC_MAX_TYPE) {
+ err = -EINVAL;
+ goto out;
+ }
+
+
+ func->type = nla_get_u8(tb[NL80211_NAN_FUNC_TYPE]);
+
+ if (!tb[NL80211_NAN_FUNC_SERVICE_ID]) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ memcpy(func->service_id, nla_data(tb[NL80211_NAN_FUNC_SERVICE_ID]),
+ sizeof(func->service_id));
+
+ func->close_range =
+ nla_get_flag(tb[NL80211_NAN_FUNC_CLOSE_RANGE]);
+
+ if (tb[NL80211_NAN_FUNC_SERVICE_INFO]) {
+ func->serv_spec_info_len =
+ nla_len(tb[NL80211_NAN_FUNC_SERVICE_INFO]);
+ func->serv_spec_info =
+ kmemdup(nla_data(tb[NL80211_NAN_FUNC_SERVICE_INFO]),
+ func->serv_spec_info_len,
+ GFP_KERNEL);
+ if (!func->serv_spec_info) {
+ err = -ENOMEM;
+ goto out;
+ }
+ }
+
+ if (tb[NL80211_NAN_FUNC_TTL])
+ func->ttl = nla_get_u32(tb[NL80211_NAN_FUNC_TTL]);
+
+ switch (func->type) {
+ case NL80211_NAN_FUNC_PUBLISH:
+ if (!tb[NL80211_NAN_FUNC_PUBLISH_TYPE]) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ func->publish_type =
+ nla_get_u8(tb[NL80211_NAN_FUNC_PUBLISH_TYPE]);
+ func->publish_bcast =
+ nla_get_flag(tb[NL80211_NAN_FUNC_PUBLISH_BCAST]);
+
+ if ((!(func->publish_type & NL80211_NAN_SOLICITED_PUBLISH)) &&
+ func->publish_bcast) {
+ err = -EINVAL;
+ goto out;
+ }
+ break;
+ case NL80211_NAN_FUNC_SUBSCRIBE:
+ func->subscribe_active =
+ nla_get_flag(tb[NL80211_NAN_FUNC_SUBSCRIBE_ACTIVE]);
+ break;
+ case NL80211_NAN_FUNC_FOLLOW_UP:
+ if (!tb[NL80211_NAN_FUNC_FOLLOW_UP_ID] ||
+ !tb[NL80211_NAN_FUNC_FOLLOW_UP_REQ_ID]) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ func->followup_id =
+ nla_get_u8(tb[NL80211_NAN_FUNC_FOLLOW_UP_ID]);
+ func->followup_reqid =
+ nla_get_u8(tb[NL80211_NAN_FUNC_FOLLOW_UP_REQ_ID]);
+ memcpy(func->followup_dest.addr,
+ nla_data(tb[NL80211_NAN_FUNC_FOLLOW_UP_DEST]),
+ sizeof(func->followup_dest.addr));
+ if (func->ttl) {
+ err = -EINVAL;
+ goto out;
+ }
+ break;
+ default:
+ err = -EINVAL;
+ goto out;
+ }
+
+ if (tb[NL80211_NAN_FUNC_SRF]) {
+ struct nlattr *srf_tb[NUM_NL80211_NAN_SRF_ATTR];
+
+ err = nla_parse_nested(srf_tb, NL80211_NAN_SRF_ATTR_MAX,
+ tb[NL80211_NAN_FUNC_SRF],
+ nl80211_nan_srf_policy,
+ genl_info_extack(info));
+ if (err)
+ goto out;
+
+ func->srf_include =
+ nla_get_flag(srf_tb[NL80211_NAN_SRF_INCLUDE]);
+
+ if (srf_tb[NL80211_NAN_SRF_BF]) {
+ if (srf_tb[NL80211_NAN_SRF_MAC_ADDRS] ||
+ !srf_tb[NL80211_NAN_SRF_BF_IDX]) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ func->srf_bf_len =
+ nla_len(srf_tb[NL80211_NAN_SRF_BF]);
+ func->srf_bf =
+ kmemdup(nla_data(srf_tb[NL80211_NAN_SRF_BF]),
+ func->srf_bf_len, GFP_KERNEL);
+ if (!func->srf_bf) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ func->srf_bf_idx =
+ nla_get_u8(srf_tb[NL80211_NAN_SRF_BF_IDX]);
+ } else {
+ struct nlattr *attr, *mac_attr =
+ srf_tb[NL80211_NAN_SRF_MAC_ADDRS];
+ int n_entries, rem, i = 0;
+
+ if (!mac_attr) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ n_entries = validate_acl_mac_addrs(mac_attr);
+ if (n_entries <= 0) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ func->srf_num_macs = n_entries;
+ func->srf_macs =
+ kzalloc(sizeof(*func->srf_macs) * n_entries,
+ GFP_KERNEL);
+ if (!func->srf_macs) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ nla_for_each_nested(attr, mac_attr, rem)
+ memcpy(func->srf_macs[i++].addr, nla_data(attr),
+ sizeof(*func->srf_macs));
+ }
+ }
+
+ if (tb[NL80211_NAN_FUNC_TX_MATCH_FILTER]) {
+ err = handle_nan_filter(tb[NL80211_NAN_FUNC_TX_MATCH_FILTER],
+ func, true);
+ if (err)
+ goto out;
+ }
+
+ if (tb[NL80211_NAN_FUNC_RX_MATCH_FILTER]) {
+ err = handle_nan_filter(tb[NL80211_NAN_FUNC_RX_MATCH_FILTER],
+ func, false);
+ if (err)
+ goto out;
+ }
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ hdr = nl80211hdr_put(msg, genl_info_snd_portid(info), info->snd_seq,
+ 0,
+ NL80211_CMD_ADD_NAN_FUNCTION);
+ /* This can't really happen - we just allocated 4KB */
+ if (WARN_ON(!hdr)) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ err = rdev_add_nan_func(rdev, wdev, func);
+out:
+ if (err < 0) {
+ cfg80211_free_nan_func(func);
+ nlmsg_free(msg);
+ return err;
+ }
+
+ /* propagate the instance id and cookie to userspace */
+ if (nla_put_u64_64bit(msg, NL80211_ATTR_COOKIE, func->cookie,
+ NL80211_ATTR_PAD))
+ goto nla_put_failure;
+
+ func_attr = nla_nest_start(msg, NL80211_ATTR_NAN_FUNC);
+ if (!func_attr)
+ goto nla_put_failure;
+
+ if (nla_put_u8(msg, NL80211_NAN_FUNC_INSTANCE_ID,
+ func->instance_id))
+ goto nla_put_failure;
+
+ nla_nest_end(msg, func_attr);
+
+ genlmsg_end(msg, hdr);
+ return genlmsg_reply(msg, info);
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return -ENOBUFS;
+}
+
+static int nl80211_nan_del_func(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct wireless_dev *wdev = info->user_ptr[1];
+ u64 cookie;
+
+ if (wdev->iftype != NL80211_IFTYPE_NAN)
+ return -EOPNOTSUPP;
+
+ if (!wdev_running(wdev))
+ return -ENOTCONN;
+
+ if (!info->attrs[NL80211_ATTR_COOKIE])
+ return -EINVAL;
+
+ if (wdev->owner_nlportid &&
+ wdev->owner_nlportid != genl_info_snd_portid(info))
+ return -ENOTCONN;
+
+ cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]);
+
+ rdev_del_nan_func(rdev, wdev, cookie);
+
+ return 0;
+}
+
+static int nl80211_nan_change_config(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct wireless_dev *wdev = info->user_ptr[1];
+ struct cfg80211_nan_conf conf = {};
+ u32 changed = 0;
+
+ if (wdev->iftype != NL80211_IFTYPE_NAN)
+ return -EOPNOTSUPP;
+
+ if (!wdev_running(wdev))
+ return -ENOTCONN;
+
+ if (info->attrs[NL80211_ATTR_NAN_MASTER_PREF]) {
+ conf.master_pref =
+ nla_get_u8(info->attrs[NL80211_ATTR_NAN_MASTER_PREF]);
+ if (conf.master_pref <= 1 || conf.master_pref == 255)
+ return -EINVAL;
+
+ changed |= CFG80211_NAN_CONF_CHANGED_PREF;
+ }
+
+ if (info->attrs[NL80211_ATTR_BANDS]) {
+ u32 bands = nla_get_u32(info->attrs[NL80211_ATTR_BANDS]);
+
+ if (bands & ~(u32)wdev->wiphy->nan_supported_bands)
+ return -EOPNOTSUPP;
+
+ if (bands && !(bands & BIT(NL80211_BAND_2GHZ)))
+ return -EINVAL;
+
+ conf.bands = bands;
+ changed |= CFG80211_NAN_CONF_CHANGED_BANDS;
+ }
+
+ if (!changed)
+ return -EINVAL;
+
+ return rdev_nan_change_conf(rdev, wdev, &conf, changed);
+}
+
+void cfg80211_nan_match(struct wireless_dev *wdev,
+ struct cfg80211_nan_match_params *match, gfp_t gfp)
+{
+ struct wiphy *wiphy = wdev->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+ struct nlattr *match_attr, *local_func_attr, *peer_func_attr;
+ struct sk_buff *msg;
+ void *hdr;
+
+ if (WARN_ON(!match->inst_id || !match->peer_inst_id || !match->addr))
+ return;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
+ if (!msg)
+ return;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NAN_MATCH);
+ if (!hdr) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
+ (wdev->netdev && nla_put_u32(msg, NL80211_ATTR_IFINDEX,
+ wdev->netdev->ifindex)) ||
+ nla_put_u64_64bit(msg, NL80211_ATTR_WDEV, wdev_id(wdev),
+ NL80211_ATTR_PAD))
+ goto nla_put_failure;
+
+ if (nla_put_u64_64bit(msg, NL80211_ATTR_COOKIE, match->cookie,
+ NL80211_ATTR_PAD) ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, match->addr))
+ goto nla_put_failure;
+
+ match_attr = nla_nest_start(msg, NL80211_ATTR_NAN_MATCH);
+ if (!match_attr)
+ goto nla_put_failure;
+
+ local_func_attr = nla_nest_start(msg, NL80211_NAN_MATCH_FUNC_LOCAL);
+ if (!local_func_attr)
+ goto nla_put_failure;
+
+ if (nla_put_u8(msg, NL80211_NAN_FUNC_INSTANCE_ID, match->inst_id))
+ goto nla_put_failure;
+
+ nla_nest_end(msg, local_func_attr);
+
+ peer_func_attr = nla_nest_start(msg, NL80211_NAN_MATCH_FUNC_PEER);
+ if (!peer_func_attr)
+ goto nla_put_failure;
+
+ if (nla_put_u8(msg, NL80211_NAN_FUNC_TYPE, match->type) ||
+ nla_put_u8(msg, NL80211_NAN_FUNC_INSTANCE_ID, match->peer_inst_id))
+ goto nla_put_failure;
+
+ if (match->info && match->info_len &&
+ nla_put(msg, NL80211_NAN_FUNC_SERVICE_INFO, match->info_len,
+ match->info))
+ goto nla_put_failure;
+
+ nla_nest_end(msg, peer_func_attr);
+ nla_nest_end(msg, match_attr);
+ genlmsg_end(msg, hdr);
+
+ if (!wdev->owner_nlportid)
+ genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy),
+ msg, 0, NL80211_MCGRP_NAN, gfp);
+ else
+ genlmsg_unicast(wiphy_net(&rdev->wiphy), msg,
+ wdev->owner_nlportid);
+
+ return;
+
+nla_put_failure:
+ nlmsg_free(msg);
+}
+EXPORT_SYMBOL(cfg80211_nan_match);
+
+void cfg80211_nan_func_terminated(struct wireless_dev *wdev,
+ u8 inst_id,
+ enum nl80211_nan_func_term_reason reason,
+ u64 cookie, gfp_t gfp)
+{
+ struct wiphy *wiphy = wdev->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+ struct sk_buff *msg;
+ struct nlattr *func_attr;
+ void *hdr;
+
+ if (WARN_ON(!inst_id))
+ return;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
+ if (!msg)
+ return;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_DEL_NAN_FUNCTION);
+ if (!hdr) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
+ (wdev->netdev && nla_put_u32(msg, NL80211_ATTR_IFINDEX,
+ wdev->netdev->ifindex)) ||
+ nla_put_u64_64bit(msg, NL80211_ATTR_WDEV, wdev_id(wdev),
+ NL80211_ATTR_PAD))
+ goto nla_put_failure;
+
+ if (nla_put_u64_64bit(msg, NL80211_ATTR_COOKIE, cookie,
+ NL80211_ATTR_PAD))
+ goto nla_put_failure;
+
+ func_attr = nla_nest_start(msg, NL80211_ATTR_NAN_FUNC);
+ if (!func_attr)
+ goto nla_put_failure;
+
+ if (nla_put_u8(msg, NL80211_NAN_FUNC_INSTANCE_ID, inst_id) ||
+ nla_put_u8(msg, NL80211_NAN_FUNC_TERM_REASON, reason))
+ goto nla_put_failure;
+
+ nla_nest_end(msg, func_attr);
+ genlmsg_end(msg, hdr);
+
+ if (!wdev->owner_nlportid)
+ genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy),
+ msg, 0, NL80211_MCGRP_NAN, gfp);
+ else
+ genlmsg_unicast(wiphy_net(&rdev->wiphy), msg,
+ wdev->owner_nlportid);
+
+ return;
+
+nla_put_failure:
+ nlmsg_free(msg);
+}
+EXPORT_SYMBOL(cfg80211_nan_func_terminated);
+
+static int nl80211_get_protocol_features(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ void *hdr;
+ struct sk_buff *msg;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ hdr = nl80211hdr_put(msg, genl_info_snd_portid(info), info->snd_seq,
+ 0,
+ NL80211_CMD_GET_PROTOCOL_FEATURES);
+ if (!hdr)
+ goto nla_put_failure;
+
+ if (nla_put_u32(msg, NL80211_ATTR_PROTOCOL_FEATURES,
+ NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP))
+ goto nla_put_failure;
+
+ genlmsg_end(msg, hdr);
+ return genlmsg_reply(msg, info);
+
+ nla_put_failure:
+ kfree_skb(msg);
+ return -ENOBUFS;
+}
+
+static int nl80211_update_ft_ies(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct cfg80211_update_ft_ies_params ft_params;
+ struct net_device *dev = info->user_ptr[1];
+
+ if (!rdev->ops->update_ft_ies)
+ return -EOPNOTSUPP;
+
+ if (!info->attrs[NL80211_ATTR_MDID] ||
+ !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
+ return -EINVAL;
+
+ memset(&ft_params, 0, sizeof(ft_params));
+ ft_params.md = nla_get_u16(info->attrs[NL80211_ATTR_MDID]);
+ ft_params.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
+ ft_params.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+
+ return rdev_update_ft_ies(rdev, dev, &ft_params);
+}
+
+static int nl80211_crit_protocol_start(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct wireless_dev *wdev = info->user_ptr[1];
+ enum nl80211_crit_proto_id proto = NL80211_CRIT_PROTO_UNSPEC;
+ u16 duration;
+ int ret;
+
+ if (!rdev->ops->crit_proto_start)
+ return -EOPNOTSUPP;
+
+ if (WARN_ON(!rdev->ops->crit_proto_stop))
+ return -EINVAL;
+
+ if (rdev->crit_proto_nlportid)
+ return -EBUSY;
+
+ /* determine protocol if provided */
+ if (info->attrs[NL80211_ATTR_CRIT_PROT_ID])
+ proto = nla_get_u16(info->attrs[NL80211_ATTR_CRIT_PROT_ID]);
+
+ if (proto >= NUM_NL80211_CRIT_PROTO)
+ return -EINVAL;
+
+ /* timeout must be provided */
+ if (!info->attrs[NL80211_ATTR_MAX_CRIT_PROT_DURATION])
+ return -EINVAL;
+
+ duration =
+ nla_get_u16(info->attrs[NL80211_ATTR_MAX_CRIT_PROT_DURATION]);
+
+ if (duration > NL80211_CRIT_PROTO_MAX_DURATION)
+ return -ERANGE;
+
+ ret = rdev_crit_proto_start(rdev, wdev, proto, duration);
+ if (!ret)
+ rdev->crit_proto_nlportid = genl_info_snd_portid(info);
+
+ return ret;
+}
+
+static int nl80211_crit_protocol_stop(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct wireless_dev *wdev = info->user_ptr[1];
+
+ if (!rdev->ops->crit_proto_stop)
+ return -EOPNOTSUPP;
+
+ if (rdev->crit_proto_nlportid) {
+ rdev->crit_proto_nlportid = 0;
+ rdev_crit_proto_stop(rdev, wdev);
+ }
+ return 0;
+}
+
+static int nl80211_vendor_cmd(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct wireless_dev *wdev =
+ __cfg80211_wdev_from_attrs(genl_info_net(info), info->attrs);
+ int i, err;
+ u32 vid, subcmd;
+
+ if (!rdev->wiphy.vendor_commands)
+ return -EOPNOTSUPP;
+
+ if (IS_ERR(wdev)) {
+ err = PTR_ERR(wdev);
+ if (err != -EINVAL)
+ return err;
+ wdev = NULL;
+ } else if (wdev->wiphy != &rdev->wiphy) {
+ return -EINVAL;
+ }
+
+ if (!info->attrs[NL80211_ATTR_VENDOR_ID] ||
+ !info->attrs[NL80211_ATTR_VENDOR_SUBCMD])
+ return -EINVAL;
+
+ vid = nla_get_u32(info->attrs[NL80211_ATTR_VENDOR_ID]);
+ subcmd = nla_get_u32(info->attrs[NL80211_ATTR_VENDOR_SUBCMD]);
+ for (i = 0; i < rdev->wiphy.n_vendor_commands; i++) {
+ const struct wiphy_vendor_command *vcmd;
+ void *data = NULL;
+ int len = 0;
+
+ vcmd = &rdev->wiphy.vendor_commands[i];
+
+ if (vcmd->info.vendor_id != vid || vcmd->info.subcmd != subcmd)
+ continue;
+
+ if (vcmd->flags & (WIPHY_VENDOR_CMD_NEED_WDEV |
+ WIPHY_VENDOR_CMD_NEED_NETDEV)) {
+ if (!wdev)
+ return -EINVAL;
+ if (vcmd->flags & WIPHY_VENDOR_CMD_NEED_NETDEV &&
+ !wdev->netdev)
+ return -EINVAL;
+
+ if (vcmd->flags & WIPHY_VENDOR_CMD_NEED_RUNNING) {
+ if (!wdev_running(wdev))
+ return -ENETDOWN;
+ }
+
+ if (!vcmd->doit)
+ return -EOPNOTSUPP;
+ } else {
+ wdev = NULL;
+ }
+
+ if (info->attrs[NL80211_ATTR_VENDOR_DATA]) {
+ data = nla_data(info->attrs[NL80211_ATTR_VENDOR_DATA]);
+ len = nla_len(info->attrs[NL80211_ATTR_VENDOR_DATA]);
+ }
+
+ rdev->cur_cmd_info = info;
+ err = rdev->wiphy.vendor_commands[i].doit(&rdev->wiphy, wdev,
+ data, len);
+ rdev->cur_cmd_info = NULL;
+ return err;
+ }
+
+ return -EOPNOTSUPP;
+}
+
+static int nl80211_prepare_vendor_dump(struct sk_buff *skb,
+ struct netlink_callback *cb,
+ struct cfg80211_registered_device **rdev,
+ struct wireless_dev **wdev)
+{
+ struct nlattr **attrbuf = genl_family_attrbuf(&nl80211_fam);
+ u32 vid, subcmd;
+ unsigned int i;
+ int vcmd_idx = -1;
+ int err;
+ void *data = NULL;
+ unsigned int data_len = 0;
+
+ if (cb->args[0]) {
+ /* subtract the 1 again here */
+ struct wiphy *wiphy = wiphy_idx_to_wiphy(cb->args[0] - 1);
+ struct wireless_dev *tmp;
+
+ if (!wiphy)
+ return -ENODEV;
+ *rdev = wiphy_to_rdev(wiphy);
+ *wdev = NULL;
+
+ if (cb->args[1]) {
+ list_for_each_entry(tmp, &wiphy->wdev_list, list) {
+ if (tmp->identifier == cb->args[1] - 1) {
+ *wdev = tmp;
+ break;
+ }
+ }
+ }
+
+ /* keep rtnl locked in successful case */
+ return 0;
+ }
+
+ err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize, attrbuf,
+ nl80211_fam.maxattr, nl80211_policy, NULL);
+ if (err)
+ return err;
+
+ if (!attrbuf[NL80211_ATTR_VENDOR_ID] ||
+ !attrbuf[NL80211_ATTR_VENDOR_SUBCMD])
+ return -EINVAL;
+
+ *wdev = __cfg80211_wdev_from_attrs(sock_net(skb->sk), attrbuf);
+ if (IS_ERR(*wdev))
+ *wdev = NULL;
+
+ *rdev = __cfg80211_rdev_from_attrs(sock_net(skb->sk), attrbuf);
+ if (IS_ERR(*rdev))
+ return PTR_ERR(*rdev);
+
+ vid = nla_get_u32(attrbuf[NL80211_ATTR_VENDOR_ID]);
+ subcmd = nla_get_u32(attrbuf[NL80211_ATTR_VENDOR_SUBCMD]);
+
+ for (i = 0; i < (*rdev)->wiphy.n_vendor_commands; i++) {
+ const struct wiphy_vendor_command *vcmd;
+
+ vcmd = &(*rdev)->wiphy.vendor_commands[i];
+
+ if (vcmd->info.vendor_id != vid || vcmd->info.subcmd != subcmd)
+ continue;
+
+ if (!vcmd->dumpit)
+ return -EOPNOTSUPP;
+
+ vcmd_idx = i;
+ break;
+ }
+
+ if (vcmd_idx < 0)
+ return -EOPNOTSUPP;
+
+ if (attrbuf[NL80211_ATTR_VENDOR_DATA]) {
+ data = nla_data(attrbuf[NL80211_ATTR_VENDOR_DATA]);
+ data_len = nla_len(attrbuf[NL80211_ATTR_VENDOR_DATA]);
+ }
+
+ /* 0 is the first index - add 1 to parse only once */
+ cb->args[0] = (*rdev)->wiphy_idx + 1;
+ /* add 1 to know if it was NULL */
+ cb->args[1] = *wdev ? (*wdev)->identifier + 1 : 0;
+ cb->args[2] = vcmd_idx;
+ cb->args[3] = (unsigned long)data;
+ cb->args[4] = data_len;
+
+ /* keep rtnl locked in successful case */
+ return 0;
+}
+
+static int nl80211_vendor_cmd_dump(struct sk_buff *skb,
+ struct netlink_callback *cb)
+{
+ struct cfg80211_registered_device *rdev;
+ struct wireless_dev *wdev;
+ unsigned int vcmd_idx;
+ const struct wiphy_vendor_command *vcmd;
+ void *data;
+ int data_len;
+ int err;
+ struct nlattr *vendor_data;
+
+ rtnl_lock();
+ err = nl80211_prepare_vendor_dump(skb, cb, &rdev, &wdev);
+ if (err)
+ goto out;
+
+ vcmd_idx = cb->args[2];
+ data = (void *)cb->args[3];
+ data_len = cb->args[4];
+ vcmd = &rdev->wiphy.vendor_commands[vcmd_idx];
+
+ if (vcmd->flags & (WIPHY_VENDOR_CMD_NEED_WDEV |
+ WIPHY_VENDOR_CMD_NEED_NETDEV)) {
+ if (!wdev) {
+ err = -EINVAL;
+ goto out;
+ }
+ if (vcmd->flags & WIPHY_VENDOR_CMD_NEED_NETDEV &&
+ !wdev->netdev) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ if (vcmd->flags & WIPHY_VENDOR_CMD_NEED_RUNNING) {
+ if (!wdev_running(wdev)) {
+ err = -ENETDOWN;
+ goto out;
+ }
+ }
+ }
+
+ while (1) {
+ void *hdr = nl80211hdr_put(skb, NETLINK_CB_PORTID(cb->skb),
+ cb->nlh->nlmsg_seq, NLM_F_MULTI,
+ NL80211_CMD_VENDOR);
+ if (!hdr)
+ break;
+
+ if (nla_put_u32(skb, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
+ (wdev && nla_put_u64_64bit(skb, NL80211_ATTR_WDEV,
+ wdev_id(wdev),
+ NL80211_ATTR_PAD))) {
+ genlmsg_cancel(skb, hdr);
+ break;
+ }
+
+ vendor_data = nla_nest_start(skb, NL80211_ATTR_VENDOR_DATA);
+ if (!vendor_data) {
+ genlmsg_cancel(skb, hdr);
+ break;
+ }
+
+ err = vcmd->dumpit(&rdev->wiphy, wdev, skb, data, data_len,
+ (unsigned long *)&cb->args[5]);
+ nla_nest_end(skb, vendor_data);
+
+ if (err == -ENOBUFS || err == -ENOENT) {
+ genlmsg_cancel(skb, hdr);
+ break;
+ } else if (err) {
+ genlmsg_cancel(skb, hdr);
+ goto out;
+ }
+
+ genlmsg_end(skb, hdr);
+ }
+
+ err = skb->len;
+ out:
+ rtnl_unlock();
+ return err;
+}
+
+struct sk_buff *__cfg80211_alloc_reply_skb(struct wiphy *wiphy,
+ enum nl80211_commands cmd,
+ enum nl80211_attrs attr,
+ int approxlen)
+{
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+
+ if (WARN_ON(!rdev->cur_cmd_info))
+ return NULL;
+
+ return __cfg80211_alloc_vendor_skb(rdev, NULL, approxlen,
+ genl_info_snd_portid(rdev->cur_cmd_info),
+ rdev->cur_cmd_info->snd_seq,
+ cmd, attr, NULL, GFP_KERNEL);
+}
+EXPORT_SYMBOL(__cfg80211_alloc_reply_skb);
+
+int cfg80211_vendor_cmd_reply(struct sk_buff *skb)
+{
+ struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0];
+ void *hdr = ((void **)skb->cb)[1];
+ struct nlattr *data = ((void **)skb->cb)[2];
+
+ /* clear CB data for netlink core to own from now on */
+ memset(skb->cb, 0, sizeof(skb->cb));
+
+ if (WARN_ON(!rdev->cur_cmd_info)) {
+ kfree_skb(skb);
+ return -EINVAL;
+ }
+
+ nla_nest_end(skb, data);
+ genlmsg_end(skb, hdr);
+ return genlmsg_reply(skb, rdev->cur_cmd_info);
+}
+EXPORT_SYMBOL_GPL(cfg80211_vendor_cmd_reply);
+
+static int nl80211_set_qos_map(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct cfg80211_qos_map *qos_map = NULL;
+ struct net_device *dev = info->user_ptr[1];
+ u8 *pos, len, num_des, des_len, des;
+ int ret;
+
+ if (!rdev->ops->set_qos_map)
+ return -EOPNOTSUPP;
+
+ if (info->attrs[NL80211_ATTR_QOS_MAP]) {
+ pos = nla_data(info->attrs[NL80211_ATTR_QOS_MAP]);
+ len = nla_len(info->attrs[NL80211_ATTR_QOS_MAP]);
+
+ if (len % 2 || len < IEEE80211_QOS_MAP_LEN_MIN ||
+ len > IEEE80211_QOS_MAP_LEN_MAX)
+ return -EINVAL;
+
+ qos_map = kzalloc(sizeof(struct cfg80211_qos_map), GFP_KERNEL);
+ if (!qos_map)
+ return -ENOMEM;
+
+ num_des = (len - IEEE80211_QOS_MAP_LEN_MIN) >> 1;
+ if (num_des) {
+ des_len = num_des *
+ sizeof(struct cfg80211_dscp_exception);
+ memcpy(qos_map->dscp_exception, pos, des_len);
+ qos_map->num_des = num_des;
+ for (des = 0; des < num_des; des++) {
+ if (qos_map->dscp_exception[des].up > 7) {
+ kfree(qos_map);
+ return -EINVAL;
+ }
+ }
+ pos += des_len;
+ }
+ memcpy(qos_map->up, pos, IEEE80211_QOS_MAP_LEN_MIN);
+ }
+
+ wdev_lock(dev->ieee80211_ptr);
+ ret = nl80211_key_allowed(dev->ieee80211_ptr);
+ if (!ret)
+ ret = rdev_set_qos_map(rdev, dev, qos_map);
+ wdev_unlock(dev->ieee80211_ptr);
+
+ kfree(qos_map);
+ return ret;
+}
+
+static int nl80211_add_tx_ts(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ const u8 *peer;
+ u8 tsid, up;
+ u16 admitted_time = 0;
+ int err;
+
+ if (!(rdev->wiphy.features & NL80211_FEATURE_SUPPORTS_WMM_ADMISSION))
+ return -EOPNOTSUPP;
+
+ if (!info->attrs[NL80211_ATTR_TSID] || !info->attrs[NL80211_ATTR_MAC] ||
+ !info->attrs[NL80211_ATTR_USER_PRIO])
+ return -EINVAL;
+
+ tsid = nla_get_u8(info->attrs[NL80211_ATTR_TSID]);
+ if (tsid >= IEEE80211_NUM_TIDS)
+ return -EINVAL;
+
+ up = nla_get_u8(info->attrs[NL80211_ATTR_USER_PRIO]);
+ if (up >= IEEE80211_NUM_UPS)
+ return -EINVAL;
+
+ /* WMM uses TIDs 0-7 even for TSPEC */
+ if (tsid >= IEEE80211_FIRST_TSPEC_TSID) {
+ /* TODO: handle 802.11 TSPEC/admission control
+ * need more attributes for that (e.g. BA session requirement);
+ * change the WMM adminssion test above to allow both then
+ */
+ return -EINVAL;
+ }
+
+ peer = nla_data(info->attrs[NL80211_ATTR_MAC]);
+
+ if (info->attrs[NL80211_ATTR_ADMITTED_TIME]) {
+ admitted_time =
+ nla_get_u16(info->attrs[NL80211_ATTR_ADMITTED_TIME]);
+ if (!admitted_time)
+ return -EINVAL;
+ }
+
+ wdev_lock(wdev);
+ switch (wdev->iftype) {
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_P2P_CLIENT:
+ if (wdev->current_bss)
+ break;
+ err = -ENOTCONN;
+ goto out;
+ default:
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ err = rdev_add_tx_ts(rdev, dev, tsid, peer, up, admitted_time);
+
+ out:
+ wdev_unlock(wdev);
+ return err;
+}
+
+static int nl80211_del_tx_ts(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ const u8 *peer;
+ u8 tsid;
+ int err;
+
+ if (!info->attrs[NL80211_ATTR_TSID] || !info->attrs[NL80211_ATTR_MAC])
+ return -EINVAL;
+
+ tsid = nla_get_u8(info->attrs[NL80211_ATTR_TSID]);
+ peer = nla_data(info->attrs[NL80211_ATTR_MAC]);
+
+ wdev_lock(wdev);
+ err = rdev_del_tx_ts(rdev, dev, tsid, peer);
+ wdev_unlock(wdev);
+
+ return err;
+}
+
+static int nl80211_tdls_channel_switch(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_chan_def chandef = {};
+ const u8 *addr;
+ u8 oper_class;
+ int err;
+
+ if (!rdev->ops->tdls_channel_switch ||
+ !(rdev->wiphy.features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH))
+ return -EOPNOTSUPP;
+
+ switch (dev->ieee80211_ptr->iftype) {
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_P2P_CLIENT:
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ if (!info->attrs[NL80211_ATTR_MAC] ||
+ !info->attrs[NL80211_ATTR_OPER_CLASS])
+ return -EINVAL;
+
+ err = nl80211_parse_chandef(rdev, info, &chandef);
+ if (err)
+ return err;
+
+ /*
+ * Don't allow wide channels on the 2.4Ghz band, as per IEEE802.11-2012
+ * section 10.22.6.2.1. Disallow 5/10Mhz channels as well for now, the
+ * specification is not defined for them.
+ */
+ if (chandef.chan->band == NL80211_BAND_2GHZ &&
+ chandef.width != NL80211_CHAN_WIDTH_20_NOHT &&
+ chandef.width != NL80211_CHAN_WIDTH_20)
+ return -EINVAL;
+
+ /* we will be active on the TDLS link */
+ if (!cfg80211_reg_can_beacon_relax(&rdev->wiphy, &chandef,
+ wdev->iftype))
+ return -EINVAL;
+
+ /* don't allow switching to DFS channels */
+ if (cfg80211_chandef_dfs_required(wdev->wiphy, &chandef, wdev->iftype))
+ return -EINVAL;
+
+ addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
+ oper_class = nla_get_u8(info->attrs[NL80211_ATTR_OPER_CLASS]);
+
+ wdev_lock(wdev);
+ err = rdev_tdls_channel_switch(rdev, dev, addr, oper_class, &chandef);
+ wdev_unlock(wdev);
+
+ return err;
+}
+
+static int nl80211_tdls_cancel_channel_switch(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ const u8 *addr;
+
+ if (!rdev->ops->tdls_channel_switch ||
+ !rdev->ops->tdls_cancel_channel_switch ||
+ !(rdev->wiphy.features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH))
+ return -EOPNOTSUPP;
+
+ switch (dev->ieee80211_ptr->iftype) {
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_P2P_CLIENT:
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ if (!info->attrs[NL80211_ATTR_MAC])
+ return -EINVAL;
+
+ addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
+
+ wdev_lock(wdev);
+ rdev_tdls_cancel_channel_switch(rdev, dev, addr);
+ wdev_unlock(wdev);
+
+ return 0;
+}
+
+static int nl80211_set_multicast_to_unicast(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ const struct nlattr *nla;
+ bool enabled;
+
+ if (!rdev->ops->set_multicast_to_unicast)
+ return -EOPNOTSUPP;
+
+ if (wdev->iftype != NL80211_IFTYPE_AP &&
+ wdev->iftype != NL80211_IFTYPE_P2P_GO)
+ return -EOPNOTSUPP;
+
+ nla = info->attrs[NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED];
+ enabled = nla_get_flag(nla);
+
+ return rdev_set_multicast_to_unicast(rdev, dev, enabled);
+}
+
+static int nl80211_set_pmk(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_pmk_conf pmk_conf = {};
+ int ret;
+
+ if (wdev->iftype != NL80211_IFTYPE_STATION &&
+ wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)
+ return -EOPNOTSUPP;
+
+ if (!wiphy_ext_feature_isset(&rdev->wiphy,
+ NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X))
+ return -EOPNOTSUPP;
+
+ if (!info->attrs[NL80211_ATTR_MAC] || !info->attrs[NL80211_ATTR_PMK])
+ return -EINVAL;
+
+ wdev_lock(wdev);
+ if (!wdev->current_bss) {
+ ret = -ENOTCONN;
+ goto out;
+ }
+
+ pmk_conf.aa = nla_data(info->attrs[NL80211_ATTR_MAC]);
+ if (memcmp(pmk_conf.aa, wdev->current_bss->pub.bssid, ETH_ALEN)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ pmk_conf.pmk = nla_data(info->attrs[NL80211_ATTR_PMK]);
+ pmk_conf.pmk_len = nla_len(info->attrs[NL80211_ATTR_PMK]);
+ if (pmk_conf.pmk_len != WLAN_PMK_LEN &&
+ pmk_conf.pmk_len != WLAN_PMK_LEN_SUITE_B_192) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (info->attrs[NL80211_ATTR_PMKR0_NAME]) {
+ int r0_name_len = nla_len(info->attrs[NL80211_ATTR_PMKR0_NAME]);
+
+ if (r0_name_len != WLAN_PMK_NAME_LEN) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ pmk_conf.pmk_r0_name =
+ nla_data(info->attrs[NL80211_ATTR_PMKR0_NAME]);
+ }
+
+ ret = rdev_set_pmk(rdev, dev, &pmk_conf);
+out:
+ wdev_unlock(wdev);
+ return ret;
+}
+
+static int nl80211_del_pmk(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ const u8 *aa;
+ int ret;
+
+ if (wdev->iftype != NL80211_IFTYPE_STATION &&
+ wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)
+ return -EOPNOTSUPP;
+
+ if (!wiphy_ext_feature_isset(&rdev->wiphy,
+ NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X))
+ return -EOPNOTSUPP;
+
+ if (!info->attrs[NL80211_ATTR_MAC])
+ return -EINVAL;
+
+ wdev_lock(wdev);
+ aa = nla_data(info->attrs[NL80211_ATTR_MAC]);
+ ret = rdev_del_pmk(rdev, dev, aa);
+ wdev_unlock(wdev);
+
+ return ret;
+}
+
+static int nl80211_get_keepalive(struct sk_buff *skb, struct genl_info *info)
+{
+ struct sk_buff *msg;
+ void *hdr;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
+ NL80211_CMD_GET_KEEPALIVE);
+ if (!hdr)
+ goto nla_put_failure;
+
+ genlmsg_end(msg, hdr);
+ return genlmsg_reply(msg, info);
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return -ENOBUFS;
+}
+
+static int nl80211_set_keepalive(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct cfg80211_keepalive_request *request;
+ int klv_cmdtype=4, ret=-1;
+ u8 index=1, trig=1, payload_len=0;
+ u8 *dst_macaddr=NULL, *payload=NULL;
+ u32 interval = 10;
+
+ if (!rdev->ops->change_keepalive)
+ return -EOPNOTSUPP;
+
+ /* read klv cmd type */
+ if (info->attrs[NL80211_ATTR_KLV_TYPE]) {
+ klv_cmdtype = nla_get_u32(info->attrs[NL80211_ATTR_KLV_TYPE]);
+ if (klv_cmdtype < 0 || klv_cmdtype > 3) {
+ ret = -EINVAL;
+ goto bad_exit;
+ }
+ }
+
+ if (info->attrs[NL80211_ATTR_KLV_INDEX])
+ index = nla_get_u32(info->attrs[NL80211_ATTR_KLV_INDEX]);
+ if (info->attrs[NL80211_ATTR_KLV_INTVL])
+ interval = nla_get_u32(info->attrs[NL80211_ATTR_KLV_INTVL]);
+ if (info->attrs[NL80211_ATTR_KLV_TRIG])
+ trig = nla_get_u32(info->attrs[NL80211_ATTR_KLV_TRIG]);
+ if (info->attrs[NL80211_ATTR_MAC])
+ dst_macaddr = nla_data(info->attrs[NL80211_ATTR_MAC]);
+ if (info->attrs[NL80211_ATTR_KLV_PAYLOAD]) {
+ /* read payload and payload length */
+ payload = nla_data(info->attrs[NL80211_ATTR_KLV_PAYLOAD]);
+ payload_len = nla_len(info->attrs[NL80211_ATTR_KLV_PAYLOAD]);
+ }
+
+ request = kzalloc(sizeof(*request)+sizeof(u8)*payload_len, GFP_KERNEL);
+ if (!request) {
+ ret = -ENOMEM;
+ goto bad_exit;
+ }
+
+ request->cmd = klv_cmdtype;
+ request->index = index;
+
+ if (klv_cmdtype == 0) {
+ request->interval = interval;
+ request->trig = trig;
+ request->payload_len = payload_len;
+ memcpy(request->dst_macaddr, dst_macaddr, ETH_ALEN);
+ memcpy(request->payload, payload, payload_len);
+ }
+
+ ret = rdev->ops->change_keepalive(&rdev->wiphy, dev, request, false);
+ kfree(request);
+
+bad_exit:
+ return ret;
+}
+
+#define NL80211_FLAG_NEED_WIPHY 0x01
+#define NL80211_FLAG_NEED_NETDEV 0x02
+#define NL80211_FLAG_NEED_RTNL 0x04
+#define NL80211_FLAG_CHECK_NETDEV_UP 0x08
+#define NL80211_FLAG_NEED_NETDEV_UP (NL80211_FLAG_NEED_NETDEV |\
+ NL80211_FLAG_CHECK_NETDEV_UP)
+#define NL80211_FLAG_NEED_WDEV 0x10
+/* If a netdev is associated, it must be UP, P2P must be started */
+#define NL80211_FLAG_NEED_WDEV_UP (NL80211_FLAG_NEED_WDEV |\
+ NL80211_FLAG_CHECK_NETDEV_UP)
+#define NL80211_FLAG_CLEAR_SKB 0x20
+
+static int nl80211_pre_doit(__genl_const struct genl_ops *ops, struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev;
+ struct wireless_dev *wdev;
+ struct net_device *dev;
+ bool rtnl = ops->internal_flags & NL80211_FLAG_NEED_RTNL;
+
+ if (rtnl)
+ rtnl_lock();
+
+ if (ops->internal_flags & NL80211_FLAG_NEED_WIPHY) {
+ rdev = cfg80211_get_dev_from_info(genl_info_net(info), info);
+ if (IS_ERR(rdev)) {
+ if (rtnl)
+ rtnl_unlock();
+ return PTR_ERR(rdev);
+ }
+ info->user_ptr[0] = rdev;
+ } else if (ops->internal_flags & NL80211_FLAG_NEED_NETDEV ||
+ ops->internal_flags & NL80211_FLAG_NEED_WDEV) {
+ ASSERT_RTNL();
+
+ wdev = __cfg80211_wdev_from_attrs(genl_info_net(info),
+ info->attrs);
+ if (IS_ERR(wdev)) {
+ if (rtnl)
+ rtnl_unlock();
+ return PTR_ERR(wdev);
+ }
+
+ dev = wdev->netdev;
+ rdev = wiphy_to_rdev(wdev->wiphy);
+
+ if (ops->internal_flags & NL80211_FLAG_NEED_NETDEV) {
+ if (!dev) {
+ if (rtnl)
+ rtnl_unlock();
+ return -EINVAL;
+ }
+
+ info->user_ptr[1] = dev;
+ } else {
+ info->user_ptr[1] = wdev;
+ }
+
+ if (ops->internal_flags & NL80211_FLAG_CHECK_NETDEV_UP &&
+ !wdev_running(wdev)) {
+ if (rtnl)
+ rtnl_unlock();
+ return -ENETDOWN;
+ }
+
+ if (dev)
+ dev_hold(dev);
+
+ info->user_ptr[0] = rdev;
+ }
+
+ return 0;
+}
+
+static void nl80211_post_doit(__genl_const struct genl_ops *ops, struct sk_buff *skb,
+ struct genl_info *info)
+{
+ if (info->user_ptr[1]) {
+ if (ops->internal_flags & NL80211_FLAG_NEED_WDEV) {
+ struct wireless_dev *wdev = info->user_ptr[1];
+
+ if (wdev->netdev)
+ dev_put(wdev->netdev);
+ } else {
+ dev_put(info->user_ptr[1]);
+ }
+ }
+
+ if (ops->internal_flags & NL80211_FLAG_NEED_RTNL)
+ rtnl_unlock();
+
+ /* If needed, clear the netlink message payload from the SKB
+ * as it might contain key data that shouldn't stick around on
+ * the heap after the SKB is freed. The netlink message header
+ * is still needed for further processing, so leave it intact.
+ */
+ if (ops->internal_flags & NL80211_FLAG_CLEAR_SKB) {
+ struct nlmsghdr *nlh = nlmsg_hdr(skb);
+
+ memset(nlmsg_data(nlh), 0, nlmsg_len(nlh));
+ }
+}
+
+static __genl_const struct genl_ops nl80211_ops[] = {
+ {
+ .cmd = NL80211_CMD_GET_WIPHY,
+ .doit = nl80211_get_wiphy,
+ .dumpit = nl80211_dump_wiphy,
+ .done = nl80211_dump_wiphy_done,
+ .policy = nl80211_policy,
+ /* can be retrieved by unprivileged users */
+ .internal_flags = NL80211_FLAG_NEED_WIPHY |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_SET_WIPHY,
+ .doit = nl80211_set_wiphy,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_GET_INTERFACE,
+ .doit = nl80211_get_interface,
+ .dumpit = nl80211_dump_interface,
+ .policy = nl80211_policy,
+ /* can be retrieved by unprivileged users */
+ .internal_flags = NL80211_FLAG_NEED_WDEV |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_SET_INTERFACE,
+ .doit = nl80211_set_interface,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_NEW_INTERFACE,
+ .doit = nl80211_new_interface,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_WIPHY |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_DEL_INTERFACE,
+ .doit = nl80211_del_interface,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_WDEV |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_GET_KEY,
+ .doit = nl80211_get_key,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_SET_KEY,
+ .doit = nl80211_set_key,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL |
+ NL80211_FLAG_CLEAR_SKB,
+ },
+ {
+ .cmd = NL80211_CMD_NEW_KEY,
+ .doit = nl80211_new_key,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL |
+ NL80211_FLAG_CLEAR_SKB,
+ },
+ {
+ .cmd = NL80211_CMD_DEL_KEY,
+ .doit = nl80211_del_key,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_SET_BEACON,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .doit = nl80211_set_beacon,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_START_AP,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .doit = nl80211_start_ap,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_STOP_AP,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .doit = nl80211_stop_ap,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_GET_STATION,
+ .doit = nl80211_get_station,
+ .dumpit = nl80211_dump_station,
+ .policy = nl80211_policy,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_SET_STATION,
+ .doit = nl80211_set_station,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_NEW_STATION,
+ .doit = nl80211_new_station,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_DEL_STATION,
+ .doit = nl80211_del_station,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_GET_MPATH,
+ .doit = nl80211_get_mpath,
+ .dumpit = nl80211_dump_mpath,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_GET_MPP,
+ .doit = nl80211_get_mpp,
+ .dumpit = nl80211_dump_mpp,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_SET_MPATH,
+ .doit = nl80211_set_mpath,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_NEW_MPATH,
+ .doit = nl80211_new_mpath,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_DEL_MPATH,
+ .doit = nl80211_del_mpath,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_SET_BSS,
+ .doit = nl80211_set_bss,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_GET_REG,
+ .doit = nl80211_get_reg_do,
+ .dumpit = nl80211_get_reg_dump,
+ .policy = nl80211_policy,
+ .internal_flags = NL80211_FLAG_NEED_RTNL,
+ /* can be retrieved by unprivileged users */
+ },
+#ifdef CPTCFG_CFG80211_CRDA_SUPPORT
+ {
+ .cmd = NL80211_CMD_SET_REG,
+ .doit = nl80211_set_reg,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_RTNL,
+ },
+#endif
+ {
+ .cmd = NL80211_CMD_REQ_SET_REG,
+ .doit = nl80211_req_set_reg,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
+ {
+ .cmd = NL80211_CMD_GET_MESH_CONFIG,
+ .doit = nl80211_get_mesh_config,
+ .policy = nl80211_policy,
+ /* can be retrieved by unprivileged users */
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_SET_MESH_CONFIG,
+ .doit = nl80211_update_mesh_config,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_TRIGGER_SCAN,
+ .doit = nl80211_trigger_scan,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_ABORT_SCAN,
+ .doit = nl80211_abort_scan,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_GET_SCAN,
+ .policy = nl80211_policy,
+ .dumpit = nl80211_dump_scan,
+ },
+ {
+ .cmd = NL80211_CMD_START_SCHED_SCAN,
+ .doit = nl80211_start_sched_scan,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_STOP_SCHED_SCAN,
+ .doit = nl80211_stop_sched_scan,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_AUTHENTICATE,
+ .doit = nl80211_authenticate,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL |
+ NL80211_FLAG_CLEAR_SKB,
+ },
+ {
+ .cmd = NL80211_CMD_ASSOCIATE,
+ .doit = nl80211_associate,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_DEAUTHENTICATE,
+ .doit = nl80211_deauthenticate,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_DISASSOCIATE,
+ .doit = nl80211_disassociate,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_JOIN_IBSS,
+ .doit = nl80211_join_ibss,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_LEAVE_IBSS,
+ .doit = nl80211_leave_ibss,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+#ifdef CPTCFG_NL80211_TESTMODE
+ {
+ .cmd = NL80211_CMD_TESTMODE,
+ .doit = nl80211_testmode_do,
+ .dumpit = nl80211_testmode_dump,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_WIPHY |
+ NL80211_FLAG_NEED_RTNL,
+ },
+#endif
+ {
+ .cmd = NL80211_CMD_CONNECT,
+ .doit = nl80211_connect,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_UPDATE_CONNECT_PARAMS,
+ .doit = nl80211_update_connect_params,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_DISCONNECT,
+ .doit = nl80211_disconnect,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_SET_WIPHY_NETNS,
+ .doit = nl80211_wiphy_netns,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_WIPHY |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_GET_SURVEY,
+ .policy = nl80211_policy,
+ .dumpit = nl80211_dump_survey,
+ },
+ {
+ .cmd = NL80211_CMD_SET_PMKSA,
+ .doit = nl80211_setdel_pmksa,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_DEL_PMKSA,
+ .doit = nl80211_setdel_pmksa,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_FLUSH_PMKSA,
+ .doit = nl80211_flush_pmksa,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_REMAIN_ON_CHANNEL,
+ .doit = nl80211_remain_on_channel,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL,
+ .doit = nl80211_cancel_remain_on_channel,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_SET_TX_BITRATE_MASK,
+ .doit = nl80211_set_tx_bitrate_mask,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_REGISTER_FRAME,
+ .doit = nl80211_register_mgmt,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_WDEV |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_FRAME,
+ .doit = nl80211_tx_mgmt,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_FRAME_WAIT_CANCEL,
+ .doit = nl80211_tx_mgmt_cancel_wait,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_SET_POWER_SAVE,
+ .doit = nl80211_set_power_save,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_GET_POWER_SAVE,
+ .doit = nl80211_get_power_save,
+ .policy = nl80211_policy,
+ /* can be retrieved by unprivileged users */
+ .internal_flags = NL80211_FLAG_NEED_NETDEV |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_SET_CQM,
+ .doit = nl80211_set_cqm,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_SET_CHANNEL,
+ .doit = nl80211_set_channel,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_SET_WDS_PEER,
+ .doit = nl80211_set_wds_peer,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_JOIN_MESH,
+ .doit = nl80211_join_mesh,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_LEAVE_MESH,
+ .doit = nl80211_leave_mesh,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_JOIN_OCB,
+ .doit = nl80211_join_ocb,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_LEAVE_OCB,
+ .doit = nl80211_leave_ocb,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+#ifdef CONFIG_PM
+ {
+ .cmd = NL80211_CMD_GET_WOWLAN,
+ .doit = nl80211_get_wowlan,
+ .policy = nl80211_policy,
+ /* can be retrieved by unprivileged users */
+ .internal_flags = NL80211_FLAG_NEED_WIPHY |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_SET_WOWLAN,
+ .doit = nl80211_set_wowlan,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_WIPHY |
+ NL80211_FLAG_NEED_RTNL,
+ },
+#endif
+ {
+ .cmd = NL80211_CMD_SET_REKEY_OFFLOAD,
+ .doit = nl80211_set_rekey_data,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL |
+ NL80211_FLAG_CLEAR_SKB,
+ },
+ {
+ .cmd = NL80211_CMD_TDLS_MGMT,
+ .doit = nl80211_tdls_mgmt,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_TDLS_OPER,
+ .doit = nl80211_tdls_oper,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_UNEXPECTED_FRAME,
+ .doit = nl80211_register_unexpected_frame,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_PROBE_CLIENT,
+ .doit = nl80211_probe_client,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_REGISTER_BEACONS,
+ .doit = nl80211_register_beacons,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_WIPHY |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_SET_NOACK_MAP,
+ .doit = nl80211_set_noack_map,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_START_P2P_DEVICE,
+ .doit = nl80211_start_p2p_device,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_WDEV |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_STOP_P2P_DEVICE,
+ .doit = nl80211_stop_p2p_device,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_START_NAN,
+ .doit = nl80211_start_nan,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_WDEV |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_STOP_NAN,
+ .doit = nl80211_stop_nan,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_ADD_NAN_FUNCTION,
+ .doit = nl80211_nan_add_func,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_DEL_NAN_FUNCTION,
+ .doit = nl80211_nan_del_func,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_CHANGE_NAN_CONFIG,
+ .doit = nl80211_nan_change_config,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_SET_MCAST_RATE,
+ .doit = nl80211_set_mcast_rate,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_SET_MAC_ACL,
+ .doit = nl80211_set_mac_acl,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_RADAR_DETECT,
+ .doit = nl80211_start_radar_detection,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_GET_PROTOCOL_FEATURES,
+ .doit = nl80211_get_protocol_features,
+ .policy = nl80211_policy,
+ },
+ {
+ .cmd = NL80211_CMD_UPDATE_FT_IES,
+ .doit = nl80211_update_ft_ies,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_CRIT_PROTOCOL_START,
+ .doit = nl80211_crit_protocol_start,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_CRIT_PROTOCOL_STOP,
+ .doit = nl80211_crit_protocol_stop,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_GET_COALESCE,
+ .doit = nl80211_get_coalesce,
+ .policy = nl80211_policy,
+ .internal_flags = NL80211_FLAG_NEED_WIPHY |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_SET_COALESCE,
+ .doit = nl80211_set_coalesce,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_WIPHY |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_CHANNEL_SWITCH,
+ .doit = nl80211_channel_switch,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_VENDOR,
+ .doit = nl80211_vendor_cmd,
+ .dumpit = nl80211_vendor_cmd_dump,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_WIPHY |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_SET_QOS_MAP,
+ .doit = nl80211_set_qos_map,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_ADD_TX_TS,
+ .doit = nl80211_add_tx_ts,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_DEL_TX_TS,
+ .doit = nl80211_del_tx_ts,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_TDLS_CHANNEL_SWITCH,
+ .doit = nl80211_tdls_channel_switch,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH,
+ .doit = nl80211_tdls_cancel_channel_switch,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_SET_MULTICAST_TO_UNICAST,
+ .doit = nl80211_set_multicast_to_unicast,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_SET_PMK,
+ .doit = nl80211_set_pmk,
+ .policy = nl80211_policy,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_DEL_PMK,
+ .doit = nl80211_del_pmk,
+ .policy = nl80211_policy,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_GET_KEEPALIVE,
+ .doit = nl80211_get_keepalive,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_SET_KEEPALIVE,
+ .doit = nl80211_set_keepalive,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV |
+ NL80211_FLAG_NEED_RTNL,
+ },
+
+};
+
+static struct genl_family nl80211_fam __genl_ro_after_init = {
+ .name = NL80211_GENL_NAME, /* have users key off the name instead */
+ .hdrsize = 0, /* no private header */
+ .version = 1, /* no particular meaning now */
+ .maxattr = NL80211_ATTR_MAX,
+ .netnsok = true,
+ .pre_doit = nl80211_pre_doit,
+ .post_doit = nl80211_post_doit,
+ .module = THIS_MODULE,
+ .ops = nl80211_ops,
+ .n_ops = ARRAY_SIZE(nl80211_ops),
+ .mcgrps = nl80211_mcgrps,
+ .n_mcgrps = ARRAY_SIZE(nl80211_mcgrps),
+};
+
+/* notification functions */
+
+void nl80211_notify_wiphy(struct cfg80211_registered_device *rdev,
+ enum nl80211_commands cmd)
+{
+ struct sk_buff *msg;
+ struct nl80211_dump_wiphy_state state = {};
+
+ WARN_ON(cmd != NL80211_CMD_NEW_WIPHY &&
+ cmd != NL80211_CMD_DEL_WIPHY);
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg)
+ return;
+
+ if (nl80211_send_wiphy(rdev, cmd, msg, 0, 0, 0, &state) < 0) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+ NL80211_MCGRP_CONFIG, GFP_KERNEL);
+}
+
+void nl80211_notify_iface(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev,
+ enum nl80211_commands cmd)
+{
+ struct sk_buff *msg;
+
+ WARN_ON(cmd != NL80211_CMD_NEW_INTERFACE &&
+ cmd != NL80211_CMD_DEL_INTERFACE);
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg)
+ return;
+
+ if (nl80211_send_iface(msg, 0, 0, 0, rdev, wdev,
+ cmd == NL80211_CMD_DEL_INTERFACE) < 0) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+ NL80211_MCGRP_CONFIG, GFP_KERNEL);
+}
+
+static int nl80211_add_scan_req(struct sk_buff *msg,
+ struct cfg80211_registered_device *rdev)
+{
+ struct cfg80211_scan_request *req = rdev->scan_req;
+ struct nlattr *nest;
+ int i;
+
+ if (WARN_ON(!req))
+ return 0;
+
+ nest = nla_nest_start(msg, NL80211_ATTR_SCAN_SSIDS);
+ if (!nest)
+ goto nla_put_failure;
+ for (i = 0; i < req->n_ssids; i++) {
+ if (nla_put(msg, i, req->ssids[i].ssid_len, req->ssids[i].ssid))
+ goto nla_put_failure;
+ }
+ nla_nest_end(msg, nest);
+
+ nest = nla_nest_start(msg, NL80211_ATTR_SCAN_FREQUENCIES);
+ if (!nest)
+ goto nla_put_failure;
+ for (i = 0; i < req->n_channels; i++) {
+ if (nla_put_u32(msg, i, req->channels[i]->center_freq))
+ goto nla_put_failure;
+ }
+ nla_nest_end(msg, nest);
+
+ if (req->ie &&
+ nla_put(msg, NL80211_ATTR_IE, req->ie_len, req->ie))
+ goto nla_put_failure;
+
+ if (req->flags &&
+ nla_put_u32(msg, NL80211_ATTR_SCAN_FLAGS, req->flags))
+ goto nla_put_failure;
+
+ if (req->info.scan_start_tsf &&
+ (nla_put_u64_64bit(msg, NL80211_ATTR_SCAN_START_TIME_TSF,
+ req->info.scan_start_tsf, NL80211_BSS_PAD) ||
+ nla_put(msg, NL80211_ATTR_SCAN_START_TIME_TSF_BSSID, ETH_ALEN,
+ req->info.tsf_bssid)))
+ goto nla_put_failure;
+
+ return 0;
+ nla_put_failure:
+ return -ENOBUFS;
+}
+
+static int nl80211_prep_scan_msg(struct sk_buff *msg,
+ struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev,
+ u32 portid, u32 seq, int flags,
+ u32 cmd)
+{
+ void *hdr;
+
+ hdr = nl80211hdr_put(msg, portid, seq, flags, cmd);
+ if (!hdr)
+ return -1;
+
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
+ (wdev->netdev && nla_put_u32(msg, NL80211_ATTR_IFINDEX,
+ wdev->netdev->ifindex)) ||
+ nla_put_u64_64bit(msg, NL80211_ATTR_WDEV, wdev_id(wdev),
+ NL80211_ATTR_PAD))
+ goto nla_put_failure;
+
+ /* ignore errors and send incomplete event anyway */
+ nl80211_add_scan_req(msg, rdev);
+
+ genlmsg_end(msg, hdr);
+ return 0;
+
+ nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ return -EMSGSIZE;
+}
+
+static int
+nl80211_prep_sched_scan_msg(struct sk_buff *msg,
+ struct cfg80211_sched_scan_request *req, u32 cmd)
+{
+ void *hdr;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
+ if (!hdr)
+ return -1;
+
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY,
+ wiphy_to_rdev(req->wiphy)->wiphy_idx) ||
+ nla_put_u32(msg, NL80211_ATTR_IFINDEX, req->dev->ifindex) ||
+ nla_put_u64_64bit(msg, NL80211_ATTR_COOKIE, req->reqid,
+ NL80211_ATTR_PAD))
+ goto nla_put_failure;
+
+ genlmsg_end(msg, hdr);
+ return 0;
+
+ nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ return -EMSGSIZE;
+}
+
+void nl80211_send_scan_start(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev)
+{
+ struct sk_buff *msg;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg)
+ return;
+
+ if (nl80211_prep_scan_msg(msg, rdev, wdev, 0, 0, 0,
+ NL80211_CMD_TRIGGER_SCAN) < 0) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+ NL80211_MCGRP_SCAN, GFP_KERNEL);
+}
+
+struct sk_buff *nl80211_build_scan_msg(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev, bool aborted)
+{
+ struct sk_buff *msg;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg)
+ return NULL;
+
+ if (nl80211_prep_scan_msg(msg, rdev, wdev, 0, 0, 0,
+ aborted ? NL80211_CMD_SCAN_ABORTED :
+ NL80211_CMD_NEW_SCAN_RESULTS) < 0) {
+ nlmsg_free(msg);
+ return NULL;
+ }
+
+ return msg;
+}
+
+/* send message created by nl80211_build_scan_msg() */
+void nl80211_send_scan_msg(struct cfg80211_registered_device *rdev,
+ struct sk_buff *msg)
+{
+ if (!msg)
+ return;
+
+ genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+ NL80211_MCGRP_SCAN, GFP_KERNEL);
+}
+
+void nl80211_send_sched_scan(struct cfg80211_sched_scan_request *req, u32 cmd)
+{
+ struct sk_buff *msg;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg)
+ return;
+
+ if (nl80211_prep_sched_scan_msg(msg, req, cmd) < 0) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ genlmsg_multicast_netns(&nl80211_fam, wiphy_net(req->wiphy), msg, 0,
+ NL80211_MCGRP_SCAN, GFP_KERNEL);
+}
+
+static bool nl80211_reg_change_event_fill(struct sk_buff *msg,
+ struct regulatory_request *request)
+{
+ /* Userspace can always count this one always being set */
+ if (nla_put_u8(msg, NL80211_ATTR_REG_INITIATOR, request->initiator))
+ goto nla_put_failure;
+
+ if (request->alpha2[0] == '0' && request->alpha2[1] == '0') {
+ if (nla_put_u8(msg, NL80211_ATTR_REG_TYPE,
+ NL80211_REGDOM_TYPE_WORLD))
+ goto nla_put_failure;
+ } else if (request->alpha2[0] == '9' && request->alpha2[1] == '9') {
+ if (nla_put_u8(msg, NL80211_ATTR_REG_TYPE,
+ NL80211_REGDOM_TYPE_CUSTOM_WORLD))
+ goto nla_put_failure;
+ } else if ((request->alpha2[0] == '9' && request->alpha2[1] == '8') ||
+ request->intersect) {
+ if (nla_put_u8(msg, NL80211_ATTR_REG_TYPE,
+ NL80211_REGDOM_TYPE_INTERSECTION))
+ goto nla_put_failure;
+ } else {
+ if (nla_put_u8(msg, NL80211_ATTR_REG_TYPE,
+ NL80211_REGDOM_TYPE_COUNTRY) ||
+ nla_put_string(msg, NL80211_ATTR_REG_ALPHA2,
+ request->alpha2))
+ goto nla_put_failure;
+ }
+
+ if (request->wiphy_idx != WIPHY_IDX_INVALID) {
+ struct wiphy *wiphy = wiphy_idx_to_wiphy(request->wiphy_idx);
+
+ if (wiphy &&
+ nla_put_u32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx))
+ goto nla_put_failure;
+
+ if (wiphy &&
+ wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED &&
+ nla_put_flag(msg, NL80211_ATTR_WIPHY_SELF_MANAGED_REG))
+ goto nla_put_failure;
+ }
+
+ return true;
+
+nla_put_failure:
+ return false;
+}
+
+/*
+ * This can happen on global regulatory changes or device specific settings
+ * based on custom regulatory domains.
+ */
+void nl80211_common_reg_change_event(enum nl80211_commands cmd_id,
+ struct regulatory_request *request)
+{
+ struct sk_buff *msg;
+ void *hdr;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg)
+ return;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, cmd_id);
+ if (!hdr) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ if (nl80211_reg_change_event_fill(msg, request) == false)
+ goto nla_put_failure;
+
+ genlmsg_end(msg, hdr);
+
+ rcu_read_lock();
+ genlmsg_multicast_allns(&nl80211_fam, msg, 0,
+ NL80211_MCGRP_REGULATORY, GFP_ATOMIC);
+ rcu_read_unlock();
+
+ return;
+
+nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ nlmsg_free(msg);
+}
+
+static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev,
+ const u8 *buf, size_t len,
+ enum nl80211_commands cmd, gfp_t gfp,
+ int uapsd_queues)
+{
+ struct sk_buff *msg;
+ void *hdr;
+
+ msg = nlmsg_new(100 + len, gfp);
+ if (!msg)
+ return;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
+ if (!hdr) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
+ nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
+ nla_put(msg, NL80211_ATTR_FRAME, len, buf))
+ goto nla_put_failure;
+
+ if (uapsd_queues >= 0) {
+ struct nlattr *nla_wmm =
+ nla_nest_start(msg, NL80211_ATTR_STA_WME);
+ if (!nla_wmm)
+ goto nla_put_failure;
+
+ if (nla_put_u8(msg, NL80211_STA_WME_UAPSD_QUEUES,
+ uapsd_queues))
+ goto nla_put_failure;
+
+ nla_nest_end(msg, nla_wmm);
+ }
+
+ genlmsg_end(msg, hdr);
+
+ genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+ NL80211_MCGRP_MLME, gfp);
+ return;
+
+ nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ nlmsg_free(msg);
+}
+
+void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev, const u8 *buf,
+ size_t len, gfp_t gfp)
+{
+ nl80211_send_mlme_event(rdev, netdev, buf, len,
+ NL80211_CMD_AUTHENTICATE, gfp, -1);
+}
+
+void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev, const u8 *buf,
+ size_t len, gfp_t gfp, int uapsd_queues)
+{
+ nl80211_send_mlme_event(rdev, netdev, buf, len,
+ NL80211_CMD_ASSOCIATE, gfp, uapsd_queues);
+}
+
+void nl80211_send_deauth(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev, const u8 *buf,
+ size_t len, gfp_t gfp)
+{
+ nl80211_send_mlme_event(rdev, netdev, buf, len,
+ NL80211_CMD_DEAUTHENTICATE, gfp, -1);
+}
+
+void nl80211_send_disassoc(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev, const u8 *buf,
+ size_t len, gfp_t gfp)
+{
+ nl80211_send_mlme_event(rdev, netdev, buf, len,
+ NL80211_CMD_DISASSOCIATE, gfp, -1);
+}
+
+void cfg80211_rx_unprot_mlme_mgmt(struct net_device *dev, const u8 *buf,
+ size_t len)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct wiphy *wiphy = wdev->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+ const struct ieee80211_mgmt *mgmt = (void *)buf;
+ u32 cmd;
+
+ if (WARN_ON(len < 2))
+ return;
+
+ if (ieee80211_is_deauth(mgmt->frame_control))
+ cmd = NL80211_CMD_UNPROT_DEAUTHENTICATE;
+ else
+ cmd = NL80211_CMD_UNPROT_DISASSOCIATE;
+
+ trace_cfg80211_rx_unprot_mlme_mgmt(dev, buf, len);
+ nl80211_send_mlme_event(rdev, dev, buf, len, cmd, GFP_ATOMIC, -1);
+}
+EXPORT_SYMBOL(cfg80211_rx_unprot_mlme_mgmt);
+
+static void nl80211_send_mlme_timeout(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev, int cmd,
+ const u8 *addr, gfp_t gfp)
+{
+ struct sk_buff *msg;
+ void *hdr;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
+ if (!msg)
+ return;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
+ if (!hdr) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
+ nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
+ nla_put_flag(msg, NL80211_ATTR_TIMED_OUT) ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr))
+ goto nla_put_failure;
+
+ genlmsg_end(msg, hdr);
+
+ genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+ NL80211_MCGRP_MLME, gfp);
+ return;
+
+ nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ nlmsg_free(msg);
+}
+
+void nl80211_send_auth_timeout(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev, const u8 *addr,
+ gfp_t gfp)
+{
+ nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_AUTHENTICATE,
+ addr, gfp);
+}
+
+void nl80211_send_assoc_timeout(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev, const u8 *addr,
+ gfp_t gfp)
+{
+ nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_ASSOCIATE,
+ addr, gfp);
+}
+
+void nl80211_send_connect_result(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev,
+ struct cfg80211_connect_resp_params *cr,
+ gfp_t gfp)
+{
+ struct sk_buff *msg;
+ void *hdr;
+
+ msg = nlmsg_new(100 + cr->req_ie_len + cr->resp_ie_len +
+ cr->fils_kek_len + cr->pmk_len +
+ (cr->pmkid ? WLAN_PMKID_LEN : 0), gfp);
+ if (!msg)
+ return;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_CONNECT);
+ if (!hdr) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
+ nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
+ (cr->bssid &&
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, cr->bssid)) ||
+ nla_put_u16(msg, NL80211_ATTR_STATUS_CODE,
+ cr->status < 0 ? WLAN_STATUS_UNSPECIFIED_FAILURE :
+ cr->status) ||
+ (cr->status < 0 &&
+ (nla_put_flag(msg, NL80211_ATTR_TIMED_OUT) ||
+ nla_put_u32(msg, NL80211_ATTR_TIMEOUT_REASON,
+ cr->timeout_reason))) ||
+ (cr->req_ie &&
+ nla_put(msg, NL80211_ATTR_REQ_IE, cr->req_ie_len, cr->req_ie)) ||
+ (cr->resp_ie &&
+ nla_put(msg, NL80211_ATTR_RESP_IE, cr->resp_ie_len,
+ cr->resp_ie)) ||
+ (cr->update_erp_next_seq_num &&
+ nla_put_u16(msg, NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM,
+ cr->fils_erp_next_seq_num)) ||
+ (cr->status == WLAN_STATUS_SUCCESS &&
+ ((cr->fils_kek &&
+ nla_put(msg, NL80211_ATTR_FILS_KEK, cr->fils_kek_len,
+ cr->fils_kek)) ||
+ (cr->pmk &&
+ nla_put(msg, NL80211_ATTR_PMK, cr->pmk_len, cr->pmk)) ||
+ (cr->pmkid &&
+ nla_put(msg, NL80211_ATTR_PMKID, WLAN_PMKID_LEN, cr->pmkid)))))
+ goto nla_put_failure;
+
+ genlmsg_end(msg, hdr);
+
+ genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+ NL80211_MCGRP_MLME, gfp);
+ return;
+
+ nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ nlmsg_free(msg);
+}
+
+void nl80211_send_roamed(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev,
+ struct cfg80211_roam_info *info, gfp_t gfp)
+{
+ struct sk_buff *msg;
+ void *hdr;
+ const u8 *bssid = info->bss ? info->bss->bssid : info->bssid;
+
+ msg = nlmsg_new(100 + info->req_ie_len + info->resp_ie_len, gfp);
+ if (!msg)
+ return;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_ROAM);
+ if (!hdr) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
+ nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid) ||
+ (info->req_ie &&
+ nla_put(msg, NL80211_ATTR_REQ_IE, info->req_ie_len,
+ info->req_ie)) ||
+ (info->resp_ie &&
+ nla_put(msg, NL80211_ATTR_RESP_IE, info->resp_ie_len,
+ info->resp_ie)))
+ goto nla_put_failure;
+
+ genlmsg_end(msg, hdr);
+
+ genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+ NL80211_MCGRP_MLME, gfp);
+ return;
+
+ nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ nlmsg_free(msg);
+}
+
+void nl80211_send_disconnected(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev, u16 reason,
+ const u8 *ie, size_t ie_len, bool from_ap)
+{
+ struct sk_buff *msg;
+ void *hdr;
+
+ msg = nlmsg_new(100 + ie_len, GFP_KERNEL);
+ if (!msg)
+ return;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_DISCONNECT);
+ if (!hdr) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
+ nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
+ (from_ap && reason &&
+ nla_put_u16(msg, NL80211_ATTR_REASON_CODE, reason)) ||
+ (from_ap &&
+ nla_put_flag(msg, NL80211_ATTR_DISCONNECTED_BY_AP)) ||
+ (ie && nla_put(msg, NL80211_ATTR_IE, ie_len, ie)))
+ goto nla_put_failure;
+
+ genlmsg_end(msg, hdr);
+
+ genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+ NL80211_MCGRP_MLME, GFP_KERNEL);
+ return;
+
+ nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ nlmsg_free(msg);
+}
+
+void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev, const u8 *bssid,
+ gfp_t gfp)
+{
+ struct sk_buff *msg;
+ void *hdr;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
+ if (!msg)
+ return;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_JOIN_IBSS);
+ if (!hdr) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
+ nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid))
+ goto nla_put_failure;
+
+ genlmsg_end(msg, hdr);
+
+ genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+ NL80211_MCGRP_MLME, gfp);
+ return;
+
+ nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ nlmsg_free(msg);
+}
+
+void cfg80211_notify_new_peer_candidate(struct net_device *dev, const u8 *addr,
+ const u8* ie, u8 ie_len, gfp_t gfp)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+ struct sk_buff *msg;
+ void *hdr;
+
+ if (WARN_ON(wdev->iftype != NL80211_IFTYPE_MESH_POINT))
+ return;
+
+ trace_cfg80211_notify_new_peer_candidate(dev, addr);
+
+ msg = nlmsg_new(100 + ie_len, gfp);
+ if (!msg)
+ return;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NEW_PEER_CANDIDATE);
+ if (!hdr) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
+ nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
+ (ie_len && ie &&
+ nla_put(msg, NL80211_ATTR_IE, ie_len , ie)))
+ goto nla_put_failure;
+
+ genlmsg_end(msg, hdr);
+
+ genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+ NL80211_MCGRP_MLME, gfp);
+ return;
+
+ nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ nlmsg_free(msg);
+}
+EXPORT_SYMBOL(cfg80211_notify_new_peer_candidate);
+
+void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev, const u8 *addr,
+ enum nl80211_key_type key_type, int key_id,
+ const u8 *tsc, gfp_t gfp)
+{
+ struct sk_buff *msg;
+ void *hdr;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
+ if (!msg)
+ return;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_MICHAEL_MIC_FAILURE);
+ if (!hdr) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
+ nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
+ (addr && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) ||
+ nla_put_u32(msg, NL80211_ATTR_KEY_TYPE, key_type) ||
+ (key_id != -1 &&
+ nla_put_u8(msg, NL80211_ATTR_KEY_IDX, key_id)) ||
+ (tsc && nla_put(msg, NL80211_ATTR_KEY_SEQ, 6, tsc)))
+ goto nla_put_failure;
+
+ genlmsg_end(msg, hdr);
+
+ genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+ NL80211_MCGRP_MLME, gfp);
+ return;
+
+ nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ nlmsg_free(msg);
+}
+
+void nl80211_send_beacon_hint_event(struct wiphy *wiphy,
+ struct ieee80211_channel *channel_before,
+ struct ieee80211_channel *channel_after)
+{
+ struct sk_buff *msg;
+ void *hdr;
+ struct nlattr *nl_freq;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
+ if (!msg)
+ return;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_BEACON_HINT);
+ if (!hdr) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ /*
+ * Since we are applying the beacon hint to a wiphy we know its
+ * wiphy_idx is valid
+ */
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY, get_wiphy_idx(wiphy)))
+ goto nla_put_failure;
+
+ /* Before */
+ nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_BEFORE);
+ if (!nl_freq)
+ goto nla_put_failure;
+ if (nl80211_msg_put_channel(msg, channel_before, false))
+ goto nla_put_failure;
+ nla_nest_end(msg, nl_freq);
+
+ /* After */
+ nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_AFTER);
+ if (!nl_freq)
+ goto nla_put_failure;
+ if (nl80211_msg_put_channel(msg, channel_after, false))
+ goto nla_put_failure;
+ nla_nest_end(msg, nl_freq);
+
+ genlmsg_end(msg, hdr);
+
+ rcu_read_lock();
+ genlmsg_multicast_allns(&nl80211_fam, msg, 0,
+ NL80211_MCGRP_REGULATORY, GFP_ATOMIC);
+ rcu_read_unlock();
+
+ return;
+
+nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ nlmsg_free(msg);
+}
+
+static void nl80211_send_remain_on_chan_event(
+ int cmd, struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev, u64 cookie,
+ struct ieee80211_channel *chan,
+ unsigned int duration, gfp_t gfp)
+{
+ struct sk_buff *msg;
+ void *hdr;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
+ if (!msg)
+ return;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
+ if (!hdr) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
+ (wdev->netdev && nla_put_u32(msg, NL80211_ATTR_IFINDEX,
+ wdev->netdev->ifindex)) ||
+ nla_put_u64_64bit(msg, NL80211_ATTR_WDEV, wdev_id(wdev),
+ NL80211_ATTR_PAD) ||
+ nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, chan->center_freq) ||
+ nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE,
+ NL80211_CHAN_NO_HT) ||
+ nla_put_u64_64bit(msg, NL80211_ATTR_COOKIE, cookie,
+ NL80211_ATTR_PAD))
+ goto nla_put_failure;
+
+ if (cmd == NL80211_CMD_REMAIN_ON_CHANNEL &&
+ nla_put_u32(msg, NL80211_ATTR_DURATION, duration))
+ goto nla_put_failure;
+
+ genlmsg_end(msg, hdr);
+
+ genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+ NL80211_MCGRP_MLME, gfp);
+ return;
+
+ nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ nlmsg_free(msg);
+}
+
+void cfg80211_ready_on_channel(struct wireless_dev *wdev, u64 cookie,
+ struct ieee80211_channel *chan,
+ unsigned int duration, gfp_t gfp)
+{
+ struct wiphy *wiphy = wdev->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+
+ trace_cfg80211_ready_on_channel(wdev, cookie, chan, duration);
+ nl80211_send_remain_on_chan_event(NL80211_CMD_REMAIN_ON_CHANNEL,
+ rdev, wdev, cookie, chan,
+ duration, gfp);
+}
+EXPORT_SYMBOL(cfg80211_ready_on_channel);
+
+void cfg80211_remain_on_channel_expired(struct wireless_dev *wdev, u64 cookie,
+ struct ieee80211_channel *chan,
+ gfp_t gfp)
+{
+ struct wiphy *wiphy = wdev->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+
+ trace_cfg80211_ready_on_channel_expired(wdev, cookie, chan);
+ nl80211_send_remain_on_chan_event(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL,
+ rdev, wdev, cookie, chan, 0, gfp);
+}
+EXPORT_SYMBOL(cfg80211_remain_on_channel_expired);
+
+void cfg80211_new_sta(struct net_device *dev, const u8 *mac_addr,
+ struct station_info *sinfo, gfp_t gfp)
+{
+ struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+ struct sk_buff *msg;
+
+ trace_cfg80211_new_sta(dev, mac_addr, sinfo);
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
+ if (!msg)
+ return;
+
+ if (nl80211_send_station(msg, NL80211_CMD_NEW_STATION, 0, 0, 0,
+ rdev, dev, mac_addr, sinfo) < 0) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+ NL80211_MCGRP_MLME, gfp);
+}
+EXPORT_SYMBOL(cfg80211_new_sta);
+
+void cfg80211_del_sta_sinfo(struct net_device *dev, const u8 *mac_addr,
+ struct station_info *sinfo, gfp_t gfp)
+{
+ struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+ struct sk_buff *msg;
+ struct station_info empty_sinfo = {};
+
+ if (!sinfo)
+ sinfo = &empty_sinfo;
+
+ trace_cfg80211_del_sta(dev, mac_addr);
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
+ if (!msg)
+ return;
+
+ if (nl80211_send_station(msg, NL80211_CMD_DEL_STATION, 0, 0, 0,
+ rdev, dev, mac_addr, sinfo) < 0) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+ NL80211_MCGRP_MLME, gfp);
+}
+EXPORT_SYMBOL(cfg80211_del_sta_sinfo);
+
+void cfg80211_conn_failed(struct net_device *dev, const u8 *mac_addr,
+ enum nl80211_connect_failed_reason reason,
+ gfp_t gfp)
+{
+ struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+ struct sk_buff *msg;
+ void *hdr;
+
+ msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
+ if (!msg)
+ return;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_CONN_FAILED);
+ if (!hdr) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr) ||
+ nla_put_u32(msg, NL80211_ATTR_CONN_FAILED_REASON, reason))
+ goto nla_put_failure;
+
+ genlmsg_end(msg, hdr);
+
+ genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+ NL80211_MCGRP_MLME, gfp);
+ return;
+
+ nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ nlmsg_free(msg);
+}
+EXPORT_SYMBOL(cfg80211_conn_failed);
+
+static bool __nl80211_unexpected_frame(struct net_device *dev, u8 cmd,
+ const u8 *addr, gfp_t gfp)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+ struct sk_buff *msg;
+ void *hdr;
+ u32 nlportid = ACCESS_ONCE(wdev->ap_unexpected_nlportid);
+
+ if (!nlportid)
+ return false;
+
+ msg = nlmsg_new(100, gfp);
+ if (!msg)
+ return true;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
+ if (!hdr) {
+ nlmsg_free(msg);
+ return true;
+ }
+
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
+ nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr))
+ goto nla_put_failure;
+
+ genlmsg_end(msg, hdr);
+ genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlportid);
+ return true;
+
+ nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ nlmsg_free(msg);
+ return true;
+}
+
+bool cfg80211_rx_spurious_frame(struct net_device *dev,
+ const u8 *addr, gfp_t gfp)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ bool ret;
+
+ trace_cfg80211_rx_spurious_frame(dev, addr);
+
+ if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
+ wdev->iftype != NL80211_IFTYPE_P2P_GO)) {
+ trace_cfg80211_return_bool(false);
+ return false;
+ }
+ ret = __nl80211_unexpected_frame(dev, NL80211_CMD_UNEXPECTED_FRAME,
+ addr, gfp);
+ trace_cfg80211_return_bool(ret);
+ return ret;
+}
+EXPORT_SYMBOL(cfg80211_rx_spurious_frame);
+
+bool cfg80211_rx_unexpected_4addr_frame(struct net_device *dev,
+ const u8 *addr, gfp_t gfp)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ bool ret;
+
+ trace_cfg80211_rx_unexpected_4addr_frame(dev, addr);
+
+ if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
+ wdev->iftype != NL80211_IFTYPE_P2P_GO &&
+ wdev->iftype != NL80211_IFTYPE_AP_VLAN)) {
+ trace_cfg80211_return_bool(false);
+ return false;
+ }
+ ret = __nl80211_unexpected_frame(dev,
+ NL80211_CMD_UNEXPECTED_4ADDR_FRAME,
+ addr, gfp);
+ trace_cfg80211_return_bool(ret);
+ return ret;
+}
+EXPORT_SYMBOL(cfg80211_rx_unexpected_4addr_frame);
+
+int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev, u32 nlportid,
+ int freq, int sig_dbm,
+ const u8 *buf, size_t len, u32 flags, gfp_t gfp)
+{
+ struct net_device *netdev = wdev->netdev;
+ struct sk_buff *msg;
+ void *hdr;
+
+ msg = nlmsg_new(100 + len, gfp);
+ if (!msg)
+ return -ENOMEM;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME);
+ if (!hdr) {
+ nlmsg_free(msg);
+ return -ENOMEM;
+ }
+
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
+ (netdev && nla_put_u32(msg, NL80211_ATTR_IFINDEX,
+ netdev->ifindex)) ||
+ nla_put_u64_64bit(msg, NL80211_ATTR_WDEV, wdev_id(wdev),
+ NL80211_ATTR_PAD) ||
+ nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq) ||
+ (sig_dbm &&
+ nla_put_u32(msg, NL80211_ATTR_RX_SIGNAL_DBM, sig_dbm)) ||
+ nla_put(msg, NL80211_ATTR_FRAME, len, buf) ||
+ (flags &&
+ nla_put_u32(msg, NL80211_ATTR_RXMGMT_FLAGS, flags)))
+ goto nla_put_failure;
+
+ genlmsg_end(msg, hdr);
+
+ return genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlportid);
+
+ nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ nlmsg_free(msg);
+ return -ENOBUFS;
+}
+
+void cfg80211_mgmt_tx_status(struct wireless_dev *wdev, u64 cookie,
+ const u8 *buf, size_t len, bool ack, gfp_t gfp)
+{
+ struct wiphy *wiphy = wdev->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+ struct net_device *netdev = wdev->netdev;
+ struct sk_buff *msg;
+ void *hdr;
+
+ trace_cfg80211_mgmt_tx_status(wdev, cookie, ack);
+
+ msg = nlmsg_new(100 + len, gfp);
+ if (!msg)
+ return;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME_TX_STATUS);
+ if (!hdr) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
+ (netdev && nla_put_u32(msg, NL80211_ATTR_IFINDEX,
+ netdev->ifindex)) ||
+ nla_put_u64_64bit(msg, NL80211_ATTR_WDEV, wdev_id(wdev),
+ NL80211_ATTR_PAD) ||
+ nla_put(msg, NL80211_ATTR_FRAME, len, buf) ||
+ nla_put_u64_64bit(msg, NL80211_ATTR_COOKIE, cookie,
+ NL80211_ATTR_PAD) ||
+ (ack && nla_put_flag(msg, NL80211_ATTR_ACK)))
+ goto nla_put_failure;
+
+ genlmsg_end(msg, hdr);
+
+ genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+ NL80211_MCGRP_MLME, gfp);
+ return;
+
+ nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ nlmsg_free(msg);
+}
+EXPORT_SYMBOL(cfg80211_mgmt_tx_status);
+
+static struct sk_buff *cfg80211_prepare_cqm(struct net_device *dev,
+ const char *mac, gfp_t gfp)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+ struct sk_buff *msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
+ void **cb;
+
+ if (!msg)
+ return NULL;
+
+ cb = (void **)msg->cb;
+
+ cb[0] = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM);
+ if (!cb[0]) {
+ nlmsg_free(msg);
+ return NULL;
+ }
+
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
+ nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex))
+ goto nla_put_failure;
+
+ if (mac && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, mac))
+ goto nla_put_failure;
+
+ cb[1] = nla_nest_start(msg, NL80211_ATTR_CQM);
+ if (!cb[1])
+ goto nla_put_failure;
+
+ cb[2] = rdev;
+
+ return msg;
+ nla_put_failure:
+ nlmsg_free(msg);
+ return NULL;
+}
+
+static void cfg80211_send_cqm(struct sk_buff *msg, gfp_t gfp)
+{
+ void **cb = (void **)msg->cb;
+ struct cfg80211_registered_device *rdev = cb[2];
+
+ nla_nest_end(msg, cb[1]);
+ genlmsg_end(msg, cb[0]);
+
+ memset(msg->cb, 0, sizeof(msg->cb));
+
+ genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+ NL80211_MCGRP_MLME, gfp);
+}
+
+void cfg80211_cqm_rssi_notify(struct net_device *dev,
+ enum nl80211_cqm_rssi_threshold_event rssi_event,
+ s32 rssi_level, gfp_t gfp)
+{
+ struct sk_buff *msg;
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+
+ trace_cfg80211_cqm_rssi_notify(dev, rssi_event, rssi_level);
+
+ if (WARN_ON(rssi_event != NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW &&
+ rssi_event != NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH))
+ return;
+
+ if (wdev->cqm_config) {
+ wdev->cqm_config->last_rssi_event_value = rssi_level;
+
+ cfg80211_cqm_rssi_update(rdev, dev);
+
+ if (rssi_level == 0)
+ rssi_level = wdev->cqm_config->last_rssi_event_value;
+ }
+
+ msg = cfg80211_prepare_cqm(dev, NULL, gfp);
+ if (!msg)
+ return;
+
+ if (nla_put_u32(msg, NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT,
+ rssi_event))
+ goto nla_put_failure;
+
+ if (rssi_level && nla_put_s32(msg, NL80211_ATTR_CQM_RSSI_LEVEL,
+ rssi_level))
+ goto nla_put_failure;
+
+ cfg80211_send_cqm(msg, gfp);
+
+ return;
+
+ nla_put_failure:
+ nlmsg_free(msg);
+}
+EXPORT_SYMBOL(cfg80211_cqm_rssi_notify);
+
+void cfg80211_cqm_txe_notify(struct net_device *dev,
+ const u8 *peer, u32 num_packets,
+ u32 rate, u32 intvl, gfp_t gfp)
+{
+ struct sk_buff *msg;
+
+ msg = cfg80211_prepare_cqm(dev, peer, gfp);
+ if (!msg)
+ return;
+
+ if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_PKTS, num_packets))
+ goto nla_put_failure;
+
+ if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_RATE, rate))
+ goto nla_put_failure;
+
+ if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_INTVL, intvl))
+ goto nla_put_failure;
+
+ cfg80211_send_cqm(msg, gfp);
+ return;
+
+ nla_put_failure:
+ nlmsg_free(msg);
+}
+EXPORT_SYMBOL(cfg80211_cqm_txe_notify);
+
+void cfg80211_cqm_pktloss_notify(struct net_device *dev,
+ const u8 *peer, u32 num_packets, gfp_t gfp)
+{
+ struct sk_buff *msg;
+
+ trace_cfg80211_cqm_pktloss_notify(dev, peer, num_packets);
+
+ msg = cfg80211_prepare_cqm(dev, peer, gfp);
+ if (!msg)
+ return;
+
+ if (nla_put_u32(msg, NL80211_ATTR_CQM_PKT_LOSS_EVENT, num_packets))
+ goto nla_put_failure;
+
+ cfg80211_send_cqm(msg, gfp);
+ return;
+
+ nla_put_failure:
+ nlmsg_free(msg);
+}
+EXPORT_SYMBOL(cfg80211_cqm_pktloss_notify);
+
+void cfg80211_cqm_beacon_loss_notify(struct net_device *dev, gfp_t gfp)
+{
+ struct sk_buff *msg;
+
+ msg = cfg80211_prepare_cqm(dev, NULL, gfp);
+ if (!msg)
+ return;
+
+ if (nla_put_flag(msg, NL80211_ATTR_CQM_BEACON_LOSS_EVENT))
+ goto nla_put_failure;
+
+ cfg80211_send_cqm(msg, gfp);
+ return;
+
+ nla_put_failure:
+ nlmsg_free(msg);
+}
+EXPORT_SYMBOL(cfg80211_cqm_beacon_loss_notify);
+
+static void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev, const u8 *bssid,
+ const u8 *replay_ctr, gfp_t gfp)
+{
+ struct sk_buff *msg;
+ struct nlattr *rekey_attr;
+ void *hdr;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
+ if (!msg)
+ return;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_SET_REKEY_OFFLOAD);
+ if (!hdr) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
+ nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid))
+ goto nla_put_failure;
+
+ rekey_attr = nla_nest_start(msg, NL80211_ATTR_REKEY_DATA);
+ if (!rekey_attr)
+ goto nla_put_failure;
+
+ if (nla_put(msg, NL80211_REKEY_DATA_REPLAY_CTR,
+ NL80211_REPLAY_CTR_LEN, replay_ctr))
+ goto nla_put_failure;
+
+ nla_nest_end(msg, rekey_attr);
+
+ genlmsg_end(msg, hdr);
+
+ genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+ NL80211_MCGRP_MLME, gfp);
+ return;
+
+ nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ nlmsg_free(msg);
+}
+
+void cfg80211_gtk_rekey_notify(struct net_device *dev, const u8 *bssid,
+ const u8 *replay_ctr, gfp_t gfp)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct wiphy *wiphy = wdev->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+
+ trace_cfg80211_gtk_rekey_notify(dev, bssid);
+ nl80211_gtk_rekey_notify(rdev, dev, bssid, replay_ctr, gfp);
+}
+EXPORT_SYMBOL(cfg80211_gtk_rekey_notify);
+
+static void
+nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev, int index,
+ const u8 *bssid, bool preauth, gfp_t gfp)
+{
+ struct sk_buff *msg;
+ struct nlattr *attr;
+ void *hdr;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
+ if (!msg)
+ return;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_PMKSA_CANDIDATE);
+ if (!hdr) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
+ nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex))
+ goto nla_put_failure;
+
+ attr = nla_nest_start(msg, NL80211_ATTR_PMKSA_CANDIDATE);
+ if (!attr)
+ goto nla_put_failure;
+
+ if (nla_put_u32(msg, NL80211_PMKSA_CANDIDATE_INDEX, index) ||
+ nla_put(msg, NL80211_PMKSA_CANDIDATE_BSSID, ETH_ALEN, bssid) ||
+ (preauth &&
+ nla_put_flag(msg, NL80211_PMKSA_CANDIDATE_PREAUTH)))
+ goto nla_put_failure;
+
+ nla_nest_end(msg, attr);
+
+ genlmsg_end(msg, hdr);
+
+ genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+ NL80211_MCGRP_MLME, gfp);
+ return;
+
+ nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ nlmsg_free(msg);
+}
+
+void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index,
+ const u8 *bssid, bool preauth, gfp_t gfp)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct wiphy *wiphy = wdev->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+
+ trace_cfg80211_pmksa_candidate_notify(dev, index, bssid, preauth);
+ nl80211_pmksa_candidate_notify(rdev, dev, index, bssid, preauth, gfp);
+}
+EXPORT_SYMBOL(cfg80211_pmksa_candidate_notify);
+
+static void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev,
+ struct cfg80211_chan_def *chandef,
+ gfp_t gfp,
+ enum nl80211_commands notif,
+ u8 count)
+{
+ struct sk_buff *msg;
+ void *hdr;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
+ if (!msg)
+ return;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, notif);
+ if (!hdr) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex))
+ goto nla_put_failure;
+
+ if (nl80211_send_chandef(msg, chandef))
+ goto nla_put_failure;
+
+ if ((notif == NL80211_CMD_CH_SWITCH_STARTED_NOTIFY) &&
+ (nla_put_u32(msg, NL80211_ATTR_CH_SWITCH_COUNT, count)))
+ goto nla_put_failure;
+
+ genlmsg_end(msg, hdr);
+
+ genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+ NL80211_MCGRP_MLME, gfp);
+ return;
+
+ nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ nlmsg_free(msg);
+}
+
+void cfg80211_ch_switch_notify(struct net_device *dev,
+ struct cfg80211_chan_def *chandef)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct wiphy *wiphy = wdev->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+
+ ASSERT_WDEV_LOCK(wdev);
+
+ trace_cfg80211_ch_switch_notify(dev, chandef);
+
+ wdev->chandef = *chandef;
+ wdev->preset_chandef = *chandef;
+ nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL,
+ NL80211_CMD_CH_SWITCH_NOTIFY, 0);
+}
+EXPORT_SYMBOL(cfg80211_ch_switch_notify);
+
+void cfg80211_ch_switch_started_notify(struct net_device *dev,
+ struct cfg80211_chan_def *chandef,
+ u8 count)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct wiphy *wiphy = wdev->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+
+ trace_cfg80211_ch_switch_started_notify(dev, chandef);
+
+ nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL,
+ NL80211_CMD_CH_SWITCH_STARTED_NOTIFY, count);
+}
+EXPORT_SYMBOL(cfg80211_ch_switch_started_notify);
+
+void
+nl80211_radar_notify(struct cfg80211_registered_device *rdev,
+ const struct cfg80211_chan_def *chandef,
+ enum nl80211_radar_event event,
+ struct net_device *netdev, gfp_t gfp)
+{
+ struct sk_buff *msg;
+ void *hdr;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
+ if (!msg)
+ return;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_RADAR_DETECT);
+ if (!hdr) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx))
+ goto nla_put_failure;
+
+ /* NOP and radar events don't need a netdev parameter */
+ if (netdev) {
+ struct wireless_dev *wdev = netdev->ieee80211_ptr;
+
+ if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
+ nla_put_u64_64bit(msg, NL80211_ATTR_WDEV, wdev_id(wdev),
+ NL80211_ATTR_PAD))
+ goto nla_put_failure;
+ }
+
+ if (nla_put_u32(msg, NL80211_ATTR_RADAR_EVENT, event))
+ goto nla_put_failure;
+
+ if (nl80211_send_chandef(msg, chandef))
+ goto nla_put_failure;
+
+ genlmsg_end(msg, hdr);
+
+ genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+ NL80211_MCGRP_MLME, gfp);
+ return;
+
+ nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ nlmsg_free(msg);
+}
+
+void cfg80211_probe_status(struct net_device *dev, const u8 *addr,
+ u64 cookie, bool acked, gfp_t gfp)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+ struct sk_buff *msg;
+ void *hdr;
+
+ trace_cfg80211_probe_status(dev, addr, cookie, acked);
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
+
+ if (!msg)
+ return;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_PROBE_CLIENT);
+ if (!hdr) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
+ nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
+ nla_put_u64_64bit(msg, NL80211_ATTR_COOKIE, cookie,
+ NL80211_ATTR_PAD) ||
+ (acked && nla_put_flag(msg, NL80211_ATTR_ACK)))
+ goto nla_put_failure;
+
+ genlmsg_end(msg, hdr);
+
+ genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+ NL80211_MCGRP_MLME, gfp);
+ return;
+
+ nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ nlmsg_free(msg);
+}
+EXPORT_SYMBOL(cfg80211_probe_status);
+
+void cfg80211_report_obss_beacon(struct wiphy *wiphy,
+ const u8 *frame, size_t len,
+ int freq, int sig_dbm)
+{
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+ struct sk_buff *msg;
+ void *hdr;
+ struct cfg80211_beacon_registration *reg;
+
+ trace_cfg80211_report_obss_beacon(wiphy, frame, len, freq, sig_dbm);
+
+ spin_lock_bh(&rdev->beacon_registrations_lock);
+ list_for_each_entry(reg, &rdev->beacon_registrations, list) {
+ msg = nlmsg_new(len + 100, GFP_ATOMIC);
+ if (!msg) {
+ spin_unlock_bh(&rdev->beacon_registrations_lock);
+ return;
+ }
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME);
+ if (!hdr)
+ goto nla_put_failure;
+
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
+ (freq &&
+ nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq)) ||
+ (sig_dbm &&
+ nla_put_u32(msg, NL80211_ATTR_RX_SIGNAL_DBM, sig_dbm)) ||
+ nla_put(msg, NL80211_ATTR_FRAME, len, frame))
+ goto nla_put_failure;
+
+ genlmsg_end(msg, hdr);
+
+ genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, reg->nlportid);
+ }
+ spin_unlock_bh(&rdev->beacon_registrations_lock);
+ return;
+
+ nla_put_failure:
+ spin_unlock_bh(&rdev->beacon_registrations_lock);
+ if (hdr)
+ genlmsg_cancel(msg, hdr);
+ nlmsg_free(msg);
+}
+EXPORT_SYMBOL(cfg80211_report_obss_beacon);
+
+#ifdef CONFIG_PM
+static int cfg80211_net_detect_results(struct sk_buff *msg,
+ struct cfg80211_wowlan_wakeup *wakeup)
+{
+ struct cfg80211_wowlan_nd_info *nd = wakeup->net_detect;
+ struct nlattr *nl_results, *nl_match, *nl_freqs;
+ int i, j;
+
+ nl_results = nla_nest_start(
+ msg, NL80211_WOWLAN_TRIG_NET_DETECT_RESULTS);
+ if (!nl_results)
+ return -EMSGSIZE;
+
+ for (i = 0; i < nd->n_matches; i++) {
+ struct cfg80211_wowlan_nd_match *match = nd->matches[i];
+
+ nl_match = nla_nest_start(msg, i);
+ if (!nl_match)
+ break;
+
+ /* The SSID attribute is optional in nl80211, but for
+ * simplicity reasons it's always present in the
+ * cfg80211 structure. If a driver can't pass the
+ * SSID, that needs to be changed. A zero length SSID
+ * is still a valid SSID (wildcard), so it cannot be
+ * used for this purpose.
+ */
+ if (nla_put(msg, NL80211_ATTR_SSID, match->ssid.ssid_len,
+ match->ssid.ssid)) {
+ nla_nest_cancel(msg, nl_match);
+ goto out;
+ }
+
+ if (match->n_channels) {
+ nl_freqs = nla_nest_start(
+ msg, NL80211_ATTR_SCAN_FREQUENCIES);
+ if (!nl_freqs) {
+ nla_nest_cancel(msg, nl_match);
+ goto out;
+ }
+
+ for (j = 0; j < match->n_channels; j++) {
+ if (nla_put_u32(msg, j, match->channels[j])) {
+ nla_nest_cancel(msg, nl_freqs);
+ nla_nest_cancel(msg, nl_match);
+ goto out;
+ }
+ }
+
+ nla_nest_end(msg, nl_freqs);
+ }
+
+ nla_nest_end(msg, nl_match);
+ }
+
+out:
+ nla_nest_end(msg, nl_results);
+ return 0;
+}
+
+void cfg80211_report_wowlan_wakeup(struct wireless_dev *wdev,
+ struct cfg80211_wowlan_wakeup *wakeup,
+ gfp_t gfp)
+{
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+ struct sk_buff *msg;
+ void *hdr;
+ int size = 200;
+
+ trace_cfg80211_report_wowlan_wakeup(wdev->wiphy, wdev, wakeup);
+
+ if (wakeup)
+ size += wakeup->packet_present_len;
+
+ msg = nlmsg_new(size, gfp);
+ if (!msg)
+ return;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_SET_WOWLAN);
+ if (!hdr)
+ goto free_msg;
+
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
+ nla_put_u64_64bit(msg, NL80211_ATTR_WDEV, wdev_id(wdev),
+ NL80211_ATTR_PAD))
+ goto free_msg;
+
+ if (wdev->netdev && nla_put_u32(msg, NL80211_ATTR_IFINDEX,
+ wdev->netdev->ifindex))
+ goto free_msg;
+
+ if (wakeup) {
+ struct nlattr *reasons;
+
+ reasons = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS);
+ if (!reasons)
+ goto free_msg;
+
+ if (wakeup->disconnect &&
+ nla_put_flag(msg, NL80211_WOWLAN_TRIG_DISCONNECT))
+ goto free_msg;
+ if (wakeup->magic_pkt &&
+ nla_put_flag(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT))
+ goto free_msg;
+ if (wakeup->gtk_rekey_failure &&
+ nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE))
+ goto free_msg;
+ if (wakeup->eap_identity_req &&
+ nla_put_flag(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST))
+ goto free_msg;
+ if (wakeup->four_way_handshake &&
+ nla_put_flag(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE))
+ goto free_msg;
+ if (wakeup->rfkill_release &&
+ nla_put_flag(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE))
+ goto free_msg;
+
+ if (wakeup->pattern_idx >= 0 &&
+ nla_put_u32(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN,
+ wakeup->pattern_idx))
+ goto free_msg;
+
+ if (wakeup->tcp_match &&
+ nla_put_flag(msg, NL80211_WOWLAN_TRIG_WAKEUP_TCP_MATCH))
+ goto free_msg;
+
+ if (wakeup->tcp_connlost &&
+ nla_put_flag(msg, NL80211_WOWLAN_TRIG_WAKEUP_TCP_CONNLOST))
+ goto free_msg;
+
+ if (wakeup->tcp_nomoretokens &&
+ nla_put_flag(msg,
+ NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS))
+ goto free_msg;
+
+ if (wakeup->packet) {
+ u32 pkt_attr = NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211;
+ u32 len_attr = NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN;
+
+ if (!wakeup->packet_80211) {
+ pkt_attr =
+ NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023;
+ len_attr =
+ NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023_LEN;
+ }
+
+ if (wakeup->packet_len &&
+ nla_put_u32(msg, len_attr, wakeup->packet_len))
+ goto free_msg;
+
+ if (nla_put(msg, pkt_attr, wakeup->packet_present_len,
+ wakeup->packet))
+ goto free_msg;
+ }
+
+ if (wakeup->net_detect &&
+ cfg80211_net_detect_results(msg, wakeup))
+ goto free_msg;
+
+ nla_nest_end(msg, reasons);
+ }
+
+ genlmsg_end(msg, hdr);
+
+ genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+ NL80211_MCGRP_MLME, gfp);
+ return;
+
+ free_msg:
+ nlmsg_free(msg);
+}
+EXPORT_SYMBOL(cfg80211_report_wowlan_wakeup);
+#endif
+
+void cfg80211_tdls_oper_request(struct net_device *dev, const u8 *peer,
+ enum nl80211_tdls_operation oper,
+ u16 reason_code, gfp_t gfp)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+ struct sk_buff *msg;
+ void *hdr;
+
+ trace_cfg80211_tdls_oper_request(wdev->wiphy, dev, peer, oper,
+ reason_code);
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
+ if (!msg)
+ return;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_TDLS_OPER);
+ if (!hdr) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
+ nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
+ nla_put_u8(msg, NL80211_ATTR_TDLS_OPERATION, oper) ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer) ||
+ (reason_code > 0 &&
+ nla_put_u16(msg, NL80211_ATTR_REASON_CODE, reason_code)))
+ goto nla_put_failure;
+
+ genlmsg_end(msg, hdr);
+
+ genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+ NL80211_MCGRP_MLME, gfp);
+ return;
+
+ nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ nlmsg_free(msg);
+}
+EXPORT_SYMBOL(cfg80211_tdls_oper_request);
+
+static int nl80211_netlink_notify(struct notifier_block * nb,
+ unsigned long state,
+ void *_notify)
+{
+ struct netlink_notify *notify = _notify;
+ struct cfg80211_registered_device *rdev;
+ struct wireless_dev *wdev;
+ struct cfg80211_beacon_registration *reg, *tmp;
+
+ if (state != NETLINK_URELEASE || notify->protocol != NETLINK_GENERIC)
+ return NOTIFY_DONE;
+
+ rcu_read_lock();
+
+ list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) {
+ struct cfg80211_sched_scan_request *sched_scan_req;
+
+ list_for_each_entry_rcu(sched_scan_req,
+ &rdev->sched_scan_req_list,
+ list) {
+ if (sched_scan_req->owner_nlportid == netlink_notify_portid(notify)) {
+ sched_scan_req->nl_owner_dead = true;
+ schedule_work(&rdev->sched_scan_stop_wk);
+ }
+ }
+
+ list_for_each_entry_rcu(wdev, &rdev->wiphy.wdev_list, list) {
+ cfg80211_mlme_unregister_socket(wdev,
+ netlink_notify_portid(notify));
+
+ if (wdev->owner_nlportid == netlink_notify_portid(notify)) {
+ wdev->nl_owner_dead = true;
+ schedule_work(&rdev->destroy_work);
+ } else if (wdev->conn_owner_nlportid == netlink_notify_portid(notify)) {
+ schedule_work(&wdev->disconnect_wk);
+ }
+ }
+
+ spin_lock_bh(&rdev->beacon_registrations_lock);
+ list_for_each_entry_safe(reg, tmp, &rdev->beacon_registrations,
+ list) {
+ if (reg->nlportid == netlink_notify_portid(notify)) {
+ list_del(®->list);
+ kfree(reg);
+ break;
+ }
+ }
+ spin_unlock_bh(&rdev->beacon_registrations_lock);
+ }
+
+ rcu_read_unlock();
+
+ /*
+ * It is possible that the user space process that is controlling the
+ * indoor setting disappeared, so notify the regulatory core.
+ */
+ regulatory_netlink_notify(netlink_notify_portid(notify));
+ return NOTIFY_OK;
+}
+
+static struct notifier_block nl80211_netlink_notifier = {
+ .notifier_call = nl80211_netlink_notify,
+};
+
+void cfg80211_ft_event(struct net_device *netdev,
+ struct cfg80211_ft_event_params *ft_event)
+{
+ struct wiphy *wiphy = netdev->ieee80211_ptr->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+ struct sk_buff *msg;
+ void *hdr;
+
+ trace_cfg80211_ft_event(wiphy, netdev, ft_event);
+
+ if (!ft_event->target_ap)
+ return;
+
+ msg = nlmsg_new(100 + ft_event->ric_ies_len, GFP_KERNEL);
+ if (!msg)
+ return;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FT_EVENT);
+ if (!hdr)
+ goto out;
+
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
+ nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, ft_event->target_ap))
+ goto out;
+
+ if (ft_event->ies &&
+ nla_put(msg, NL80211_ATTR_IE, ft_event->ies_len, ft_event->ies))
+ goto out;
+ if (ft_event->ric_ies &&
+ nla_put(msg, NL80211_ATTR_IE_RIC, ft_event->ric_ies_len,
+ ft_event->ric_ies))
+ goto out;
+
+ genlmsg_end(msg, hdr);
+
+ genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+ NL80211_MCGRP_MLME, GFP_KERNEL);
+ return;
+ out:
+ nlmsg_free(msg);
+}
+EXPORT_SYMBOL(cfg80211_ft_event);
+
+void cfg80211_crit_proto_stopped(struct wireless_dev *wdev, gfp_t gfp)
+{
+ struct cfg80211_registered_device *rdev;
+ struct sk_buff *msg;
+ void *hdr;
+ u32 nlportid;
+
+ rdev = wiphy_to_rdev(wdev->wiphy);
+ if (!rdev->crit_proto_nlportid)
+ return;
+
+ nlportid = rdev->crit_proto_nlportid;
+ rdev->crit_proto_nlportid = 0;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
+ if (!msg)
+ return;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_CRIT_PROTOCOL_STOP);
+ if (!hdr)
+ goto nla_put_failure;
+
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
+ nla_put_u64_64bit(msg, NL80211_ATTR_WDEV, wdev_id(wdev),
+ NL80211_ATTR_PAD))
+ goto nla_put_failure;
+
+ genlmsg_end(msg, hdr);
+
+ genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlportid);
+ return;
+
+ nla_put_failure:
+ if (hdr)
+ genlmsg_cancel(msg, hdr);
+ nlmsg_free(msg);
+}
+EXPORT_SYMBOL(cfg80211_crit_proto_stopped);
+
+void nl80211_send_ap_stopped(struct wireless_dev *wdev)
+{
+ struct wiphy *wiphy = wdev->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+ struct sk_buff *msg;
+ void *hdr;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg)
+ return;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_STOP_AP);
+ if (!hdr)
+ goto out;
+
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
+ nla_put_u32(msg, NL80211_ATTR_IFINDEX, wdev->netdev->ifindex) ||
+ nla_put_u64_64bit(msg, NL80211_ATTR_WDEV, wdev_id(wdev),
+ NL80211_ATTR_PAD))
+ goto out;
+
+ genlmsg_end(msg, hdr);
+
+ genlmsg_multicast_netns(&nl80211_fam, wiphy_net(wiphy), msg, 0,
+ NL80211_MCGRP_MLME, GFP_KERNEL);
+ return;
+ out:
+ nlmsg_free(msg);
+}
+
+/* initialisation/exit functions */
+
+int __init nl80211_init(void)
+{
+ int err;
+
+ err = genl_register_family(&nl80211_fam);
+ if (err)
+ return err;
+
+ err = netlink_register_notifier(&nl80211_netlink_notifier);
+ if (err)
+ goto err_out;
+
+ return 0;
+ err_out:
+ genl_unregister_family(&nl80211_fam);
+ return err;
+}
+
+void nl80211_exit(void)
+{
+ netlink_unregister_notifier(&nl80211_netlink_notifier);
+ genl_unregister_family(&nl80211_fam);
+}
diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h
new file mode 100644
index 0000000..b969333
--- /dev/null
+++ b/net/wireless/nl80211.h
@@ -0,0 +1,95 @@
+#ifndef __NET_WIRELESS_NL80211_H
+#define __NET_WIRELESS_NL80211_H
+
+#include "core.h"
+
+int nl80211_init(void);
+void nl80211_exit(void);
+void nl80211_notify_wiphy(struct cfg80211_registered_device *rdev,
+ enum nl80211_commands cmd);
+void nl80211_notify_iface(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev,
+ enum nl80211_commands cmd);
+void nl80211_send_scan_start(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev);
+struct sk_buff *nl80211_build_scan_msg(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev, bool aborted);
+void nl80211_send_scan_msg(struct cfg80211_registered_device *rdev,
+ struct sk_buff *msg);
+void nl80211_send_sched_scan(struct cfg80211_sched_scan_request *req, u32 cmd);
+void nl80211_common_reg_change_event(enum nl80211_commands cmd_id,
+ struct regulatory_request *request);
+
+static inline void
+nl80211_send_reg_change_event(struct regulatory_request *request)
+{
+ nl80211_common_reg_change_event(NL80211_CMD_REG_CHANGE, request);
+}
+
+static inline void
+nl80211_send_wiphy_reg_change_event(struct regulatory_request *request)
+{
+ nl80211_common_reg_change_event(NL80211_CMD_WIPHY_REG_CHANGE, request);
+}
+
+void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev,
+ const u8 *buf, size_t len, gfp_t gfp);
+void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev,
+ const u8 *buf, size_t len, gfp_t gfp,
+ int uapsd_queues);
+void nl80211_send_deauth(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev,
+ const u8 *buf, size_t len, gfp_t gfp);
+void nl80211_send_disassoc(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev,
+ const u8 *buf, size_t len, gfp_t gfp);
+void nl80211_send_auth_timeout(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev,
+ const u8 *addr, gfp_t gfp);
+void nl80211_send_assoc_timeout(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev,
+ const u8 *addr, gfp_t gfp);
+void nl80211_send_connect_result(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev,
+ struct cfg80211_connect_resp_params *params,
+ gfp_t gfp);
+void nl80211_send_roamed(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev,
+ struct cfg80211_roam_info *info, gfp_t gfp);
+void nl80211_send_disconnected(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev, u16 reason,
+ const u8 *ie, size_t ie_len, bool from_ap);
+
+void
+nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev, const u8 *addr,
+ enum nl80211_key_type key_type,
+ int key_id, const u8 *tsc, gfp_t gfp);
+
+void
+nl80211_send_beacon_hint_event(struct wiphy *wiphy,
+ struct ieee80211_channel *channel_before,
+ struct ieee80211_channel *channel_after);
+
+void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev, const u8 *bssid,
+ gfp_t gfp);
+
+int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev, u32 nlpid,
+ int freq, int sig_dbm,
+ const u8 *buf, size_t len, u32 flags, gfp_t gfp);
+
+void
+nl80211_radar_notify(struct cfg80211_registered_device *rdev,
+ const struct cfg80211_chan_def *chandef,
+ enum nl80211_radar_event event,
+ struct net_device *netdev, gfp_t gfp);
+
+void nl80211_send_ap_stopped(struct wireless_dev *wdev);
+
+void cfg80211_rdev_free_coalesce(struct cfg80211_registered_device *rdev);
+
+#endif /* __NET_WIRELESS_NL80211_H */
diff --git a/net/wireless/ocb.c b/net/wireless/ocb.c
new file mode 100644
index 0000000..e64dbf1
--- /dev/null
+++ b/net/wireless/ocb.c
@@ -0,0 +1,91 @@
+/*
+ * OCB mode implementation
+ *
+ * Copyright: (c) 2014 Czech Technical University in Prague
+ * (c) 2014 Volkswagen Group Research
+ * Author: Rostislav Lisovy <rostislav.lisovy@fel.cvut.cz>
+ * Funded by: Volkswagen Group Research
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/ieee80211.h>
+#include <net/cfg80211.h>
+#include "nl80211.h"
+#include "core.h"
+#include "rdev-ops.h"
+
+int __cfg80211_join_ocb(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct ocb_setup *setup)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ int err;
+
+ ASSERT_WDEV_LOCK(wdev);
+
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_OCB)
+ return -EOPNOTSUPP;
+
+ if (!rdev->ops->join_ocb)
+ return -EOPNOTSUPP;
+
+ if (WARN_ON(!setup->chandef.chan))
+ return -EINVAL;
+
+ err = rdev_join_ocb(rdev, dev, setup);
+ if (!err)
+ wdev->chandef = setup->chandef;
+
+ return err;
+}
+
+int cfg80211_join_ocb(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct ocb_setup *setup)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ int err;
+
+ wdev_lock(wdev);
+ err = __cfg80211_join_ocb(rdev, dev, setup);
+ wdev_unlock(wdev);
+
+ return err;
+}
+
+int __cfg80211_leave_ocb(struct cfg80211_registered_device *rdev,
+ struct net_device *dev)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ int err;
+
+ ASSERT_WDEV_LOCK(wdev);
+
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_OCB)
+ return -EOPNOTSUPP;
+
+ if (!rdev->ops->leave_ocb)
+ return -EOPNOTSUPP;
+
+ err = rdev_leave_ocb(rdev, dev);
+ if (!err)
+ memset(&wdev->chandef, 0, sizeof(wdev->chandef));
+
+ return err;
+}
+
+int cfg80211_leave_ocb(struct cfg80211_registered_device *rdev,
+ struct net_device *dev)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ int err;
+
+ wdev_lock(wdev);
+ err = __cfg80211_leave_ocb(rdev, dev);
+ wdev_unlock(wdev);
+
+ return err;
+}
diff --git a/net/wireless/of.c b/net/wireless/of.c
new file mode 100644
index 0000000..de221f0
--- /dev/null
+++ b/net/wireless/of.c
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2017 Rafał Miłecki <rafal@milecki.pl>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/of.h>
+#include <net/cfg80211.h>
+#include "core.h"
+
+static bool wiphy_freq_limits_valid_chan(struct wiphy *wiphy,
+ struct ieee80211_freq_range *freq_limits,
+ unsigned int n_freq_limits,
+ struct ieee80211_channel *chan)
+{
+ u32 bw = MHZ_TO_KHZ(20);
+ int i;
+
+ for (i = 0; i < n_freq_limits; i++) {
+ struct ieee80211_freq_range *limit = &freq_limits[i];
+
+ if (cfg80211_does_bw_fit_range(limit,
+ MHZ_TO_KHZ(chan->center_freq),
+ bw))
+ return true;
+ }
+
+ return false;
+}
+
+static void wiphy_freq_limits_apply(struct wiphy *wiphy,
+ struct ieee80211_freq_range *freq_limits,
+ unsigned int n_freq_limits)
+{
+ enum nl80211_band band;
+ int i;
+
+ if (WARN_ON(!n_freq_limits))
+ return;
+
+ for (band = 0; band < NUM_NL80211_BANDS; band++) {
+ struct ieee80211_supported_band *sband = wiphy->bands[band];
+
+ if (!sband)
+ continue;
+
+ for (i = 0; i < sband->n_channels; i++) {
+ struct ieee80211_channel *chan = &sband->channels[i];
+
+ if (chan->flags & IEEE80211_CHAN_DISABLED)
+ continue;
+
+ if (!wiphy_freq_limits_valid_chan(wiphy, freq_limits,
+ n_freq_limits,
+ chan)) {
+ pr_debug("Disabling freq %d MHz as it's out of OF limits\n",
+ chan->center_freq);
+ chan->flags |= IEEE80211_CHAN_DISABLED;
+ }
+ }
+ }
+}
+
+void wiphy_read_of_freq_limits(struct wiphy *wiphy)
+{
+ struct device *dev = wiphy_dev(wiphy);
+ struct device_node *np;
+ struct property *prop;
+ struct ieee80211_freq_range *freq_limits;
+ unsigned int n_freq_limits;
+ const __be32 *p;
+ int len, i;
+ int err = 0;
+
+ if (!dev)
+ return;
+ np = dev_of_node(dev);
+ if (!np)
+ return;
+
+ prop = of_find_property(np, "ieee80211-freq-limit", &len);
+ if (!prop)
+ return;
+
+ if (!len || len % sizeof(u32) || len / sizeof(u32) % 2) {
+ dev_err(dev, "ieee80211-freq-limit wrong format");
+ return;
+ }
+ n_freq_limits = len / sizeof(u32) / 2;
+
+ freq_limits = kcalloc(n_freq_limits, sizeof(*freq_limits), GFP_KERNEL);
+ if (!freq_limits) {
+ err = -ENOMEM;
+ goto out_kfree;
+ }
+
+ p = NULL;
+ for (i = 0; i < n_freq_limits; i++) {
+ struct ieee80211_freq_range *limit = &freq_limits[i];
+
+ p = of_prop_next_u32(prop, p, &limit->start_freq_khz);
+ if (!p) {
+ err = -EINVAL;
+ goto out_kfree;
+ }
+
+ p = of_prop_next_u32(prop, p, &limit->end_freq_khz);
+ if (!p) {
+ err = -EINVAL;
+ goto out_kfree;
+ }
+
+ if (!limit->start_freq_khz ||
+ !limit->end_freq_khz ||
+ limit->start_freq_khz >= limit->end_freq_khz) {
+ err = -EINVAL;
+ goto out_kfree;
+ }
+ }
+
+ wiphy_freq_limits_apply(wiphy, freq_limits, n_freq_limits);
+
+out_kfree:
+ kfree(freq_limits);
+ if (err)
+ dev_err(dev, "Failed to get limits: %d\n", err);
+}
+EXPORT_SYMBOL(wiphy_read_of_freq_limits);
diff --git a/net/wireless/radiotap.c b/net/wireless/radiotap.c
new file mode 100644
index 0000000..6582d15
--- /dev/null
+++ b/net/wireless/radiotap.c
@@ -0,0 +1,370 @@
+/*
+ * Radiotap parser
+ *
+ * Copyright 2007 Andy Green <andy@warmcat.com>
+ * Copyright 2009 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See COPYING for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/export.h>
+#include <net/cfg80211.h>
+#include <net/ieee80211_radiotap.h>
+#include <asm/unaligned.h>
+
+/* function prototypes and related defs are in include/net/cfg80211.h */
+
+static const struct radiotap_align_size rtap_namespace_sizes[] = {
+ [IEEE80211_RADIOTAP_TSFT] = { .align = 8, .size = 8, },
+ [IEEE80211_RADIOTAP_FLAGS] = { .align = 1, .size = 1, },
+ [IEEE80211_RADIOTAP_RATE] = { .align = 1, .size = 1, },
+ [IEEE80211_RADIOTAP_CHANNEL] = { .align = 2, .size = 4, },
+ [IEEE80211_RADIOTAP_FHSS] = { .align = 2, .size = 2, },
+ [IEEE80211_RADIOTAP_DBM_ANTSIGNAL] = { .align = 1, .size = 1, },
+ [IEEE80211_RADIOTAP_DBM_ANTNOISE] = { .align = 1, .size = 1, },
+ [IEEE80211_RADIOTAP_LOCK_QUALITY] = { .align = 2, .size = 2, },
+ [IEEE80211_RADIOTAP_TX_ATTENUATION] = { .align = 2, .size = 2, },
+ [IEEE80211_RADIOTAP_DB_TX_ATTENUATION] = { .align = 2, .size = 2, },
+ [IEEE80211_RADIOTAP_DBM_TX_POWER] = { .align = 1, .size = 1, },
+ [IEEE80211_RADIOTAP_ANTENNA] = { .align = 1, .size = 1, },
+ [IEEE80211_RADIOTAP_DB_ANTSIGNAL] = { .align = 1, .size = 1, },
+ [IEEE80211_RADIOTAP_DB_ANTNOISE] = { .align = 1, .size = 1, },
+ [IEEE80211_RADIOTAP_RX_FLAGS] = { .align = 2, .size = 2, },
+ [IEEE80211_RADIOTAP_TX_FLAGS] = { .align = 2, .size = 2, },
+ [IEEE80211_RADIOTAP_RTS_RETRIES] = { .align = 1, .size = 1, },
+ [IEEE80211_RADIOTAP_DATA_RETRIES] = { .align = 1, .size = 1, },
+ [IEEE80211_RADIOTAP_MCS] = { .align = 1, .size = 3, },
+ [IEEE80211_RADIOTAP_AMPDU_STATUS] = { .align = 4, .size = 8, },
+ [IEEE80211_RADIOTAP_VHT] = { .align = 2, .size = 12, },
+ /*
+ * add more here as they are defined in radiotap.h
+ */
+};
+
+static const struct ieee80211_radiotap_namespace radiotap_ns = {
+ .n_bits = ARRAY_SIZE(rtap_namespace_sizes),
+ .align_size = rtap_namespace_sizes,
+};
+
+/**
+ * ieee80211_radiotap_iterator_init - radiotap parser iterator initialization
+ * @iterator: radiotap_iterator to initialize
+ * @radiotap_header: radiotap header to parse
+ * @max_length: total length we can parse into (eg, whole packet length)
+ *
+ * Returns: 0 or a negative error code if there is a problem.
+ *
+ * This function initializes an opaque iterator struct which can then
+ * be passed to ieee80211_radiotap_iterator_next() to visit every radiotap
+ * argument which is present in the header. It knows about extended
+ * present headers and handles them.
+ *
+ * How to use:
+ * call __ieee80211_radiotap_iterator_init() to init a semi-opaque iterator
+ * struct ieee80211_radiotap_iterator (no need to init the struct beforehand)
+ * checking for a good 0 return code. Then loop calling
+ * __ieee80211_radiotap_iterator_next()... it returns either 0,
+ * -ENOENT if there are no more args to parse, or -EINVAL if there is a problem.
+ * The iterator's @this_arg member points to the start of the argument
+ * associated with the current argument index that is present, which can be
+ * found in the iterator's @this_arg_index member. This arg index corresponds
+ * to the IEEE80211_RADIOTAP_... defines.
+ *
+ * Radiotap header length:
+ * You can find the CPU-endian total radiotap header length in
+ * iterator->max_length after executing ieee80211_radiotap_iterator_init()
+ * successfully.
+ *
+ * Alignment Gotcha:
+ * You must take care when dereferencing iterator.this_arg
+ * for multibyte types... the pointer is not aligned. Use
+ * get_unaligned((type *)iterator.this_arg) to dereference
+ * iterator.this_arg for type "type" safely on all arches.
+ *
+ * Example code:
+ * See Documentation/networking/radiotap-headers.txt
+ */
+
+int ieee80211_radiotap_iterator_init(
+ struct ieee80211_radiotap_iterator *iterator,
+ struct ieee80211_radiotap_header *radiotap_header,
+ int max_length, const struct ieee80211_radiotap_vendor_namespaces *vns)
+{
+ /* check the radiotap header can actually be present */
+ if (max_length < sizeof(struct ieee80211_radiotap_header))
+ return -EINVAL;
+
+ /* Linux only supports version 0 radiotap format */
+ if (radiotap_header->it_version)
+ return -EINVAL;
+
+ /* sanity check for allowed length and radiotap length field */
+ if (max_length < get_unaligned_le16(&radiotap_header->it_len))
+ return -EINVAL;
+
+ iterator->_rtheader = radiotap_header;
+ iterator->_max_length = get_unaligned_le16(&radiotap_header->it_len);
+ iterator->_arg_index = 0;
+ iterator->_bitmap_shifter = get_unaligned_le32(&radiotap_header->it_present);
+ iterator->_arg = (uint8_t *)radiotap_header + sizeof(*radiotap_header);
+ iterator->_reset_on_ext = 0;
+ iterator->_next_bitmap = &radiotap_header->it_present;
+ iterator->_next_bitmap++;
+ iterator->_vns = vns;
+ iterator->current_namespace = &radiotap_ns;
+ iterator->is_radiotap_ns = 1;
+
+ /* find payload start allowing for extended bitmap(s) */
+
+ if (iterator->_bitmap_shifter & (1<<IEEE80211_RADIOTAP_EXT)) {
+ if ((unsigned long)iterator->_arg -
+ (unsigned long)iterator->_rtheader + sizeof(uint32_t) >
+ (unsigned long)iterator->_max_length)
+ return -EINVAL;
+ while (get_unaligned_le32(iterator->_arg) &
+ (1 << IEEE80211_RADIOTAP_EXT)) {
+ iterator->_arg += sizeof(uint32_t);
+
+ /*
+ * check for insanity where the present bitmaps
+ * keep claiming to extend up to or even beyond the
+ * stated radiotap header length
+ */
+
+ if ((unsigned long)iterator->_arg -
+ (unsigned long)iterator->_rtheader +
+ sizeof(uint32_t) >
+ (unsigned long)iterator->_max_length)
+ return -EINVAL;
+ }
+
+ iterator->_arg += sizeof(uint32_t);
+
+ /*
+ * no need to check again for blowing past stated radiotap
+ * header length, because ieee80211_radiotap_iterator_next
+ * checks it before it is dereferenced
+ */
+ }
+
+ iterator->this_arg = iterator->_arg;
+
+ /* we are all initialized happily */
+
+ return 0;
+}
+EXPORT_SYMBOL(ieee80211_radiotap_iterator_init);
+
+static void find_ns(struct ieee80211_radiotap_iterator *iterator,
+ uint32_t oui, uint8_t subns)
+{
+ int i;
+
+ iterator->current_namespace = NULL;
+
+ if (!iterator->_vns)
+ return;
+
+ for (i = 0; i < iterator->_vns->n_ns; i++) {
+ if (iterator->_vns->ns[i].oui != oui)
+ continue;
+ if (iterator->_vns->ns[i].subns != subns)
+ continue;
+
+ iterator->current_namespace = &iterator->_vns->ns[i];
+ break;
+ }
+}
+
+
+
+/**
+ * ieee80211_radiotap_iterator_next - return next radiotap parser iterator arg
+ * @iterator: radiotap_iterator to move to next arg (if any)
+ *
+ * Returns: 0 if there is an argument to handle,
+ * -ENOENT if there are no more args or -EINVAL
+ * if there is something else wrong.
+ *
+ * This function provides the next radiotap arg index (IEEE80211_RADIOTAP_*)
+ * in @this_arg_index and sets @this_arg to point to the
+ * payload for the field. It takes care of alignment handling and extended
+ * present fields. @this_arg can be changed by the caller (eg,
+ * incremented to move inside a compound argument like
+ * IEEE80211_RADIOTAP_CHANNEL). The args pointed to are in
+ * little-endian format whatever the endianess of your CPU.
+ *
+ * Alignment Gotcha:
+ * You must take care when dereferencing iterator.this_arg
+ * for multibyte types... the pointer is not aligned. Use
+ * get_unaligned((type *)iterator.this_arg) to dereference
+ * iterator.this_arg for type "type" safely on all arches.
+ */
+
+int ieee80211_radiotap_iterator_next(
+ struct ieee80211_radiotap_iterator *iterator)
+{
+ while (1) {
+ int hit = 0;
+ int pad, align, size, subns;
+ uint32_t oui;
+
+ /* if no more EXT bits, that's it */
+ if ((iterator->_arg_index % 32) == IEEE80211_RADIOTAP_EXT &&
+ !(iterator->_bitmap_shifter & 1))
+ return -ENOENT;
+
+ if (!(iterator->_bitmap_shifter & 1))
+ goto next_entry; /* arg not present */
+
+ /* get alignment/size of data */
+ switch (iterator->_arg_index % 32) {
+ case IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE:
+ case IEEE80211_RADIOTAP_EXT:
+ align = 1;
+ size = 0;
+ break;
+ case IEEE80211_RADIOTAP_VENDOR_NAMESPACE:
+ align = 2;
+ size = 6;
+ break;
+ default:
+ if (!iterator->current_namespace ||
+ iterator->_arg_index >= iterator->current_namespace->n_bits) {
+ if (iterator->current_namespace == &radiotap_ns)
+ return -ENOENT;
+ align = 0;
+ } else {
+ align = iterator->current_namespace->align_size[iterator->_arg_index].align;
+ size = iterator->current_namespace->align_size[iterator->_arg_index].size;
+ }
+ if (!align) {
+ /* skip all subsequent data */
+ iterator->_arg = iterator->_next_ns_data;
+ /* give up on this namespace */
+ iterator->current_namespace = NULL;
+ goto next_entry;
+ }
+ break;
+ }
+
+ /*
+ * arg is present, account for alignment padding
+ *
+ * Note that these alignments are relative to the start
+ * of the radiotap header. There is no guarantee
+ * that the radiotap header itself is aligned on any
+ * kind of boundary.
+ *
+ * The above is why get_unaligned() is used to dereference
+ * multibyte elements from the radiotap area.
+ */
+
+ pad = ((unsigned long)iterator->_arg -
+ (unsigned long)iterator->_rtheader) & (align - 1);
+
+ if (pad)
+ iterator->_arg += align - pad;
+
+ if (iterator->_arg_index % 32 == IEEE80211_RADIOTAP_VENDOR_NAMESPACE) {
+ int vnslen;
+
+ if ((unsigned long)iterator->_arg + size -
+ (unsigned long)iterator->_rtheader >
+ (unsigned long)iterator->_max_length)
+ return -EINVAL;
+
+ oui = (*iterator->_arg << 16) |
+ (*(iterator->_arg + 1) << 8) |
+ *(iterator->_arg + 2);
+ subns = *(iterator->_arg + 3);
+
+ find_ns(iterator, oui, subns);
+
+ vnslen = get_unaligned_le16(iterator->_arg + 4);
+ iterator->_next_ns_data = iterator->_arg + size + vnslen;
+ if (!iterator->current_namespace)
+ size += vnslen;
+ }
+
+ /*
+ * this is what we will return to user, but we need to
+ * move on first so next call has something fresh to test
+ */
+ iterator->this_arg_index = iterator->_arg_index;
+ iterator->this_arg = iterator->_arg;
+ iterator->this_arg_size = size;
+
+ /* internally move on the size of this arg */
+ iterator->_arg += size;
+
+ /*
+ * check for insanity where we are given a bitmap that
+ * claims to have more arg content than the length of the
+ * radiotap section. We will normally end up equalling this
+ * max_length on the last arg, never exceeding it.
+ */
+
+ if ((unsigned long)iterator->_arg -
+ (unsigned long)iterator->_rtheader >
+ (unsigned long)iterator->_max_length)
+ return -EINVAL;
+
+ /* these special ones are valid in each bitmap word */
+ switch (iterator->_arg_index % 32) {
+ case IEEE80211_RADIOTAP_VENDOR_NAMESPACE:
+ iterator->_reset_on_ext = 1;
+
+ iterator->is_radiotap_ns = 0;
+ /*
+ * If parser didn't register this vendor
+ * namespace with us, allow it to show it
+ * as 'raw. Do do that, set argument index
+ * to vendor namespace.
+ */
+ iterator->this_arg_index =
+ IEEE80211_RADIOTAP_VENDOR_NAMESPACE;
+ if (!iterator->current_namespace)
+ hit = 1;
+ goto next_entry;
+ case IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE:
+ iterator->_reset_on_ext = 1;
+ iterator->current_namespace = &radiotap_ns;
+ iterator->is_radiotap_ns = 1;
+ goto next_entry;
+ case IEEE80211_RADIOTAP_EXT:
+ /*
+ * bit 31 was set, there is more
+ * -- move to next u32 bitmap
+ */
+ iterator->_bitmap_shifter =
+ get_unaligned_le32(iterator->_next_bitmap);
+ iterator->_next_bitmap++;
+ if (iterator->_reset_on_ext)
+ iterator->_arg_index = 0;
+ else
+ iterator->_arg_index++;
+ iterator->_reset_on_ext = 0;
+ break;
+ default:
+ /* we've got a hit! */
+ hit = 1;
+ next_entry:
+ iterator->_bitmap_shifter >>= 1;
+ iterator->_arg_index++;
+ }
+
+ /* if we found a valid arg earlier, return it now */
+ if (hit)
+ return 0;
+ }
+}
+EXPORT_SYMBOL(ieee80211_radiotap_iterator_next);
diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
new file mode 100644
index 0000000..60d93d2
--- /dev/null
+++ b/net/wireless/rdev-ops.h
@@ -0,0 +1,1192 @@
+#ifndef __CFG80211_RDEV_OPS
+#define __CFG80211_RDEV_OPS
+
+#include <linux/rtnetlink.h>
+#include <net/cfg80211.h>
+#include "core.h"
+#include "trace.h"
+
+static inline int rdev_suspend(struct cfg80211_registered_device *rdev,
+ struct cfg80211_wowlan *wowlan)
+{
+ int ret;
+ trace_rdev_suspend(&rdev->wiphy, wowlan);
+ ret = rdev->ops->suspend(&rdev->wiphy, wowlan);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_resume(struct cfg80211_registered_device *rdev)
+{
+ int ret;
+ trace_rdev_resume(&rdev->wiphy);
+ ret = rdev->ops->resume(&rdev->wiphy);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline void rdev_set_wakeup(struct cfg80211_registered_device *rdev,
+ bool enabled)
+{
+ trace_rdev_set_wakeup(&rdev->wiphy, enabled);
+ rdev->ops->set_wakeup(&rdev->wiphy, enabled);
+ trace_rdev_return_void(&rdev->wiphy);
+}
+
+static inline struct wireless_dev
+*rdev_add_virtual_intf(struct cfg80211_registered_device *rdev, char *name,
+ unsigned char name_assign_type,
+ enum nl80211_iftype type,
+ struct vif_params *params)
+{
+ struct wireless_dev *ret;
+ trace_rdev_add_virtual_intf(&rdev->wiphy, name, type);
+ ret = rdev->ops->add_virtual_intf(&rdev->wiphy, name, name_assign_type,
+ type, params);
+ trace_rdev_return_wdev(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int
+rdev_del_virtual_intf(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev)
+{
+ int ret;
+ trace_rdev_del_virtual_intf(&rdev->wiphy, wdev);
+ ret = rdev->ops->del_virtual_intf(&rdev->wiphy, wdev);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int
+rdev_change_virtual_intf(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, enum nl80211_iftype type,
+ struct vif_params *params)
+{
+ int ret;
+ trace_rdev_change_virtual_intf(&rdev->wiphy, dev, type);
+ ret = rdev->ops->change_virtual_intf(&rdev->wiphy, dev, type, params);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_add_key(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev, u8 key_index,
+ bool pairwise, const u8 *mac_addr,
+ struct key_params *params)
+{
+ int ret;
+ trace_rdev_add_key(&rdev->wiphy, netdev, key_index, pairwise, mac_addr);
+ ret = rdev->ops->add_key(&rdev->wiphy, netdev, key_index, pairwise,
+ mac_addr, params);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int
+rdev_get_key(struct cfg80211_registered_device *rdev, struct net_device *netdev,
+ u8 key_index, bool pairwise, const u8 *mac_addr, void *cookie,
+ void (*callback)(void *cookie, struct key_params*))
+{
+ int ret;
+ trace_rdev_get_key(&rdev->wiphy, netdev, key_index, pairwise, mac_addr);
+ ret = rdev->ops->get_key(&rdev->wiphy, netdev, key_index, pairwise,
+ mac_addr, cookie, callback);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_del_key(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev, u8 key_index,
+ bool pairwise, const u8 *mac_addr)
+{
+ int ret;
+ trace_rdev_del_key(&rdev->wiphy, netdev, key_index, pairwise, mac_addr);
+ ret = rdev->ops->del_key(&rdev->wiphy, netdev, key_index, pairwise,
+ mac_addr);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int
+rdev_set_default_key(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev, u8 key_index, bool unicast,
+ bool multicast)
+{
+ int ret;
+ trace_rdev_set_default_key(&rdev->wiphy, netdev, key_index,
+ unicast, multicast);
+ ret = rdev->ops->set_default_key(&rdev->wiphy, netdev, key_index,
+ unicast, multicast);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int
+rdev_set_default_mgmt_key(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev, u8 key_index)
+{
+ int ret;
+ trace_rdev_set_default_mgmt_key(&rdev->wiphy, netdev, key_index);
+ ret = rdev->ops->set_default_mgmt_key(&rdev->wiphy, netdev,
+ key_index);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_start_ap(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct cfg80211_ap_settings *settings)
+{
+ int ret;
+ trace_rdev_start_ap(&rdev->wiphy, dev, settings);
+ ret = rdev->ops->start_ap(&rdev->wiphy, dev, settings);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_change_beacon(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct cfg80211_beacon_data *info)
+{
+ int ret;
+ trace_rdev_change_beacon(&rdev->wiphy, dev, info);
+ ret = rdev->ops->change_beacon(&rdev->wiphy, dev, info);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_stop_ap(struct cfg80211_registered_device *rdev,
+ struct net_device *dev)
+{
+ int ret;
+ trace_rdev_stop_ap(&rdev->wiphy, dev);
+ ret = rdev->ops->stop_ap(&rdev->wiphy, dev);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_add_station(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, u8 *mac,
+ struct station_parameters *params)
+{
+ int ret;
+ trace_rdev_add_station(&rdev->wiphy, dev, mac, params);
+ ret = rdev->ops->add_station(&rdev->wiphy, dev, mac, params);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_del_station(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct station_del_parameters *params)
+{
+ int ret;
+ trace_rdev_del_station(&rdev->wiphy, dev, params);
+ ret = rdev->ops->del_station(&rdev->wiphy, dev, params);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_change_station(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, u8 *mac,
+ struct station_parameters *params)
+{
+ int ret;
+ trace_rdev_change_station(&rdev->wiphy, dev, mac, params);
+ ret = rdev->ops->change_station(&rdev->wiphy, dev, mac, params);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_get_station(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, const u8 *mac,
+ struct station_info *sinfo)
+{
+ int ret;
+ trace_rdev_get_station(&rdev->wiphy, dev, mac);
+ ret = rdev->ops->get_station(&rdev->wiphy, dev, mac, sinfo);
+ trace_rdev_return_int_station_info(&rdev->wiphy, ret, sinfo);
+ return ret;
+}
+
+static inline int rdev_dump_station(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, int idx, u8 *mac,
+ struct station_info *sinfo)
+{
+ int ret;
+ trace_rdev_dump_station(&rdev->wiphy, dev, idx, mac);
+ ret = rdev->ops->dump_station(&rdev->wiphy, dev, idx, mac, sinfo);
+ trace_rdev_return_int_station_info(&rdev->wiphy, ret, sinfo);
+ return ret;
+}
+
+static inline int rdev_add_mpath(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, u8 *dst, u8 *next_hop)
+{
+ int ret;
+ trace_rdev_add_mpath(&rdev->wiphy, dev, dst, next_hop);
+ ret = rdev->ops->add_mpath(&rdev->wiphy, dev, dst, next_hop);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_del_mpath(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, u8 *dst)
+{
+ int ret;
+ trace_rdev_del_mpath(&rdev->wiphy, dev, dst);
+ ret = rdev->ops->del_mpath(&rdev->wiphy, dev, dst);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_change_mpath(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, u8 *dst,
+ u8 *next_hop)
+{
+ int ret;
+ trace_rdev_change_mpath(&rdev->wiphy, dev, dst, next_hop);
+ ret = rdev->ops->change_mpath(&rdev->wiphy, dev, dst, next_hop);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_get_mpath(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, u8 *dst, u8 *next_hop,
+ struct mpath_info *pinfo)
+{
+ int ret;
+ trace_rdev_get_mpath(&rdev->wiphy, dev, dst, next_hop);
+ ret = rdev->ops->get_mpath(&rdev->wiphy, dev, dst, next_hop, pinfo);
+ trace_rdev_return_int_mpath_info(&rdev->wiphy, ret, pinfo);
+ return ret;
+
+}
+
+static inline int rdev_get_mpp(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, u8 *dst, u8 *mpp,
+ struct mpath_info *pinfo)
+{
+ int ret;
+
+ trace_rdev_get_mpp(&rdev->wiphy, dev, dst, mpp);
+ ret = rdev->ops->get_mpp(&rdev->wiphy, dev, dst, mpp, pinfo);
+ trace_rdev_return_int_mpath_info(&rdev->wiphy, ret, pinfo);
+ return ret;
+}
+
+static inline int rdev_dump_mpath(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, int idx, u8 *dst,
+ u8 *next_hop, struct mpath_info *pinfo)
+
+{
+ int ret;
+ trace_rdev_dump_mpath(&rdev->wiphy, dev, idx, dst, next_hop);
+ ret = rdev->ops->dump_mpath(&rdev->wiphy, dev, idx, dst, next_hop,
+ pinfo);
+ trace_rdev_return_int_mpath_info(&rdev->wiphy, ret, pinfo);
+ return ret;
+}
+
+static inline int rdev_dump_mpp(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, int idx, u8 *dst,
+ u8 *mpp, struct mpath_info *pinfo)
+
+{
+ int ret;
+
+ trace_rdev_dump_mpp(&rdev->wiphy, dev, idx, dst, mpp);
+ ret = rdev->ops->dump_mpp(&rdev->wiphy, dev, idx, dst, mpp, pinfo);
+ trace_rdev_return_int_mpath_info(&rdev->wiphy, ret, pinfo);
+ return ret;
+}
+
+static inline int
+rdev_get_mesh_config(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, struct mesh_config *conf)
+{
+ int ret;
+ trace_rdev_get_mesh_config(&rdev->wiphy, dev);
+ ret = rdev->ops->get_mesh_config(&rdev->wiphy, dev, conf);
+ trace_rdev_return_int_mesh_config(&rdev->wiphy, ret, conf);
+ return ret;
+}
+
+static inline int
+rdev_update_mesh_config(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, u32 mask,
+ const struct mesh_config *nconf)
+{
+ int ret;
+ trace_rdev_update_mesh_config(&rdev->wiphy, dev, mask, nconf);
+ ret = rdev->ops->update_mesh_config(&rdev->wiphy, dev, mask, nconf);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_join_mesh(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ const struct mesh_config *conf,
+ const struct mesh_setup *setup)
+{
+ int ret;
+ trace_rdev_join_mesh(&rdev->wiphy, dev, conf, setup);
+ ret = rdev->ops->join_mesh(&rdev->wiphy, dev, conf, setup);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+
+static inline int rdev_leave_mesh(struct cfg80211_registered_device *rdev,
+ struct net_device *dev)
+{
+ int ret;
+ trace_rdev_leave_mesh(&rdev->wiphy, dev);
+ ret = rdev->ops->leave_mesh(&rdev->wiphy, dev);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_join_ocb(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct ocb_setup *setup)
+{
+ int ret;
+ trace_rdev_join_ocb(&rdev->wiphy, dev, setup);
+ ret = rdev->ops->join_ocb(&rdev->wiphy, dev, setup);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_leave_ocb(struct cfg80211_registered_device *rdev,
+ struct net_device *dev)
+{
+ int ret;
+ trace_rdev_leave_ocb(&rdev->wiphy, dev);
+ ret = rdev->ops->leave_ocb(&rdev->wiphy, dev);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_change_bss(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct bss_parameters *params)
+
+{
+ int ret;
+ trace_rdev_change_bss(&rdev->wiphy, dev, params);
+ ret = rdev->ops->change_bss(&rdev->wiphy, dev, params);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_set_txq_params(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct ieee80211_txq_params *params)
+
+{
+ int ret;
+ trace_rdev_set_txq_params(&rdev->wiphy, dev, params);
+ ret = rdev->ops->set_txq_params(&rdev->wiphy, dev, params);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int
+rdev_libertas_set_mesh_channel(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct ieee80211_channel *chan)
+{
+ int ret;
+ trace_rdev_libertas_set_mesh_channel(&rdev->wiphy, dev, chan);
+ ret = rdev->ops->libertas_set_mesh_channel(&rdev->wiphy, dev, chan);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int
+rdev_set_monitor_channel(struct cfg80211_registered_device *rdev,
+ struct cfg80211_chan_def *chandef)
+{
+ int ret;
+ trace_rdev_set_monitor_channel(&rdev->wiphy, chandef);
+ ret = rdev->ops->set_monitor_channel(&rdev->wiphy, chandef);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_scan(struct cfg80211_registered_device *rdev,
+ struct cfg80211_scan_request *request)
+{
+ int ret;
+ trace_rdev_scan(&rdev->wiphy, request);
+ ret = rdev->ops->scan(&rdev->wiphy, request);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline void rdev_abort_scan(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev)
+{
+ trace_rdev_abort_scan(&rdev->wiphy, wdev);
+ rdev->ops->abort_scan(&rdev->wiphy, wdev);
+ trace_rdev_return_void(&rdev->wiphy);
+}
+
+static inline int rdev_auth(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct cfg80211_auth_request *req)
+{
+ int ret;
+ trace_rdev_auth(&rdev->wiphy, dev, req);
+ ret = rdev->ops->auth(&rdev->wiphy, dev, req);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_assoc(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct cfg80211_assoc_request *req)
+{
+ int ret;
+ trace_rdev_assoc(&rdev->wiphy, dev, req);
+ ret = rdev->ops->assoc(&rdev->wiphy, dev, req);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_deauth(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct cfg80211_deauth_request *req)
+{
+ int ret;
+ trace_rdev_deauth(&rdev->wiphy, dev, req);
+ ret = rdev->ops->deauth(&rdev->wiphy, dev, req);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_disassoc(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct cfg80211_disassoc_request *req)
+{
+ int ret;
+ trace_rdev_disassoc(&rdev->wiphy, dev, req);
+ ret = rdev->ops->disassoc(&rdev->wiphy, dev, req);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_connect(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct cfg80211_connect_params *sme)
+{
+ int ret;
+ trace_rdev_connect(&rdev->wiphy, dev, sme);
+ ret = rdev->ops->connect(&rdev->wiphy, dev, sme);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int
+rdev_update_connect_params(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct cfg80211_connect_params *sme, u32 changed)
+{
+ int ret;
+ trace_rdev_update_connect_params(&rdev->wiphy, dev, sme, changed);
+ ret = rdev->ops->update_connect_params(&rdev->wiphy, dev, sme, changed);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_disconnect(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, u16 reason_code)
+{
+ int ret;
+ trace_rdev_disconnect(&rdev->wiphy, dev, reason_code);
+ ret = rdev->ops->disconnect(&rdev->wiphy, dev, reason_code);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_join_ibss(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct cfg80211_ibss_params *params)
+{
+ int ret;
+ trace_rdev_join_ibss(&rdev->wiphy, dev, params);
+ ret = rdev->ops->join_ibss(&rdev->wiphy, dev, params);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_leave_ibss(struct cfg80211_registered_device *rdev,
+ struct net_device *dev)
+{
+ int ret;
+ trace_rdev_leave_ibss(&rdev->wiphy, dev);
+ ret = rdev->ops->leave_ibss(&rdev->wiphy, dev);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int
+rdev_set_wiphy_params(struct cfg80211_registered_device *rdev, u32 changed)
+{
+ int ret;
+ trace_rdev_set_wiphy_params(&rdev->wiphy, changed);
+ ret = rdev->ops->set_wiphy_params(&rdev->wiphy, changed);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_set_tx_power(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev,
+ enum nl80211_tx_power_setting type, int mbm)
+{
+ int ret;
+ trace_rdev_set_tx_power(&rdev->wiphy, wdev, type, mbm);
+ ret = rdev->ops->set_tx_power(&rdev->wiphy, wdev, type, mbm);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_get_tx_power(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev, int *dbm)
+{
+ int ret;
+ trace_rdev_get_tx_power(&rdev->wiphy, wdev);
+ ret = rdev->ops->get_tx_power(&rdev->wiphy, wdev, dbm);
+ trace_rdev_return_int_int(&rdev->wiphy, ret, *dbm);
+ return ret;
+}
+
+static inline int rdev_set_wds_peer(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, const u8 *addr)
+{
+ int ret;
+ trace_rdev_set_wds_peer(&rdev->wiphy, dev, addr);
+ ret = rdev->ops->set_wds_peer(&rdev->wiphy, dev, addr);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int
+rdev_set_multicast_to_unicast(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ const bool enabled)
+{
+ int ret;
+ trace_rdev_set_multicast_to_unicast(&rdev->wiphy, dev, enabled);
+ ret = rdev->ops->set_multicast_to_unicast(&rdev->wiphy, dev, enabled);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline void rdev_rfkill_poll(struct cfg80211_registered_device *rdev)
+{
+ trace_rdev_rfkill_poll(&rdev->wiphy);
+ rdev->ops->rfkill_poll(&rdev->wiphy);
+ trace_rdev_return_void(&rdev->wiphy);
+}
+
+
+#ifdef CPTCFG_NL80211_TESTMODE
+static inline int rdev_testmode_cmd(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev,
+ void *data, int len)
+{
+ int ret;
+ trace_rdev_testmode_cmd(&rdev->wiphy, wdev);
+ ret = rdev->ops->testmode_cmd(&rdev->wiphy, wdev, data, len);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_testmode_dump(struct cfg80211_registered_device *rdev,
+ struct sk_buff *skb,
+ struct netlink_callback *cb, void *data,
+ int len)
+{
+ int ret;
+ trace_rdev_testmode_dump(&rdev->wiphy);
+ ret = rdev->ops->testmode_dump(&rdev->wiphy, skb, cb, data, len);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+#endif
+
+static inline int
+rdev_set_bitrate_mask(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, const u8 *peer,
+ const struct cfg80211_bitrate_mask *mask)
+{
+ int ret;
+ trace_rdev_set_bitrate_mask(&rdev->wiphy, dev, peer, mask);
+ ret = rdev->ops->set_bitrate_mask(&rdev->wiphy, dev, peer, mask);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_dump_survey(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev, int idx,
+ struct survey_info *info)
+{
+ int ret;
+ trace_rdev_dump_survey(&rdev->wiphy, netdev, idx);
+ ret = rdev->ops->dump_survey(&rdev->wiphy, netdev, idx, info);
+ if (ret < 0)
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ else
+ trace_rdev_return_int_survey_info(&rdev->wiphy, ret, info);
+ return ret;
+}
+
+static inline int rdev_set_pmksa(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev,
+ struct cfg80211_pmksa *pmksa)
+{
+ int ret;
+ trace_rdev_set_pmksa(&rdev->wiphy, netdev, pmksa);
+ ret = rdev->ops->set_pmksa(&rdev->wiphy, netdev, pmksa);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_del_pmksa(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev,
+ struct cfg80211_pmksa *pmksa)
+{
+ int ret;
+ trace_rdev_del_pmksa(&rdev->wiphy, netdev, pmksa);
+ ret = rdev->ops->del_pmksa(&rdev->wiphy, netdev, pmksa);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_flush_pmksa(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev)
+{
+ int ret;
+ trace_rdev_flush_pmksa(&rdev->wiphy, netdev);
+ ret = rdev->ops->flush_pmksa(&rdev->wiphy, netdev);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int
+rdev_remain_on_channel(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev,
+ struct ieee80211_channel *chan,
+ unsigned int duration, u64 *cookie)
+{
+ int ret;
+ trace_rdev_remain_on_channel(&rdev->wiphy, wdev, chan, duration);
+ ret = rdev->ops->remain_on_channel(&rdev->wiphy, wdev, chan,
+ duration, cookie);
+ trace_rdev_return_int_cookie(&rdev->wiphy, ret, *cookie);
+ return ret;
+}
+
+static inline int
+rdev_cancel_remain_on_channel(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev, u64 cookie)
+{
+ int ret;
+ trace_rdev_cancel_remain_on_channel(&rdev->wiphy, wdev, cookie);
+ ret = rdev->ops->cancel_remain_on_channel(&rdev->wiphy, wdev, cookie);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_mgmt_tx(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev,
+ struct cfg80211_mgmt_tx_params *params,
+ u64 *cookie)
+{
+ int ret;
+ trace_rdev_mgmt_tx(&rdev->wiphy, wdev, params);
+ ret = rdev->ops->mgmt_tx(&rdev->wiphy, wdev, params, cookie);
+ trace_rdev_return_int_cookie(&rdev->wiphy, ret, *cookie);
+ return ret;
+}
+
+static inline int
+rdev_mgmt_tx_cancel_wait(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev, u64 cookie)
+{
+ int ret;
+ trace_rdev_mgmt_tx_cancel_wait(&rdev->wiphy, wdev, cookie);
+ ret = rdev->ops->mgmt_tx_cancel_wait(&rdev->wiphy, wdev, cookie);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_set_power_mgmt(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, bool enabled,
+ int timeout)
+{
+ int ret;
+ trace_rdev_set_power_mgmt(&rdev->wiphy, dev, enabled, timeout);
+ ret = rdev->ops->set_power_mgmt(&rdev->wiphy, dev, enabled, timeout);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int
+rdev_set_cqm_rssi_config(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, s32 rssi_thold, u32 rssi_hyst)
+{
+ int ret;
+ trace_rdev_set_cqm_rssi_config(&rdev->wiphy, dev, rssi_thold,
+ rssi_hyst);
+ ret = rdev->ops->set_cqm_rssi_config(&rdev->wiphy, dev, rssi_thold,
+ rssi_hyst);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int
+rdev_set_cqm_rssi_range_config(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, s32 low, s32 high)
+{
+ int ret;
+ trace_rdev_set_cqm_rssi_range_config(&rdev->wiphy, dev, low, high);
+ ret = rdev->ops->set_cqm_rssi_range_config(&rdev->wiphy, dev,
+ low, high);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int
+rdev_set_cqm_txe_config(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, u32 rate, u32 pkts, u32 intvl)
+{
+ int ret;
+ trace_rdev_set_cqm_txe_config(&rdev->wiphy, dev, rate, pkts, intvl);
+ ret = rdev->ops->set_cqm_txe_config(&rdev->wiphy, dev, rate, pkts,
+ intvl);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline void
+rdev_mgmt_frame_register(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev, u16 frame_type, bool reg)
+{
+ might_sleep();
+
+ trace_rdev_mgmt_frame_register(&rdev->wiphy, wdev , frame_type, reg);
+ rdev->ops->mgmt_frame_register(&rdev->wiphy, wdev , frame_type, reg);
+ trace_rdev_return_void(&rdev->wiphy);
+}
+
+static inline int rdev_set_antenna(struct cfg80211_registered_device *rdev,
+ u32 tx_ant, u32 rx_ant)
+{
+ int ret;
+ trace_rdev_set_antenna(&rdev->wiphy, tx_ant, rx_ant);
+ ret = rdev->ops->set_antenna(&rdev->wiphy, tx_ant, rx_ant);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_get_antenna(struct cfg80211_registered_device *rdev,
+ u32 *tx_ant, u32 *rx_ant)
+{
+ int ret;
+ trace_rdev_get_antenna(&rdev->wiphy);
+ ret = rdev->ops->get_antenna(&rdev->wiphy, tx_ant, rx_ant);
+ if (ret)
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ else
+ trace_rdev_return_int_tx_rx(&rdev->wiphy, ret, *tx_ant,
+ *rx_ant);
+ return ret;
+}
+
+static inline int
+rdev_sched_scan_start(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct cfg80211_sched_scan_request *request)
+{
+ int ret;
+ trace_rdev_sched_scan_start(&rdev->wiphy, dev, request->reqid);
+ ret = rdev->ops->sched_scan_start(&rdev->wiphy, dev, request);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_sched_scan_stop(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, u64 reqid)
+{
+ int ret;
+ trace_rdev_sched_scan_stop(&rdev->wiphy, dev, reqid);
+ ret = rdev->ops->sched_scan_stop(&rdev->wiphy, dev, reqid);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_set_rekey_data(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct cfg80211_gtk_rekey_data *data)
+{
+ int ret;
+ trace_rdev_set_rekey_data(&rdev->wiphy, dev);
+ ret = rdev->ops->set_rekey_data(&rdev->wiphy, dev, data);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_tdls_mgmt(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, u8 *peer,
+ u8 action_code, u8 dialog_token,
+ u16 status_code, u32 peer_capability,
+ bool initiator, const u8 *buf, size_t len)
+{
+ int ret;
+ trace_rdev_tdls_mgmt(&rdev->wiphy, dev, peer, action_code,
+ dialog_token, status_code, peer_capability,
+ initiator, buf, len);
+ ret = rdev->ops->tdls_mgmt(&rdev->wiphy, dev, peer, action_code,
+ dialog_token, status_code, peer_capability,
+ initiator, buf, len);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_tdls_oper(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, u8 *peer,
+ enum nl80211_tdls_operation oper)
+{
+ int ret;
+ trace_rdev_tdls_oper(&rdev->wiphy, dev, peer, oper);
+ ret = rdev->ops->tdls_oper(&rdev->wiphy, dev, peer, oper);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_probe_client(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, const u8 *peer,
+ u64 *cookie)
+{
+ int ret;
+ trace_rdev_probe_client(&rdev->wiphy, dev, peer);
+ ret = rdev->ops->probe_client(&rdev->wiphy, dev, peer, cookie);
+ trace_rdev_return_int_cookie(&rdev->wiphy, ret, *cookie);
+ return ret;
+}
+
+static inline int rdev_set_noack_map(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, u16 noack_map)
+{
+ int ret;
+ trace_rdev_set_noack_map(&rdev->wiphy, dev, noack_map);
+ ret = rdev->ops->set_noack_map(&rdev->wiphy, dev, noack_map);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int
+rdev_get_channel(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev,
+ struct cfg80211_chan_def *chandef)
+{
+ int ret;
+
+ trace_rdev_get_channel(&rdev->wiphy, wdev);
+ ret = rdev->ops->get_channel(&rdev->wiphy, wdev, chandef);
+ trace_rdev_return_chandef(&rdev->wiphy, ret, chandef);
+
+ return ret;
+}
+
+static inline int rdev_start_p2p_device(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev)
+{
+ int ret;
+
+ trace_rdev_start_p2p_device(&rdev->wiphy, wdev);
+ ret = rdev->ops->start_p2p_device(&rdev->wiphy, wdev);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline void rdev_stop_p2p_device(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev)
+{
+ trace_rdev_stop_p2p_device(&rdev->wiphy, wdev);
+ rdev->ops->stop_p2p_device(&rdev->wiphy, wdev);
+ trace_rdev_return_void(&rdev->wiphy);
+}
+
+static inline int rdev_start_nan(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev,
+ struct cfg80211_nan_conf *conf)
+{
+ int ret;
+
+ trace_rdev_start_nan(&rdev->wiphy, wdev, conf);
+ ret = rdev->ops->start_nan(&rdev->wiphy, wdev, conf);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline void rdev_stop_nan(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev)
+{
+ trace_rdev_stop_nan(&rdev->wiphy, wdev);
+ rdev->ops->stop_nan(&rdev->wiphy, wdev);
+ trace_rdev_return_void(&rdev->wiphy);
+}
+
+static inline int
+rdev_add_nan_func(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev,
+ struct cfg80211_nan_func *nan_func)
+{
+ int ret;
+
+ trace_rdev_add_nan_func(&rdev->wiphy, wdev, nan_func);
+ ret = rdev->ops->add_nan_func(&rdev->wiphy, wdev, nan_func);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline void rdev_del_nan_func(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev, u64 cookie)
+{
+ trace_rdev_del_nan_func(&rdev->wiphy, wdev, cookie);
+ rdev->ops->del_nan_func(&rdev->wiphy, wdev, cookie);
+ trace_rdev_return_void(&rdev->wiphy);
+}
+
+static inline int
+rdev_nan_change_conf(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev,
+ struct cfg80211_nan_conf *conf, u32 changes)
+{
+ int ret;
+
+ trace_rdev_nan_change_conf(&rdev->wiphy, wdev, conf, changes);
+ if (rdev->ops->nan_change_conf)
+ ret = rdev->ops->nan_change_conf(&rdev->wiphy, wdev, conf,
+ changes);
+ else
+ ret = -ENOTSUPP;
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_set_mac_acl(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct cfg80211_acl_data *params)
+{
+ int ret;
+
+ trace_rdev_set_mac_acl(&rdev->wiphy, dev, params);
+ ret = rdev->ops->set_mac_acl(&rdev->wiphy, dev, params);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_update_ft_ies(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct cfg80211_update_ft_ies_params *ftie)
+{
+ int ret;
+
+ trace_rdev_update_ft_ies(&rdev->wiphy, dev, ftie);
+ ret = rdev->ops->update_ft_ies(&rdev->wiphy, dev, ftie);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_crit_proto_start(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev,
+ enum nl80211_crit_proto_id protocol,
+ u16 duration)
+{
+ int ret;
+
+ trace_rdev_crit_proto_start(&rdev->wiphy, wdev, protocol, duration);
+ ret = rdev->ops->crit_proto_start(&rdev->wiphy, wdev,
+ protocol, duration);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline void rdev_crit_proto_stop(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev)
+{
+ trace_rdev_crit_proto_stop(&rdev->wiphy, wdev);
+ rdev->ops->crit_proto_stop(&rdev->wiphy, wdev);
+ trace_rdev_return_void(&rdev->wiphy);
+}
+
+static inline int rdev_channel_switch(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct cfg80211_csa_settings *params)
+{
+ int ret;
+
+ trace_rdev_channel_switch(&rdev->wiphy, dev, params);
+ ret = rdev->ops->channel_switch(&rdev->wiphy, dev, params);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_set_qos_map(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct cfg80211_qos_map *qos_map)
+{
+ int ret = -EOPNOTSUPP;
+
+ if (rdev->ops->set_qos_map) {
+ trace_rdev_set_qos_map(&rdev->wiphy, dev, qos_map);
+ ret = rdev->ops->set_qos_map(&rdev->wiphy, dev, qos_map);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ }
+
+ return ret;
+}
+
+static inline int
+rdev_set_ap_chanwidth(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, struct cfg80211_chan_def *chandef)
+{
+ int ret;
+
+ trace_rdev_set_ap_chanwidth(&rdev->wiphy, dev, chandef);
+ ret = rdev->ops->set_ap_chanwidth(&rdev->wiphy, dev, chandef);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+
+ return ret;
+}
+
+static inline int
+rdev_add_tx_ts(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, u8 tsid, const u8 *peer,
+ u8 user_prio, u16 admitted_time)
+{
+ int ret = -EOPNOTSUPP;
+
+ trace_rdev_add_tx_ts(&rdev->wiphy, dev, tsid, peer,
+ user_prio, admitted_time);
+ if (rdev->ops->add_tx_ts)
+ ret = rdev->ops->add_tx_ts(&rdev->wiphy, dev, tsid, peer,
+ user_prio, admitted_time);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+
+ return ret;
+}
+
+static inline int
+rdev_del_tx_ts(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, u8 tsid, const u8 *peer)
+{
+ int ret = -EOPNOTSUPP;
+
+ trace_rdev_del_tx_ts(&rdev->wiphy, dev, tsid, peer);
+ if (rdev->ops->del_tx_ts)
+ ret = rdev->ops->del_tx_ts(&rdev->wiphy, dev, tsid, peer);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+
+ return ret;
+}
+
+static inline int
+rdev_tdls_channel_switch(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, const u8 *addr,
+ u8 oper_class, struct cfg80211_chan_def *chandef)
+{
+ int ret;
+
+ trace_rdev_tdls_channel_switch(&rdev->wiphy, dev, addr, oper_class,
+ chandef);
+ ret = rdev->ops->tdls_channel_switch(&rdev->wiphy, dev, addr,
+ oper_class, chandef);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline void
+rdev_tdls_cancel_channel_switch(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, const u8 *addr)
+{
+ trace_rdev_tdls_cancel_channel_switch(&rdev->wiphy, dev, addr);
+ rdev->ops->tdls_cancel_channel_switch(&rdev->wiphy, dev, addr);
+ trace_rdev_return_void(&rdev->wiphy);
+}
+
+static inline int
+rdev_start_radar_detection(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct cfg80211_chan_def *chandef,
+ u32 cac_time_ms)
+{
+ int ret = -ENOTSUPP;
+
+ trace_rdev_start_radar_detection(&rdev->wiphy, dev, chandef,
+ cac_time_ms);
+ if (rdev->ops->start_radar_detection)
+ ret = rdev->ops->start_radar_detection(&rdev->wiphy, dev,
+ chandef, cac_time_ms);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int
+rdev_set_mcast_rate(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ int mcast_rate[NUM_NL80211_BANDS])
+{
+ int ret = -ENOTSUPP;
+
+ trace_rdev_set_mcast_rate(&rdev->wiphy, dev, mcast_rate);
+ if (rdev->ops->set_mcast_rate)
+ ret = rdev->ops->set_mcast_rate(&rdev->wiphy, dev, mcast_rate);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int
+rdev_set_coalesce(struct cfg80211_registered_device *rdev,
+ struct cfg80211_coalesce *coalesce)
+{
+ int ret = -ENOTSUPP;
+
+ trace_rdev_set_coalesce(&rdev->wiphy, coalesce);
+ if (rdev->ops->set_coalesce)
+ ret = rdev->ops->set_coalesce(&rdev->wiphy, coalesce);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_set_pmk(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct cfg80211_pmk_conf *pmk_conf)
+{
+ int ret = -EOPNOTSUPP;
+
+ trace_rdev_set_pmk(&rdev->wiphy, dev, pmk_conf);
+ if (rdev->ops->set_pmk)
+ ret = rdev->ops->set_pmk(&rdev->wiphy, dev, pmk_conf);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_del_pmk(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, const u8 *aa)
+{
+ int ret = -EOPNOTSUPP;
+
+ trace_rdev_del_pmk(&rdev->wiphy, dev, aa);
+ if (rdev->ops->del_pmk)
+ ret = rdev->ops->del_pmk(&rdev->wiphy, dev, aa);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+#endif /* __CFG80211_RDEV_OPS */
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
new file mode 100644
index 0000000..adad8f4
--- /dev/null
+++ b/net/wireless/reg.c
@@ -0,0 +1,3351 @@
+/*
+ * Copyright 2002-2005, Instant802 Networks, Inc.
+ * Copyright 2005-2006, Devicescape Software, Inc.
+ * Copyright 2007 Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2008-2011 Luis R. Rodriguez <mcgrof@qca.qualcomm.com>
+ * Copyright 2013-2014 Intel Mobile Communications GmbH
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+
+/**
+ * DOC: Wireless regulatory infrastructure
+ *
+ * The usual implementation is for a driver to read a device EEPROM to
+ * determine which regulatory domain it should be operating under, then
+ * looking up the allowable channels in a driver-local table and finally
+ * registering those channels in the wiphy structure.
+ *
+ * Another set of compliance enforcement is for drivers to use their
+ * own compliance limits which can be stored on the EEPROM. The host
+ * driver or firmware may ensure these are used.
+ *
+ * In addition to all this we provide an extra layer of regulatory
+ * conformance. For drivers which do not have any regulatory
+ * information CRDA provides the complete regulatory solution.
+ * For others it provides a community effort on further restrictions
+ * to enhance compliance.
+ *
+ * Note: When number of rules --> infinity we will not be able to
+ * index on alpha2 any more, instead we'll probably have to
+ * rely on some SHA1 checksum of the regdomain for example.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/export.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/ctype.h>
+#include <linux/nl80211.h>
+#include <linux/platform_device.h>
+#include <linux/moduleparam.h>
+#include <net/cfg80211.h>
+#include "core.h"
+#include "reg.h"
+#include "rdev-ops.h"
+#include "regdb.h"
+#include "nl80211.h"
+
+/*
+ * Grace period we give before making sure all current interfaces reside on
+ * channels allowed by the current regulatory domain.
+ */
+#define REG_ENFORCE_GRACE_MS 60000
+
+/**
+ * enum reg_request_treatment - regulatory request treatment
+ *
+ * @REG_REQ_OK: continue processing the regulatory request
+ * @REG_REQ_IGNORE: ignore the regulatory request
+ * @REG_REQ_INTERSECT: the regulatory domain resulting from this request should
+ * be intersected with the current one.
+ * @REG_REQ_ALREADY_SET: the regulatory request will not change the current
+ * regulatory settings, and no further processing is required.
+ */
+enum reg_request_treatment {
+ REG_REQ_OK,
+ REG_REQ_IGNORE,
+ REG_REQ_INTERSECT,
+ REG_REQ_ALREADY_SET,
+};
+
+static struct regulatory_request core_request_world = {
+ .initiator = NL80211_REGDOM_SET_BY_CORE,
+ .alpha2[0] = '0',
+ .alpha2[1] = '0',
+ .intersect = false,
+ .processed = true,
+ .country_ie_env = ENVIRON_ANY,
+};
+
+/*
+ * Receipt of information from last regulatory request,
+ * protected by RTNL (and can be accessed with RCU protection)
+ */
+static struct regulatory_request __rcu *last_request =
+ (void __force __rcu *)&core_request_world;
+
+/* To trigger userspace events */
+static struct platform_device *reg_pdev;
+
+/*
+ * Central wireless core regulatory domains, we only need two,
+ * the current one and a world regulatory domain in case we have no
+ * information to give us an alpha2.
+ * (protected by RTNL, can be read under RCU)
+ */
+const struct ieee80211_regdomain __rcu *cfg80211_regdomain;
+
+/*
+ * Number of devices that registered to the core
+ * that support cellular base station regulatory hints
+ * (protected by RTNL)
+ */
+static int reg_num_devs_support_basehint;
+
+/*
+ * State variable indicating if the platform on which the devices
+ * are attached is operating in an indoor environment. The state variable
+ * is relevant for all registered devices.
+ */
+static bool reg_is_indoor;
+static spinlock_t reg_indoor_lock;
+
+/* Used to track the userspace process controlling the indoor setting */
+static u32 reg_is_indoor_portid;
+
+static void restore_regulatory_settings(bool reset_user);
+
+static const struct ieee80211_regdomain *get_cfg80211_regdom(void)
+{
+ return rtnl_dereference(cfg80211_regdomain);
+}
+
+const struct ieee80211_regdomain *get_wiphy_regdom(struct wiphy *wiphy)
+{
+ return rtnl_dereference(wiphy->regd);
+}
+
+static const char *reg_dfs_region_str(enum nl80211_dfs_regions dfs_region)
+{
+ switch (dfs_region) {
+ case NL80211_DFS_UNSET:
+ return "unset";
+ case NL80211_DFS_FCC:
+ return "FCC";
+ case NL80211_DFS_ETSI:
+ return "ETSI";
+ case NL80211_DFS_JP:
+ return "JP";
+ }
+ return "Unknown";
+}
+
+enum nl80211_dfs_regions reg_get_dfs_region(struct wiphy *wiphy)
+{
+ const struct ieee80211_regdomain *regd = NULL;
+ const struct ieee80211_regdomain *wiphy_regd = NULL;
+
+ regd = get_cfg80211_regdom();
+ if (!wiphy)
+ goto out;
+
+ wiphy_regd = get_wiphy_regdom(wiphy);
+ if (!wiphy_regd)
+ goto out;
+
+ if (wiphy_regd->dfs_region == regd->dfs_region)
+ goto out;
+
+ pr_debug("%s: device specific dfs_region (%s) disagrees with cfg80211's central dfs_region (%s)\n",
+ dev_name(&wiphy->dev),
+ reg_dfs_region_str(wiphy_regd->dfs_region),
+ reg_dfs_region_str(regd->dfs_region));
+
+out:
+ return regd->dfs_region;
+}
+
+static void rcu_free_regdom(const struct ieee80211_regdomain *r)
+{
+ if (!r)
+ return;
+ kfree_rcu((struct ieee80211_regdomain *)r, rcu_head);
+}
+
+static struct regulatory_request *get_last_request(void)
+{
+ return rcu_dereference_rtnl(last_request);
+}
+
+/* Used to queue up regulatory hints */
+static LIST_HEAD(reg_requests_list);
+static spinlock_t reg_requests_lock;
+
+/* Used to queue up beacon hints for review */
+static LIST_HEAD(reg_pending_beacons);
+static spinlock_t reg_pending_beacons_lock;
+
+/* Used to keep track of processed beacon hints */
+static LIST_HEAD(reg_beacon_list);
+
+struct reg_beacon {
+ struct list_head list;
+ struct ieee80211_channel chan;
+};
+
+static void reg_check_chans_work(struct work_struct *work);
+static DECLARE_DELAYED_WORK(reg_check_chans, reg_check_chans_work);
+
+static void reg_todo(struct work_struct *work);
+static DECLARE_WORK(reg_work, reg_todo);
+
+/* We keep a static world regulatory domain in case of the absence of CRDA */
+static const struct ieee80211_regdomain world_regdom = {
+ .n_reg_rules = 8,
+ .alpha2 = "00",
+ .reg_rules = {
+ /* IEEE 802.11b/g, channels 1..11 */
+ REG_RULE(2412-10, 2462+10, 40, 6, 20, 0),
+ /* IEEE 802.11b/g, channels 12..13. */
+ REG_RULE(2467-10, 2472+10, 20, 6, 20,
+ NL80211_RRF_NO_IR | NL80211_RRF_AUTO_BW),
+ /* IEEE 802.11 channel 14 - Only JP enables
+ * this and for 802.11b only */
+ REG_RULE(2484-10, 2484+10, 20, 6, 20,
+ NL80211_RRF_NO_IR |
+ NL80211_RRF_NO_OFDM),
+ /* IEEE 802.11a, channel 36..48 */
+ REG_RULE(5180-10, 5240+10, 80, 6, 20,
+ NL80211_RRF_NO_IR |
+ NL80211_RRF_AUTO_BW),
+
+ /* IEEE 802.11a, channel 52..64 - DFS required */
+ REG_RULE(5260-10, 5320+10, 80, 6, 20,
+ NL80211_RRF_NO_IR |
+ NL80211_RRF_AUTO_BW |
+ NL80211_RRF_DFS),
+
+ /* IEEE 802.11a, channel 100..144 - DFS required */
+ REG_RULE(5500-10, 5720+10, 160, 6, 20,
+ NL80211_RRF_NO_IR |
+ NL80211_RRF_DFS),
+
+ /* IEEE 802.11a, channel 149..165 */
+ REG_RULE(5745-10, 5825+10, 80, 6, 20,
+ NL80211_RRF_NO_IR),
+
+ /* IEEE 802.11ad (60GHz), channels 1..3 */
+ REG_RULE(56160+2160*1-1080, 56160+2160*3+1080, 2160, 0, 0, 0),
+ }
+};
+
+/* protected by RTNL */
+static const struct ieee80211_regdomain *cfg80211_world_regdom =
+ &world_regdom;
+
+static char *ieee80211_regdom = "00";
+static char user_alpha2[2];
+
+module_param(ieee80211_regdom, charp, 0444);
+MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code");
+
+static void reg_free_request(struct regulatory_request *request)
+{
+ if (request == &core_request_world)
+ return;
+
+ if (request != get_last_request())
+ kfree(request);
+}
+
+static void reg_free_last_request(void)
+{
+ struct regulatory_request *lr = get_last_request();
+
+ if (lr != &core_request_world && lr)
+ kfree_rcu(lr, rcu_head);
+}
+
+static void reg_update_last_request(struct regulatory_request *request)
+{
+ struct regulatory_request *lr;
+
+ lr = get_last_request();
+ if (lr == request)
+ return;
+
+ reg_free_last_request();
+ rcu_assign_pointer(last_request, request);
+}
+
+static void reset_regdomains(bool full_reset,
+ const struct ieee80211_regdomain *new_regdom)
+{
+ const struct ieee80211_regdomain *r;
+
+ ASSERT_RTNL();
+
+ r = get_cfg80211_regdom();
+
+ /* avoid freeing static information or freeing something twice */
+ if (r == cfg80211_world_regdom)
+ r = NULL;
+ if (cfg80211_world_regdom == &world_regdom)
+ cfg80211_world_regdom = NULL;
+ if (r == &world_regdom)
+ r = NULL;
+
+ rcu_free_regdom(r);
+ rcu_free_regdom(cfg80211_world_regdom);
+
+ cfg80211_world_regdom = &world_regdom;
+ rcu_assign_pointer(cfg80211_regdomain, new_regdom);
+
+ if (!full_reset)
+ return;
+
+ reg_update_last_request(&core_request_world);
+}
+
+/*
+ * Dynamic world regulatory domain requested by the wireless
+ * core upon initialization
+ */
+static void update_world_regdomain(const struct ieee80211_regdomain *rd)
+{
+ struct regulatory_request *lr;
+
+ lr = get_last_request();
+
+ WARN_ON(!lr);
+
+ reset_regdomains(false, rd);
+
+ cfg80211_world_regdom = rd;
+}
+
+bool is_world_regdom(const char *alpha2)
+{
+ if (!alpha2)
+ return false;
+ return alpha2[0] == '0' && alpha2[1] == '0';
+}
+
+static bool is_alpha2_set(const char *alpha2)
+{
+ if (!alpha2)
+ return false;
+ return alpha2[0] && alpha2[1];
+}
+
+static bool is_unknown_alpha2(const char *alpha2)
+{
+ if (!alpha2)
+ return false;
+ /*
+ * Special case where regulatory domain was built by driver
+ * but a specific alpha2 cannot be determined
+ */
+ return alpha2[0] == '9' && alpha2[1] == '9';
+}
+
+static bool is_intersected_alpha2(const char *alpha2)
+{
+ if (!alpha2)
+ return false;
+ /*
+ * Special case where regulatory domain is the
+ * result of an intersection between two regulatory domain
+ * structures
+ */
+ return alpha2[0] == '9' && alpha2[1] == '8';
+}
+
+static bool is_an_alpha2(const char *alpha2)
+{
+ if (!alpha2)
+ return false;
+ return isalpha(alpha2[0]) && isalpha(alpha2[1]);
+}
+
+static bool alpha2_equal(const char *alpha2_x, const char *alpha2_y)
+{
+ if (!alpha2_x || !alpha2_y)
+ return false;
+ return alpha2_x[0] == alpha2_y[0] && alpha2_x[1] == alpha2_y[1];
+}
+
+static bool regdom_changes(const char *alpha2)
+{
+ const struct ieee80211_regdomain *r = get_cfg80211_regdom();
+
+ if (!r)
+ return true;
+ return !alpha2_equal(r->alpha2, alpha2);
+}
+
+/*
+ * The NL80211_REGDOM_SET_BY_USER regdom alpha2 is cached, this lets
+ * you know if a valid regulatory hint with NL80211_REGDOM_SET_BY_USER
+ * has ever been issued.
+ */
+static bool is_user_regdom_saved(void)
+{
+ if (user_alpha2[0] == '9' && user_alpha2[1] == '7')
+ return false;
+
+ /* This would indicate a mistake on the design */
+ if (WARN(!is_world_regdom(user_alpha2) && !is_an_alpha2(user_alpha2),
+ "Unexpected user alpha2: %c%c\n",
+ user_alpha2[0], user_alpha2[1]))
+ return false;
+
+ return true;
+}
+
+static const struct ieee80211_regdomain *
+reg_copy_regd(const struct ieee80211_regdomain *src_regd)
+{
+ struct ieee80211_regdomain *regd;
+ int size_of_regd;
+ unsigned int i;
+
+ size_of_regd =
+ sizeof(struct ieee80211_regdomain) +
+ src_regd->n_reg_rules * sizeof(struct ieee80211_reg_rule);
+
+ regd = kzalloc(size_of_regd, GFP_KERNEL);
+ if (!regd)
+ return ERR_PTR(-ENOMEM);
+
+ memcpy(regd, src_regd, sizeof(struct ieee80211_regdomain));
+
+ for (i = 0; i < src_regd->n_reg_rules; i++)
+ memcpy(®d->reg_rules[i], &src_regd->reg_rules[i],
+ sizeof(struct ieee80211_reg_rule));
+
+ return regd;
+}
+
+#ifdef CPTCFG_CFG80211_INTERNAL_REGDB
+struct reg_regdb_apply_request {
+ struct list_head list;
+ const struct ieee80211_regdomain *regdom;
+};
+
+static LIST_HEAD(reg_regdb_apply_list);
+static DEFINE_MUTEX(reg_regdb_apply_mutex);
+
+static void reg_regdb_apply(struct work_struct *work)
+{
+ struct reg_regdb_apply_request *request;
+
+ rtnl_lock();
+
+ mutex_lock(®_regdb_apply_mutex);
+ while (!list_empty(®_regdb_apply_list)) {
+ request = list_first_entry(®_regdb_apply_list,
+ struct reg_regdb_apply_request,
+ list);
+ list_del(&request->list);
+
+ set_regdom(request->regdom, REGD_SOURCE_INTERNAL_DB);
+ kfree(request);
+ }
+ mutex_unlock(®_regdb_apply_mutex);
+
+ rtnl_unlock();
+}
+
+static DECLARE_WORK(reg_regdb_work, reg_regdb_apply);
+
+static int reg_query_builtin(const char *alpha2)
+{
+ const struct ieee80211_regdomain *regdom = NULL;
+ struct reg_regdb_apply_request *request;
+ unsigned int i;
+
+ for (i = 0; i < reg_regdb_size; i++) {
+ if (alpha2_equal(alpha2, reg_regdb[i]->alpha2)) {
+ regdom = reg_regdb[i];
+ break;
+ }
+ }
+
+ if (!regdom)
+ return -ENODATA;
+
+ request = kzalloc(sizeof(struct reg_regdb_apply_request), GFP_KERNEL);
+ if (!request)
+ return -ENOMEM;
+
+ request->regdom = reg_copy_regd(regdom);
+ if (IS_ERR_OR_NULL(request->regdom)) {
+ kfree(request);
+ return -ENOMEM;
+ }
+
+ mutex_lock(®_regdb_apply_mutex);
+ list_add_tail(&request->list, ®_regdb_apply_list);
+ mutex_unlock(®_regdb_apply_mutex);
+
+ schedule_work(®_regdb_work);
+
+ return 0;
+}
+
+/* Feel free to add any other sanity checks here */
+static void reg_regdb_size_check(void)
+{
+ /* We should ideally BUILD_BUG_ON() but then random builds would fail */
+ WARN_ONCE(!reg_regdb_size, "db.txt is empty, you should update it...");
+}
+#else
+static inline void reg_regdb_size_check(void) {}
+static inline int reg_query_builtin(const char *alpha2)
+{
+ return -ENODATA;
+}
+#endif /* CPTCFG_CFG80211_INTERNAL_REGDB */
+
+#ifdef CPTCFG_CFG80211_CRDA_SUPPORT
+/* Max number of consecutive attempts to communicate with CRDA */
+#ifdef CONFIG_CFG80211_INTERNAL_REGDB
+#define REG_MAX_CRDA_TIMEOUTS 1
+#else
+#define REG_MAX_CRDA_TIMEOUTS 10
+#endif
+
+static u32 reg_crda_timeouts;
+
+static void crda_timeout_work(struct work_struct *work);
+static DECLARE_DELAYED_WORK(crda_timeout, crda_timeout_work);
+
+static void crda_timeout_work(struct work_struct *work)
+{
+ pr_debug("Timeout while waiting for CRDA to reply, restoring regulatory settings\n");
+ rtnl_lock();
+ reg_crda_timeouts++;
+ restore_regulatory_settings(true);
+ rtnl_unlock();
+}
+
+static void cancel_crda_timeout(void)
+{
+ cancel_delayed_work(&crda_timeout);
+}
+
+static void cancel_crda_timeout_sync(void)
+{
+ cancel_delayed_work_sync(&crda_timeout);
+}
+
+static void reset_crda_timeouts(void)
+{
+ reg_crda_timeouts = 0;
+}
+
+/*
+ * This lets us keep regulatory code which is updated on a regulatory
+ * basis in userspace.
+ */
+static int call_crda(const char *alpha2)
+{
+ char country[12];
+ char *env[] = { country, NULL };
+ int ret;
+
+ snprintf(country, sizeof(country), "COUNTRY=%c%c",
+ alpha2[0], alpha2[1]);
+
+ if (reg_crda_timeouts > REG_MAX_CRDA_TIMEOUTS) {
+ pr_debug("Exceeded CRDA call max attempts. Not calling CRDA\n");
+ return -EINVAL;
+ }
+
+ if (!is_world_regdom((char *) alpha2))
+ pr_debug("Calling CRDA for country: %c%c\n",
+ alpha2[0], alpha2[1]);
+ else
+ pr_debug("Calling CRDA to update world regulatory domain\n");
+
+ ret = kobject_uevent_env(®_pdev->dev.kobj, KOBJ_CHANGE, env);
+ if (ret)
+ return ret;
+
+ queue_delayed_work(system_power_efficient_wq,
+ &crda_timeout, msecs_to_jiffies(3142));
+ return 0;
+}
+#else
+static inline void cancel_crda_timeout(void) {}
+static inline void cancel_crda_timeout_sync(void) {}
+static inline void reset_crda_timeouts(void) {}
+static inline int call_crda(const char *alpha2)
+{
+ return -ENODATA;
+}
+#endif /* CPTCFG_CFG80211_CRDA_SUPPORT */
+
+static bool reg_query_database(struct regulatory_request *request)
+{
+ /* query internal regulatory database (if it exists) */
+ if (reg_query_builtin(request->alpha2) == 0)
+ return true;
+
+ if (call_crda(request->alpha2) == 0)
+ return true;
+
+ return false;
+}
+
+bool reg_is_valid_request(const char *alpha2)
+{
+ struct regulatory_request *lr = get_last_request();
+
+ if (!lr || lr->processed)
+ return false;
+
+ return alpha2_equal(lr->alpha2, alpha2);
+}
+
+static const struct ieee80211_regdomain *reg_get_regdomain(struct wiphy *wiphy)
+{
+ struct regulatory_request *lr = get_last_request();
+
+ /*
+ * Follow the driver's regulatory domain, if present, unless a country
+ * IE has been processed or a user wants to help complaince further
+ */
+ if (lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE &&
+ lr->initiator != NL80211_REGDOM_SET_BY_USER &&
+ wiphy->regd)
+ return get_wiphy_regdom(wiphy);
+
+ return get_cfg80211_regdom();
+}
+
+static unsigned int
+reg_get_max_bandwidth_from_range(const struct ieee80211_regdomain *rd,
+ const struct ieee80211_reg_rule *rule)
+{
+ const struct ieee80211_freq_range *freq_range = &rule->freq_range;
+ const struct ieee80211_freq_range *freq_range_tmp;
+ const struct ieee80211_reg_rule *tmp;
+ u32 start_freq, end_freq, idx, no;
+
+ for (idx = 0; idx < rd->n_reg_rules; idx++)
+ if (rule == &rd->reg_rules[idx])
+ break;
+
+ if (idx == rd->n_reg_rules)
+ return 0;
+
+ /* get start_freq */
+ no = idx;
+
+ while (no) {
+ tmp = &rd->reg_rules[--no];
+ freq_range_tmp = &tmp->freq_range;
+
+ if (freq_range_tmp->end_freq_khz < freq_range->start_freq_khz)
+ break;
+
+ freq_range = freq_range_tmp;
+ }
+
+ start_freq = freq_range->start_freq_khz;
+
+ /* get end_freq */
+ freq_range = &rule->freq_range;
+ no = idx;
+
+ while (no < rd->n_reg_rules - 1) {
+ tmp = &rd->reg_rules[++no];
+ freq_range_tmp = &tmp->freq_range;
+
+ if (freq_range_tmp->start_freq_khz > freq_range->end_freq_khz)
+ break;
+
+ freq_range = freq_range_tmp;
+ }
+
+ end_freq = freq_range->end_freq_khz;
+
+ return end_freq - start_freq;
+}
+
+unsigned int reg_get_max_bandwidth(const struct ieee80211_regdomain *rd,
+ const struct ieee80211_reg_rule *rule)
+{
+ unsigned int bw = reg_get_max_bandwidth_from_range(rd, rule);
+
+ if (rule->flags & NL80211_RRF_NO_160MHZ)
+ bw = min_t(unsigned int, bw, MHZ_TO_KHZ(80));
+ if (rule->flags & NL80211_RRF_NO_80MHZ)
+ bw = min_t(unsigned int, bw, MHZ_TO_KHZ(40));
+
+ /*
+ * HT40+/HT40- limits are handled per-channel. Only limit BW if both
+ * are not allowed.
+ */
+ if (rule->flags & NL80211_RRF_NO_HT40MINUS &&
+ rule->flags & NL80211_RRF_NO_HT40PLUS)
+ bw = min_t(unsigned int, bw, MHZ_TO_KHZ(20));
+
+ return bw;
+}
+
+/* Sanity check on a regulatory rule */
+static bool is_valid_reg_rule(const struct ieee80211_reg_rule *rule)
+{
+ const struct ieee80211_freq_range *freq_range = &rule->freq_range;
+ u32 freq_diff;
+
+ if (freq_range->start_freq_khz <= 0 || freq_range->end_freq_khz <= 0)
+ return false;
+
+ if (freq_range->start_freq_khz > freq_range->end_freq_khz)
+ return false;
+
+ freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz;
+
+ if (freq_range->end_freq_khz <= freq_range->start_freq_khz ||
+ freq_range->max_bandwidth_khz > freq_diff)
+ return false;
+
+ return true;
+}
+
+static bool is_valid_rd(const struct ieee80211_regdomain *rd)
+{
+ const struct ieee80211_reg_rule *reg_rule = NULL;
+ unsigned int i;
+
+ if (!rd->n_reg_rules)
+ return false;
+
+ if (WARN_ON(rd->n_reg_rules > NL80211_MAX_SUPP_REG_RULES))
+ return false;
+
+ for (i = 0; i < rd->n_reg_rules; i++) {
+ reg_rule = &rd->reg_rules[i];
+ if (!is_valid_reg_rule(reg_rule))
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * freq_in_rule_band - tells us if a frequency is in a frequency band
+ * @freq_range: frequency rule we want to query
+ * @freq_khz: frequency we are inquiring about
+ *
+ * This lets us know if a specific frequency rule is or is not relevant to
+ * a specific frequency's band. Bands are device specific and artificial
+ * definitions (the "2.4 GHz band", the "5 GHz band" and the "60GHz band"),
+ * however it is safe for now to assume that a frequency rule should not be
+ * part of a frequency's band if the start freq or end freq are off by more
+ * than 2 GHz for the 2.4 and 5 GHz bands, and by more than 10 GHz for the
+ * 60 GHz band.
+ * This resolution can be lowered and should be considered as we add
+ * regulatory rule support for other "bands".
+ **/
+static bool freq_in_rule_band(const struct ieee80211_freq_range *freq_range,
+ u32 freq_khz)
+{
+#define ONE_GHZ_IN_KHZ 1000000
+ /*
+ * From 802.11ad: directional multi-gigabit (DMG):
+ * Pertaining to operation in a frequency band containing a channel
+ * with the Channel starting frequency above 45 GHz.
+ */
+ u32 limit = freq_khz > 45 * ONE_GHZ_IN_KHZ ?
+ 10 * ONE_GHZ_IN_KHZ : 2 * ONE_GHZ_IN_KHZ;
+ if (abs(freq_khz - freq_range->start_freq_khz) <= limit)
+ return true;
+ if (abs(freq_khz - freq_range->end_freq_khz) <= limit)
+ return true;
+ return false;
+#undef ONE_GHZ_IN_KHZ
+}
+
+/*
+ * Later on we can perhaps use the more restrictive DFS
+ * region but we don't have information for that yet so
+ * for now simply disallow conflicts.
+ */
+static enum nl80211_dfs_regions
+reg_intersect_dfs_region(const enum nl80211_dfs_regions dfs_region1,
+ const enum nl80211_dfs_regions dfs_region2)
+{
+ if (dfs_region1 != dfs_region2)
+ return NL80211_DFS_UNSET;
+ return dfs_region1;
+}
+
+/*
+ * Helper for regdom_intersect(), this does the real
+ * mathematical intersection fun
+ */
+static int reg_rules_intersect(const struct ieee80211_regdomain *rd1,
+ const struct ieee80211_regdomain *rd2,
+ const struct ieee80211_reg_rule *rule1,
+ const struct ieee80211_reg_rule *rule2,
+ struct ieee80211_reg_rule *intersected_rule)
+{
+ const struct ieee80211_freq_range *freq_range1, *freq_range2;
+ struct ieee80211_freq_range *freq_range;
+ const struct ieee80211_power_rule *power_rule1, *power_rule2;
+ struct ieee80211_power_rule *power_rule;
+ u32 freq_diff, max_bandwidth1, max_bandwidth2;
+
+ freq_range1 = &rule1->freq_range;
+ freq_range2 = &rule2->freq_range;
+ freq_range = &intersected_rule->freq_range;
+
+ power_rule1 = &rule1->power_rule;
+ power_rule2 = &rule2->power_rule;
+ power_rule = &intersected_rule->power_rule;
+
+ freq_range->start_freq_khz = max(freq_range1->start_freq_khz,
+ freq_range2->start_freq_khz);
+ freq_range->end_freq_khz = min(freq_range1->end_freq_khz,
+ freq_range2->end_freq_khz);
+
+ max_bandwidth1 = freq_range1->max_bandwidth_khz;
+ max_bandwidth2 = freq_range2->max_bandwidth_khz;
+
+ if (rule1->flags & NL80211_RRF_AUTO_BW)
+ max_bandwidth1 = reg_get_max_bandwidth(rd1, rule1);
+ if (rule2->flags & NL80211_RRF_AUTO_BW)
+ max_bandwidth2 = reg_get_max_bandwidth(rd2, rule2);
+
+ freq_range->max_bandwidth_khz = min(max_bandwidth1, max_bandwidth2);
+
+ intersected_rule->flags = rule1->flags | rule2->flags;
+
+ /*
+ * In case NL80211_RRF_AUTO_BW requested for both rules
+ * set AUTO_BW in intersected rule also. Next we will
+ * calculate BW correctly in handle_channel function.
+ * In other case remove AUTO_BW flag while we calculate
+ * maximum bandwidth correctly and auto calculation is
+ * not required.
+ */
+ if ((rule1->flags & NL80211_RRF_AUTO_BW) &&
+ (rule2->flags & NL80211_RRF_AUTO_BW))
+ intersected_rule->flags |= NL80211_RRF_AUTO_BW;
+ else
+ intersected_rule->flags &= ~NL80211_RRF_AUTO_BW;
+
+ freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz;
+ if (freq_range->max_bandwidth_khz > freq_diff)
+ freq_range->max_bandwidth_khz = freq_diff;
+
+ power_rule->max_eirp = min(power_rule1->max_eirp,
+ power_rule2->max_eirp);
+ power_rule->max_antenna_gain = min(power_rule1->max_antenna_gain,
+ power_rule2->max_antenna_gain);
+
+ intersected_rule->dfs_cac_ms = max(rule1->dfs_cac_ms,
+ rule2->dfs_cac_ms);
+
+ if (!is_valid_reg_rule(intersected_rule))
+ return -EINVAL;
+
+ return 0;
+}
+
+/* check whether old rule contains new rule */
+static bool rule_contains(struct ieee80211_reg_rule *r1,
+ struct ieee80211_reg_rule *r2)
+{
+ /* for simplicity, currently consider only same flags */
+ if (r1->flags != r2->flags)
+ return false;
+
+ /* verify r1 is more restrictive */
+ if ((r1->power_rule.max_antenna_gain >
+ r2->power_rule.max_antenna_gain) ||
+ r1->power_rule.max_eirp > r2->power_rule.max_eirp)
+ return false;
+
+ /* make sure r2's range is contained within r1 */
+ if (r1->freq_range.start_freq_khz > r2->freq_range.start_freq_khz ||
+ r1->freq_range.end_freq_khz < r2->freq_range.end_freq_khz)
+ return false;
+
+ /* and finally verify that r1.max_bw >= r2.max_bw */
+ if (r1->freq_range.max_bandwidth_khz <
+ r2->freq_range.max_bandwidth_khz)
+ return false;
+
+ return true;
+}
+
+/* add or extend current rules. do nothing if rule is already contained */
+static void add_rule(struct ieee80211_reg_rule *rule,
+ struct ieee80211_reg_rule *reg_rules, u32 *n_rules)
+{
+ struct ieee80211_reg_rule *tmp_rule;
+ int i;
+
+ for (i = 0; i < *n_rules; i++) {
+ tmp_rule = ®_rules[i];
+ /* rule is already contained - do nothing */
+ if (rule_contains(tmp_rule, rule))
+ return;
+
+ /* extend rule if possible */
+ if (rule_contains(rule, tmp_rule)) {
+ memcpy(tmp_rule, rule, sizeof(*rule));
+ return;
+ }
+ }
+
+ memcpy(®_rules[*n_rules], rule, sizeof(*rule));
+ (*n_rules)++;
+}
+
+/**
+ * regdom_intersect - do the intersection between two regulatory domains
+ * @rd1: first regulatory domain
+ * @rd2: second regulatory domain
+ *
+ * Use this function to get the intersection between two regulatory domains.
+ * Once completed we will mark the alpha2 for the rd as intersected, "98",
+ * as no one single alpha2 can represent this regulatory domain.
+ *
+ * Returns a pointer to the regulatory domain structure which will hold the
+ * resulting intersection of rules between rd1 and rd2. We will
+ * kzalloc() this structure for you.
+ */
+static struct ieee80211_regdomain *
+regdom_intersect(const struct ieee80211_regdomain *rd1,
+ const struct ieee80211_regdomain *rd2)
+{
+ int r, size_of_regd;
+ unsigned int x, y;
+ unsigned int num_rules = 0;
+ const struct ieee80211_reg_rule *rule1, *rule2;
+ struct ieee80211_reg_rule intersected_rule;
+ struct ieee80211_regdomain *rd;
+
+ if (!rd1 || !rd2)
+ return NULL;
+
+ /*
+ * First we get a count of the rules we'll need, then we actually
+ * build them. This is to so we can malloc() and free() a
+ * regdomain once. The reason we use reg_rules_intersect() here
+ * is it will return -EINVAL if the rule computed makes no sense.
+ * All rules that do check out OK are valid.
+ */
+
+ for (x = 0; x < rd1->n_reg_rules; x++) {
+ rule1 = &rd1->reg_rules[x];
+ for (y = 0; y < rd2->n_reg_rules; y++) {
+ rule2 = &rd2->reg_rules[y];
+ if (!reg_rules_intersect(rd1, rd2, rule1, rule2,
+ &intersected_rule))
+ num_rules++;
+ }
+ }
+
+ if (!num_rules)
+ return NULL;
+
+ size_of_regd = sizeof(struct ieee80211_regdomain) +
+ num_rules * sizeof(struct ieee80211_reg_rule);
+
+ rd = kzalloc(size_of_regd, GFP_KERNEL);
+ if (!rd)
+ return NULL;
+
+ for (x = 0; x < rd1->n_reg_rules; x++) {
+ rule1 = &rd1->reg_rules[x];
+ for (y = 0; y < rd2->n_reg_rules; y++) {
+ rule2 = &rd2->reg_rules[y];
+ r = reg_rules_intersect(rd1, rd2, rule1, rule2,
+ &intersected_rule);
+ /*
+ * No need to memset here the intersected rule here as
+ * we're not using the stack anymore
+ */
+ if (r)
+ continue;
+
+ add_rule(&intersected_rule, rd->reg_rules,
+ &rd->n_reg_rules);
+ }
+ }
+
+ rd->alpha2[0] = '9';
+ rd->alpha2[1] = '8';
+ rd->dfs_region = reg_intersect_dfs_region(rd1->dfs_region,
+ rd2->dfs_region);
+
+ return rd;
+}
+
+/*
+ * XXX: add support for the rest of enum nl80211_reg_rule_flags, we may
+ * want to just have the channel structure use these
+ */
+static u32 map_regdom_flags(u32 rd_flags)
+{
+ u32 channel_flags = 0;
+ if (rd_flags & NL80211_RRF_NO_IR_ALL)
+ channel_flags |= IEEE80211_CHAN_NO_IR;
+ if (rd_flags & NL80211_RRF_DFS)
+ channel_flags |= IEEE80211_CHAN_RADAR;
+ if (rd_flags & NL80211_RRF_NO_OFDM)
+ channel_flags |= IEEE80211_CHAN_NO_OFDM;
+ if (rd_flags & NL80211_RRF_NO_OUTDOOR)
+ channel_flags |= IEEE80211_CHAN_INDOOR_ONLY;
+ if (rd_flags & NL80211_RRF_IR_CONCURRENT)
+ channel_flags |= IEEE80211_CHAN_IR_CONCURRENT;
+ if (rd_flags & NL80211_RRF_NO_HT40MINUS)
+ channel_flags |= IEEE80211_CHAN_NO_HT40MINUS;
+ if (rd_flags & NL80211_RRF_NO_HT40PLUS)
+ channel_flags |= IEEE80211_CHAN_NO_HT40PLUS;
+ if (rd_flags & NL80211_RRF_NO_80MHZ)
+ channel_flags |= IEEE80211_CHAN_NO_80MHZ;
+ if (rd_flags & NL80211_RRF_NO_160MHZ)
+ channel_flags |= IEEE80211_CHAN_NO_160MHZ;
+ return channel_flags;
+}
+
+static const struct ieee80211_reg_rule *
+freq_reg_info_regd(u32 center_freq,
+ const struct ieee80211_regdomain *regd, u32 bw)
+{
+ int i;
+ bool band_rule_found = false;
+ bool bw_fits = false;
+
+ if (!regd)
+ return ERR_PTR(-EINVAL);
+
+ for (i = 0; i < regd->n_reg_rules; i++) {
+ const struct ieee80211_reg_rule *rr;
+ const struct ieee80211_freq_range *fr = NULL;
+
+ rr = ®d->reg_rules[i];
+ fr = &rr->freq_range;
+
+ /*
+ * We only need to know if one frequency rule was
+ * was in center_freq's band, that's enough, so lets
+ * not overwrite it once found
+ */
+ if (!band_rule_found)
+ band_rule_found = freq_in_rule_band(fr, center_freq);
+
+ bw_fits = cfg80211_does_bw_fit_range(fr, center_freq, bw);
+
+ if (band_rule_found && bw_fits)
+ return rr;
+ }
+
+ if (!band_rule_found)
+ return ERR_PTR(-ERANGE);
+
+ return ERR_PTR(-EINVAL);
+}
+
+static const struct ieee80211_reg_rule *
+__freq_reg_info(struct wiphy *wiphy, u32 center_freq, u32 min_bw)
+{
+ const struct ieee80211_regdomain *regd = reg_get_regdomain(wiphy);
+ const struct ieee80211_reg_rule *reg_rule = NULL;
+ u32 bw;
+
+ for (bw = MHZ_TO_KHZ(20); bw >= min_bw; bw = bw / 2) {
+ reg_rule = freq_reg_info_regd(center_freq, regd, bw);
+ if (!IS_ERR(reg_rule))
+ return reg_rule;
+ }
+
+ return reg_rule;
+}
+
+const struct ieee80211_reg_rule *freq_reg_info(struct wiphy *wiphy,
+ u32 center_freq)
+{
+ return __freq_reg_info(wiphy, center_freq, MHZ_TO_KHZ(20));
+}
+EXPORT_SYMBOL(freq_reg_info);
+
+const char *reg_initiator_name(enum nl80211_reg_initiator initiator)
+{
+ switch (initiator) {
+ case NL80211_REGDOM_SET_BY_CORE:
+ return "core";
+ case NL80211_REGDOM_SET_BY_USER:
+ return "user";
+ case NL80211_REGDOM_SET_BY_DRIVER:
+ return "driver";
+ case NL80211_REGDOM_SET_BY_COUNTRY_IE:
+ return "country IE";
+ default:
+ WARN_ON(1);
+ return "bug";
+ }
+}
+EXPORT_SYMBOL(reg_initiator_name);
+
+static uint32_t reg_rule_to_chan_bw_flags(const struct ieee80211_regdomain *regd,
+ const struct ieee80211_reg_rule *reg_rule,
+ const struct ieee80211_channel *chan)
+{
+ const struct ieee80211_freq_range *freq_range = NULL;
+ u32 max_bandwidth_khz, bw_flags = 0;
+
+ freq_range = ®_rule->freq_range;
+
+ max_bandwidth_khz = freq_range->max_bandwidth_khz;
+ /* Check if auto calculation requested */
+ if (reg_rule->flags & NL80211_RRF_AUTO_BW)
+ max_bandwidth_khz = reg_get_max_bandwidth(regd, reg_rule);
+
+ /* If we get a reg_rule we can assume that at least 5Mhz fit */
+ if (!cfg80211_does_bw_fit_range(freq_range,
+ MHZ_TO_KHZ(chan->center_freq),
+ MHZ_TO_KHZ(10)))
+ bw_flags |= IEEE80211_CHAN_NO_10MHZ;
+ if (!cfg80211_does_bw_fit_range(freq_range,
+ MHZ_TO_KHZ(chan->center_freq),
+ MHZ_TO_KHZ(20)))
+ bw_flags |= IEEE80211_CHAN_NO_20MHZ;
+
+ if (max_bandwidth_khz < MHZ_TO_KHZ(10))
+ bw_flags |= IEEE80211_CHAN_NO_10MHZ;
+ if (max_bandwidth_khz < MHZ_TO_KHZ(20))
+ bw_flags |= IEEE80211_CHAN_NO_20MHZ;
+ if (max_bandwidth_khz < MHZ_TO_KHZ(40))
+ bw_flags |= IEEE80211_CHAN_NO_HT40;
+ if (max_bandwidth_khz < MHZ_TO_KHZ(80))
+ bw_flags |= IEEE80211_CHAN_NO_80MHZ;
+ if (max_bandwidth_khz < MHZ_TO_KHZ(160))
+ bw_flags |= IEEE80211_CHAN_NO_160MHZ;
+ return bw_flags;
+}
+
+/*
+ * Note that right now we assume the desired channel bandwidth
+ * is always 20 MHz for each individual channel (HT40 uses 20 MHz
+ * per channel, the primary and the extension channel).
+ */
+static void handle_channel(struct wiphy *wiphy,
+ enum nl80211_reg_initiator initiator,
+ struct ieee80211_channel *chan)
+{
+ u32 flags, bw_flags = 0;
+ const struct ieee80211_reg_rule *reg_rule = NULL;
+ const struct ieee80211_power_rule *power_rule = NULL;
+ struct wiphy *request_wiphy = NULL;
+ struct regulatory_request *lr = get_last_request();
+ const struct ieee80211_regdomain *regd;
+
+ request_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx);
+
+ flags = chan->orig_flags;
+
+ reg_rule = freq_reg_info(wiphy, MHZ_TO_KHZ(chan->center_freq));
+ if (IS_ERR(reg_rule)) {
+ /*
+ * We will disable all channels that do not match our
+ * received regulatory rule unless the hint is coming
+ * from a Country IE and the Country IE had no information
+ * about a band. The IEEE 802.11 spec allows for an AP
+ * to send only a subset of the regulatory rules allowed,
+ * so an AP in the US that only supports 2.4 GHz may only send
+ * a country IE with information for the 2.4 GHz band
+ * while 5 GHz is still supported.
+ */
+ if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE &&
+ PTR_ERR(reg_rule) == -ERANGE)
+ return;
+
+ if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER &&
+ request_wiphy && request_wiphy == wiphy &&
+ request_wiphy->regulatory_flags & REGULATORY_STRICT_REG) {
+ pr_debug("Disabling freq %d MHz for good\n",
+ chan->center_freq);
+ chan->orig_flags |= IEEE80211_CHAN_DISABLED;
+ chan->flags = chan->orig_flags;
+ } else {
+ pr_debug("Disabling freq %d MHz\n",
+ chan->center_freq);
+ chan->flags |= IEEE80211_CHAN_DISABLED;
+ }
+ return;
+ }
+
+ regd = reg_get_regdomain(wiphy);
+
+ power_rule = ®_rule->power_rule;
+ bw_flags = reg_rule_to_chan_bw_flags(regd, reg_rule, chan);
+
+ if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER &&
+ request_wiphy && request_wiphy == wiphy &&
+ request_wiphy->regulatory_flags & REGULATORY_STRICT_REG) {
+ /*
+ * This guarantees the driver's requested regulatory domain
+ * will always be used as a base for further regulatory
+ * settings
+ */
+ chan->flags = chan->orig_flags =
+ map_regdom_flags(reg_rule->flags) | bw_flags;
+ chan->max_antenna_gain = chan->orig_mag =
+ (int) MBI_TO_DBI(power_rule->max_antenna_gain);
+ chan->max_reg_power = chan->max_power = chan->orig_mpwr =
+ (int) MBM_TO_DBM(power_rule->max_eirp);
+
+ if (chan->flags & IEEE80211_CHAN_RADAR) {
+ chan->dfs_cac_ms = IEEE80211_DFS_MIN_CAC_TIME_MS;
+ if (reg_rule->dfs_cac_ms)
+ chan->dfs_cac_ms = reg_rule->dfs_cac_ms;
+ }
+
+ return;
+ }
+
+ chan->dfs_state = NL80211_DFS_USABLE;
+ chan->dfs_state_entered = jiffies;
+
+ chan->beacon_found = false;
+ chan->flags = flags | bw_flags | map_regdom_flags(reg_rule->flags);
+ chan->max_antenna_gain =
+ min_t(int, chan->orig_mag,
+ MBI_TO_DBI(power_rule->max_antenna_gain));
+ chan->max_reg_power = (int) MBM_TO_DBM(power_rule->max_eirp);
+
+ if (chan->flags & IEEE80211_CHAN_RADAR) {
+ if (reg_rule->dfs_cac_ms)
+ chan->dfs_cac_ms = reg_rule->dfs_cac_ms;
+ else
+ chan->dfs_cac_ms = IEEE80211_DFS_MIN_CAC_TIME_MS;
+ }
+
+ if (chan->orig_mpwr) {
+ /*
+ * Devices that use REGULATORY_COUNTRY_IE_FOLLOW_POWER
+ * will always follow the passed country IE power settings.
+ */
+ if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE &&
+ wiphy->regulatory_flags & REGULATORY_COUNTRY_IE_FOLLOW_POWER)
+ chan->max_power = chan->max_reg_power;
+ else
+ chan->max_power = min(chan->orig_mpwr,
+ chan->max_reg_power);
+ } else
+ chan->max_power = chan->max_reg_power;
+}
+
+static void handle_band(struct wiphy *wiphy,
+ enum nl80211_reg_initiator initiator,
+ struct ieee80211_supported_band *sband)
+{
+ unsigned int i;
+
+ if (!sband)
+ return;
+
+ for (i = 0; i < sband->n_channels; i++)
+ handle_channel(wiphy, initiator, &sband->channels[i]);
+}
+
+static bool reg_request_cell_base(struct regulatory_request *request)
+{
+ if (request->initiator != NL80211_REGDOM_SET_BY_USER)
+ return false;
+ return request->user_reg_hint_type == NL80211_USER_REG_HINT_CELL_BASE;
+}
+
+bool reg_last_request_cell_base(void)
+{
+ return reg_request_cell_base(get_last_request());
+}
+
+#ifdef CPTCFG_CFG80211_REG_CELLULAR_HINTS
+/* Core specific check */
+static enum reg_request_treatment
+reg_ignore_cell_hint(struct regulatory_request *pending_request)
+{
+ struct regulatory_request *lr = get_last_request();
+
+ if (!reg_num_devs_support_basehint)
+ return REG_REQ_IGNORE;
+
+ if (reg_request_cell_base(lr) &&
+ !regdom_changes(pending_request->alpha2))
+ return REG_REQ_ALREADY_SET;
+
+ return REG_REQ_OK;
+}
+
+/* Device specific check */
+static bool reg_dev_ignore_cell_hint(struct wiphy *wiphy)
+{
+ return !(wiphy->features & NL80211_FEATURE_CELL_BASE_REG_HINTS);
+}
+#else
+static enum reg_request_treatment
+reg_ignore_cell_hint(struct regulatory_request *pending_request)
+{
+ return REG_REQ_IGNORE;
+}
+
+static bool reg_dev_ignore_cell_hint(struct wiphy *wiphy)
+{
+ return true;
+}
+#endif
+
+static bool wiphy_strict_alpha2_regd(struct wiphy *wiphy)
+{
+ if (wiphy->regulatory_flags & REGULATORY_STRICT_REG &&
+ !(wiphy->regulatory_flags & REGULATORY_CUSTOM_REG))
+ return true;
+ return false;
+}
+
+static bool ignore_reg_update(struct wiphy *wiphy,
+ enum nl80211_reg_initiator initiator)
+{
+ struct regulatory_request *lr = get_last_request();
+
+ if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED)
+ return true;
+
+ if (!lr) {
+ pr_debug("Ignoring regulatory request set by %s since last_request is not set\n",
+ reg_initiator_name(initiator));
+ return true;
+ }
+
+ if (initiator == NL80211_REGDOM_SET_BY_CORE &&
+ wiphy->regulatory_flags & REGULATORY_CUSTOM_REG) {
+ pr_debug("Ignoring regulatory request set by %s since the driver uses its own custom regulatory domain\n",
+ reg_initiator_name(initiator));
+ return true;
+ }
+
+ /*
+ * wiphy->regd will be set once the device has its own
+ * desired regulatory domain set
+ */
+ if (wiphy_strict_alpha2_regd(wiphy) && !wiphy->regd &&
+ initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE &&
+ !is_world_regdom(lr->alpha2)) {
+ pr_debug("Ignoring regulatory request set by %s since the driver requires its own regulatory domain to be set first\n",
+ reg_initiator_name(initiator));
+ return true;
+ }
+
+ if (reg_request_cell_base(lr))
+ return reg_dev_ignore_cell_hint(wiphy);
+
+ return false;
+}
+
+static bool reg_is_world_roaming(struct wiphy *wiphy)
+{
+ const struct ieee80211_regdomain *cr = get_cfg80211_regdom();
+ const struct ieee80211_regdomain *wr = get_wiphy_regdom(wiphy);
+ struct regulatory_request *lr = get_last_request();
+
+ if (is_world_regdom(cr->alpha2) || (wr && is_world_regdom(wr->alpha2)))
+ return true;
+
+ if (lr && lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE &&
+ wiphy->regulatory_flags & REGULATORY_CUSTOM_REG)
+ return true;
+
+ return false;
+}
+
+static void handle_reg_beacon(struct wiphy *wiphy, unsigned int chan_idx,
+ struct reg_beacon *reg_beacon)
+{
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_channel *chan;
+ bool channel_changed = false;
+ struct ieee80211_channel chan_before;
+
+ sband = wiphy->bands[reg_beacon->chan.band];
+ chan = &sband->channels[chan_idx];
+
+ if (likely(chan->center_freq != reg_beacon->chan.center_freq))
+ return;
+
+ if (chan->beacon_found)
+ return;
+
+ chan->beacon_found = true;
+
+ if (!reg_is_world_roaming(wiphy))
+ return;
+
+ if (wiphy->regulatory_flags & REGULATORY_DISABLE_BEACON_HINTS)
+ return;
+
+ chan_before.center_freq = chan->center_freq;
+ chan_before.flags = chan->flags;
+
+ if (chan->flags & IEEE80211_CHAN_NO_IR) {
+ chan->flags &= ~IEEE80211_CHAN_NO_IR;
+ channel_changed = true;
+ }
+
+ if (channel_changed)
+ nl80211_send_beacon_hint_event(wiphy, &chan_before, chan);
+}
+
+/*
+ * Called when a scan on a wiphy finds a beacon on
+ * new channel
+ */
+static void wiphy_update_new_beacon(struct wiphy *wiphy,
+ struct reg_beacon *reg_beacon)
+{
+ unsigned int i;
+ struct ieee80211_supported_band *sband;
+
+ if (!wiphy->bands[reg_beacon->chan.band])
+ return;
+
+ sband = wiphy->bands[reg_beacon->chan.band];
+
+ for (i = 0; i < sband->n_channels; i++)
+ handle_reg_beacon(wiphy, i, reg_beacon);
+}
+
+/*
+ * Called upon reg changes or a new wiphy is added
+ */
+static void wiphy_update_beacon_reg(struct wiphy *wiphy)
+{
+ unsigned int i;
+ struct ieee80211_supported_band *sband;
+ struct reg_beacon *reg_beacon;
+
+ list_for_each_entry(reg_beacon, ®_beacon_list, list) {
+ if (!wiphy->bands[reg_beacon->chan.band])
+ continue;
+ sband = wiphy->bands[reg_beacon->chan.band];
+ for (i = 0; i < sband->n_channels; i++)
+ handle_reg_beacon(wiphy, i, reg_beacon);
+ }
+}
+
+/* Reap the advantages of previously found beacons */
+static void reg_process_beacons(struct wiphy *wiphy)
+{
+ /*
+ * Means we are just firing up cfg80211, so no beacons would
+ * have been processed yet.
+ */
+ if (!last_request)
+ return;
+ wiphy_update_beacon_reg(wiphy);
+}
+
+static bool is_ht40_allowed(struct ieee80211_channel *chan)
+{
+ if (!chan)
+ return false;
+ if (chan->flags & IEEE80211_CHAN_DISABLED)
+ return false;
+ /* This would happen when regulatory rules disallow HT40 completely */
+ if ((chan->flags & IEEE80211_CHAN_NO_HT40) == IEEE80211_CHAN_NO_HT40)
+ return false;
+ return true;
+}
+
+static void reg_process_ht_flags_channel(struct wiphy *wiphy,
+ struct ieee80211_channel *channel)
+{
+ struct ieee80211_supported_band *sband = wiphy->bands[channel->band];
+ struct ieee80211_channel *channel_before = NULL, *channel_after = NULL;
+ unsigned int i;
+
+ if (!is_ht40_allowed(channel)) {
+ channel->flags |= IEEE80211_CHAN_NO_HT40;
+ return;
+ }
+
+ /*
+ * We need to ensure the extension channels exist to
+ * be able to use HT40- or HT40+, this finds them (or not)
+ */
+ for (i = 0; i < sband->n_channels; i++) {
+ struct ieee80211_channel *c = &sband->channels[i];
+
+ if (c->center_freq == (channel->center_freq - 20))
+ channel_before = c;
+ if (c->center_freq == (channel->center_freq + 20))
+ channel_after = c;
+ }
+
+ /*
+ * Please note that this assumes target bandwidth is 20 MHz,
+ * if that ever changes we also need to change the below logic
+ * to include that as well.
+ */
+ if (!is_ht40_allowed(channel_before))
+ channel->flags |= IEEE80211_CHAN_NO_HT40MINUS;
+ else
+ channel->flags &= ~IEEE80211_CHAN_NO_HT40MINUS;
+
+ if (!is_ht40_allowed(channel_after))
+ channel->flags |= IEEE80211_CHAN_NO_HT40PLUS;
+ else
+ channel->flags &= ~IEEE80211_CHAN_NO_HT40PLUS;
+}
+
+static void reg_process_ht_flags_band(struct wiphy *wiphy,
+ struct ieee80211_supported_band *sband)
+{
+ unsigned int i;
+
+ if (!sband)
+ return;
+
+ for (i = 0; i < sband->n_channels; i++)
+ reg_process_ht_flags_channel(wiphy, &sband->channels[i]);
+}
+
+static void reg_process_ht_flags(struct wiphy *wiphy)
+{
+ enum nl80211_band band;
+
+ if (!wiphy)
+ return;
+
+ for (band = 0; band < NUM_NL80211_BANDS; band++)
+ reg_process_ht_flags_band(wiphy, wiphy->bands[band]);
+}
+
+static void reg_call_notifier(struct wiphy *wiphy,
+ struct regulatory_request *request)
+{
+ if (wiphy->reg_notifier)
+ wiphy->reg_notifier(wiphy, request);
+}
+
+static bool reg_wdev_chan_valid(struct wiphy *wiphy, struct wireless_dev *wdev)
+{
+ struct cfg80211_chan_def chandef;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+ enum nl80211_iftype iftype;
+
+ wdev_lock(wdev);
+ iftype = wdev->iftype;
+
+ /* make sure the interface is active */
+ if (!wdev->netdev || !netif_running(wdev->netdev))
+ goto wdev_inactive_unlock;
+
+ switch (iftype) {
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_P2P_GO:
+ if (!wdev->beacon_interval)
+ goto wdev_inactive_unlock;
+ chandef = wdev->chandef;
+ break;
+ case NL80211_IFTYPE_ADHOC:
+ if (!wdev->ssid_len)
+ goto wdev_inactive_unlock;
+ chandef = wdev->chandef;
+ break;
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_P2P_CLIENT:
+ if (!wdev->current_bss ||
+ !wdev->current_bss->pub.channel)
+ goto wdev_inactive_unlock;
+
+ if (!rdev->ops->get_channel ||
+ rdev_get_channel(rdev, wdev, &chandef))
+ cfg80211_chandef_create(&chandef,
+ wdev->current_bss->pub.channel,
+ NL80211_CHAN_NO_HT);
+ break;
+ case NL80211_IFTYPE_MONITOR:
+ case NL80211_IFTYPE_AP_VLAN:
+ case NL80211_IFTYPE_P2P_DEVICE:
+ /* no enforcement required */
+ break;
+ default:
+ /* others not implemented for now */
+ WARN_ON(1);
+ break;
+ }
+
+ wdev_unlock(wdev);
+
+ switch (iftype) {
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_P2P_GO:
+ case NL80211_IFTYPE_ADHOC:
+ return cfg80211_reg_can_beacon_relax(wiphy, &chandef, iftype);
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_P2P_CLIENT:
+ return cfg80211_chandef_usable(wiphy, &chandef,
+ IEEE80211_CHAN_DISABLED);
+ default:
+ break;
+ }
+
+ return true;
+
+wdev_inactive_unlock:
+ wdev_unlock(wdev);
+ return true;
+}
+
+static void reg_leave_invalid_chans(struct wiphy *wiphy)
+{
+ struct wireless_dev *wdev;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+
+ ASSERT_RTNL();
+
+ list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list)
+ if (!reg_wdev_chan_valid(wiphy, wdev))
+ cfg80211_leave(rdev, wdev);
+}
+
+static void reg_check_chans_work(struct work_struct *work)
+{
+ struct cfg80211_registered_device *rdev;
+
+ pr_debug("Verifying active interfaces after reg change\n");
+ rtnl_lock();
+
+ list_for_each_entry(rdev, &cfg80211_rdev_list, list)
+ if (!(rdev->wiphy.regulatory_flags &
+ REGULATORY_IGNORE_STALE_KICKOFF))
+ reg_leave_invalid_chans(&rdev->wiphy);
+
+ rtnl_unlock();
+}
+
+static void reg_check_channels(void)
+{
+ /*
+ * Give usermode a chance to do something nicer (move to another
+ * channel, orderly disconnection), before forcing a disconnection.
+ */
+ mod_delayed_work(system_power_efficient_wq,
+ ®_check_chans,
+ msecs_to_jiffies(REG_ENFORCE_GRACE_MS));
+}
+
+static void wiphy_update_regulatory(struct wiphy *wiphy,
+ enum nl80211_reg_initiator initiator)
+{
+ enum nl80211_band band;
+ struct regulatory_request *lr = get_last_request();
+
+ if (ignore_reg_update(wiphy, initiator)) {
+ /*
+ * Regulatory updates set by CORE are ignored for custom
+ * regulatory cards. Let us notify the changes to the driver,
+ * as some drivers used this to restore its orig_* reg domain.
+ */
+ if (initiator == NL80211_REGDOM_SET_BY_CORE &&
+ wiphy->regulatory_flags & REGULATORY_CUSTOM_REG)
+ reg_call_notifier(wiphy, lr);
+ return;
+ }
+
+ lr->dfs_region = get_cfg80211_regdom()->dfs_region;
+
+ for (band = 0; band < NUM_NL80211_BANDS; band++)
+ handle_band(wiphy, initiator, wiphy->bands[band]);
+
+ reg_process_beacons(wiphy);
+ reg_process_ht_flags(wiphy);
+ reg_call_notifier(wiphy, lr);
+}
+
+static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator)
+{
+ struct cfg80211_registered_device *rdev;
+ struct wiphy *wiphy;
+
+ ASSERT_RTNL();
+
+ list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
+ wiphy = &rdev->wiphy;
+ wiphy_update_regulatory(wiphy, initiator);
+ }
+
+ reg_check_channels();
+}
+
+static void handle_channel_custom(struct wiphy *wiphy,
+ struct ieee80211_channel *chan,
+ const struct ieee80211_regdomain *regd)
+{
+ u32 bw_flags = 0;
+ const struct ieee80211_reg_rule *reg_rule = NULL;
+ const struct ieee80211_power_rule *power_rule = NULL;
+ u32 bw;
+
+ for (bw = MHZ_TO_KHZ(20); bw >= MHZ_TO_KHZ(5); bw = bw / 2) {
+ reg_rule = freq_reg_info_regd(MHZ_TO_KHZ(chan->center_freq),
+ regd, bw);
+ if (!IS_ERR(reg_rule))
+ break;
+ }
+
+ if (IS_ERR(reg_rule)) {
+ pr_debug("Disabling freq %d MHz as custom regd has no rule that fits it\n",
+ chan->center_freq);
+ if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) {
+ chan->flags |= IEEE80211_CHAN_DISABLED;
+ } else {
+ chan->orig_flags |= IEEE80211_CHAN_DISABLED;
+ chan->flags = chan->orig_flags;
+ }
+ return;
+ }
+
+ power_rule = ®_rule->power_rule;
+ bw_flags = reg_rule_to_chan_bw_flags(regd, reg_rule, chan);
+
+ chan->dfs_state_entered = jiffies;
+ chan->dfs_state = NL80211_DFS_USABLE;
+
+ chan->beacon_found = false;
+
+ if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED)
+ chan->flags = chan->orig_flags | bw_flags |
+ map_regdom_flags(reg_rule->flags);
+ else
+ chan->flags |= map_regdom_flags(reg_rule->flags) | bw_flags;
+
+ chan->max_antenna_gain = (int) MBI_TO_DBI(power_rule->max_antenna_gain);
+ chan->max_reg_power = chan->max_power =
+ (int) MBM_TO_DBM(power_rule->max_eirp);
+
+ if (chan->flags & IEEE80211_CHAN_RADAR) {
+ if (reg_rule->dfs_cac_ms)
+ chan->dfs_cac_ms = reg_rule->dfs_cac_ms;
+ else
+ chan->dfs_cac_ms = IEEE80211_DFS_MIN_CAC_TIME_MS;
+ }
+
+ chan->max_power = chan->max_reg_power;
+}
+
+static void handle_band_custom(struct wiphy *wiphy,
+ struct ieee80211_supported_band *sband,
+ const struct ieee80211_regdomain *regd)
+{
+ unsigned int i;
+
+ if (!sband)
+ return;
+
+ for (i = 0; i < sband->n_channels; i++)
+ handle_channel_custom(wiphy, &sband->channels[i], regd);
+}
+
+/* Used by drivers prior to wiphy registration */
+void wiphy_apply_custom_regulatory(struct wiphy *wiphy,
+ const struct ieee80211_regdomain *regd)
+{
+ enum nl80211_band band;
+ unsigned int bands_set = 0;
+
+ WARN(!(wiphy->regulatory_flags & REGULATORY_CUSTOM_REG),
+ "wiphy should have REGULATORY_CUSTOM_REG\n");
+ wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
+
+ for (band = 0; band < NUM_NL80211_BANDS; band++) {
+ if (!wiphy->bands[band])
+ continue;
+ handle_band_custom(wiphy, wiphy->bands[band], regd);
+ bands_set++;
+ }
+
+ /*
+ * no point in calling this if it won't have any effect
+ * on your device's supported bands.
+ */
+ WARN_ON(!bands_set);
+}
+EXPORT_SYMBOL(wiphy_apply_custom_regulatory);
+
+static void reg_set_request_processed(void)
+{
+ bool need_more_processing = false;
+ struct regulatory_request *lr = get_last_request();
+
+ lr->processed = true;
+
+ spin_lock(®_requests_lock);
+ if (!list_empty(®_requests_list))
+ need_more_processing = true;
+ spin_unlock(®_requests_lock);
+
+ cancel_crda_timeout();
+
+ if (need_more_processing)
+ schedule_work(®_work);
+}
+
+/**
+ * reg_process_hint_core - process core regulatory requests
+ * @pending_request: a pending core regulatory request
+ *
+ * The wireless subsystem can use this function to process
+ * a regulatory request issued by the regulatory core.
+ */
+static enum reg_request_treatment
+reg_process_hint_core(struct regulatory_request *core_request)
+{
+ if (reg_query_database(core_request)) {
+ core_request->intersect = false;
+ core_request->processed = false;
+ reg_update_last_request(core_request);
+ return REG_REQ_OK;
+ }
+
+ return REG_REQ_IGNORE;
+}
+
+static enum reg_request_treatment
+__reg_process_hint_user(struct regulatory_request *user_request)
+{
+ struct regulatory_request *lr = get_last_request();
+
+ if (reg_request_cell_base(user_request))
+ return reg_ignore_cell_hint(user_request);
+
+ if (reg_request_cell_base(lr))
+ return REG_REQ_IGNORE;
+
+ if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE)
+ return REG_REQ_INTERSECT;
+ /*
+ * If the user knows better the user should set the regdom
+ * to their country before the IE is picked up
+ */
+ if (lr->initiator == NL80211_REGDOM_SET_BY_USER &&
+ lr->intersect)
+ return REG_REQ_IGNORE;
+ /*
+ * Process user requests only after previous user/driver/core
+ * requests have been processed
+ */
+ if ((lr->initiator == NL80211_REGDOM_SET_BY_CORE ||
+ lr->initiator == NL80211_REGDOM_SET_BY_DRIVER ||
+ lr->initiator == NL80211_REGDOM_SET_BY_USER) &&
+ regdom_changes(lr->alpha2))
+ return REG_REQ_IGNORE;
+
+ if (!regdom_changes(user_request->alpha2))
+ return REG_REQ_ALREADY_SET;
+
+ return REG_REQ_OK;
+}
+
+/**
+ * reg_process_hint_user - process user regulatory requests
+ * @user_request: a pending user regulatory request
+ *
+ * The wireless subsystem can use this function to process
+ * a regulatory request initiated by userspace.
+ */
+static enum reg_request_treatment
+reg_process_hint_user(struct regulatory_request *user_request)
+{
+ enum reg_request_treatment treatment;
+
+ treatment = __reg_process_hint_user(user_request);
+ if (treatment == REG_REQ_IGNORE ||
+ treatment == REG_REQ_ALREADY_SET)
+ return REG_REQ_IGNORE;
+
+ user_request->intersect = treatment == REG_REQ_INTERSECT;
+ user_request->processed = false;
+
+ if (reg_query_database(user_request)) {
+ reg_update_last_request(user_request);
+ user_alpha2[0] = user_request->alpha2[0];
+ user_alpha2[1] = user_request->alpha2[1];
+ return REG_REQ_OK;
+ }
+
+ return REG_REQ_IGNORE;
+}
+
+static enum reg_request_treatment
+__reg_process_hint_driver(struct regulatory_request *driver_request)
+{
+ struct regulatory_request *lr = get_last_request();
+
+ if (lr->initiator == NL80211_REGDOM_SET_BY_CORE) {
+ if (regdom_changes(driver_request->alpha2))
+ return REG_REQ_OK;
+ return REG_REQ_ALREADY_SET;
+ }
+
+ /*
+ * This would happen if you unplug and plug your card
+ * back in or if you add a new device for which the previously
+ * loaded card also agrees on the regulatory domain.
+ */
+ if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER &&
+ !regdom_changes(driver_request->alpha2))
+ return REG_REQ_ALREADY_SET;
+
+ return REG_REQ_INTERSECT;
+}
+
+/**
+ * reg_process_hint_driver - process driver regulatory requests
+ * @driver_request: a pending driver regulatory request
+ *
+ * The wireless subsystem can use this function to process
+ * a regulatory request issued by an 802.11 driver.
+ *
+ * Returns one of the different reg request treatment values.
+ */
+static enum reg_request_treatment
+reg_process_hint_driver(struct wiphy *wiphy,
+ struct regulatory_request *driver_request)
+{
+ const struct ieee80211_regdomain *regd, *tmp;
+ enum reg_request_treatment treatment;
+
+ treatment = __reg_process_hint_driver(driver_request);
+
+ switch (treatment) {
+ case REG_REQ_OK:
+ break;
+ case REG_REQ_IGNORE:
+ return REG_REQ_IGNORE;
+ case REG_REQ_INTERSECT:
+ case REG_REQ_ALREADY_SET:
+ regd = reg_copy_regd(get_cfg80211_regdom());
+ if (IS_ERR(regd))
+ return REG_REQ_IGNORE;
+
+ tmp = get_wiphy_regdom(wiphy);
+ rcu_assign_pointer(wiphy->regd, regd);
+ rcu_free_regdom(tmp);
+ }
+
+
+ driver_request->intersect = treatment == REG_REQ_INTERSECT;
+ driver_request->processed = false;
+
+ /*
+ * Since CRDA will not be called in this case as we already
+ * have applied the requested regulatory domain before we just
+ * inform userspace we have processed the request
+ */
+ if (treatment == REG_REQ_ALREADY_SET) {
+ nl80211_send_reg_change_event(driver_request);
+ reg_update_last_request(driver_request);
+ reg_set_request_processed();
+ return REG_REQ_ALREADY_SET;
+ }
+
+ if (reg_query_database(driver_request)) {
+ reg_update_last_request(driver_request);
+ return REG_REQ_OK;
+ }
+
+ return REG_REQ_IGNORE;
+}
+
+static enum reg_request_treatment
+__reg_process_hint_country_ie(struct wiphy *wiphy,
+ struct regulatory_request *country_ie_request)
+{
+ struct wiphy *last_wiphy = NULL;
+ struct regulatory_request *lr = get_last_request();
+
+ if (reg_request_cell_base(lr)) {
+ /* Trust a Cell base station over the AP's country IE */
+ if (regdom_changes(country_ie_request->alpha2))
+ return REG_REQ_IGNORE;
+ return REG_REQ_ALREADY_SET;
+ } else {
+ if (wiphy->regulatory_flags & REGULATORY_COUNTRY_IE_IGNORE)
+ return REG_REQ_IGNORE;
+ }
+
+ if (unlikely(!is_an_alpha2(country_ie_request->alpha2)))
+ return -EINVAL;
+
+ if (lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE)
+ return REG_REQ_OK;
+
+ last_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx);
+
+ if (last_wiphy != wiphy) {
+ /*
+ * Two cards with two APs claiming different
+ * Country IE alpha2s. We could
+ * intersect them, but that seems unlikely
+ * to be correct. Reject second one for now.
+ */
+ if (regdom_changes(country_ie_request->alpha2))
+ return REG_REQ_IGNORE;
+ return REG_REQ_ALREADY_SET;
+ }
+
+ if (regdom_changes(country_ie_request->alpha2))
+ return REG_REQ_OK;
+ return REG_REQ_ALREADY_SET;
+}
+
+/**
+ * reg_process_hint_country_ie - process regulatory requests from country IEs
+ * @country_ie_request: a regulatory request from a country IE
+ *
+ * The wireless subsystem can use this function to process
+ * a regulatory request issued by a country Information Element.
+ *
+ * Returns one of the different reg request treatment values.
+ */
+static enum reg_request_treatment
+reg_process_hint_country_ie(struct wiphy *wiphy,
+ struct regulatory_request *country_ie_request)
+{
+ enum reg_request_treatment treatment;
+
+ treatment = __reg_process_hint_country_ie(wiphy, country_ie_request);
+
+ switch (treatment) {
+ case REG_REQ_OK:
+ break;
+ case REG_REQ_IGNORE:
+ return REG_REQ_IGNORE;
+ case REG_REQ_ALREADY_SET:
+ reg_free_request(country_ie_request);
+ return REG_REQ_ALREADY_SET;
+ case REG_REQ_INTERSECT:
+ /*
+ * This doesn't happen yet, not sure we
+ * ever want to support it for this case.
+ */
+ WARN_ONCE(1, "Unexpected intersection for country IEs");
+ return REG_REQ_IGNORE;
+ }
+
+ country_ie_request->intersect = false;
+ country_ie_request->processed = false;
+
+ if (reg_query_database(country_ie_request)) {
+ reg_update_last_request(country_ie_request);
+ return REG_REQ_OK;
+ }
+
+ return REG_REQ_IGNORE;
+}
+
+bool reg_dfs_domain_same(struct wiphy *wiphy1, struct wiphy *wiphy2)
+{
+ const struct ieee80211_regdomain *wiphy1_regd = NULL;
+ const struct ieee80211_regdomain *wiphy2_regd = NULL;
+ const struct ieee80211_regdomain *cfg80211_regd = NULL;
+ bool dfs_domain_same;
+
+ rcu_read_lock();
+
+ cfg80211_regd = rcu_dereference(cfg80211_regdomain);
+ wiphy1_regd = rcu_dereference(wiphy1->regd);
+ if (!wiphy1_regd)
+ wiphy1_regd = cfg80211_regd;
+
+ wiphy2_regd = rcu_dereference(wiphy2->regd);
+ if (!wiphy2_regd)
+ wiphy2_regd = cfg80211_regd;
+
+ dfs_domain_same = wiphy1_regd->dfs_region == wiphy2_regd->dfs_region;
+
+ rcu_read_unlock();
+
+ return dfs_domain_same;
+}
+
+static void reg_copy_dfs_chan_state(struct ieee80211_channel *dst_chan,
+ struct ieee80211_channel *src_chan)
+{
+ if (!(dst_chan->flags & IEEE80211_CHAN_RADAR) ||
+ !(src_chan->flags & IEEE80211_CHAN_RADAR))
+ return;
+
+ if (dst_chan->flags & IEEE80211_CHAN_DISABLED ||
+ src_chan->flags & IEEE80211_CHAN_DISABLED)
+ return;
+
+ if (src_chan->center_freq == dst_chan->center_freq &&
+ dst_chan->dfs_state == NL80211_DFS_USABLE) {
+ dst_chan->dfs_state = src_chan->dfs_state;
+ dst_chan->dfs_state_entered = src_chan->dfs_state_entered;
+ }
+}
+
+static void wiphy_share_dfs_chan_state(struct wiphy *dst_wiphy,
+ struct wiphy *src_wiphy)
+{
+ struct ieee80211_supported_band *src_sband, *dst_sband;
+ struct ieee80211_channel *src_chan, *dst_chan;
+ int i, j, band;
+
+ if (!reg_dfs_domain_same(dst_wiphy, src_wiphy))
+ return;
+
+ for (band = 0; band < NUM_NL80211_BANDS; band++) {
+ dst_sband = dst_wiphy->bands[band];
+ src_sband = src_wiphy->bands[band];
+ if (!dst_sband || !src_sband)
+ continue;
+
+ for (i = 0; i < dst_sband->n_channels; i++) {
+ dst_chan = &dst_sband->channels[i];
+ for (j = 0; j < src_sband->n_channels; j++) {
+ src_chan = &src_sband->channels[j];
+ reg_copy_dfs_chan_state(dst_chan, src_chan);
+ }
+ }
+ }
+}
+
+static void wiphy_all_share_dfs_chan_state(struct wiphy *wiphy)
+{
+ struct cfg80211_registered_device *rdev;
+
+ ASSERT_RTNL();
+
+ list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
+ if (wiphy == &rdev->wiphy)
+ continue;
+ wiphy_share_dfs_chan_state(wiphy, &rdev->wiphy);
+ }
+}
+
+/* This processes *all* regulatory hints */
+static void reg_process_hint(struct regulatory_request *reg_request)
+{
+ struct wiphy *wiphy = NULL;
+ enum reg_request_treatment treatment;
+
+ if (reg_request->wiphy_idx != WIPHY_IDX_INVALID)
+ wiphy = wiphy_idx_to_wiphy(reg_request->wiphy_idx);
+
+ switch (reg_request->initiator) {
+ case NL80211_REGDOM_SET_BY_CORE:
+ treatment = reg_process_hint_core(reg_request);
+ break;
+ case NL80211_REGDOM_SET_BY_USER:
+ treatment = reg_process_hint_user(reg_request);
+ break;
+ case NL80211_REGDOM_SET_BY_DRIVER:
+ if (!wiphy)
+ goto out_free;
+ treatment = reg_process_hint_driver(wiphy, reg_request);
+ break;
+ case NL80211_REGDOM_SET_BY_COUNTRY_IE:
+ if (!wiphy)
+ goto out_free;
+ treatment = reg_process_hint_country_ie(wiphy, reg_request);
+ break;
+ default:
+ WARN(1, "invalid initiator %d\n", reg_request->initiator);
+ goto out_free;
+ }
+
+ if (treatment == REG_REQ_IGNORE)
+ goto out_free;
+
+ WARN(treatment != REG_REQ_OK && treatment != REG_REQ_ALREADY_SET,
+ "unexpected treatment value %d\n", treatment);
+
+ /* This is required so that the orig_* parameters are saved.
+ * NOTE: treatment must be set for any case that reaches here!
+ */
+ if (treatment == REG_REQ_ALREADY_SET && wiphy &&
+ wiphy->regulatory_flags & REGULATORY_STRICT_REG) {
+ wiphy_update_regulatory(wiphy, reg_request->initiator);
+ wiphy_all_share_dfs_chan_state(wiphy);
+ reg_check_channels();
+ }
+
+ return;
+
+out_free:
+ reg_free_request(reg_request);
+}
+
+static bool reg_only_self_managed_wiphys(void)
+{
+ struct cfg80211_registered_device *rdev;
+ struct wiphy *wiphy;
+ bool self_managed_found = false;
+
+ ASSERT_RTNL();
+
+ list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
+ wiphy = &rdev->wiphy;
+ if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED)
+ self_managed_found = true;
+ else
+ return false;
+ }
+
+ /* make sure at least one self-managed wiphy exists */
+ return self_managed_found;
+}
+
+/*
+ * Processes regulatory hints, this is all the NL80211_REGDOM_SET_BY_*
+ * Regulatory hints come on a first come first serve basis and we
+ * must process each one atomically.
+ */
+static void reg_process_pending_hints(void)
+{
+ struct regulatory_request *reg_request, *lr;
+
+ lr = get_last_request();
+
+ /* When last_request->processed becomes true this will be rescheduled */
+ if (lr && !lr->processed) {
+ reg_process_hint(lr);
+ return;
+ }
+
+ spin_lock(®_requests_lock);
+
+ if (list_empty(®_requests_list)) {
+ spin_unlock(®_requests_lock);
+ return;
+ }
+
+ reg_request = list_first_entry(®_requests_list,
+ struct regulatory_request,
+ list);
+ list_del_init(®_request->list);
+
+ spin_unlock(®_requests_lock);
+
+ if (reg_only_self_managed_wiphys()) {
+ reg_free_request(reg_request);
+ return;
+ }
+
+ reg_process_hint(reg_request);
+
+ lr = get_last_request();
+
+ spin_lock(®_requests_lock);
+ if (!list_empty(®_requests_list) && lr && lr->processed)
+ schedule_work(®_work);
+ spin_unlock(®_requests_lock);
+}
+
+/* Processes beacon hints -- this has nothing to do with country IEs */
+static void reg_process_pending_beacon_hints(void)
+{
+ struct cfg80211_registered_device *rdev;
+ struct reg_beacon *pending_beacon, *tmp;
+
+ /* This goes through the _pending_ beacon list */
+ spin_lock_bh(®_pending_beacons_lock);
+
+ list_for_each_entry_safe(pending_beacon, tmp,
+ ®_pending_beacons, list) {
+ list_del_init(&pending_beacon->list);
+
+ /* Applies the beacon hint to current wiphys */
+ list_for_each_entry(rdev, &cfg80211_rdev_list, list)
+ wiphy_update_new_beacon(&rdev->wiphy, pending_beacon);
+
+ /* Remembers the beacon hint for new wiphys or reg changes */
+ list_add_tail(&pending_beacon->list, ®_beacon_list);
+ }
+
+ spin_unlock_bh(®_pending_beacons_lock);
+}
+
+static void reg_process_self_managed_hints(void)
+{
+ struct cfg80211_registered_device *rdev;
+ struct wiphy *wiphy;
+ const struct ieee80211_regdomain *tmp;
+ const struct ieee80211_regdomain *regd;
+ enum nl80211_band band;
+ struct regulatory_request request = {};
+
+ list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
+ wiphy = &rdev->wiphy;
+
+ spin_lock(®_requests_lock);
+ regd = rdev->requested_regd;
+ rdev->requested_regd = NULL;
+ spin_unlock(®_requests_lock);
+
+ if (regd == NULL)
+ continue;
+
+ tmp = get_wiphy_regdom(wiphy);
+ rcu_assign_pointer(wiphy->regd, regd);
+ rcu_free_regdom(tmp);
+
+ for (band = 0; band < NUM_NL80211_BANDS; band++)
+ handle_band_custom(wiphy, wiphy->bands[band], regd);
+
+ reg_process_ht_flags(wiphy);
+
+ request.wiphy_idx = get_wiphy_idx(wiphy);
+ request.alpha2[0] = regd->alpha2[0];
+ request.alpha2[1] = regd->alpha2[1];
+ request.initiator = NL80211_REGDOM_SET_BY_DRIVER;
+
+ nl80211_send_wiphy_reg_change_event(&request);
+ }
+
+ reg_check_channels();
+}
+
+static void reg_todo(struct work_struct *work)
+{
+ rtnl_lock();
+ reg_process_pending_hints();
+ reg_process_pending_beacon_hints();
+ reg_process_self_managed_hints();
+ rtnl_unlock();
+}
+
+static void queue_regulatory_request(struct regulatory_request *request)
+{
+ request->alpha2[0] = toupper(request->alpha2[0]);
+ request->alpha2[1] = toupper(request->alpha2[1]);
+
+ spin_lock(®_requests_lock);
+ list_add_tail(&request->list, ®_requests_list);
+ spin_unlock(®_requests_lock);
+
+ schedule_work(®_work);
+}
+
+/*
+ * Core regulatory hint -- happens during cfg80211_init()
+ * and when we restore regulatory settings.
+ */
+static int regulatory_hint_core(const char *alpha2)
+{
+ struct regulatory_request *request;
+
+ request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL);
+ if (!request)
+ return -ENOMEM;
+
+ request->alpha2[0] = alpha2[0];
+ request->alpha2[1] = alpha2[1];
+ request->initiator = NL80211_REGDOM_SET_BY_CORE;
+
+ queue_regulatory_request(request);
+
+ return 0;
+}
+
+/* User hints */
+int regulatory_hint_user(const char *alpha2,
+ enum nl80211_user_reg_hint_type user_reg_hint_type)
+{
+ struct regulatory_request *request;
+
+ if (WARN_ON(!alpha2))
+ return -EINVAL;
+
+ request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL);
+ if (!request)
+ return -ENOMEM;
+
+ request->wiphy_idx = WIPHY_IDX_INVALID;
+ request->alpha2[0] = alpha2[0];
+ request->alpha2[1] = alpha2[1];
+ request->initiator = NL80211_REGDOM_SET_BY_USER;
+ request->user_reg_hint_type = user_reg_hint_type;
+
+ /* Allow calling CRDA again */
+ reset_crda_timeouts();
+
+ queue_regulatory_request(request);
+
+ return 0;
+}
+
+int regulatory_hint_indoor(bool is_indoor, u32 portid)
+{
+ spin_lock(®_indoor_lock);
+
+ /* It is possible that more than one user space process is trying to
+ * configure the indoor setting. To handle such cases, clear the indoor
+ * setting in case that some process does not think that the device
+ * is operating in an indoor environment. In addition, if a user space
+ * process indicates that it is controlling the indoor setting, save its
+ * portid, i.e., make it the owner.
+ */
+ reg_is_indoor = is_indoor;
+ if (reg_is_indoor) {
+ if (!reg_is_indoor_portid)
+ reg_is_indoor_portid = portid;
+ } else {
+ reg_is_indoor_portid = 0;
+ }
+
+ spin_unlock(®_indoor_lock);
+
+ if (!is_indoor)
+ reg_check_channels();
+
+ return 0;
+}
+
+void regulatory_netlink_notify(u32 portid)
+{
+ spin_lock(®_indoor_lock);
+
+ if (reg_is_indoor_portid != portid) {
+ spin_unlock(®_indoor_lock);
+ return;
+ }
+
+ reg_is_indoor = false;
+ reg_is_indoor_portid = 0;
+
+ spin_unlock(®_indoor_lock);
+
+ reg_check_channels();
+}
+
+/* Driver hints */
+int regulatory_hint(struct wiphy *wiphy, const char *alpha2)
+{
+ struct regulatory_request *request;
+
+ if (WARN_ON(!alpha2 || !wiphy))
+ return -EINVAL;
+
+ wiphy->regulatory_flags &= ~REGULATORY_CUSTOM_REG;
+
+ request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL);
+ if (!request)
+ return -ENOMEM;
+
+ request->wiphy_idx = get_wiphy_idx(wiphy);
+
+ request->alpha2[0] = alpha2[0];
+ request->alpha2[1] = alpha2[1];
+ request->initiator = NL80211_REGDOM_SET_BY_DRIVER;
+
+ /* Allow calling CRDA again */
+ reset_crda_timeouts();
+
+ queue_regulatory_request(request);
+
+ return 0;
+}
+EXPORT_SYMBOL(regulatory_hint);
+
+void regulatory_hint_country_ie(struct wiphy *wiphy, enum nl80211_band band,
+ const u8 *country_ie, u8 country_ie_len)
+{
+ char alpha2[2];
+ enum environment_cap env = ENVIRON_ANY;
+ struct regulatory_request *request = NULL, *lr;
+
+ /* IE len must be evenly divisible by 2 */
+ if (country_ie_len & 0x01)
+ return;
+
+ if (country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN)
+ return;
+
+ request = kzalloc(sizeof(*request), GFP_KERNEL);
+ if (!request)
+ return;
+
+ alpha2[0] = country_ie[0];
+ alpha2[1] = country_ie[1];
+
+ if (country_ie[2] == 'I')
+ env = ENVIRON_INDOOR;
+ else if (country_ie[2] == 'O')
+ env = ENVIRON_OUTDOOR;
+
+ rcu_read_lock();
+ lr = get_last_request();
+
+ if (unlikely(!lr))
+ goto out;
+
+ /*
+ * We will run this only upon a successful connection on cfg80211.
+ * We leave conflict resolution to the workqueue, where can hold
+ * the RTNL.
+ */
+ if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE &&
+ lr->wiphy_idx != WIPHY_IDX_INVALID)
+ goto out;
+
+ request->wiphy_idx = get_wiphy_idx(wiphy);
+ request->alpha2[0] = alpha2[0];
+ request->alpha2[1] = alpha2[1];
+ request->initiator = NL80211_REGDOM_SET_BY_COUNTRY_IE;
+ request->country_ie_env = env;
+
+ /* Allow calling CRDA again */
+ reset_crda_timeouts();
+
+ queue_regulatory_request(request);
+ request = NULL;
+out:
+ kfree(request);
+ rcu_read_unlock();
+}
+
+static void restore_alpha2(char *alpha2, bool reset_user)
+{
+ /* indicates there is no alpha2 to consider for restoration */
+ alpha2[0] = '9';
+ alpha2[1] = '7';
+
+ /* The user setting has precedence over the module parameter */
+ if (is_user_regdom_saved()) {
+ /* Unless we're asked to ignore it and reset it */
+ if (reset_user) {
+ pr_debug("Restoring regulatory settings including user preference\n");
+ user_alpha2[0] = '9';
+ user_alpha2[1] = '7';
+
+ /*
+ * If we're ignoring user settings, we still need to
+ * check the module parameter to ensure we put things
+ * back as they were for a full restore.
+ */
+ if (!is_world_regdom(ieee80211_regdom)) {
+ pr_debug("Keeping preference on module parameter ieee80211_regdom: %c%c\n",
+ ieee80211_regdom[0], ieee80211_regdom[1]);
+ alpha2[0] = ieee80211_regdom[0];
+ alpha2[1] = ieee80211_regdom[1];
+ }
+ } else {
+ pr_debug("Restoring regulatory settings while preserving user preference for: %c%c\n",
+ user_alpha2[0], user_alpha2[1]);
+ alpha2[0] = user_alpha2[0];
+ alpha2[1] = user_alpha2[1];
+ }
+ } else if (!is_world_regdom(ieee80211_regdom)) {
+ pr_debug("Keeping preference on module parameter ieee80211_regdom: %c%c\n",
+ ieee80211_regdom[0], ieee80211_regdom[1]);
+ alpha2[0] = ieee80211_regdom[0];
+ alpha2[1] = ieee80211_regdom[1];
+ } else
+ pr_debug("Restoring regulatory settings\n");
+}
+
+static void restore_custom_reg_settings(struct wiphy *wiphy)
+{
+ struct ieee80211_supported_band *sband;
+ enum nl80211_band band;
+ struct ieee80211_channel *chan;
+ int i;
+
+ for (band = 0; band < NUM_NL80211_BANDS; band++) {
+ sband = wiphy->bands[band];
+ if (!sband)
+ continue;
+ for (i = 0; i < sband->n_channels; i++) {
+ chan = &sband->channels[i];
+ chan->flags = chan->orig_flags;
+ chan->max_antenna_gain = chan->orig_mag;
+ chan->max_power = chan->orig_mpwr;
+ chan->beacon_found = false;
+ }
+ }
+}
+
+/*
+ * Restoring regulatory settings involves ingoring any
+ * possibly stale country IE information and user regulatory
+ * settings if so desired, this includes any beacon hints
+ * learned as we could have traveled outside to another country
+ * after disconnection. To restore regulatory settings we do
+ * exactly what we did at bootup:
+ *
+ * - send a core regulatory hint
+ * - send a user regulatory hint if applicable
+ *
+ * Device drivers that send a regulatory hint for a specific country
+ * keep their own regulatory domain on wiphy->regd so that does does
+ * not need to be remembered.
+ */
+static void restore_regulatory_settings(bool reset_user)
+{
+ char alpha2[2];
+ char world_alpha2[2];
+ struct reg_beacon *reg_beacon, *btmp;
+ LIST_HEAD(tmp_reg_req_list);
+ struct cfg80211_registered_device *rdev;
+
+ ASSERT_RTNL();
+
+ /*
+ * Clear the indoor setting in case that it is not controlled by user
+ * space, as otherwise there is no guarantee that the device is still
+ * operating in an indoor environment.
+ */
+ spin_lock(®_indoor_lock);
+ if (reg_is_indoor && !reg_is_indoor_portid) {
+ reg_is_indoor = false;
+ reg_check_channels();
+ }
+ spin_unlock(®_indoor_lock);
+
+ reset_regdomains(true, &world_regdom);
+ restore_alpha2(alpha2, reset_user);
+
+ /*
+ * If there's any pending requests we simply
+ * stash them to a temporary pending queue and
+ * add then after we've restored regulatory
+ * settings.
+ */
+ spin_lock(®_requests_lock);
+ list_splice_tail_init(®_requests_list, &tmp_reg_req_list);
+ spin_unlock(®_requests_lock);
+
+ /* Clear beacon hints */
+ spin_lock_bh(®_pending_beacons_lock);
+ list_for_each_entry_safe(reg_beacon, btmp, ®_pending_beacons, list) {
+ list_del(®_beacon->list);
+ kfree(reg_beacon);
+ }
+ spin_unlock_bh(®_pending_beacons_lock);
+
+ list_for_each_entry_safe(reg_beacon, btmp, ®_beacon_list, list) {
+ list_del(®_beacon->list);
+ kfree(reg_beacon);
+ }
+
+ /* First restore to the basic regulatory settings */
+ world_alpha2[0] = cfg80211_world_regdom->alpha2[0];
+ world_alpha2[1] = cfg80211_world_regdom->alpha2[1];
+
+ list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
+ if (rdev->wiphy.regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED)
+ continue;
+ if (rdev->wiphy.regulatory_flags & REGULATORY_CUSTOM_REG)
+ restore_custom_reg_settings(&rdev->wiphy);
+ }
+
+ regulatory_hint_core(world_alpha2);
+
+ /*
+ * This restores the ieee80211_regdom module parameter
+ * preference or the last user requested regulatory
+ * settings, user regulatory settings takes precedence.
+ */
+ if (is_an_alpha2(alpha2))
+ regulatory_hint_user(alpha2, NL80211_USER_REG_HINT_USER);
+
+ spin_lock(®_requests_lock);
+ list_splice_tail_init(&tmp_reg_req_list, ®_requests_list);
+ spin_unlock(®_requests_lock);
+
+ pr_debug("Kicking the queue\n");
+
+ schedule_work(®_work);
+}
+
+void regulatory_hint_disconnect(void)
+{
+ pr_debug("All devices are disconnected, going to restore regulatory settings\n");
+ restore_regulatory_settings(false);
+}
+
+static bool freq_is_chan_12_13_14(u16 freq)
+{
+ if (freq == ieee80211_channel_to_frequency(12, NL80211_BAND_2GHZ) ||
+ freq == ieee80211_channel_to_frequency(13, NL80211_BAND_2GHZ) ||
+ freq == ieee80211_channel_to_frequency(14, NL80211_BAND_2GHZ))
+ return true;
+ return false;
+}
+
+static bool pending_reg_beacon(struct ieee80211_channel *beacon_chan)
+{
+ struct reg_beacon *pending_beacon;
+
+ list_for_each_entry(pending_beacon, ®_pending_beacons, list)
+ if (beacon_chan->center_freq ==
+ pending_beacon->chan.center_freq)
+ return true;
+ return false;
+}
+
+int regulatory_hint_found_beacon(struct wiphy *wiphy,
+ struct ieee80211_channel *beacon_chan,
+ gfp_t gfp)
+{
+ struct reg_beacon *reg_beacon;
+ bool processing;
+
+ if (beacon_chan->beacon_found ||
+ beacon_chan->flags & IEEE80211_CHAN_RADAR ||
+ (beacon_chan->band == NL80211_BAND_2GHZ &&
+ !freq_is_chan_12_13_14(beacon_chan->center_freq)))
+ return 0;
+
+ spin_lock_bh(®_pending_beacons_lock);
+ processing = pending_reg_beacon(beacon_chan);
+ spin_unlock_bh(®_pending_beacons_lock);
+
+ if (processing)
+ return 0;
+
+ reg_beacon = kzalloc(sizeof(struct reg_beacon), gfp);
+ if (!reg_beacon)
+ return -ENOMEM;
+
+ pr_debug("Found new beacon on frequency: %d MHz (Ch %d) on %s\n",
+ beacon_chan->center_freq,
+ ieee80211_frequency_to_channel(beacon_chan->center_freq),
+ wiphy_name(wiphy));
+
+ memcpy(®_beacon->chan, beacon_chan,
+ sizeof(struct ieee80211_channel));
+
+ /*
+ * Since we can be called from BH or and non-BH context
+ * we must use spin_lock_bh()
+ */
+ spin_lock_bh(®_pending_beacons_lock);
+ list_add_tail(®_beacon->list, ®_pending_beacons);
+ spin_unlock_bh(®_pending_beacons_lock);
+
+ schedule_work(®_work);
+
+ return 0;
+}
+
+static void print_rd_rules(const struct ieee80211_regdomain *rd)
+{
+ unsigned int i;
+ const struct ieee80211_reg_rule *reg_rule = NULL;
+ const struct ieee80211_freq_range *freq_range = NULL;
+ const struct ieee80211_power_rule *power_rule = NULL;
+ char bw[32], cac_time[32];
+
+ pr_debug(" (start_freq - end_freq @ bandwidth), (max_antenna_gain, max_eirp), (dfs_cac_time)\n");
+
+ for (i = 0; i < rd->n_reg_rules; i++) {
+ reg_rule = &rd->reg_rules[i];
+ freq_range = ®_rule->freq_range;
+ power_rule = ®_rule->power_rule;
+
+ if (reg_rule->flags & NL80211_RRF_AUTO_BW)
+ snprintf(bw, sizeof(bw), "%d KHz, %d KHz AUTO",
+ freq_range->max_bandwidth_khz,
+ reg_get_max_bandwidth(rd, reg_rule));
+ else
+ snprintf(bw, sizeof(bw), "%d KHz",
+ freq_range->max_bandwidth_khz);
+
+ if (reg_rule->flags & NL80211_RRF_DFS)
+ scnprintf(cac_time, sizeof(cac_time), "%u s",
+ reg_rule->dfs_cac_ms/1000);
+ else
+ scnprintf(cac_time, sizeof(cac_time), "N/A");
+
+
+ /*
+ * There may not be documentation for max antenna gain
+ * in certain regions
+ */
+ if (power_rule->max_antenna_gain)
+ pr_debug(" (%d KHz - %d KHz @ %s), (%d mBi, %d mBm), (%s)\n",
+ freq_range->start_freq_khz,
+ freq_range->end_freq_khz,
+ bw,
+ power_rule->max_antenna_gain,
+ power_rule->max_eirp,
+ cac_time);
+ else
+ pr_debug(" (%d KHz - %d KHz @ %s), (N/A, %d mBm), (%s)\n",
+ freq_range->start_freq_khz,
+ freq_range->end_freq_khz,
+ bw,
+ power_rule->max_eirp,
+ cac_time);
+ }
+}
+
+bool reg_supported_dfs_region(enum nl80211_dfs_regions dfs_region)
+{
+ switch (dfs_region) {
+ case NL80211_DFS_UNSET:
+ case NL80211_DFS_FCC:
+ case NL80211_DFS_ETSI:
+ case NL80211_DFS_JP:
+ return true;
+ default:
+ pr_debug("Ignoring uknown DFS master region: %d\n", dfs_region);
+ return false;
+ }
+}
+
+static void print_regdomain(const struct ieee80211_regdomain *rd)
+{
+ struct regulatory_request *lr = get_last_request();
+
+ if (is_intersected_alpha2(rd->alpha2)) {
+ if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) {
+ struct cfg80211_registered_device *rdev;
+ rdev = cfg80211_rdev_by_wiphy_idx(lr->wiphy_idx);
+ if (rdev) {
+ pr_debug("Current regulatory domain updated by AP to: %c%c\n",
+ rdev->country_ie_alpha2[0],
+ rdev->country_ie_alpha2[1]);
+ } else
+ pr_debug("Current regulatory domain intersected:\n");
+ } else
+ pr_debug("Current regulatory domain intersected:\n");
+ } else if (is_world_regdom(rd->alpha2)) {
+ pr_debug("World regulatory domain updated:\n");
+ } else {
+ if (is_unknown_alpha2(rd->alpha2))
+ pr_debug("Regulatory domain changed to driver built-in settings (unknown country)\n");
+ else {
+ if (reg_request_cell_base(lr))
+ pr_debug("Regulatory domain changed to country: %c%c by Cell Station\n",
+ rd->alpha2[0], rd->alpha2[1]);
+ else
+ pr_debug("Regulatory domain changed to country: %c%c\n",
+ rd->alpha2[0], rd->alpha2[1]);
+ }
+ }
+
+ pr_debug(" DFS Master region: %s", reg_dfs_region_str(rd->dfs_region));
+ print_rd_rules(rd);
+}
+
+static void print_regdomain_info(const struct ieee80211_regdomain *rd)
+{
+ pr_debug("Regulatory domain: %c%c\n", rd->alpha2[0], rd->alpha2[1]);
+ print_rd_rules(rd);
+}
+
+static int reg_set_rd_core(const struct ieee80211_regdomain *rd)
+{
+ if (!is_world_regdom(rd->alpha2))
+ return -EINVAL;
+ update_world_regdomain(rd);
+ return 0;
+}
+
+static int reg_set_rd_user(const struct ieee80211_regdomain *rd,
+ struct regulatory_request *user_request)
+{
+ const struct ieee80211_regdomain *intersected_rd = NULL;
+
+ if (!regdom_changes(rd->alpha2))
+ return -EALREADY;
+
+ if (!is_valid_rd(rd)) {
+ pr_err("Invalid regulatory domain detected: %c%c\n",
+ rd->alpha2[0], rd->alpha2[1]);
+ print_regdomain_info(rd);
+ return -EINVAL;
+ }
+
+ if (!user_request->intersect) {
+ reset_regdomains(false, rd);
+ return 0;
+ }
+
+ intersected_rd = regdom_intersect(rd, get_cfg80211_regdom());
+ if (!intersected_rd)
+ return -EINVAL;
+
+ kfree(rd);
+ rd = NULL;
+ reset_regdomains(false, intersected_rd);
+
+ return 0;
+}
+
+static int reg_set_rd_driver(const struct ieee80211_regdomain *rd,
+ struct regulatory_request *driver_request)
+{
+ const struct ieee80211_regdomain *regd;
+ const struct ieee80211_regdomain *intersected_rd = NULL;
+ const struct ieee80211_regdomain *tmp;
+ struct wiphy *request_wiphy;
+
+ if (is_world_regdom(rd->alpha2))
+ return -EINVAL;
+
+ if (!regdom_changes(rd->alpha2))
+ return -EALREADY;
+
+ if (!is_valid_rd(rd)) {
+ pr_err("Invalid regulatory domain detected: %c%c\n",
+ rd->alpha2[0], rd->alpha2[1]);
+ print_regdomain_info(rd);
+ return -EINVAL;
+ }
+
+ request_wiphy = wiphy_idx_to_wiphy(driver_request->wiphy_idx);
+ if (!request_wiphy)
+ return -ENODEV;
+
+ if (!driver_request->intersect) {
+ if (request_wiphy->regd)
+ return -EALREADY;
+
+ regd = reg_copy_regd(rd);
+ if (IS_ERR(regd))
+ return PTR_ERR(regd);
+
+ rcu_assign_pointer(request_wiphy->regd, regd);
+ reset_regdomains(false, rd);
+ return 0;
+ }
+
+ intersected_rd = regdom_intersect(rd, get_cfg80211_regdom());
+ if (!intersected_rd)
+ return -EINVAL;
+
+ /*
+ * We can trash what CRDA provided now.
+ * However if a driver requested this specific regulatory
+ * domain we keep it for its private use
+ */
+ tmp = get_wiphy_regdom(request_wiphy);
+ rcu_assign_pointer(request_wiphy->regd, rd);
+ rcu_free_regdom(tmp);
+
+ rd = NULL;
+
+ reset_regdomains(false, intersected_rd);
+
+ return 0;
+}
+
+static int reg_set_rd_country_ie(const struct ieee80211_regdomain *rd,
+ struct regulatory_request *country_ie_request)
+{
+ struct wiphy *request_wiphy;
+
+ if (!is_alpha2_set(rd->alpha2) && !is_an_alpha2(rd->alpha2) &&
+ !is_unknown_alpha2(rd->alpha2))
+ return -EINVAL;
+
+ /*
+ * Lets only bother proceeding on the same alpha2 if the current
+ * rd is non static (it means CRDA was present and was used last)
+ * and the pending request came in from a country IE
+ */
+
+ if (!is_valid_rd(rd)) {
+ pr_err("Invalid regulatory domain detected: %c%c\n",
+ rd->alpha2[0], rd->alpha2[1]);
+ print_regdomain_info(rd);
+ return -EINVAL;
+ }
+
+ request_wiphy = wiphy_idx_to_wiphy(country_ie_request->wiphy_idx);
+ if (!request_wiphy)
+ return -ENODEV;
+
+ if (country_ie_request->intersect)
+ return -EINVAL;
+
+ reset_regdomains(false, rd);
+ return 0;
+}
+
+/*
+ * Use this call to set the current regulatory domain. Conflicts with
+ * multiple drivers can be ironed out later. Caller must've already
+ * kmalloc'd the rd structure.
+ */
+int set_regdom(const struct ieee80211_regdomain *rd,
+ enum ieee80211_regd_source regd_src)
+{
+ struct regulatory_request *lr;
+ bool user_reset = false;
+ int r;
+
+ if (!reg_is_valid_request(rd->alpha2)) {
+ kfree(rd);
+ return -EINVAL;
+ }
+
+ if (regd_src == REGD_SOURCE_CRDA)
+ reset_crda_timeouts();
+
+ lr = get_last_request();
+
+ /* Note that this doesn't update the wiphys, this is done below */
+ switch (lr->initiator) {
+ case NL80211_REGDOM_SET_BY_CORE:
+ r = reg_set_rd_core(rd);
+ break;
+ case NL80211_REGDOM_SET_BY_USER:
+ r = reg_set_rd_user(rd, lr);
+ user_reset = true;
+ break;
+ case NL80211_REGDOM_SET_BY_DRIVER:
+ r = reg_set_rd_driver(rd, lr);
+ break;
+ case NL80211_REGDOM_SET_BY_COUNTRY_IE:
+ r = reg_set_rd_country_ie(rd, lr);
+ break;
+ default:
+ WARN(1, "invalid initiator %d\n", lr->initiator);
+ kfree(rd);
+ return -EINVAL;
+ }
+
+ if (r) {
+ switch (r) {
+ case -EALREADY:
+ reg_set_request_processed();
+ break;
+ default:
+ /* Back to world regulatory in case of errors */
+ restore_regulatory_settings(user_reset);
+ }
+
+ kfree(rd);
+ return r;
+ }
+
+ /* This would make this whole thing pointless */
+ if (WARN_ON(!lr->intersect && rd != get_cfg80211_regdom()))
+ return -EINVAL;
+
+ /* update all wiphys now with the new established regulatory domain */
+ update_all_wiphy_regulatory(lr->initiator);
+
+ print_regdomain(get_cfg80211_regdom());
+
+ nl80211_send_reg_change_event(lr);
+
+ reg_set_request_processed();
+
+ return 0;
+}
+
+static int __regulatory_set_wiphy_regd(struct wiphy *wiphy,
+ struct ieee80211_regdomain *rd)
+{
+ const struct ieee80211_regdomain *regd;
+ const struct ieee80211_regdomain *prev_regd;
+ struct cfg80211_registered_device *rdev;
+
+ if (WARN_ON(!wiphy || !rd))
+ return -EINVAL;
+
+ if (WARN(!(wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED),
+ "wiphy should have REGULATORY_WIPHY_SELF_MANAGED\n"))
+ return -EPERM;
+
+ if (WARN(!is_valid_rd(rd), "Invalid regulatory domain detected\n")) {
+ print_regdomain_info(rd);
+ return -EINVAL;
+ }
+
+ regd = reg_copy_regd(rd);
+ if (IS_ERR(regd))
+ return PTR_ERR(regd);
+
+ rdev = wiphy_to_rdev(wiphy);
+
+ spin_lock(®_requests_lock);
+ prev_regd = rdev->requested_regd;
+ rdev->requested_regd = regd;
+ spin_unlock(®_requests_lock);
+
+ kfree(prev_regd);
+ return 0;
+}
+
+int regulatory_set_wiphy_regd(struct wiphy *wiphy,
+ struct ieee80211_regdomain *rd)
+{
+ int ret = __regulatory_set_wiphy_regd(wiphy, rd);
+
+ if (ret)
+ return ret;
+
+ schedule_work(®_work);
+ return 0;
+}
+EXPORT_SYMBOL(regulatory_set_wiphy_regd);
+
+int regulatory_set_wiphy_regd_sync_rtnl(struct wiphy *wiphy,
+ struct ieee80211_regdomain *rd)
+{
+ int ret;
+
+ ASSERT_RTNL();
+
+ ret = __regulatory_set_wiphy_regd(wiphy, rd);
+ if (ret)
+ return ret;
+
+ /* process the request immediately */
+ reg_process_self_managed_hints();
+ return 0;
+}
+EXPORT_SYMBOL(regulatory_set_wiphy_regd_sync_rtnl);
+
+void wiphy_regulatory_register(struct wiphy *wiphy)
+{
+ struct regulatory_request *lr;
+
+ /* self-managed devices ignore external hints */
+ if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED)
+ wiphy->regulatory_flags |= REGULATORY_DISABLE_BEACON_HINTS |
+ REGULATORY_COUNTRY_IE_IGNORE;
+
+ if (!reg_dev_ignore_cell_hint(wiphy))
+ reg_num_devs_support_basehint++;
+
+ lr = get_last_request();
+ wiphy_update_regulatory(wiphy, lr->initiator);
+ wiphy_all_share_dfs_chan_state(wiphy);
+}
+
+void wiphy_regulatory_deregister(struct wiphy *wiphy)
+{
+ struct wiphy *request_wiphy = NULL;
+ struct regulatory_request *lr;
+
+ lr = get_last_request();
+
+ if (!reg_dev_ignore_cell_hint(wiphy))
+ reg_num_devs_support_basehint--;
+
+ rcu_free_regdom(get_wiphy_regdom(wiphy));
+ RCU_INIT_POINTER(wiphy->regd, NULL);
+
+ if (lr)
+ request_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx);
+
+ if (!request_wiphy || request_wiphy != wiphy)
+ return;
+
+ lr->wiphy_idx = WIPHY_IDX_INVALID;
+ lr->country_ie_env = ENVIRON_ANY;
+}
+
+/*
+ * See http://www.fcc.gov/document/5-ghz-unlicensed-spectrum-unii, for
+ * UNII band definitions
+ */
+int cfg80211_get_unii(int freq)
+{
+ /* UNII-1 */
+ if (freq >= 5150 && freq <= 5250)
+ return 0;
+
+ /* UNII-2A */
+ if (freq > 5250 && freq <= 5350)
+ return 1;
+
+ /* UNII-2B */
+ if (freq > 5350 && freq <= 5470)
+ return 2;
+
+ /* UNII-2C */
+ if (freq > 5470 && freq <= 5725)
+ return 3;
+
+ /* UNII-3 */
+ if (freq > 5725 && freq <= 5825)
+ return 4;
+
+ return -EINVAL;
+}
+
+bool regulatory_indoor_allowed(void)
+{
+ return reg_is_indoor;
+}
+
+bool regulatory_pre_cac_allowed(struct wiphy *wiphy)
+{
+ const struct ieee80211_regdomain *regd = NULL;
+ const struct ieee80211_regdomain *wiphy_regd = NULL;
+ bool pre_cac_allowed = false;
+
+ rcu_read_lock();
+
+ regd = rcu_dereference(cfg80211_regdomain);
+ wiphy_regd = rcu_dereference(wiphy->regd);
+ if (!wiphy_regd) {
+ if (regd->dfs_region == NL80211_DFS_ETSI)
+ pre_cac_allowed = true;
+
+ rcu_read_unlock();
+
+ return pre_cac_allowed;
+ }
+
+ if (regd->dfs_region == wiphy_regd->dfs_region &&
+ wiphy_regd->dfs_region == NL80211_DFS_ETSI)
+ pre_cac_allowed = true;
+
+ rcu_read_unlock();
+
+ return pre_cac_allowed;
+}
+
+void regulatory_propagate_dfs_state(struct wiphy *wiphy,
+ struct cfg80211_chan_def *chandef,
+ enum nl80211_dfs_state dfs_state,
+ enum nl80211_radar_event event)
+{
+ struct cfg80211_registered_device *rdev;
+
+ ASSERT_RTNL();
+
+ if (WARN_ON(!cfg80211_chandef_valid(chandef)))
+ return;
+
+ list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
+ if (wiphy == &rdev->wiphy)
+ continue;
+
+ if (!reg_dfs_domain_same(wiphy, &rdev->wiphy))
+ continue;
+
+ if (!ieee80211_get_channel(&rdev->wiphy,
+ chandef->chan->center_freq))
+ continue;
+
+ cfg80211_set_dfs_state(&rdev->wiphy, chandef, dfs_state);
+
+ if (event == NL80211_RADAR_DETECTED ||
+ event == NL80211_RADAR_CAC_FINISHED)
+ cfg80211_sched_dfs_chan_update(rdev);
+
+ nl80211_radar_notify(rdev, chandef, event, NULL, GFP_KERNEL);
+ }
+}
+
+int __init regulatory_init(void)
+{
+ int err = 0;
+
+ reg_pdev = platform_device_register_simple("regulatory", 0, NULL, 0);
+ if (IS_ERR(reg_pdev))
+ return PTR_ERR(reg_pdev);
+
+ spin_lock_init(®_requests_lock);
+ spin_lock_init(®_pending_beacons_lock);
+ spin_lock_init(®_indoor_lock);
+
+ reg_regdb_size_check();
+
+ rcu_assign_pointer(cfg80211_regdomain, cfg80211_world_regdom);
+
+ user_alpha2[0] = '9';
+ user_alpha2[1] = '7';
+
+ /* We always try to get an update for the static regdomain */
+ err = regulatory_hint_core(cfg80211_world_regdom->alpha2);
+ if (err) {
+ if (err == -ENOMEM) {
+ platform_device_unregister(reg_pdev);
+ return err;
+ }
+ /*
+ * N.B. kobject_uevent_env() can fail mainly for when we're out
+ * memory which is handled and propagated appropriately above
+ * but it can also fail during a netlink_broadcast() or during
+ * early boot for call_usermodehelper(). For now treat these
+ * errors as non-fatal.
+ */
+ pr_err("kobject_uevent_env() was unable to call CRDA during init\n");
+ }
+
+ /*
+ * Finally, if the user set the module parameter treat it
+ * as a user hint.
+ */
+ if (!is_world_regdom(ieee80211_regdom))
+ regulatory_hint_user(ieee80211_regdom,
+ NL80211_USER_REG_HINT_USER);
+
+ return 0;
+}
+
+void regulatory_exit(void)
+{
+ struct regulatory_request *reg_request, *tmp;
+ struct reg_beacon *reg_beacon, *btmp;
+
+ cancel_work_sync(®_work);
+ cancel_crda_timeout_sync();
+ cancel_delayed_work_sync(®_check_chans);
+
+ /* Lock to suppress warnings */
+ rtnl_lock();
+ reset_regdomains(true, NULL);
+ rtnl_unlock();
+
+ dev_set_uevent_suppress(®_pdev->dev, true);
+
+ platform_device_unregister(reg_pdev);
+
+ list_for_each_entry_safe(reg_beacon, btmp, ®_pending_beacons, list) {
+ list_del(®_beacon->list);
+ kfree(reg_beacon);
+ }
+
+ list_for_each_entry_safe(reg_beacon, btmp, ®_beacon_list, list) {
+ list_del(®_beacon->list);
+ kfree(reg_beacon);
+ }
+
+ list_for_each_entry_safe(reg_request, tmp, ®_requests_list, list) {
+ list_del(®_request->list);
+ kfree(reg_request);
+ }
+}
diff --git a/net/wireless/reg.h b/net/wireless/reg.h
new file mode 100644
index 0000000..ca7fedf
--- /dev/null
+++ b/net/wireless/reg.h
@@ -0,0 +1,182 @@
+#ifndef __NET_WIRELESS_REG_H
+#define __NET_WIRELESS_REG_H
+/*
+ * Copyright 2008-2011 Luis R. Rodriguez <mcgrof@qca.qualcomm.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+enum ieee80211_regd_source {
+ REGD_SOURCE_INTERNAL_DB,
+ REGD_SOURCE_CRDA,
+};
+
+extern const struct ieee80211_regdomain __rcu *cfg80211_regdomain;
+
+bool reg_is_valid_request(const char *alpha2);
+bool is_world_regdom(const char *alpha2);
+bool reg_supported_dfs_region(enum nl80211_dfs_regions dfs_region);
+enum nl80211_dfs_regions reg_get_dfs_region(struct wiphy *wiphy);
+
+int regulatory_hint_user(const char *alpha2,
+ enum nl80211_user_reg_hint_type user_reg_hint_type);
+
+/**
+ * regulatory_hint_indoor - hint operation in indoor env. or not
+ * @is_indoor: if true indicates that user space thinks that the
+ * device is operating in an indoor environment.
+ * @portid: the netlink port ID on which the hint was given.
+ */
+int regulatory_hint_indoor(bool is_indoor, u32 portid);
+
+/**
+ * regulatory_netlink_notify - notify on released netlink socket
+ * @portid: the netlink socket port ID
+ */
+void regulatory_netlink_notify(u32 portid);
+
+void wiphy_regulatory_register(struct wiphy *wiphy);
+void wiphy_regulatory_deregister(struct wiphy *wiphy);
+
+int __init regulatory_init(void);
+void regulatory_exit(void);
+
+int set_regdom(const struct ieee80211_regdomain *rd,
+ enum ieee80211_regd_source regd_src);
+
+unsigned int reg_get_max_bandwidth(const struct ieee80211_regdomain *rd,
+ const struct ieee80211_reg_rule *rule);
+
+bool reg_last_request_cell_base(void);
+const struct ieee80211_regdomain *get_wiphy_regdom(struct wiphy *wiphy);
+
+/**
+ * regulatory_hint_found_beacon - hints a beacon was found on a channel
+ * @wiphy: the wireless device where the beacon was found on
+ * @beacon_chan: the channel on which the beacon was found on
+ * @gfp: context flags
+ *
+ * This informs the wireless core that a beacon from an AP was found on
+ * the channel provided. This allows the wireless core to make educated
+ * guesses on regulatory to help with world roaming. This is only used for
+ * world roaming -- when we do not know our current location. This is
+ * only useful on channels 12, 13 and 14 on the 2 GHz band as channels
+ * 1-11 are already enabled by the world regulatory domain; and on
+ * non-radar 5 GHz channels.
+ *
+ * Drivers do not need to call this, cfg80211 will do it for after a scan
+ * on a newly found BSS. If you cannot make use of this feature you can
+ * set the wiphy->disable_beacon_hints to true.
+ */
+int regulatory_hint_found_beacon(struct wiphy *wiphy,
+ struct ieee80211_channel *beacon_chan,
+ gfp_t gfp);
+
+/**
+ * regulatory_hint_country_ie - hints a country IE as a regulatory domain
+ * @wiphy: the wireless device giving the hint (used only for reporting
+ * conflicts)
+ * @band: the band on which the country IE was received on. This determines
+ * the band we'll process the country IE channel triplets for.
+ * @country_ie: pointer to the country IE
+ * @country_ie_len: length of the country IE
+ *
+ * We will intersect the rd with the what CRDA tells us should apply
+ * for the alpha2 this country IE belongs to, this prevents APs from
+ * sending us incorrect or outdated information against a country.
+ *
+ * The AP is expected to provide Country IE channel triplets for the
+ * band it is on. It is technically possible for APs to send channel
+ * country IE triplets even for channels outside of the band they are
+ * in but for that they would have to use the regulatory extension
+ * in combination with a triplet but this behaviour is currently
+ * not observed. For this reason if a triplet is seen with channel
+ * information for a band the BSS is not present in it will be ignored.
+ */
+void regulatory_hint_country_ie(struct wiphy *wiphy,
+ enum nl80211_band band,
+ const u8 *country_ie,
+ u8 country_ie_len);
+
+/**
+ * regulatory_hint_disconnect - informs all devices have been disconneted
+ *
+ * Regulotory rules can be enhanced further upon scanning and upon
+ * connection to an AP. These rules become stale if we disconnect
+ * and go to another country, whether or not we suspend and resume.
+ * If we suspend, go to another country and resume we'll automatically
+ * get disconnected shortly after resuming and things will be reset as well.
+ * This routine is a helper to restore regulatory settings to how they were
+ * prior to our first connect attempt. This includes ignoring country IE and
+ * beacon regulatory hints. The ieee80211_regdom module parameter will always
+ * be respected but if a user had set the regulatory domain that will take
+ * precedence.
+ *
+ * Must be called from process context.
+ */
+void regulatory_hint_disconnect(void);
+
+/**
+ * cfg80211_get_unii - get the U-NII band for the frequency
+ * @freq: the frequency for which we want to get the UNII band.
+
+ * Get a value specifying the U-NII band frequency belongs to.
+ * U-NII bands are defined by the FCC in C.F.R 47 part 15.
+ *
+ * Returns -EINVAL if freq is invalid, 0 for UNII-1, 1 for UNII-2A,
+ * 2 for UNII-2B, 3 for UNII-2C and 4 for UNII-3.
+ */
+int cfg80211_get_unii(int freq);
+
+/**
+ * regulatory_indoor_allowed - is indoor operation allowed
+ */
+bool regulatory_indoor_allowed(void);
+
+/*
+ * Grace period to timeout pre-CAC results on the dfs channels. This timeout
+ * value is used for Non-ETSI domain.
+ * TODO: May be make this timeout available through regdb?
+ */
+#define REG_PRE_CAC_EXPIRY_GRACE_MS 2000
+
+/**
+ * regulatory_pre_cac_allowed - if pre-CAC allowed in the current dfs domain
+ * @wiphy: wiphy for which pre-CAC capability is checked.
+
+ * Pre-CAC is allowed only in ETSI domain.
+ */
+bool regulatory_pre_cac_allowed(struct wiphy *wiphy);
+
+/**
+ * regulatory_propagate_dfs_state - Propagate DFS channel state to other wiphys
+ * @wiphy - wiphy on which radar is detected and the event will be propagated
+ * to other available wiphys having the same DFS domain
+ * @chandef - Channel definition of radar detected channel
+ * @dfs_state - DFS channel state to be set
+ * @event - Type of radar event which triggered this DFS state change
+ *
+ * This function should be called with rtnl lock held.
+ */
+void regulatory_propagate_dfs_state(struct wiphy *wiphy,
+ struct cfg80211_chan_def *chandef,
+ enum nl80211_dfs_state dfs_state,
+ enum nl80211_radar_event event);
+
+/**
+ * reg_dfs_domain_same - Checks if both wiphy have same DFS domain configured
+ * @wiphy1 - wiphy it's dfs_region to be checked against that of wiphy2
+ * @wiphy2 - wiphy it's dfs_region to be checked against that of wiphy1
+ */
+bool reg_dfs_domain_same(struct wiphy *wiphy1, struct wiphy *wiphy2);
+#endif /* __NET_WIRELESS_REG_H */
diff --git a/net/wireless/regdb.h b/net/wireless/regdb.h
new file mode 100644
index 0000000..3279cfc
--- /dev/null
+++ b/net/wireless/regdb.h
@@ -0,0 +1,23 @@
+#ifndef __REGDB_H__
+#define __REGDB_H__
+
+/*
+ * Copyright 2009 John W. Linville <linville@tuxdriver.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+extern const struct ieee80211_regdomain *reg_regdb[];
+extern int reg_regdb_size;
+
+#endif /* __REGDB_H__ */
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
new file mode 100644
index 0000000..a5d9a4c
--- /dev/null
+++ b/net/wireless/scan.c
@@ -0,0 +1,1833 @@
+/*
+ * cfg80211 scan result handling
+ *
+ * Copyright 2008 Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2013-2014 Intel Mobile Communications GmbH
+ * Copyright 2016 Intel Deutschland GmbH
+ */
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/wireless.h>
+#include <linux/nl80211.h>
+#include <linux/etherdevice.h>
+#include <net/arp.h>
+#include <net/cfg80211.h>
+#include <net/cfg80211-wext.h>
+#include <net/iw_handler.h>
+#include "core.h"
+#include "nl80211.h"
+#include "wext-compat.h"
+#include "rdev-ops.h"
+
+/**
+ * DOC: BSS tree/list structure
+ *
+ * At the top level, the BSS list is kept in both a list in each
+ * registered device (@bss_list) as well as an RB-tree for faster
+ * lookup. In the RB-tree, entries can be looked up using their
+ * channel, MESHID, MESHCONF (for MBSSes) or channel, BSSID, SSID
+ * for other BSSes.
+ *
+ * Due to the possibility of hidden SSIDs, there's a second level
+ * structure, the "hidden_list" and "hidden_beacon_bss" pointer.
+ * The hidden_list connects all BSSes belonging to a single AP
+ * that has a hidden SSID, and connects beacon and probe response
+ * entries. For a probe response entry for a hidden SSID, the
+ * hidden_beacon_bss pointer points to the BSS struct holding the
+ * beacon's information.
+ *
+ * Reference counting is done for all these references except for
+ * the hidden_list, so that a beacon BSS struct that is otherwise
+ * not referenced has one reference for being on the bss_list and
+ * one for each probe response entry that points to it using the
+ * hidden_beacon_bss pointer. When a BSS struct that has such a
+ * pointer is get/put, the refcount update is also propagated to
+ * the referenced struct, this ensure that it cannot get removed
+ * while somebody is using the probe response version.
+ *
+ * Note that the hidden_beacon_bss pointer never changes, due to
+ * the reference counting. Therefore, no locking is needed for
+ * it.
+ *
+ * Also note that the hidden_beacon_bss pointer is only relevant
+ * if the driver uses something other than the IEs, e.g. private
+ * data stored stored in the BSS struct, since the beacon IEs are
+ * also linked into the probe response struct.
+ */
+
+/*
+ * Limit the number of BSS entries stored in mac80211. Each one is
+ * a bit over 4k at most, so this limits to roughly 4-5M of memory.
+ * If somebody wants to really attack this though, they'd likely
+ * use small beacons, and only one type of frame, limiting each of
+ * the entries to a much smaller size (in order to generate more
+ * entries in total, so overhead is bigger.)
+ */
+static int bss_entries_limit = 1000;
+module_param(bss_entries_limit, int, 0644);
+MODULE_PARM_DESC(bss_entries_limit,
+ "limit to number of scan BSS entries (per wiphy, default 1000)");
+
+#define IEEE80211_SCAN_RESULT_EXPIRE (30 * HZ)
+
+static void bss_free(struct cfg80211_internal_bss *bss)
+{
+ struct cfg80211_bss_ies *ies;
+
+ if (WARN_ON(atomic_read(&bss->hold)))
+ return;
+
+ ies = (void *)rcu_access_pointer(bss->pub.beacon_ies);
+ if (ies && !bss->pub.hidden_beacon_bss)
+ kfree_rcu(ies, rcu_head);
+ ies = (void *)rcu_access_pointer(bss->pub.proberesp_ies);
+ if (ies)
+ kfree_rcu(ies, rcu_head);
+
+ /*
+ * This happens when the module is removed, it doesn't
+ * really matter any more save for completeness
+ */
+ if (!list_empty(&bss->hidden_list))
+ list_del(&bss->hidden_list);
+
+ kfree(bss);
+}
+
+static inline void bss_ref_get(struct cfg80211_registered_device *rdev,
+ struct cfg80211_internal_bss *bss)
+{
+ lockdep_assert_held(&rdev->bss_lock);
+
+ bss->refcount++;
+ if (bss->pub.hidden_beacon_bss) {
+ bss = container_of(bss->pub.hidden_beacon_bss,
+ struct cfg80211_internal_bss,
+ pub);
+ bss->refcount++;
+ }
+}
+
+static inline void bss_ref_put(struct cfg80211_registered_device *rdev,
+ struct cfg80211_internal_bss *bss)
+{
+ lockdep_assert_held(&rdev->bss_lock);
+
+ if (bss->pub.hidden_beacon_bss) {
+ struct cfg80211_internal_bss *hbss;
+ hbss = container_of(bss->pub.hidden_beacon_bss,
+ struct cfg80211_internal_bss,
+ pub);
+ hbss->refcount--;
+ if (hbss->refcount == 0)
+ bss_free(hbss);
+ }
+ bss->refcount--;
+ if (bss->refcount == 0)
+ bss_free(bss);
+}
+
+static bool __cfg80211_unlink_bss(struct cfg80211_registered_device *rdev,
+ struct cfg80211_internal_bss *bss)
+{
+ lockdep_assert_held(&rdev->bss_lock);
+
+ if (!list_empty(&bss->hidden_list)) {
+ /*
+ * don't remove the beacon entry if it has
+ * probe responses associated with it
+ */
+ if (!bss->pub.hidden_beacon_bss)
+ return false;
+ /*
+ * if it's a probe response entry break its
+ * link to the other entries in the group
+ */
+ list_del_init(&bss->hidden_list);
+ }
+
+ list_del_init(&bss->list);
+ rb_erase(&bss->rbn, &rdev->bss_tree);
+ rdev->bss_entries--;
+ WARN_ONCE((rdev->bss_entries == 0) ^ list_empty(&rdev->bss_list),
+ "rdev bss entries[%d]/list[empty:%d] corruption\n",
+ rdev->bss_entries, list_empty(&rdev->bss_list));
+ bss_ref_put(rdev, bss);
+ return true;
+}
+
+static void __cfg80211_bss_expire(struct cfg80211_registered_device *rdev,
+ unsigned long expire_time)
+{
+ struct cfg80211_internal_bss *bss, *tmp;
+ bool expired = false;
+
+ lockdep_assert_held(&rdev->bss_lock);
+
+ list_for_each_entry_safe(bss, tmp, &rdev->bss_list, list) {
+ if (atomic_read(&bss->hold))
+ continue;
+ if (!time_after(expire_time, bss->ts))
+ continue;
+
+ if (__cfg80211_unlink_bss(rdev, bss))
+ expired = true;
+ }
+
+ if (expired)
+ rdev->bss_generation++;
+}
+
+static bool cfg80211_bss_expire_oldest(struct cfg80211_registered_device *rdev)
+{
+ struct cfg80211_internal_bss *bss, *oldest = NULL;
+ bool ret;
+
+ lockdep_assert_held(&rdev->bss_lock);
+
+ list_for_each_entry(bss, &rdev->bss_list, list) {
+ if (atomic_read(&bss->hold))
+ continue;
+
+ if (!list_empty(&bss->hidden_list) &&
+ !bss->pub.hidden_beacon_bss)
+ continue;
+
+ if (oldest && time_before(oldest->ts, bss->ts))
+ continue;
+ oldest = bss;
+ }
+
+ if (WARN_ON(!oldest))
+ return false;
+
+ /*
+ * The callers make sure to increase rdev->bss_generation if anything
+ * gets removed (and a new entry added), so there's no need to also do
+ * it here.
+ */
+
+ ret = __cfg80211_unlink_bss(rdev, oldest);
+ WARN_ON(!ret);
+ return ret;
+}
+
+void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev,
+ bool send_message)
+{
+ struct cfg80211_scan_request *request;
+ struct wireless_dev *wdev;
+ struct sk_buff *msg;
+#ifdef CPTCFG_CFG80211_WEXT
+ union iwreq_data wrqu;
+#endif
+
+ ASSERT_RTNL();
+
+ if (rdev->scan_msg) {
+ nl80211_send_scan_msg(rdev, rdev->scan_msg);
+ rdev->scan_msg = NULL;
+ return;
+ }
+
+ request = rdev->scan_req;
+ if (!request)
+ return;
+
+ wdev = request->wdev;
+
+ /*
+ * This must be before sending the other events!
+ * Otherwise, wpa_supplicant gets completely confused with
+ * wext events.
+ */
+ if (wdev->netdev)
+ cfg80211_sme_scan_done(wdev->netdev);
+
+ if (!request->info.aborted &&
+ request->flags & NL80211_SCAN_FLAG_FLUSH) {
+ /* flush entries from previous scans */
+ spin_lock_bh(&rdev->bss_lock);
+ __cfg80211_bss_expire(rdev, request->scan_start);
+ spin_unlock_bh(&rdev->bss_lock);
+ }
+
+ msg = nl80211_build_scan_msg(rdev, wdev, request->info.aborted);
+
+#ifdef CPTCFG_CFG80211_WEXT
+ if (wdev->netdev && !request->info.aborted) {
+ memset(&wrqu, 0, sizeof(wrqu));
+
+ wireless_send_event(wdev->netdev, SIOCGIWSCAN, &wrqu, NULL);
+ }
+#endif
+
+ if (wdev->netdev)
+ dev_put(wdev->netdev);
+
+ rdev->scan_req = NULL;
+ kfree(request);
+
+ if (!send_message)
+ rdev->scan_msg = msg;
+ else
+ nl80211_send_scan_msg(rdev, msg);
+}
+
+void __cfg80211_scan_done(struct work_struct *wk)
+{
+ struct cfg80211_registered_device *rdev;
+
+ rdev = container_of(wk, struct cfg80211_registered_device,
+ scan_done_wk);
+
+ rtnl_lock();
+ ___cfg80211_scan_done(rdev, true);
+ rtnl_unlock();
+}
+
+void cfg80211_scan_done(struct cfg80211_scan_request *request,
+ struct cfg80211_scan_info *info)
+{
+ trace_cfg80211_scan_done(request, info);
+ WARN_ON(request != wiphy_to_rdev(request->wiphy)->scan_req);
+
+ request->info = *info;
+ request->notified = true;
+ queue_work(cfg80211_wq, &wiphy_to_rdev(request->wiphy)->scan_done_wk);
+}
+EXPORT_SYMBOL(cfg80211_scan_done);
+
+void cfg80211_add_sched_scan_req(struct cfg80211_registered_device *rdev,
+ struct cfg80211_sched_scan_request *req)
+{
+ ASSERT_RTNL();
+
+ list_add_rcu(&req->list, &rdev->sched_scan_req_list);
+}
+
+static void cfg80211_del_sched_scan_req(struct cfg80211_registered_device *rdev,
+ struct cfg80211_sched_scan_request *req)
+{
+ ASSERT_RTNL();
+
+ list_del_rcu(&req->list);
+ kfree_rcu(req, rcu_head);
+}
+
+static struct cfg80211_sched_scan_request *
+cfg80211_find_sched_scan_req(struct cfg80211_registered_device *rdev, u64 reqid)
+{
+ struct cfg80211_sched_scan_request *pos;
+
+ WARN_ON_ONCE(!rcu_read_lock_held() && !lockdep_rtnl_is_held());
+
+ list_for_each_entry_rcu(pos, &rdev->sched_scan_req_list, list) {
+ if (pos->reqid == reqid)
+ return pos;
+ }
+ return NULL;
+}
+
+/*
+ * Determines if a scheduled scan request can be handled. When a legacy
+ * scheduled scan is running no other scheduled scan is allowed regardless
+ * whether the request is for legacy or multi-support scan. When a multi-support
+ * scheduled scan is running a request for legacy scan is not allowed. In this
+ * case a request for multi-support scan can be handled if resources are
+ * available, ie. struct wiphy::max_sched_scan_reqs limit is not yet reached.
+ */
+int cfg80211_sched_scan_req_possible(struct cfg80211_registered_device *rdev,
+ bool want_multi)
+{
+ struct cfg80211_sched_scan_request *pos;
+ int i = 0;
+
+ list_for_each_entry(pos, &rdev->sched_scan_req_list, list) {
+ /* request id zero means legacy in progress */
+ if (!i && !pos->reqid)
+ return -EINPROGRESS;
+ i++;
+ }
+
+ if (i) {
+ /* no legacy allowed when multi request(s) are active */
+ if (!want_multi)
+ return -EINPROGRESS;
+
+ /* resource limit reached */
+ if (i == rdev->wiphy.max_sched_scan_reqs)
+ return -ENOSPC;
+ }
+ return 0;
+}
+
+void cfg80211_sched_scan_results_wk(struct work_struct *work)
+{
+ struct cfg80211_registered_device *rdev;
+ struct cfg80211_sched_scan_request *req, *tmp;
+
+ rdev = container_of(work, struct cfg80211_registered_device,
+ sched_scan_res_wk);
+
+ rtnl_lock();
+ list_for_each_entry_safe(req, tmp, &rdev->sched_scan_req_list, list) {
+ if (req->report_results) {
+ req->report_results = false;
+ if (req->flags & NL80211_SCAN_FLAG_FLUSH) {
+ /* flush entries from previous scans */
+ spin_lock_bh(&rdev->bss_lock);
+ __cfg80211_bss_expire(rdev, req->scan_start);
+ spin_unlock_bh(&rdev->bss_lock);
+ req->scan_start = jiffies;
+ }
+ nl80211_send_sched_scan(req,
+ NL80211_CMD_SCHED_SCAN_RESULTS);
+ }
+ }
+ rtnl_unlock();
+}
+
+void cfg80211_sched_scan_results(struct wiphy *wiphy, u64 reqid)
+{
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+ struct cfg80211_sched_scan_request *request;
+
+ trace_cfg80211_sched_scan_results(wiphy, reqid);
+ /* ignore if we're not scanning */
+
+ rcu_read_lock();
+ request = cfg80211_find_sched_scan_req(rdev, reqid);
+ if (request) {
+ request->report_results = true;
+ queue_work(cfg80211_wq, &rdev->sched_scan_res_wk);
+ }
+ rcu_read_unlock();
+}
+EXPORT_SYMBOL(cfg80211_sched_scan_results);
+
+void cfg80211_sched_scan_stopped_rtnl(struct wiphy *wiphy, u64 reqid)
+{
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+
+ ASSERT_RTNL();
+
+ trace_cfg80211_sched_scan_stopped(wiphy, reqid);
+
+ __cfg80211_stop_sched_scan(rdev, reqid, true);
+}
+EXPORT_SYMBOL(cfg80211_sched_scan_stopped_rtnl);
+
+void cfg80211_sched_scan_stopped(struct wiphy *wiphy, u64 reqid)
+{
+ rtnl_lock();
+ cfg80211_sched_scan_stopped_rtnl(wiphy, reqid);
+ rtnl_unlock();
+}
+EXPORT_SYMBOL(cfg80211_sched_scan_stopped);
+
+int cfg80211_stop_sched_scan_req(struct cfg80211_registered_device *rdev,
+ struct cfg80211_sched_scan_request *req,
+ bool driver_initiated)
+{
+ ASSERT_RTNL();
+
+ if (!driver_initiated) {
+ int err = rdev_sched_scan_stop(rdev, req->dev, req->reqid);
+ if (err)
+ return err;
+ }
+
+ nl80211_send_sched_scan(req, NL80211_CMD_SCHED_SCAN_STOPPED);
+
+ cfg80211_del_sched_scan_req(rdev, req);
+
+ return 0;
+}
+
+int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev,
+ u64 reqid, bool driver_initiated)
+{
+ struct cfg80211_sched_scan_request *sched_scan_req;
+
+ ASSERT_RTNL();
+
+ sched_scan_req = cfg80211_find_sched_scan_req(rdev, reqid);
+ if (!sched_scan_req)
+ return -ENOENT;
+
+ return cfg80211_stop_sched_scan_req(rdev, sched_scan_req,
+ driver_initiated);
+}
+
+void cfg80211_bss_age(struct cfg80211_registered_device *rdev,
+ unsigned long age_secs)
+{
+ struct cfg80211_internal_bss *bss;
+ unsigned long age_jiffies = msecs_to_jiffies(age_secs * MSEC_PER_SEC);
+
+ spin_lock_bh(&rdev->bss_lock);
+ list_for_each_entry(bss, &rdev->bss_list, list)
+ bss->ts -= age_jiffies;
+ spin_unlock_bh(&rdev->bss_lock);
+}
+
+void cfg80211_bss_expire(struct cfg80211_registered_device *rdev)
+{
+ __cfg80211_bss_expire(rdev, jiffies - IEEE80211_SCAN_RESULT_EXPIRE);
+}
+
+const u8 *cfg80211_find_ie_match(u8 eid, const u8 *ies, int len,
+ const u8 *match, int match_len,
+ int match_offset)
+{
+ /* match_offset can't be smaller than 2, unless match_len is
+ * zero, in which case match_offset must be zero as well.
+ */
+ if (WARN_ON((match_len && match_offset < 2) ||
+ (!match_len && match_offset)))
+ return NULL;
+
+ while (len >= 2 && len >= ies[1] + 2) {
+ if ((ies[0] == eid) &&
+ (ies[1] + 2 >= match_offset + match_len) &&
+ !memcmp(ies + match_offset, match, match_len))
+ return ies;
+
+ len -= ies[1] + 2;
+ ies += ies[1] + 2;
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL(cfg80211_find_ie_match);
+
+const u8 *cfg80211_find_vendor_ie(unsigned int oui, int oui_type,
+ const u8 *ies, int len)
+{
+ const u8 *ie;
+ u8 match[] = { oui >> 16, oui >> 8, oui, oui_type };
+ int match_len = (oui_type < 0) ? 3 : sizeof(match);
+
+ if (WARN_ON(oui_type > 0xff))
+ return NULL;
+
+ ie = cfg80211_find_ie_match(WLAN_EID_VENDOR_SPECIFIC, ies, len,
+ match, match_len, 2);
+
+ if (ie && (ie[1] < 4))
+ return NULL;
+
+ return ie;
+}
+EXPORT_SYMBOL(cfg80211_find_vendor_ie);
+
+static bool is_bss(struct cfg80211_bss *a, const u8 *bssid,
+ const u8 *ssid, size_t ssid_len)
+{
+ const struct cfg80211_bss_ies *ies;
+ const u8 *ssidie;
+
+ if (bssid && !ether_addr_equal(a->bssid, bssid))
+ return false;
+
+ if (!ssid)
+ return true;
+
+ ies = rcu_access_pointer(a->ies);
+ if (!ies)
+ return false;
+ ssidie = cfg80211_find_ie(WLAN_EID_SSID, ies->data, ies->len);
+ if (!ssidie)
+ return false;
+ if (ssidie[1] != ssid_len)
+ return false;
+ return memcmp(ssidie + 2, ssid, ssid_len) == 0;
+}
+
+/**
+ * enum bss_compare_mode - BSS compare mode
+ * @BSS_CMP_REGULAR: regular compare mode (for insertion and normal find)
+ * @BSS_CMP_HIDE_ZLEN: find hidden SSID with zero-length mode
+ * @BSS_CMP_HIDE_NUL: find hidden SSID with NUL-ed out mode
+ */
+enum bss_compare_mode {
+ BSS_CMP_REGULAR,
+ BSS_CMP_HIDE_ZLEN,
+ BSS_CMP_HIDE_NUL,
+};
+
+static int cmp_bss(struct cfg80211_bss *a,
+ struct cfg80211_bss *b,
+ enum bss_compare_mode mode)
+{
+ const struct cfg80211_bss_ies *a_ies, *b_ies;
+ const u8 *ie1 = NULL;
+ const u8 *ie2 = NULL;
+ int i, r;
+
+ if (a->channel != b->channel)
+ return b->channel->center_freq - a->channel->center_freq;
+
+ a_ies = rcu_access_pointer(a->ies);
+ if (!a_ies)
+ return -1;
+ b_ies = rcu_access_pointer(b->ies);
+ if (!b_ies)
+ return 1;
+
+ if (WLAN_CAPABILITY_IS_STA_BSS(a->capability))
+ ie1 = cfg80211_find_ie(WLAN_EID_MESH_ID,
+ a_ies->data, a_ies->len);
+ if (WLAN_CAPABILITY_IS_STA_BSS(b->capability))
+ ie2 = cfg80211_find_ie(WLAN_EID_MESH_ID,
+ b_ies->data, b_ies->len);
+ if (ie1 && ie2) {
+ int mesh_id_cmp;
+
+ if (ie1[1] == ie2[1])
+ mesh_id_cmp = memcmp(ie1 + 2, ie2 + 2, ie1[1]);
+ else
+ mesh_id_cmp = ie2[1] - ie1[1];
+
+ ie1 = cfg80211_find_ie(WLAN_EID_MESH_CONFIG,
+ a_ies->data, a_ies->len);
+ ie2 = cfg80211_find_ie(WLAN_EID_MESH_CONFIG,
+ b_ies->data, b_ies->len);
+ if (ie1 && ie2) {
+ if (mesh_id_cmp)
+ return mesh_id_cmp;
+ if (ie1[1] != ie2[1])
+ return ie2[1] - ie1[1];
+ return memcmp(ie1 + 2, ie2 + 2, ie1[1]);
+ }
+ }
+
+ r = memcmp(a->bssid, b->bssid, sizeof(a->bssid));
+ if (r)
+ return r;
+
+ ie1 = cfg80211_find_ie(WLAN_EID_SSID, a_ies->data, a_ies->len);
+ ie2 = cfg80211_find_ie(WLAN_EID_SSID, b_ies->data, b_ies->len);
+
+ if (!ie1 && !ie2)
+ return 0;
+
+ /*
+ * Note that with "hide_ssid", the function returns a match if
+ * the already-present BSS ("b") is a hidden SSID beacon for
+ * the new BSS ("a").
+ */
+
+ /* sort missing IE before (left of) present IE */
+ if (!ie1)
+ return -1;
+ if (!ie2)
+ return 1;
+
+ switch (mode) {
+ case BSS_CMP_HIDE_ZLEN:
+ /*
+ * In ZLEN mode we assume the BSS entry we're
+ * looking for has a zero-length SSID. So if
+ * the one we're looking at right now has that,
+ * return 0. Otherwise, return the difference
+ * in length, but since we're looking for the
+ * 0-length it's really equivalent to returning
+ * the length of the one we're looking at.
+ *
+ * No content comparison is needed as we assume
+ * the content length is zero.
+ */
+ return ie2[1];
+ case BSS_CMP_REGULAR:
+ default:
+ /* sort by length first, then by contents */
+ if (ie1[1] != ie2[1])
+ return ie2[1] - ie1[1];
+ return memcmp(ie1 + 2, ie2 + 2, ie1[1]);
+ case BSS_CMP_HIDE_NUL:
+ if (ie1[1] != ie2[1])
+ return ie2[1] - ie1[1];
+ /* this is equivalent to memcmp(zeroes, ie2 + 2, len) */
+ for (i = 0; i < ie2[1]; i++)
+ if (ie2[i + 2])
+ return -1;
+ return 0;
+ }
+}
+
+static bool cfg80211_bss_type_match(u16 capability,
+ enum nl80211_band band,
+ enum ieee80211_bss_type bss_type)
+{
+ bool ret = true;
+ u16 mask, val;
+
+ if (bss_type == IEEE80211_BSS_TYPE_ANY)
+ return ret;
+
+ if (band == NL80211_BAND_60GHZ) {
+ mask = WLAN_CAPABILITY_DMG_TYPE_MASK;
+ switch (bss_type) {
+ case IEEE80211_BSS_TYPE_ESS:
+ val = WLAN_CAPABILITY_DMG_TYPE_AP;
+ break;
+ case IEEE80211_BSS_TYPE_PBSS:
+ val = WLAN_CAPABILITY_DMG_TYPE_PBSS;
+ break;
+ case IEEE80211_BSS_TYPE_IBSS:
+ val = WLAN_CAPABILITY_DMG_TYPE_IBSS;
+ break;
+ default:
+ return false;
+ }
+ } else {
+ mask = WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS;
+ switch (bss_type) {
+ case IEEE80211_BSS_TYPE_ESS:
+ val = WLAN_CAPABILITY_ESS;
+ break;
+ case IEEE80211_BSS_TYPE_IBSS:
+ val = WLAN_CAPABILITY_IBSS;
+ break;
+ case IEEE80211_BSS_TYPE_MBSS:
+ val = 0;
+ break;
+ default:
+ return false;
+ }
+ }
+
+ ret = ((capability & mask) == val);
+ return ret;
+}
+
+/* Returned bss is reference counted and must be cleaned up appropriately. */
+struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy,
+ struct ieee80211_channel *channel,
+ const u8 *bssid,
+ const u8 *ssid, size_t ssid_len,
+ enum ieee80211_bss_type bss_type,
+ enum ieee80211_privacy privacy)
+{
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+ struct cfg80211_internal_bss *bss, *res = NULL;
+ unsigned long now = jiffies;
+ int bss_privacy;
+
+ trace_cfg80211_get_bss(wiphy, channel, bssid, ssid, ssid_len, bss_type,
+ privacy);
+
+ spin_lock_bh(&rdev->bss_lock);
+
+ list_for_each_entry(bss, &rdev->bss_list, list) {
+ if (!cfg80211_bss_type_match(bss->pub.capability,
+ bss->pub.channel->band, bss_type))
+ continue;
+
+ bss_privacy = (bss->pub.capability & WLAN_CAPABILITY_PRIVACY);
+ if ((privacy == IEEE80211_PRIVACY_ON && !bss_privacy) ||
+ (privacy == IEEE80211_PRIVACY_OFF && bss_privacy))
+ continue;
+ if (channel && bss->pub.channel != channel)
+ continue;
+ if (!is_valid_ether_addr(bss->pub.bssid))
+ continue;
+ /* Don't get expired BSS structs */
+ if (time_after(now, bss->ts + IEEE80211_SCAN_RESULT_EXPIRE) &&
+ !atomic_read(&bss->hold))
+ continue;
+ if (is_bss(&bss->pub, bssid, ssid, ssid_len)) {
+ res = bss;
+ bss_ref_get(rdev, res);
+ break;
+ }
+ }
+
+ spin_unlock_bh(&rdev->bss_lock);
+ if (!res)
+ return NULL;
+ trace_cfg80211_return_bss(&res->pub);
+ return &res->pub;
+}
+EXPORT_SYMBOL(cfg80211_get_bss);
+
+static void rb_insert_bss(struct cfg80211_registered_device *rdev,
+ struct cfg80211_internal_bss *bss)
+{
+ struct rb_node **p = &rdev->bss_tree.rb_node;
+ struct rb_node *parent = NULL;
+ struct cfg80211_internal_bss *tbss;
+ int cmp;
+
+ while (*p) {
+ parent = *p;
+ tbss = rb_entry(parent, struct cfg80211_internal_bss, rbn);
+
+ cmp = cmp_bss(&bss->pub, &tbss->pub, BSS_CMP_REGULAR);
+
+ if (WARN_ON(!cmp)) {
+ /* will sort of leak this BSS */
+ return;
+ }
+
+ if (cmp < 0)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ }
+
+ rb_link_node(&bss->rbn, parent, p);
+ rb_insert_color(&bss->rbn, &rdev->bss_tree);
+}
+
+static struct cfg80211_internal_bss *
+rb_find_bss(struct cfg80211_registered_device *rdev,
+ struct cfg80211_internal_bss *res,
+ enum bss_compare_mode mode)
+{
+ struct rb_node *n = rdev->bss_tree.rb_node;
+ struct cfg80211_internal_bss *bss;
+ int r;
+
+ while (n) {
+ bss = rb_entry(n, struct cfg80211_internal_bss, rbn);
+ r = cmp_bss(&res->pub, &bss->pub, mode);
+
+ if (r == 0)
+ return bss;
+ else if (r < 0)
+ n = n->rb_left;
+ else
+ n = n->rb_right;
+ }
+
+ return NULL;
+}
+
+static bool cfg80211_combine_bsses(struct cfg80211_registered_device *rdev,
+ struct cfg80211_internal_bss *new)
+{
+ const struct cfg80211_bss_ies *ies;
+ struct cfg80211_internal_bss *bss;
+ const u8 *ie;
+ int i, ssidlen;
+ u8 fold = 0;
+ u32 n_entries = 0;
+
+ ies = rcu_access_pointer(new->pub.beacon_ies);
+ if (WARN_ON(!ies))
+ return false;
+
+ ie = cfg80211_find_ie(WLAN_EID_SSID, ies->data, ies->len);
+ if (!ie) {
+ /* nothing to do */
+ return true;
+ }
+
+ ssidlen = ie[1];
+ for (i = 0; i < ssidlen; i++)
+ fold |= ie[2 + i];
+
+ if (fold) {
+ /* not a hidden SSID */
+ return true;
+ }
+
+ /* This is the bad part ... */
+
+ list_for_each_entry(bss, &rdev->bss_list, list) {
+ /*
+ * we're iterating all the entries anyway, so take the
+ * opportunity to validate the list length accounting
+ */
+ n_entries++;
+
+ if (!ether_addr_equal(bss->pub.bssid, new->pub.bssid))
+ continue;
+ if (bss->pub.channel != new->pub.channel)
+ continue;
+ if (bss->pub.scan_width != new->pub.scan_width)
+ continue;
+ if (rcu_access_pointer(bss->pub.beacon_ies))
+ continue;
+ ies = rcu_access_pointer(bss->pub.ies);
+ if (!ies)
+ continue;
+ ie = cfg80211_find_ie(WLAN_EID_SSID, ies->data, ies->len);
+ if (!ie)
+ continue;
+ if (ssidlen && ie[1] != ssidlen)
+ continue;
+ if (WARN_ON_ONCE(bss->pub.hidden_beacon_bss))
+ continue;
+ if (WARN_ON_ONCE(!list_empty(&bss->hidden_list)))
+ list_del(&bss->hidden_list);
+ /* combine them */
+ list_add(&bss->hidden_list, &new->hidden_list);
+ bss->pub.hidden_beacon_bss = &new->pub;
+ new->refcount += bss->refcount;
+ rcu_assign_pointer(bss->pub.beacon_ies,
+ new->pub.beacon_ies);
+ }
+
+ WARN_ONCE(n_entries != rdev->bss_entries,
+ "rdev bss entries[%d]/list[len:%d] corruption\n",
+ rdev->bss_entries, n_entries);
+
+ return true;
+}
+
+/* Returned bss is reference counted and must be cleaned up appropriately. */
+static struct cfg80211_internal_bss *
+cfg80211_bss_update(struct cfg80211_registered_device *rdev,
+ struct cfg80211_internal_bss *tmp,
+ bool signal_valid)
+{
+ struct cfg80211_internal_bss *found = NULL;
+
+ if (WARN_ON(!tmp->pub.channel))
+ return NULL;
+
+ tmp->ts = jiffies;
+
+ spin_lock_bh(&rdev->bss_lock);
+
+ if (WARN_ON(!rcu_access_pointer(tmp->pub.ies))) {
+ spin_unlock_bh(&rdev->bss_lock);
+ return NULL;
+ }
+
+ found = rb_find_bss(rdev, tmp, BSS_CMP_REGULAR);
+
+ if (found) {
+ /* Update IEs */
+ if (rcu_access_pointer(tmp->pub.proberesp_ies)) {
+ const struct cfg80211_bss_ies *old;
+
+ old = rcu_access_pointer(found->pub.proberesp_ies);
+
+ rcu_assign_pointer(found->pub.proberesp_ies,
+ tmp->pub.proberesp_ies);
+ /* Override possible earlier Beacon frame IEs */
+ rcu_assign_pointer(found->pub.ies,
+ tmp->pub.proberesp_ies);
+ if (old)
+ kfree_rcu((struct cfg80211_bss_ies *)old,
+ rcu_head);
+ } else if (rcu_access_pointer(tmp->pub.beacon_ies)) {
+ const struct cfg80211_bss_ies *old;
+ struct cfg80211_internal_bss *bss;
+
+ if (found->pub.hidden_beacon_bss &&
+ !list_empty(&found->hidden_list)) {
+ const struct cfg80211_bss_ies *f;
+
+ /*
+ * The found BSS struct is one of the probe
+ * response members of a group, but we're
+ * receiving a beacon (beacon_ies in the tmp
+ * bss is used). This can only mean that the
+ * AP changed its beacon from not having an
+ * SSID to showing it, which is confusing so
+ * drop this information.
+ */
+
+ f = rcu_access_pointer(tmp->pub.beacon_ies);
+ kfree_rcu((struct cfg80211_bss_ies *)f,
+ rcu_head);
+ goto drop;
+ }
+
+ old = rcu_access_pointer(found->pub.beacon_ies);
+
+ rcu_assign_pointer(found->pub.beacon_ies,
+ tmp->pub.beacon_ies);
+
+ /* Override IEs if they were from a beacon before */
+ if (old == rcu_access_pointer(found->pub.ies))
+ rcu_assign_pointer(found->pub.ies,
+ tmp->pub.beacon_ies);
+
+ /* Assign beacon IEs to all sub entries */
+ list_for_each_entry(bss, &found->hidden_list,
+ hidden_list) {
+ const struct cfg80211_bss_ies *ies;
+
+ ies = rcu_access_pointer(bss->pub.beacon_ies);
+ WARN_ON(ies != old);
+
+ rcu_assign_pointer(bss->pub.beacon_ies,
+ tmp->pub.beacon_ies);
+ }
+
+ if (old)
+ kfree_rcu((struct cfg80211_bss_ies *)old,
+ rcu_head);
+ }
+
+ found->pub.beacon_interval = tmp->pub.beacon_interval;
+ /*
+ * don't update the signal if beacon was heard on
+ * adjacent channel.
+ */
+ if (signal_valid)
+ found->pub.signal = tmp->pub.signal;
+ found->pub.capability = tmp->pub.capability;
+ found->ts = tmp->ts;
+ found->ts_boottime = tmp->ts_boottime;
+ found->parent_tsf = tmp->parent_tsf;
+ ether_addr_copy(found->parent_bssid, tmp->parent_bssid);
+ } else {
+ struct cfg80211_internal_bss *new;
+ struct cfg80211_internal_bss *hidden;
+ struct cfg80211_bss_ies *ies;
+
+ /*
+ * create a copy -- the "res" variable that is passed in
+ * is allocated on the stack since it's not needed in the
+ * more common case of an update
+ */
+ new = kzalloc(sizeof(*new) + rdev->wiphy.bss_priv_size,
+ GFP_ATOMIC);
+ if (!new) {
+ ies = (void *)rcu_dereference(tmp->pub.beacon_ies);
+ if (ies)
+ kfree_rcu(ies, rcu_head);
+ ies = (void *)rcu_dereference(tmp->pub.proberesp_ies);
+ if (ies)
+ kfree_rcu(ies, rcu_head);
+ goto drop;
+ }
+ memcpy(new, tmp, sizeof(*new));
+ new->refcount = 1;
+ INIT_LIST_HEAD(&new->hidden_list);
+
+ if (rcu_access_pointer(tmp->pub.proberesp_ies)) {
+ hidden = rb_find_bss(rdev, tmp, BSS_CMP_HIDE_ZLEN);
+ if (!hidden)
+ hidden = rb_find_bss(rdev, tmp,
+ BSS_CMP_HIDE_NUL);
+ if (hidden) {
+ new->pub.hidden_beacon_bss = &hidden->pub;
+ list_add(&new->hidden_list,
+ &hidden->hidden_list);
+ hidden->refcount++;
+ rcu_assign_pointer(new->pub.beacon_ies,
+ hidden->pub.beacon_ies);
+ }
+ } else {
+ /*
+ * Ok so we found a beacon, and don't have an entry. If
+ * it's a beacon with hidden SSID, we might be in for an
+ * expensive search for any probe responses that should
+ * be grouped with this beacon for updates ...
+ */
+ if (!cfg80211_combine_bsses(rdev, new)) {
+ kfree(new);
+ goto drop;
+ }
+ }
+
+ if (rdev->bss_entries >= bss_entries_limit &&
+ !cfg80211_bss_expire_oldest(rdev)) {
+ kfree(new);
+ goto drop;
+ }
+
+ list_add_tail(&new->list, &rdev->bss_list);
+ rdev->bss_entries++;
+ rb_insert_bss(rdev, new);
+ found = new;
+ }
+
+ rdev->bss_generation++;
+ bss_ref_get(rdev, found);
+ spin_unlock_bh(&rdev->bss_lock);
+
+ return found;
+ drop:
+ spin_unlock_bh(&rdev->bss_lock);
+ return NULL;
+}
+
+static struct ieee80211_channel *
+cfg80211_get_bss_channel(struct wiphy *wiphy, const u8 *ie, size_t ielen,
+ struct ieee80211_channel *channel)
+{
+ const u8 *tmp;
+ u32 freq;
+ int channel_number = -1;
+
+ tmp = cfg80211_find_ie(WLAN_EID_DS_PARAMS, ie, ielen);
+ if (tmp && tmp[1] == 1) {
+ channel_number = tmp[2];
+ } else {
+ tmp = cfg80211_find_ie(WLAN_EID_HT_OPERATION, ie, ielen);
+ if (tmp && tmp[1] >= sizeof(struct ieee80211_ht_operation)) {
+ struct ieee80211_ht_operation *htop = (void *)(tmp + 2);
+
+ channel_number = htop->primary_chan;
+ }
+ }
+
+ if (channel_number < 0)
+ return channel;
+
+ freq = ieee80211_channel_to_frequency(channel_number, channel->band);
+ channel = ieee80211_get_channel(wiphy, freq);
+ if (!channel)
+ return NULL;
+ if (channel->flags & IEEE80211_CHAN_DISABLED)
+ return NULL;
+ return channel;
+}
+
+/* Returned bss is reference counted and must be cleaned up appropriately. */
+struct cfg80211_bss *
+cfg80211_inform_bss_data(struct wiphy *wiphy,
+ struct cfg80211_inform_bss *data,
+ enum cfg80211_bss_frame_type ftype,
+ const u8 *bssid, u64 tsf, u16 capability,
+ u16 beacon_interval, const u8 *ie, size_t ielen,
+ gfp_t gfp)
+{
+ struct cfg80211_bss_ies *ies;
+ struct ieee80211_channel *channel;
+ struct cfg80211_internal_bss tmp = {}, *res;
+ int bss_type;
+ bool signal_valid;
+
+ if (WARN_ON(!wiphy))
+ return NULL;
+
+ if (WARN_ON(wiphy->signal_type == CFG80211_SIGNAL_TYPE_UNSPEC &&
+ (data->signal < 0 || data->signal > 100)))
+ return NULL;
+
+ channel = cfg80211_get_bss_channel(wiphy, ie, ielen, data->chan);
+ if (!channel)
+ return NULL;
+
+ memcpy(tmp.pub.bssid, bssid, ETH_ALEN);
+ tmp.pub.channel = channel;
+ tmp.pub.scan_width = data->scan_width;
+ tmp.pub.signal = data->signal;
+ tmp.pub.beacon_interval = beacon_interval;
+ tmp.pub.capability = capability;
+ tmp.ts_boottime = data->boottime_ns;
+
+ /*
+ * If we do not know here whether the IEs are from a Beacon or Probe
+ * Response frame, we need to pick one of the options and only use it
+ * with the driver that does not provide the full Beacon/Probe Response
+ * frame. Use Beacon frame pointer to avoid indicating that this should
+ * override the IEs pointer should we have received an earlier
+ * indication of Probe Response data.
+ */
+ ies = kzalloc(sizeof(*ies) + ielen, gfp);
+ if (!ies)
+ return NULL;
+ ies->len = ielen;
+ ies->tsf = tsf;
+ ies->from_beacon = false;
+ memcpy(ies->data, ie, ielen);
+
+ switch (ftype) {
+ case CFG80211_BSS_FTYPE_BEACON:
+ ies->from_beacon = true;
+ /* fall through to assign */
+ case CFG80211_BSS_FTYPE_UNKNOWN:
+ rcu_assign_pointer(tmp.pub.beacon_ies, ies);
+ break;
+ case CFG80211_BSS_FTYPE_PRESP:
+ rcu_assign_pointer(tmp.pub.proberesp_ies, ies);
+ break;
+ }
+ rcu_assign_pointer(tmp.pub.ies, ies);
+
+ signal_valid = abs(data->chan->center_freq - channel->center_freq) <=
+ wiphy->max_adj_channel_rssi_comp;
+ res = cfg80211_bss_update(wiphy_to_rdev(wiphy), &tmp, signal_valid);
+ if (!res)
+ return NULL;
+
+ if (channel->band == NL80211_BAND_60GHZ) {
+ bss_type = res->pub.capability & WLAN_CAPABILITY_DMG_TYPE_MASK;
+ if (bss_type == WLAN_CAPABILITY_DMG_TYPE_AP ||
+ bss_type == WLAN_CAPABILITY_DMG_TYPE_PBSS)
+ regulatory_hint_found_beacon(wiphy, channel, gfp);
+ } else {
+ if (res->pub.capability & WLAN_CAPABILITY_ESS)
+ regulatory_hint_found_beacon(wiphy, channel, gfp);
+ }
+
+ trace_cfg80211_return_bss(&res->pub);
+ /* cfg80211_bss_update gives us a referenced result */
+ return &res->pub;
+}
+EXPORT_SYMBOL(cfg80211_inform_bss_data);
+
+/* cfg80211_inform_bss_width_frame helper */
+struct cfg80211_bss *
+cfg80211_inform_bss_frame_data(struct wiphy *wiphy,
+ struct cfg80211_inform_bss *data,
+ struct ieee80211_mgmt *mgmt, size_t len,
+ gfp_t gfp)
+
+{
+ struct cfg80211_internal_bss tmp = {}, *res;
+ struct cfg80211_bss_ies *ies;
+ struct ieee80211_channel *channel;
+ bool signal_valid;
+ size_t ielen = len - offsetof(struct ieee80211_mgmt,
+ u.probe_resp.variable);
+ int bss_type;
+
+ BUILD_BUG_ON(offsetof(struct ieee80211_mgmt, u.probe_resp.variable) !=
+ offsetof(struct ieee80211_mgmt, u.beacon.variable));
+
+ trace_cfg80211_inform_bss_frame(wiphy, data, mgmt, len);
+
+ if (WARN_ON(!mgmt))
+ return NULL;
+
+ if (WARN_ON(!wiphy))
+ return NULL;
+
+ if (WARN_ON(wiphy->signal_type == CFG80211_SIGNAL_TYPE_UNSPEC &&
+ (data->signal < 0 || data->signal > 100)))
+ return NULL;
+
+ if (WARN_ON(len < offsetof(struct ieee80211_mgmt, u.probe_resp.variable)))
+ return NULL;
+
+ channel = cfg80211_get_bss_channel(wiphy, mgmt->u.beacon.variable,
+ ielen, data->chan);
+ if (!channel)
+ return NULL;
+
+ ies = kzalloc(sizeof(*ies) + ielen, gfp);
+ if (!ies)
+ return NULL;
+ ies->len = ielen;
+ ies->tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp);
+ ies->from_beacon = ieee80211_is_beacon(mgmt->frame_control);
+ memcpy(ies->data, mgmt->u.probe_resp.variable, ielen);
+
+ if (ieee80211_is_probe_resp(mgmt->frame_control))
+ rcu_assign_pointer(tmp.pub.proberesp_ies, ies);
+ else
+ rcu_assign_pointer(tmp.pub.beacon_ies, ies);
+ rcu_assign_pointer(tmp.pub.ies, ies);
+
+ memcpy(tmp.pub.bssid, mgmt->bssid, ETH_ALEN);
+ tmp.pub.channel = channel;
+ tmp.pub.scan_width = data->scan_width;
+ tmp.pub.signal = data->signal;
+ tmp.pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int);
+ tmp.pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info);
+ tmp.ts_boottime = data->boottime_ns;
+ tmp.parent_tsf = data->parent_tsf;
+ ether_addr_copy(tmp.parent_bssid, data->parent_bssid);
+
+ signal_valid = abs(data->chan->center_freq - channel->center_freq) <=
+ wiphy->max_adj_channel_rssi_comp;
+ res = cfg80211_bss_update(wiphy_to_rdev(wiphy), &tmp, signal_valid);
+ if (!res)
+ return NULL;
+
+ if (channel->band == NL80211_BAND_60GHZ) {
+ bss_type = res->pub.capability & WLAN_CAPABILITY_DMG_TYPE_MASK;
+ if (bss_type == WLAN_CAPABILITY_DMG_TYPE_AP ||
+ bss_type == WLAN_CAPABILITY_DMG_TYPE_PBSS)
+ regulatory_hint_found_beacon(wiphy, channel, gfp);
+ } else {
+ if (res->pub.capability & WLAN_CAPABILITY_ESS)
+ regulatory_hint_found_beacon(wiphy, channel, gfp);
+ }
+
+ trace_cfg80211_return_bss(&res->pub);
+ /* cfg80211_bss_update gives us a referenced result */
+ return &res->pub;
+}
+EXPORT_SYMBOL(cfg80211_inform_bss_frame_data);
+
+void cfg80211_ref_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
+{
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+ struct cfg80211_internal_bss *bss;
+
+ if (!pub)
+ return;
+
+ bss = container_of(pub, struct cfg80211_internal_bss, pub);
+
+ spin_lock_bh(&rdev->bss_lock);
+ bss_ref_get(rdev, bss);
+ spin_unlock_bh(&rdev->bss_lock);
+}
+EXPORT_SYMBOL(cfg80211_ref_bss);
+
+void cfg80211_put_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
+{
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+ struct cfg80211_internal_bss *bss;
+
+ if (!pub)
+ return;
+
+ bss = container_of(pub, struct cfg80211_internal_bss, pub);
+
+ spin_lock_bh(&rdev->bss_lock);
+ bss_ref_put(rdev, bss);
+ spin_unlock_bh(&rdev->bss_lock);
+}
+EXPORT_SYMBOL(cfg80211_put_bss);
+
+void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
+{
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+ struct cfg80211_internal_bss *bss;
+
+ if (WARN_ON(!pub))
+ return;
+
+ bss = container_of(pub, struct cfg80211_internal_bss, pub);
+
+ spin_lock_bh(&rdev->bss_lock);
+ if (!list_empty(&bss->list)) {
+ if (__cfg80211_unlink_bss(rdev, bss))
+ rdev->bss_generation++;
+ }
+ spin_unlock_bh(&rdev->bss_lock);
+}
+EXPORT_SYMBOL(cfg80211_unlink_bss);
+
+#ifdef CPTCFG_CFG80211_WEXT
+static struct cfg80211_registered_device *
+cfg80211_get_dev_from_ifindex(struct net *net, int ifindex)
+{
+ struct cfg80211_registered_device *rdev;
+ struct net_device *dev;
+
+ ASSERT_RTNL();
+
+ dev = dev_get_by_index(net, ifindex);
+ if (!dev)
+ return ERR_PTR(-ENODEV);
+ if (dev->ieee80211_ptr)
+ rdev = wiphy_to_rdev(dev->ieee80211_ptr->wiphy);
+ else
+ rdev = ERR_PTR(-ENODEV);
+ dev_put(dev);
+ return rdev;
+}
+
+int cfg80211_wext_siwscan(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct cfg80211_registered_device *rdev;
+ struct wiphy *wiphy;
+ struct iw_scan_req *wreq = NULL;
+ struct cfg80211_scan_request *creq = NULL;
+ int i, err, n_channels = 0;
+ enum nl80211_band band;
+
+ if (!netif_running(dev))
+ return -ENETDOWN;
+
+ if (wrqu->data.length == sizeof(struct iw_scan_req))
+ wreq = (struct iw_scan_req *)extra;
+
+ rdev = cfg80211_get_dev_from_ifindex(dev_net(dev), dev->ifindex);
+
+ if (IS_ERR(rdev))
+ return PTR_ERR(rdev);
+
+ if (rdev->scan_req || rdev->scan_msg) {
+ err = -EBUSY;
+ goto out;
+ }
+
+ wiphy = &rdev->wiphy;
+
+ /* Determine number of channels, needed to allocate creq */
+ if (wreq && wreq->num_channels)
+ n_channels = wreq->num_channels;
+ else
+ n_channels = ieee80211_get_num_supported_channels(wiphy);
+
+ creq = kzalloc(sizeof(*creq) + sizeof(struct cfg80211_ssid) +
+ n_channels * sizeof(void *),
+ GFP_ATOMIC);
+ if (!creq) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ creq->wiphy = wiphy;
+ creq->wdev = dev->ieee80211_ptr;
+ /* SSIDs come after channels */
+ creq->ssids = (void *)&creq->channels[n_channels];
+ creq->n_channels = n_channels;
+ creq->n_ssids = 1;
+ creq->scan_start = jiffies;
+
+ /* translate "Scan on frequencies" request */
+ i = 0;
+ for (band = 0; band < NUM_NL80211_BANDS; band++) {
+ int j;
+
+ if (!wiphy->bands[band])
+ continue;
+
+ for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
+ /* ignore disabled channels */
+ if (wiphy->bands[band]->channels[j].flags &
+ IEEE80211_CHAN_DISABLED)
+ continue;
+
+ /* If we have a wireless request structure and the
+ * wireless request specifies frequencies, then search
+ * for the matching hardware channel.
+ */
+ if (wreq && wreq->num_channels) {
+ int k;
+ int wiphy_freq = wiphy->bands[band]->channels[j].center_freq;
+ for (k = 0; k < wreq->num_channels; k++) {
+ struct iw_freq *freq =
+ &wreq->channel_list[k];
+ int wext_freq =
+ cfg80211_wext_freq(freq);
+
+ if (wext_freq == wiphy_freq)
+ goto wext_freq_found;
+ }
+ goto wext_freq_not_found;
+ }
+
+ wext_freq_found:
+ creq->channels[i] = &wiphy->bands[band]->channels[j];
+ i++;
+ wext_freq_not_found: ;
+ }
+ }
+ /* No channels found? */
+ if (!i) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ /* Set real number of channels specified in creq->channels[] */
+ creq->n_channels = i;
+
+ /* translate "Scan for SSID" request */
+ if (wreq) {
+ if (wrqu->data.flags & IW_SCAN_THIS_ESSID) {
+ if (wreq->essid_len > IEEE80211_MAX_SSID_LEN) {
+ err = -EINVAL;
+ goto out;
+ }
+ memcpy(creq->ssids[0].ssid, wreq->essid, wreq->essid_len);
+ creq->ssids[0].ssid_len = wreq->essid_len;
+ }
+ if (wreq->scan_type == IW_SCAN_TYPE_PASSIVE)
+ creq->n_ssids = 0;
+ }
+
+ for (i = 0; i < NUM_NL80211_BANDS; i++)
+ if (wiphy->bands[i])
+ creq->rates[i] = (1 << wiphy->bands[i]->n_bitrates) - 1;
+
+ eth_broadcast_addr(creq->bssid);
+
+ rdev->scan_req = creq;
+ err = rdev_scan(rdev, creq);
+ if (err) {
+ rdev->scan_req = NULL;
+ /* creq will be freed below */
+ } else {
+ nl80211_send_scan_start(rdev, dev->ieee80211_ptr);
+ /* creq now owned by driver */
+ creq = NULL;
+ dev_hold(dev);
+ }
+ out:
+ kfree(creq);
+ return err;
+}
+EXPORT_WEXT_HANDLER(cfg80211_wext_siwscan);
+
+static char *ieee80211_scan_add_ies(struct iw_request_info *info,
+ const struct cfg80211_bss_ies *ies,
+ char *current_ev, char *end_buf)
+{
+ const u8 *pos, *end, *next;
+ struct iw_event iwe;
+
+ if (!ies)
+ return current_ev;
+
+ /*
+ * If needed, fragment the IEs buffer (at IE boundaries) into short
+ * enough fragments to fit into IW_GENERIC_IE_MAX octet messages.
+ */
+ pos = ies->data;
+ end = pos + ies->len;
+
+ while (end - pos > IW_GENERIC_IE_MAX) {
+ next = pos + 2 + pos[1];
+ while (next + 2 + next[1] - pos < IW_GENERIC_IE_MAX)
+ next = next + 2 + next[1];
+
+ memset(&iwe, 0, sizeof(iwe));
+ iwe.cmd = IWEVGENIE;
+ iwe.u.data.length = next - pos;
+ current_ev = iwe_stream_add_point_check(info, current_ev,
+ end_buf, &iwe,
+ (void *)pos);
+ if (IS_ERR(current_ev))
+ return current_ev;
+ pos = next;
+ }
+
+ if (end > pos) {
+ memset(&iwe, 0, sizeof(iwe));
+ iwe.cmd = IWEVGENIE;
+ iwe.u.data.length = end - pos;
+ current_ev = iwe_stream_add_point_check(info, current_ev,
+ end_buf, &iwe,
+ (void *)pos);
+ if (IS_ERR(current_ev))
+ return current_ev;
+ }
+
+ return current_ev;
+}
+
+static char *
+ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
+ struct cfg80211_internal_bss *bss, char *current_ev,
+ char *end_buf)
+{
+ const struct cfg80211_bss_ies *ies;
+ struct iw_event iwe;
+ const u8 *ie;
+ u8 buf[50];
+ u8 *cfg, *p, *tmp;
+ int rem, i, sig;
+ bool ismesh = false;
+
+ memset(&iwe, 0, sizeof(iwe));
+ iwe.cmd = SIOCGIWAP;
+ iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
+ memcpy(iwe.u.ap_addr.sa_data, bss->pub.bssid, ETH_ALEN);
+ current_ev = iwe_stream_add_event_check(info, current_ev, end_buf, &iwe,
+ IW_EV_ADDR_LEN);
+ if (IS_ERR(current_ev))
+ return current_ev;
+
+ memset(&iwe, 0, sizeof(iwe));
+ iwe.cmd = SIOCGIWFREQ;
+ iwe.u.freq.m = ieee80211_frequency_to_channel(bss->pub.channel->center_freq);
+ iwe.u.freq.e = 0;
+ current_ev = iwe_stream_add_event_check(info, current_ev, end_buf, &iwe,
+ IW_EV_FREQ_LEN);
+ if (IS_ERR(current_ev))
+ return current_ev;
+
+ memset(&iwe, 0, sizeof(iwe));
+ iwe.cmd = SIOCGIWFREQ;
+ iwe.u.freq.m = bss->pub.channel->center_freq;
+ iwe.u.freq.e = 6;
+ current_ev = iwe_stream_add_event_check(info, current_ev, end_buf, &iwe,
+ IW_EV_FREQ_LEN);
+ if (IS_ERR(current_ev))
+ return current_ev;
+
+ if (wiphy->signal_type != CFG80211_SIGNAL_TYPE_NONE) {
+ memset(&iwe, 0, sizeof(iwe));
+ iwe.cmd = IWEVQUAL;
+ iwe.u.qual.updated = IW_QUAL_LEVEL_UPDATED |
+ IW_QUAL_NOISE_INVALID |
+ IW_QUAL_QUAL_UPDATED;
+ switch (wiphy->signal_type) {
+ case CFG80211_SIGNAL_TYPE_MBM:
+ sig = bss->pub.signal / 100;
+ iwe.u.qual.level = sig;
+ iwe.u.qual.updated |= IW_QUAL_DBM;
+ if (sig < -110) /* rather bad */
+ sig = -110;
+ else if (sig > -40) /* perfect */
+ sig = -40;
+ /* will give a range of 0 .. 70 */
+ iwe.u.qual.qual = sig + 110;
+ break;
+ case CFG80211_SIGNAL_TYPE_UNSPEC:
+ iwe.u.qual.level = bss->pub.signal;
+ /* will give range 0 .. 100 */
+ iwe.u.qual.qual = bss->pub.signal;
+ break;
+ default:
+ /* not reached */
+ break;
+ }
+ current_ev = iwe_stream_add_event_check(info, current_ev,
+ end_buf, &iwe,
+ IW_EV_QUAL_LEN);
+ if (IS_ERR(current_ev))
+ return current_ev;
+ }
+
+ memset(&iwe, 0, sizeof(iwe));
+ iwe.cmd = SIOCGIWENCODE;
+ if (bss->pub.capability & WLAN_CAPABILITY_PRIVACY)
+ iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
+ else
+ iwe.u.data.flags = IW_ENCODE_DISABLED;
+ iwe.u.data.length = 0;
+ current_ev = iwe_stream_add_point_check(info, current_ev, end_buf,
+ &iwe, "");
+ if (IS_ERR(current_ev))
+ return current_ev;
+
+ rcu_read_lock();
+ ies = rcu_dereference(bss->pub.ies);
+ rem = ies->len;
+ ie = ies->data;
+
+ while (rem >= 2) {
+ /* invalid data */
+ if (ie[1] > rem - 2)
+ break;
+
+ switch (ie[0]) {
+ case WLAN_EID_SSID:
+ memset(&iwe, 0, sizeof(iwe));
+ iwe.cmd = SIOCGIWESSID;
+ iwe.u.data.length = ie[1];
+ iwe.u.data.flags = 1;
+ current_ev = iwe_stream_add_point_check(info,
+ current_ev,
+ end_buf, &iwe,
+ (u8 *)ie + 2);
+ if (IS_ERR(current_ev))
+ goto unlock;
+ break;
+ case WLAN_EID_MESH_ID:
+ memset(&iwe, 0, sizeof(iwe));
+ iwe.cmd = SIOCGIWESSID;
+ iwe.u.data.length = ie[1];
+ iwe.u.data.flags = 1;
+ current_ev = iwe_stream_add_point_check(info,
+ current_ev,
+ end_buf, &iwe,
+ (u8 *)ie + 2);
+ if (IS_ERR(current_ev))
+ goto unlock;
+ break;
+ case WLAN_EID_MESH_CONFIG:
+ ismesh = true;
+ if (ie[1] != sizeof(struct ieee80211_meshconf_ie))
+ break;
+ cfg = (u8 *)ie + 2;
+ memset(&iwe, 0, sizeof(iwe));
+ iwe.cmd = IWEVCUSTOM;
+ sprintf(buf, "Mesh Network Path Selection Protocol ID: "
+ "0x%02X", cfg[0]);
+ iwe.u.data.length = strlen(buf);
+ current_ev = iwe_stream_add_point_check(info,
+ current_ev,
+ end_buf,
+ &iwe, buf);
+ if (IS_ERR(current_ev))
+ goto unlock;
+ sprintf(buf, "Path Selection Metric ID: 0x%02X",
+ cfg[1]);
+ iwe.u.data.length = strlen(buf);
+ current_ev = iwe_stream_add_point_check(info,
+ current_ev,
+ end_buf,
+ &iwe, buf);
+ if (IS_ERR(current_ev))
+ goto unlock;
+ sprintf(buf, "Congestion Control Mode ID: 0x%02X",
+ cfg[2]);
+ iwe.u.data.length = strlen(buf);
+ current_ev = iwe_stream_add_point_check(info,
+ current_ev,
+ end_buf,
+ &iwe, buf);
+ if (IS_ERR(current_ev))
+ goto unlock;
+ sprintf(buf, "Synchronization ID: 0x%02X", cfg[3]);
+ iwe.u.data.length = strlen(buf);
+ current_ev = iwe_stream_add_point_check(info,
+ current_ev,
+ end_buf,
+ &iwe, buf);
+ if (IS_ERR(current_ev))
+ goto unlock;
+ sprintf(buf, "Authentication ID: 0x%02X", cfg[4]);
+ iwe.u.data.length = strlen(buf);
+ current_ev = iwe_stream_add_point_check(info,
+ current_ev,
+ end_buf,
+ &iwe, buf);
+ if (IS_ERR(current_ev))
+ goto unlock;
+ sprintf(buf, "Formation Info: 0x%02X", cfg[5]);
+ iwe.u.data.length = strlen(buf);
+ current_ev = iwe_stream_add_point_check(info,
+ current_ev,
+ end_buf,
+ &iwe, buf);
+ if (IS_ERR(current_ev))
+ goto unlock;
+ sprintf(buf, "Capabilities: 0x%02X", cfg[6]);
+ iwe.u.data.length = strlen(buf);
+ current_ev = iwe_stream_add_point_check(info,
+ current_ev,
+ end_buf,
+ &iwe, buf);
+ if (IS_ERR(current_ev))
+ goto unlock;
+ break;
+ case WLAN_EID_SUPP_RATES:
+ case WLAN_EID_EXT_SUPP_RATES:
+ /* display all supported rates in readable format */
+ p = current_ev + iwe_stream_lcp_len(info);
+
+ memset(&iwe, 0, sizeof(iwe));
+ iwe.cmd = SIOCGIWRATE;
+ /* Those two flags are ignored... */
+ iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
+
+ for (i = 0; i < ie[1]; i++) {
+ iwe.u.bitrate.value =
+ ((ie[i + 2] & 0x7f) * 500000);
+ tmp = p;
+ p = iwe_stream_add_value(info, current_ev, p,
+ end_buf, &iwe,
+ IW_EV_PARAM_LEN);
+ if (p == tmp) {
+ current_ev = ERR_PTR(-E2BIG);
+ goto unlock;
+ }
+ }
+ current_ev = p;
+ break;
+ }
+ rem -= ie[1] + 2;
+ ie += ie[1] + 2;
+ }
+
+ if (bss->pub.capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS) ||
+ ismesh) {
+ memset(&iwe, 0, sizeof(iwe));
+ iwe.cmd = SIOCGIWMODE;
+ if (ismesh)
+ iwe.u.mode = IW_MODE_MESH;
+ else if (bss->pub.capability & WLAN_CAPABILITY_ESS)
+ iwe.u.mode = IW_MODE_MASTER;
+ else
+ iwe.u.mode = IW_MODE_ADHOC;
+ current_ev = iwe_stream_add_event_check(info, current_ev,
+ end_buf, &iwe,
+ IW_EV_UINT_LEN);
+ if (IS_ERR(current_ev))
+ goto unlock;
+ }
+
+ memset(&iwe, 0, sizeof(iwe));
+ iwe.cmd = IWEVCUSTOM;
+ sprintf(buf, "tsf=%016llx", (unsigned long long)(ies->tsf));
+ iwe.u.data.length = strlen(buf);
+ current_ev = iwe_stream_add_point_check(info, current_ev, end_buf,
+ &iwe, buf);
+ if (IS_ERR(current_ev))
+ goto unlock;
+ memset(&iwe, 0, sizeof(iwe));
+ iwe.cmd = IWEVCUSTOM;
+ sprintf(buf, " Last beacon: %ums ago",
+ elapsed_jiffies_msecs(bss->ts));
+ iwe.u.data.length = strlen(buf);
+ current_ev = iwe_stream_add_point_check(info, current_ev,
+ end_buf, &iwe, buf);
+ if (IS_ERR(current_ev))
+ goto unlock;
+
+ current_ev = ieee80211_scan_add_ies(info, ies, current_ev, end_buf);
+
+ unlock:
+ rcu_read_unlock();
+ return current_ev;
+}
+
+
+static int ieee80211_scan_results(struct cfg80211_registered_device *rdev,
+ struct iw_request_info *info,
+ char *buf, size_t len)
+{
+ char *current_ev = buf;
+ char *end_buf = buf + len;
+ struct cfg80211_internal_bss *bss;
+ int err = 0;
+
+ spin_lock_bh(&rdev->bss_lock);
+ cfg80211_bss_expire(rdev);
+
+ list_for_each_entry(bss, &rdev->bss_list, list) {
+ if (buf + len - current_ev <= IW_EV_ADDR_LEN) {
+ err = -E2BIG;
+ break;
+ }
+ current_ev = ieee80211_bss(&rdev->wiphy, info, bss,
+ current_ev, end_buf);
+ if (IS_ERR(current_ev)) {
+ err = PTR_ERR(current_ev);
+ break;
+ }
+ }
+ spin_unlock_bh(&rdev->bss_lock);
+
+ if (err)
+ return err;
+ return current_ev - buf;
+}
+
+
+int cfg80211_wext_giwscan(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *data, char *extra)
+{
+ struct cfg80211_registered_device *rdev;
+ int res;
+
+ if (!netif_running(dev))
+ return -ENETDOWN;
+
+ rdev = cfg80211_get_dev_from_ifindex(dev_net(dev), dev->ifindex);
+
+ if (IS_ERR(rdev))
+ return PTR_ERR(rdev);
+
+ if (rdev->scan_req || rdev->scan_msg)
+ return -EAGAIN;
+
+ res = ieee80211_scan_results(rdev, info, extra, data->length);
+ data->length = 0;
+ if (res >= 0) {
+ data->length = res;
+ res = 0;
+ }
+
+ return res;
+}
+EXPORT_WEXT_HANDLER(cfg80211_wext_giwscan);
+#endif
diff --git a/net/wireless/sme.c b/net/wireless/sme.c
new file mode 100644
index 0000000..590d9cd
--- /dev/null
+++ b/net/wireless/sme.c
@@ -0,0 +1,1177 @@
+/*
+ * SME code for cfg80211
+ * both driver SME event handling and the SME implementation
+ * (for nl80211's connect() and wext)
+ *
+ * Copyright 2009 Johannes Berg <johannes@sipsolutions.net>
+ * Copyright (C) 2009 Intel Corporation. All rights reserved.
+ * Copyright 2017 Intel Deutschland GmbH
+ */
+
+#include <linux/etherdevice.h>
+#include <linux/if_arp.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/wireless.h>
+#include <linux/export.h>
+#include <net/iw_handler.h>
+#include <net/cfg80211.h>
+#include <net/rtnetlink.h>
+#include "nl80211.h"
+#include "reg.h"
+#include "rdev-ops.h"
+
+/*
+ * Software SME in cfg80211, using auth/assoc/deauth calls to the
+ * driver. This is is for implementing nl80211's connect/disconnect
+ * and wireless extensions (if configured.)
+ */
+
+struct cfg80211_conn {
+ struct cfg80211_connect_params params;
+ /* these are sub-states of the _CONNECTING sme_state */
+ enum {
+ CFG80211_CONN_SCANNING,
+ CFG80211_CONN_SCAN_AGAIN,
+ CFG80211_CONN_AUTHENTICATE_NEXT,
+ CFG80211_CONN_AUTHENTICATING,
+ CFG80211_CONN_AUTH_FAILED_TIMEOUT,
+ CFG80211_CONN_ASSOCIATE_NEXT,
+ CFG80211_CONN_ASSOCIATING,
+ CFG80211_CONN_ASSOC_FAILED,
+ CFG80211_CONN_ASSOC_FAILED_TIMEOUT,
+ CFG80211_CONN_DEAUTH,
+ CFG80211_CONN_ABANDON,
+ CFG80211_CONN_CONNECTED,
+ } state;
+ u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN];
+ const u8 *ie;
+ size_t ie_len;
+ bool auto_auth, prev_bssid_valid;
+};
+
+static void cfg80211_sme_free(struct wireless_dev *wdev)
+{
+ if (!wdev->conn)
+ return;
+
+ kfree(wdev->conn->ie);
+ kfree(wdev->conn);
+ wdev->conn = NULL;
+}
+
+static int cfg80211_conn_scan(struct wireless_dev *wdev)
+{
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+ struct cfg80211_scan_request *request;
+ int n_channels, err;
+
+ ASSERT_RTNL();
+ ASSERT_WDEV_LOCK(wdev);
+
+ if (rdev->scan_req || rdev->scan_msg)
+ return -EBUSY;
+
+ if (wdev->conn->params.channel)
+ n_channels = 1;
+ else
+ n_channels = ieee80211_get_num_supported_channels(wdev->wiphy);
+
+ request = kzalloc(sizeof(*request) + sizeof(request->ssids[0]) +
+ sizeof(request->channels[0]) * n_channels,
+ GFP_KERNEL);
+ if (!request)
+ return -ENOMEM;
+
+ if (wdev->conn->params.channel) {
+ enum nl80211_band band = wdev->conn->params.channel->band;
+ struct ieee80211_supported_band *sband =
+ wdev->wiphy->bands[band];
+
+ if (!sband) {
+ kfree(request);
+ return -EINVAL;
+ }
+ request->channels[0] = wdev->conn->params.channel;
+ request->rates[band] = (1 << sband->n_bitrates) - 1;
+ } else {
+ int i = 0, j;
+ enum nl80211_band band;
+ struct ieee80211_supported_band *bands;
+ struct ieee80211_channel *channel;
+
+ for (band = 0; band < NUM_NL80211_BANDS; band++) {
+ bands = wdev->wiphy->bands[band];
+ if (!bands)
+ continue;
+ for (j = 0; j < bands->n_channels; j++) {
+ channel = &bands->channels[j];
+ if (channel->flags & IEEE80211_CHAN_DISABLED)
+ continue;
+ request->channels[i++] = channel;
+ }
+ request->rates[band] = (1 << bands->n_bitrates) - 1;
+ }
+ n_channels = i;
+ }
+ request->n_channels = n_channels;
+ request->ssids = (void *)&request->channels[n_channels];
+ request->n_ssids = 1;
+
+ memcpy(request->ssids[0].ssid, wdev->conn->params.ssid,
+ wdev->conn->params.ssid_len);
+ request->ssids[0].ssid_len = wdev->conn->params.ssid_len;
+
+ eth_broadcast_addr(request->bssid);
+
+ request->wdev = wdev;
+ request->wiphy = &rdev->wiphy;
+ request->scan_start = jiffies;
+
+ rdev->scan_req = request;
+
+ err = rdev_scan(rdev, request);
+ if (!err) {
+ wdev->conn->state = CFG80211_CONN_SCANNING;
+ nl80211_send_scan_start(rdev, wdev);
+ dev_hold(wdev->netdev);
+ } else {
+ rdev->scan_req = NULL;
+ kfree(request);
+ }
+ return err;
+}
+
+static int cfg80211_conn_do_work(struct wireless_dev *wdev,
+ enum nl80211_timeout_reason *treason)
+{
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+ struct cfg80211_connect_params *params;
+ struct cfg80211_assoc_request req = {};
+ int err;
+
+ ASSERT_WDEV_LOCK(wdev);
+
+ if (!wdev->conn)
+ return 0;
+
+ params = &wdev->conn->params;
+
+ switch (wdev->conn->state) {
+ case CFG80211_CONN_SCANNING:
+ /* didn't find it during scan ... */
+ return -ENOENT;
+ case CFG80211_CONN_SCAN_AGAIN:
+ return cfg80211_conn_scan(wdev);
+ case CFG80211_CONN_AUTHENTICATE_NEXT:
+ if (WARN_ON(!rdev->ops->auth))
+ return -EOPNOTSUPP;
+ wdev->conn->state = CFG80211_CONN_AUTHENTICATING;
+ return cfg80211_mlme_auth(rdev, wdev->netdev,
+ params->channel, params->auth_type,
+ params->bssid,
+ params->ssid, params->ssid_len,
+ NULL, 0,
+ params->key, params->key_len,
+ params->key_idx, NULL, 0);
+ case CFG80211_CONN_AUTH_FAILED_TIMEOUT:
+ *treason = NL80211_TIMEOUT_AUTH;
+ return -ENOTCONN;
+ case CFG80211_CONN_ASSOCIATE_NEXT:
+ if (WARN_ON(!rdev->ops->assoc))
+ return -EOPNOTSUPP;
+ wdev->conn->state = CFG80211_CONN_ASSOCIATING;
+ if (wdev->conn->prev_bssid_valid)
+ req.prev_bssid = wdev->conn->prev_bssid;
+ req.ie = params->ie;
+ req.ie_len = params->ie_len;
+ req.use_mfp = params->mfp != NL80211_MFP_NO;
+ req.crypto = params->crypto;
+ req.flags = params->flags;
+ req.ht_capa = params->ht_capa;
+ req.ht_capa_mask = params->ht_capa_mask;
+ req.vht_capa = params->vht_capa;
+ req.vht_capa_mask = params->vht_capa_mask;
+
+ err = cfg80211_mlme_assoc(rdev, wdev->netdev, params->channel,
+ params->bssid, params->ssid,
+ params->ssid_len, &req);
+ if (err)
+ cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid,
+ NULL, 0,
+ WLAN_REASON_DEAUTH_LEAVING,
+ false);
+ return err;
+ case CFG80211_CONN_ASSOC_FAILED_TIMEOUT:
+ *treason = NL80211_TIMEOUT_ASSOC;
+ /* fall through */
+ case CFG80211_CONN_ASSOC_FAILED:
+ cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid,
+ NULL, 0,
+ WLAN_REASON_DEAUTH_LEAVING, false);
+ return -ENOTCONN;
+ case CFG80211_CONN_DEAUTH:
+ cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid,
+ NULL, 0,
+ WLAN_REASON_DEAUTH_LEAVING, false);
+ /* fall through */
+ case CFG80211_CONN_ABANDON:
+ /* free directly, disconnected event already sent */
+ cfg80211_sme_free(wdev);
+ return 0;
+ default:
+ return 0;
+ }
+}
+
+void cfg80211_conn_work(struct work_struct *work)
+{
+ struct cfg80211_registered_device *rdev =
+ container_of(work, struct cfg80211_registered_device, conn_work);
+ struct wireless_dev *wdev;
+ u8 bssid_buf[ETH_ALEN], *bssid = NULL;
+ enum nl80211_timeout_reason treason;
+
+ rtnl_lock();
+
+ list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
+ if (!wdev->netdev)
+ continue;
+
+ wdev_lock(wdev);
+ if (!netif_running(wdev->netdev)) {
+ wdev_unlock(wdev);
+ continue;
+ }
+ if (!wdev->conn ||
+ wdev->conn->state == CFG80211_CONN_CONNECTED) {
+ wdev_unlock(wdev);
+ continue;
+ }
+ if (wdev->conn->params.bssid) {
+ memcpy(bssid_buf, wdev->conn->params.bssid, ETH_ALEN);
+ bssid = bssid_buf;
+ }
+ treason = NL80211_TIMEOUT_UNSPECIFIED;
+ if (cfg80211_conn_do_work(wdev, &treason)) {
+ struct cfg80211_connect_resp_params cr;
+
+ memset(&cr, 0, sizeof(cr));
+ cr.status = -1;
+ cr.bssid = bssid;
+ cr.timeout_reason = treason;
+ __cfg80211_connect_result(wdev->netdev, &cr, false);
+ }
+ wdev_unlock(wdev);
+ }
+
+ rtnl_unlock();
+}
+
+/* Returned bss is reference counted and must be cleaned up appropriately. */
+static struct cfg80211_bss *cfg80211_get_conn_bss(struct wireless_dev *wdev)
+{
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+ struct cfg80211_bss *bss;
+
+ ASSERT_WDEV_LOCK(wdev);
+
+ bss = cfg80211_get_bss(wdev->wiphy, wdev->conn->params.channel,
+ wdev->conn->params.bssid,
+ wdev->conn->params.ssid,
+ wdev->conn->params.ssid_len,
+ wdev->conn_bss_type,
+ IEEE80211_PRIVACY(wdev->conn->params.privacy));
+ if (!bss)
+ return NULL;
+
+ memcpy(wdev->conn->bssid, bss->bssid, ETH_ALEN);
+ wdev->conn->params.bssid = wdev->conn->bssid;
+ wdev->conn->params.channel = bss->channel;
+ wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT;
+ schedule_work(&rdev->conn_work);
+
+ return bss;
+}
+
+static void __cfg80211_sme_scan_done(struct net_device *dev)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+ struct cfg80211_bss *bss;
+
+ ASSERT_WDEV_LOCK(wdev);
+
+ if (!wdev->conn)
+ return;
+
+ if (wdev->conn->state != CFG80211_CONN_SCANNING &&
+ wdev->conn->state != CFG80211_CONN_SCAN_AGAIN)
+ return;
+
+ bss = cfg80211_get_conn_bss(wdev);
+ if (bss)
+ cfg80211_put_bss(&rdev->wiphy, bss);
+ else
+ schedule_work(&rdev->conn_work);
+}
+
+void cfg80211_sme_scan_done(struct net_device *dev)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+
+ wdev_lock(wdev);
+ __cfg80211_sme_scan_done(dev);
+ wdev_unlock(wdev);
+}
+
+void cfg80211_sme_rx_auth(struct wireless_dev *wdev, const u8 *buf, size_t len)
+{
+ struct wiphy *wiphy = wdev->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
+ u16 status_code = le16_to_cpu(mgmt->u.auth.status_code);
+
+ ASSERT_WDEV_LOCK(wdev);
+
+ if (!wdev->conn || wdev->conn->state == CFG80211_CONN_CONNECTED)
+ return;
+
+ if (status_code == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG &&
+ wdev->conn->auto_auth &&
+ wdev->conn->params.auth_type != NL80211_AUTHTYPE_NETWORK_EAP) {
+ /* select automatically between only open, shared, leap */
+ switch (wdev->conn->params.auth_type) {
+ case NL80211_AUTHTYPE_OPEN_SYSTEM:
+ if (wdev->connect_keys)
+ wdev->conn->params.auth_type =
+ NL80211_AUTHTYPE_SHARED_KEY;
+ else
+ wdev->conn->params.auth_type =
+ NL80211_AUTHTYPE_NETWORK_EAP;
+ break;
+ case NL80211_AUTHTYPE_SHARED_KEY:
+ wdev->conn->params.auth_type =
+ NL80211_AUTHTYPE_NETWORK_EAP;
+ break;
+ default:
+ /* huh? */
+ wdev->conn->params.auth_type =
+ NL80211_AUTHTYPE_OPEN_SYSTEM;
+ break;
+ }
+ wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT;
+ schedule_work(&rdev->conn_work);
+ } else if (status_code != WLAN_STATUS_SUCCESS) {
+ struct cfg80211_connect_resp_params cr;
+
+ memset(&cr, 0, sizeof(cr));
+ cr.status = status_code;
+ cr.bssid = mgmt->bssid;
+ cr.timeout_reason = NL80211_TIMEOUT_UNSPECIFIED;
+ __cfg80211_connect_result(wdev->netdev, &cr, false);
+ } else if (wdev->conn->state == CFG80211_CONN_AUTHENTICATING) {
+ wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT;
+ schedule_work(&rdev->conn_work);
+ }
+}
+
+bool cfg80211_sme_rx_assoc_resp(struct wireless_dev *wdev, u16 status)
+{
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+
+ if (!wdev->conn)
+ return false;
+
+ if (status == WLAN_STATUS_SUCCESS) {
+ wdev->conn->state = CFG80211_CONN_CONNECTED;
+ return false;
+ }
+
+ if (wdev->conn->prev_bssid_valid) {
+ /*
+ * Some stupid APs don't accept reassoc, so we
+ * need to fall back to trying regular assoc;
+ * return true so no event is sent to userspace.
+ */
+ wdev->conn->prev_bssid_valid = false;
+ wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT;
+ schedule_work(&rdev->conn_work);
+ return true;
+ }
+
+ wdev->conn->state = CFG80211_CONN_ASSOC_FAILED;
+ schedule_work(&rdev->conn_work);
+ return false;
+}
+
+void cfg80211_sme_deauth(struct wireless_dev *wdev)
+{
+ cfg80211_sme_free(wdev);
+}
+
+void cfg80211_sme_auth_timeout(struct wireless_dev *wdev)
+{
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+
+ if (!wdev->conn)
+ return;
+
+ wdev->conn->state = CFG80211_CONN_AUTH_FAILED_TIMEOUT;
+ schedule_work(&rdev->conn_work);
+}
+
+void cfg80211_sme_disassoc(struct wireless_dev *wdev)
+{
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+
+ if (!wdev->conn)
+ return;
+
+ wdev->conn->state = CFG80211_CONN_DEAUTH;
+ schedule_work(&rdev->conn_work);
+}
+
+void cfg80211_sme_assoc_timeout(struct wireless_dev *wdev)
+{
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+
+ if (!wdev->conn)
+ return;
+
+ wdev->conn->state = CFG80211_CONN_ASSOC_FAILED_TIMEOUT;
+ schedule_work(&rdev->conn_work);
+}
+
+void cfg80211_sme_abandon_assoc(struct wireless_dev *wdev)
+{
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+
+ if (!wdev->conn)
+ return;
+
+ wdev->conn->state = CFG80211_CONN_ABANDON;
+ schedule_work(&rdev->conn_work);
+}
+
+static int cfg80211_sme_get_conn_ies(struct wireless_dev *wdev,
+ const u8 *ies, size_t ies_len,
+ const u8 **out_ies, size_t *out_ies_len)
+{
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+ u8 *buf;
+ size_t offs;
+
+ if (!rdev->wiphy.extended_capabilities_len ||
+ (ies && cfg80211_find_ie(WLAN_EID_EXT_CAPABILITY, ies, ies_len))) {
+ *out_ies = kmemdup(ies, ies_len, GFP_KERNEL);
+ if (!*out_ies)
+ return -ENOMEM;
+ *out_ies_len = ies_len;
+ return 0;
+ }
+
+ buf = kmalloc(ies_len + rdev->wiphy.extended_capabilities_len + 2,
+ GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ if (ies_len) {
+ static const u8 before_extcapa[] = {
+ /* not listing IEs expected to be created by driver */
+ WLAN_EID_RSN,
+ WLAN_EID_QOS_CAPA,
+ WLAN_EID_RRM_ENABLED_CAPABILITIES,
+ WLAN_EID_MOBILITY_DOMAIN,
+ WLAN_EID_SUPPORTED_REGULATORY_CLASSES,
+ WLAN_EID_BSS_COEX_2040,
+ };
+
+ offs = ieee80211_ie_split(ies, ies_len, before_extcapa,
+ ARRAY_SIZE(before_extcapa), 0);
+ memcpy(buf, ies, offs);
+ /* leave a whole for extended capabilities IE */
+ memcpy(buf + offs + rdev->wiphy.extended_capabilities_len + 2,
+ ies + offs, ies_len - offs);
+ } else {
+ offs = 0;
+ }
+
+ /* place extended capabilities IE (with only driver capabilities) */
+ buf[offs] = WLAN_EID_EXT_CAPABILITY;
+ buf[offs + 1] = rdev->wiphy.extended_capabilities_len;
+ memcpy(buf + offs + 2,
+ rdev->wiphy.extended_capabilities,
+ rdev->wiphy.extended_capabilities_len);
+
+ *out_ies = buf;
+ *out_ies_len = ies_len + rdev->wiphy.extended_capabilities_len + 2;
+
+ return 0;
+}
+
+static int cfg80211_sme_connect(struct wireless_dev *wdev,
+ struct cfg80211_connect_params *connect,
+ const u8 *prev_bssid)
+{
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+ struct cfg80211_bss *bss;
+ int err;
+
+ if (!rdev->ops->auth || !rdev->ops->assoc)
+ return -EOPNOTSUPP;
+
+ if (wdev->current_bss) {
+ if (!prev_bssid)
+ return -EALREADY;
+ if (prev_bssid &&
+ !ether_addr_equal(prev_bssid, wdev->current_bss->pub.bssid))
+ return -ENOTCONN;
+ cfg80211_unhold_bss(wdev->current_bss);
+ cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub);
+ wdev->current_bss = NULL;
+
+ cfg80211_sme_free(wdev);
+ }
+
+ if (WARN_ON(wdev->conn))
+ return -EINPROGRESS;
+
+ wdev->conn = kzalloc(sizeof(*wdev->conn), GFP_KERNEL);
+ if (!wdev->conn)
+ return -ENOMEM;
+
+ /*
+ * Copy all parameters, and treat explicitly IEs, BSSID, SSID.
+ */
+ memcpy(&wdev->conn->params, connect, sizeof(*connect));
+ if (connect->bssid) {
+ wdev->conn->params.bssid = wdev->conn->bssid;
+ memcpy(wdev->conn->bssid, connect->bssid, ETH_ALEN);
+ }
+
+ if (cfg80211_sme_get_conn_ies(wdev, connect->ie, connect->ie_len,
+ &wdev->conn->ie,
+ &wdev->conn->params.ie_len)) {
+ kfree(wdev->conn);
+ wdev->conn = NULL;
+ return -ENOMEM;
+ }
+ wdev->conn->params.ie = wdev->conn->ie;
+
+ if (connect->auth_type == NL80211_AUTHTYPE_AUTOMATIC) {
+ wdev->conn->auto_auth = true;
+ /* start with open system ... should mostly work */
+ wdev->conn->params.auth_type =
+ NL80211_AUTHTYPE_OPEN_SYSTEM;
+ } else {
+ wdev->conn->auto_auth = false;
+ }
+
+ wdev->conn->params.ssid = wdev->ssid;
+ wdev->conn->params.ssid_len = wdev->ssid_len;
+
+ /* see if we have the bss already */
+ bss = cfg80211_get_conn_bss(wdev);
+
+ if (prev_bssid) {
+ memcpy(wdev->conn->prev_bssid, prev_bssid, ETH_ALEN);
+ wdev->conn->prev_bssid_valid = true;
+ }
+
+ /* we're good if we have a matching bss struct */
+ if (bss) {
+ enum nl80211_timeout_reason treason;
+
+ err = cfg80211_conn_do_work(wdev, &treason);
+ cfg80211_put_bss(wdev->wiphy, bss);
+ } else {
+ /* otherwise we'll need to scan for the AP first */
+ err = cfg80211_conn_scan(wdev);
+
+ /*
+ * If we can't scan right now, then we need to scan again
+ * after the current scan finished, since the parameters
+ * changed (unless we find a good AP anyway).
+ */
+ if (err == -EBUSY) {
+ err = 0;
+ wdev->conn->state = CFG80211_CONN_SCAN_AGAIN;
+ }
+ }
+
+ if (err)
+ cfg80211_sme_free(wdev);
+
+ return err;
+}
+
+static int cfg80211_sme_disconnect(struct wireless_dev *wdev, u16 reason)
+{
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+ int err;
+
+ if (!wdev->conn)
+ return 0;
+
+ if (!rdev->ops->deauth)
+ return -EOPNOTSUPP;
+
+ if (wdev->conn->state == CFG80211_CONN_SCANNING ||
+ wdev->conn->state == CFG80211_CONN_SCAN_AGAIN) {
+ err = 0;
+ goto out;
+ }
+
+ /* wdev->conn->params.bssid must be set if > SCANNING */
+ err = cfg80211_mlme_deauth(rdev, wdev->netdev,
+ wdev->conn->params.bssid,
+ NULL, 0, reason, false);
+ out:
+ cfg80211_sme_free(wdev);
+ return err;
+}
+
+/*
+ * code shared for in-device and software SME
+ */
+
+static bool cfg80211_is_all_idle(void)
+{
+ struct cfg80211_registered_device *rdev;
+ struct wireless_dev *wdev;
+ bool is_all_idle = true;
+
+ /*
+ * All devices must be idle as otherwise if you are actively
+ * scanning some new beacon hints could be learned and would
+ * count as new regulatory hints.
+ */
+ list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
+ list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
+ wdev_lock(wdev);
+ if (wdev->conn || wdev->current_bss)
+ is_all_idle = false;
+ wdev_unlock(wdev);
+ }
+ }
+
+ return is_all_idle;
+}
+
+static void disconnect_work(struct work_struct *work)
+{
+ rtnl_lock();
+ if (cfg80211_is_all_idle())
+ regulatory_hint_disconnect();
+ rtnl_unlock();
+}
+
+static DECLARE_WORK(cfg80211_disconnect_work, disconnect_work);
+
+
+/*
+ * API calls for drivers implementing connect/disconnect and
+ * SME event handling
+ */
+
+/* This method must consume bss one way or another */
+void __cfg80211_connect_result(struct net_device *dev,
+ struct cfg80211_connect_resp_params *cr,
+ bool wextev)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ const u8 *country_ie;
+#ifdef CPTCFG_CFG80211_WEXT
+ union iwreq_data wrqu;
+#endif
+
+ ASSERT_WDEV_LOCK(wdev);
+
+ if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION &&
+ wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) {
+ cfg80211_put_bss(wdev->wiphy, cr->bss);
+ return;
+ }
+
+ nl80211_send_connect_result(wiphy_to_rdev(wdev->wiphy), dev, cr,
+ GFP_KERNEL);
+
+#ifdef CPTCFG_CFG80211_WEXT
+ if (wextev) {
+ if (cr->req_ie && cr->status == WLAN_STATUS_SUCCESS) {
+ memset(&wrqu, 0, sizeof(wrqu));
+ wrqu.data.length = cr->req_ie_len;
+ wireless_send_event(dev, IWEVASSOCREQIE, &wrqu,
+ cr->req_ie);
+ }
+
+ if (cr->resp_ie && cr->status == WLAN_STATUS_SUCCESS) {
+ memset(&wrqu, 0, sizeof(wrqu));
+ wrqu.data.length = cr->resp_ie_len;
+ wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu,
+ cr->resp_ie);
+ }
+
+ memset(&wrqu, 0, sizeof(wrqu));
+ wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+ if (cr->bssid && cr->status == WLAN_STATUS_SUCCESS) {
+ memcpy(wrqu.ap_addr.sa_data, cr->bssid, ETH_ALEN);
+ memcpy(wdev->wext.prev_bssid, cr->bssid, ETH_ALEN);
+ wdev->wext.prev_bssid_valid = true;
+ }
+ wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
+ }
+#endif
+
+ if (!cr->bss && (cr->status == WLAN_STATUS_SUCCESS)) {
+ WARN_ON_ONCE(!wiphy_to_rdev(wdev->wiphy)->ops->connect);
+ cr->bss = cfg80211_get_bss(wdev->wiphy, NULL, cr->bssid,
+ wdev->ssid, wdev->ssid_len,
+ wdev->conn_bss_type,
+ IEEE80211_PRIVACY_ANY);
+ if (cr->bss)
+ cfg80211_hold_bss(bss_from_pub(cr->bss));
+ }
+
+ if (wdev->current_bss) {
+ cfg80211_unhold_bss(wdev->current_bss);
+ cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub);
+ wdev->current_bss = NULL;
+ }
+
+ if (cr->status != WLAN_STATUS_SUCCESS) {
+ kzfree(wdev->connect_keys);
+ wdev->connect_keys = NULL;
+ wdev->ssid_len = 0;
+ wdev->conn_owner_nlportid = 0;
+ if (cr->bss) {
+ cfg80211_unhold_bss(bss_from_pub(cr->bss));
+ cfg80211_put_bss(wdev->wiphy, cr->bss);
+ }
+ cfg80211_sme_free(wdev);
+ return;
+ }
+
+ if (WARN_ON(!cr->bss))
+ return;
+
+ wdev->current_bss = bss_from_pub(cr->bss);
+
+ if (!(wdev->wiphy->flags & WIPHY_FLAG_HAS_STATIC_WEP))
+ cfg80211_upload_connect_keys(wdev);
+
+ rcu_read_lock();
+ country_ie = ieee80211_bss_get_ie(cr->bss, WLAN_EID_COUNTRY);
+ if (!country_ie) {
+ rcu_read_unlock();
+ return;
+ }
+
+ country_ie = kmemdup(country_ie, 2 + country_ie[1], GFP_ATOMIC);
+ rcu_read_unlock();
+
+ if (!country_ie)
+ return;
+
+ /*
+ * ieee80211_bss_get_ie() ensures we can access:
+ * - country_ie + 2, the start of the country ie data, and
+ * - and country_ie[1] which is the IE length
+ */
+ regulatory_hint_country_ie(wdev->wiphy, cr->bss->channel->band,
+ country_ie + 2, country_ie[1]);
+ kfree(country_ie);
+}
+
+/* Consumes bss object one way or another */
+void cfg80211_connect_done(struct net_device *dev,
+ struct cfg80211_connect_resp_params *params,
+ gfp_t gfp)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+ struct cfg80211_event *ev;
+ unsigned long flags;
+ u8 *next;
+
+ if (params->bss) {
+ /* Make sure the bss entry provided by the driver is valid. */
+ struct cfg80211_internal_bss *ibss = bss_from_pub(params->bss);
+
+ if (WARN_ON(list_empty(&ibss->list))) {
+ cfg80211_put_bss(wdev->wiphy, params->bss);
+ return;
+ }
+ }
+
+ ev = kzalloc(sizeof(*ev) + (params->bssid ? ETH_ALEN : 0) +
+ params->req_ie_len + params->resp_ie_len +
+ params->fils_kek_len + params->pmk_len +
+ (params->pmkid ? WLAN_PMKID_LEN : 0), gfp);
+ if (!ev) {
+ cfg80211_put_bss(wdev->wiphy, params->bss);
+ return;
+ }
+
+ ev->type = EVENT_CONNECT_RESULT;
+ next = ((u8 *)ev) + sizeof(*ev);
+ if (params->bssid) {
+ ev->cr.bssid = next;
+ memcpy((void *)ev->cr.bssid, params->bssid, ETH_ALEN);
+ next += ETH_ALEN;
+ }
+ if (params->req_ie_len) {
+ ev->cr.req_ie = next;
+ ev->cr.req_ie_len = params->req_ie_len;
+ memcpy((void *)ev->cr.req_ie, params->req_ie,
+ params->req_ie_len);
+ next += params->req_ie_len;
+ }
+ if (params->resp_ie_len) {
+ ev->cr.resp_ie = next;
+ ev->cr.resp_ie_len = params->resp_ie_len;
+ memcpy((void *)ev->cr.resp_ie, params->resp_ie,
+ params->resp_ie_len);
+ next += params->resp_ie_len;
+ }
+ if (params->fils_kek_len) {
+ ev->cr.fils_kek = next;
+ ev->cr.fils_kek_len = params->fils_kek_len;
+ memcpy((void *)ev->cr.fils_kek, params->fils_kek,
+ params->fils_kek_len);
+ next += params->fils_kek_len;
+ }
+ if (params->pmk_len) {
+ ev->cr.pmk = next;
+ ev->cr.pmk_len = params->pmk_len;
+ memcpy((void *)ev->cr.pmk, params->pmk, params->pmk_len);
+ next += params->pmk_len;
+ }
+ if (params->pmkid) {
+ ev->cr.pmkid = next;
+ memcpy((void *)ev->cr.pmkid, params->pmkid, WLAN_PMKID_LEN);
+ next += WLAN_PMKID_LEN;
+ }
+ ev->cr.update_erp_next_seq_num = params->update_erp_next_seq_num;
+ if (params->update_erp_next_seq_num)
+ ev->cr.fils_erp_next_seq_num = params->fils_erp_next_seq_num;
+ if (params->bss)
+ cfg80211_hold_bss(bss_from_pub(params->bss));
+ ev->cr.bss = params->bss;
+ ev->cr.status = params->status;
+ ev->cr.timeout_reason = params->timeout_reason;
+
+ spin_lock_irqsave(&wdev->event_lock, flags);
+ list_add_tail(&ev->list, &wdev->event_list);
+ spin_unlock_irqrestore(&wdev->event_lock, flags);
+ queue_work(cfg80211_wq, &rdev->event_work);
+}
+EXPORT_SYMBOL(cfg80211_connect_done);
+
+/* Consumes bss object one way or another */
+void __cfg80211_roamed(struct wireless_dev *wdev,
+ struct cfg80211_roam_info *info)
+{
+#ifdef CPTCFG_CFG80211_WEXT
+ union iwreq_data wrqu;
+#endif
+ ASSERT_WDEV_LOCK(wdev);
+
+ if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION &&
+ wdev->iftype != NL80211_IFTYPE_P2P_CLIENT))
+ goto out;
+
+ if (WARN_ON(!wdev->current_bss))
+ goto out;
+
+ cfg80211_unhold_bss(wdev->current_bss);
+ cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub);
+ wdev->current_bss = NULL;
+
+ if (WARN_ON(!info->bss))
+ return;
+
+ cfg80211_hold_bss(bss_from_pub(info->bss));
+ wdev->current_bss = bss_from_pub(info->bss);
+
+ nl80211_send_roamed(wiphy_to_rdev(wdev->wiphy),
+ wdev->netdev, info, GFP_KERNEL);
+
+#ifdef CPTCFG_CFG80211_WEXT
+ if (info->req_ie) {
+ memset(&wrqu, 0, sizeof(wrqu));
+ wrqu.data.length = info->req_ie_len;
+ wireless_send_event(wdev->netdev, IWEVASSOCREQIE,
+ &wrqu, info->req_ie);
+ }
+
+ if (info->resp_ie) {
+ memset(&wrqu, 0, sizeof(wrqu));
+ wrqu.data.length = info->resp_ie_len;
+ wireless_send_event(wdev->netdev, IWEVASSOCRESPIE,
+ &wrqu, info->resp_ie);
+ }
+
+ memset(&wrqu, 0, sizeof(wrqu));
+ wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+ memcpy(wrqu.ap_addr.sa_data, info->bss->bssid, ETH_ALEN);
+ memcpy(wdev->wext.prev_bssid, info->bss->bssid, ETH_ALEN);
+ wdev->wext.prev_bssid_valid = true;
+ wireless_send_event(wdev->netdev, SIOCGIWAP, &wrqu, NULL);
+#endif
+
+ return;
+out:
+ cfg80211_put_bss(wdev->wiphy, info->bss);
+}
+
+/* Consumes info->bss object one way or another */
+void cfg80211_roamed(struct net_device *dev, struct cfg80211_roam_info *info,
+ gfp_t gfp)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+ struct cfg80211_event *ev;
+ unsigned long flags;
+
+ if (!info->bss) {
+ info->bss = cfg80211_get_bss(wdev->wiphy, info->channel,
+ info->bssid, wdev->ssid,
+ wdev->ssid_len,
+ wdev->conn_bss_type,
+ IEEE80211_PRIVACY_ANY);
+ }
+
+ if (WARN_ON(!info->bss))
+ return;
+
+ ev = kzalloc(sizeof(*ev) + info->req_ie_len + info->resp_ie_len, gfp);
+ if (!ev) {
+ cfg80211_put_bss(wdev->wiphy, info->bss);
+ return;
+ }
+
+ ev->type = EVENT_ROAMED;
+ ev->rm.req_ie = ((u8 *)ev) + sizeof(*ev);
+ ev->rm.req_ie_len = info->req_ie_len;
+ memcpy((void *)ev->rm.req_ie, info->req_ie, info->req_ie_len);
+ ev->rm.resp_ie = ((u8 *)ev) + sizeof(*ev) + info->req_ie_len;
+ ev->rm.resp_ie_len = info->resp_ie_len;
+ memcpy((void *)ev->rm.resp_ie, info->resp_ie, info->resp_ie_len);
+ ev->rm.bss = info->bss;
+
+ spin_lock_irqsave(&wdev->event_lock, flags);
+ list_add_tail(&ev->list, &wdev->event_list);
+ spin_unlock_irqrestore(&wdev->event_lock, flags);
+ queue_work(cfg80211_wq, &rdev->event_work);
+}
+EXPORT_SYMBOL(cfg80211_roamed);
+
+void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,
+ size_t ie_len, u16 reason, bool from_ap)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+ int i;
+#ifdef CPTCFG_CFG80211_WEXT
+ union iwreq_data wrqu;
+#endif
+
+ ASSERT_WDEV_LOCK(wdev);
+
+ if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION &&
+ wdev->iftype != NL80211_IFTYPE_P2P_CLIENT))
+ return;
+
+ if (wdev->current_bss) {
+ cfg80211_unhold_bss(wdev->current_bss);
+ cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub);
+ }
+
+ wdev->current_bss = NULL;
+ wdev->ssid_len = 0;
+ wdev->conn_owner_nlportid = 0;
+
+ nl80211_send_disconnected(rdev, dev, reason, ie, ie_len, from_ap);
+
+ /* stop critical protocol if supported */
+ if (rdev->ops->crit_proto_stop && rdev->crit_proto_nlportid) {
+ rdev->crit_proto_nlportid = 0;
+ rdev_crit_proto_stop(rdev, wdev);
+ }
+
+ /*
+ * Delete all the keys ... pairwise keys can't really
+ * exist any more anyway, but default keys might.
+ */
+ if (rdev->ops->del_key)
+ for (i = 0; i < 6; i++)
+ rdev_del_key(rdev, dev, i, false, NULL);
+
+ rdev_set_qos_map(rdev, dev, NULL);
+
+#ifdef CPTCFG_CFG80211_WEXT
+ memset(&wrqu, 0, sizeof(wrqu));
+ wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+ wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
+ wdev->wext.connect.ssid_len = 0;
+#endif
+
+ schedule_work(&cfg80211_disconnect_work);
+}
+
+void cfg80211_disconnected(struct net_device *dev, u16 reason,
+ const u8 *ie, size_t ie_len,
+ bool locally_generated, gfp_t gfp)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+ struct cfg80211_event *ev;
+ unsigned long flags;
+
+ ev = kzalloc(sizeof(*ev) + ie_len, gfp);
+ if (!ev)
+ return;
+
+ ev->type = EVENT_DISCONNECTED;
+ ev->dc.ie = ((u8 *)ev) + sizeof(*ev);
+ ev->dc.ie_len = ie_len;
+ memcpy((void *)ev->dc.ie, ie, ie_len);
+ ev->dc.reason = reason;
+ ev->dc.locally_generated = locally_generated;
+
+ spin_lock_irqsave(&wdev->event_lock, flags);
+ list_add_tail(&ev->list, &wdev->event_list);
+ spin_unlock_irqrestore(&wdev->event_lock, flags);
+ queue_work(cfg80211_wq, &rdev->event_work);
+}
+EXPORT_SYMBOL(cfg80211_disconnected);
+
+/*
+ * API calls for nl80211/wext compatibility code
+ */
+int cfg80211_connect(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct cfg80211_connect_params *connect,
+ struct cfg80211_cached_keys *connkeys,
+ const u8 *prev_bssid)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ int err;
+
+ ASSERT_WDEV_LOCK(wdev);
+
+ if (WARN_ON(wdev->connect_keys)) {
+ kzfree(wdev->connect_keys);
+ wdev->connect_keys = NULL;
+ }
+
+ cfg80211_oper_and_ht_capa(&connect->ht_capa_mask,
+ rdev->wiphy.ht_capa_mod_mask);
+
+ if (connkeys && connkeys->def >= 0) {
+ int idx;
+ u32 cipher;
+
+ idx = connkeys->def;
+ cipher = connkeys->params[idx].cipher;
+ /* If given a WEP key we may need it for shared key auth */
+ if (cipher == WLAN_CIPHER_SUITE_WEP40 ||
+ cipher == WLAN_CIPHER_SUITE_WEP104) {
+ connect->key_idx = idx;
+ connect->key = connkeys->params[idx].key;
+ connect->key_len = connkeys->params[idx].key_len;
+
+ /*
+ * If ciphers are not set (e.g. when going through
+ * iwconfig), we have to set them appropriately here.
+ */
+ if (connect->crypto.cipher_group == 0)
+ connect->crypto.cipher_group = cipher;
+
+ if (connect->crypto.n_ciphers_pairwise == 0) {
+ connect->crypto.n_ciphers_pairwise = 1;
+ connect->crypto.ciphers_pairwise[0] = cipher;
+ }
+ }
+
+ connect->crypto.wep_keys = connkeys->params;
+ connect->crypto.wep_tx_key = connkeys->def;
+ } else {
+ if (WARN_ON(connkeys))
+ return -EINVAL;
+ }
+
+ wdev->connect_keys = connkeys;
+ memcpy(wdev->ssid, connect->ssid, connect->ssid_len);
+ wdev->ssid_len = connect->ssid_len;
+
+ wdev->conn_bss_type = connect->pbss ? IEEE80211_BSS_TYPE_PBSS :
+ IEEE80211_BSS_TYPE_ESS;
+
+ if (!rdev->ops->connect)
+ err = cfg80211_sme_connect(wdev, connect, prev_bssid);
+ else
+ err = rdev_connect(rdev, dev, connect);
+
+ if (err) {
+ wdev->connect_keys = NULL;
+ wdev->ssid_len = 0;
+ return err;
+ }
+
+ return 0;
+}
+
+int cfg80211_disconnect(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, u16 reason, bool wextev)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ int err = 0;
+
+ ASSERT_WDEV_LOCK(wdev);
+
+ kzfree(wdev->connect_keys);
+ wdev->connect_keys = NULL;
+
+ wdev->conn_owner_nlportid = 0;
+
+ if (wdev->conn)
+ err = cfg80211_sme_disconnect(wdev, reason);
+ else if (!rdev->ops->disconnect)
+ cfg80211_mlme_down(rdev, dev);
+ else if (wdev->ssid_len)
+ err = rdev_disconnect(rdev, dev, reason);
+
+ return err;
+}
+
+/*
+ * Used to clean up after the connection / connection attempt owner socket
+ * disconnects
+ */
+void cfg80211_autodisconnect_wk(struct work_struct *work)
+{
+ struct wireless_dev *wdev =
+ container_of(work, struct wireless_dev, disconnect_wk);
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+
+ wdev_lock(wdev);
+
+ if (wdev->conn_owner_nlportid) {
+ /*
+ * Use disconnect_bssid if still connecting and ops->disconnect
+ * not implemented. Otherwise we can use cfg80211_disconnect.
+ */
+ if (rdev->ops->disconnect || wdev->current_bss)
+ cfg80211_disconnect(rdev, wdev->netdev,
+ WLAN_REASON_DEAUTH_LEAVING, true);
+ else
+ cfg80211_mlme_deauth(rdev, wdev->netdev,
+ wdev->disconnect_bssid, NULL, 0,
+ WLAN_REASON_DEAUTH_LEAVING, false);
+ }
+
+ wdev_unlock(wdev);
+}
diff --git a/net/wireless/sysfs.c b/net/wireless/sysfs.c
new file mode 100644
index 0000000..ef4e5da
--- /dev/null
+++ b/net/wireless/sysfs.c
@@ -0,0 +1,186 @@
+/*
+ * This file provides /sys/class/ieee80211/<wiphy name>/
+ * and some default attributes.
+ *
+ * Copyright 2005-2006 Jiri Benc <jbenc@suse.cz>
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * This file is GPLv2 as found in COPYING.
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/nl80211.h>
+#include <linux/rtnetlink.h>
+#include <net/cfg80211.h>
+#include "sysfs.h"
+#include "core.h"
+#include "rdev-ops.h"
+
+static inline struct cfg80211_registered_device *dev_to_rdev(
+ struct device *dev)
+{
+ return container_of(dev, struct cfg80211_registered_device, wiphy.dev);
+}
+
+#define SHOW_FMT(name, fmt, member) \
+static ssize_t name ## _show(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ return sprintf(buf, fmt "\n", dev_to_rdev(dev)->member); \
+} \
+static DEVICE_ATTR_RO(name)
+
+SHOW_FMT(index, "%d", wiphy_idx);
+SHOW_FMT(macaddress, "%pM", wiphy.perm_addr);
+SHOW_FMT(address_mask, "%pM", wiphy.addr_mask);
+
+static ssize_t name_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct wiphy *wiphy = &dev_to_rdev(dev)->wiphy;
+
+ return sprintf(buf, "%s\n", wiphy_name(wiphy));
+}
+static DEVICE_ATTR_RO(name);
+
+static ssize_t addresses_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct wiphy *wiphy = &dev_to_rdev(dev)->wiphy;
+ char *start = buf;
+ int i;
+
+ if (!wiphy->addresses)
+ return sprintf(buf, "%pM\n", wiphy->perm_addr);
+
+ for (i = 0; i < wiphy->n_addresses; i++)
+ buf += sprintf(buf, "%pM\n", wiphy->addresses[i].addr);
+
+ return buf - start;
+}
+static DEVICE_ATTR_RO(addresses);
+
+static struct attribute *ieee80211_attrs[] = {
+ &dev_attr_index.attr,
+ &dev_attr_macaddress.attr,
+ &dev_attr_address_mask.attr,
+ &dev_attr_addresses.attr,
+ &dev_attr_name.attr,
+ NULL,
+};
+
+#if LINUX_VERSION_IS_GEQ(3,11,0)
+ATTRIBUTE_GROUPS(ieee80211);
+#else
+#define BP_ATTR_GRP_STRUCT device_attribute
+ATTRIBUTE_GROUPS_BACKPORT(ieee80211);
+#endif
+
+static void wiphy_dev_release(struct device *dev)
+{
+ struct cfg80211_registered_device *rdev = dev_to_rdev(dev);
+
+ cfg80211_dev_free(rdev);
+}
+
+static int wiphy_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+ /* TODO, we probably need stuff here */
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static void cfg80211_leave_all(struct cfg80211_registered_device *rdev)
+{
+ struct wireless_dev *wdev;
+
+ list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list)
+ cfg80211_leave(rdev, wdev);
+}
+
+static int wiphy_suspend(struct device *dev)
+{
+ struct cfg80211_registered_device *rdev = dev_to_rdev(dev);
+ int ret = 0;
+
+ rdev->suspend_at = get_seconds();
+
+ rtnl_lock();
+ if (rdev->wiphy.registered) {
+ if (!rdev->wiphy.wowlan_config) {
+ cfg80211_leave_all(rdev);
+ cfg80211_process_rdev_events(rdev);
+ }
+ if (rdev->ops->suspend)
+ ret = rdev_suspend(rdev, rdev->wiphy.wowlan_config);
+ if (ret == 1) {
+ /* Driver refuse to configure wowlan */
+ cfg80211_leave_all(rdev);
+ cfg80211_process_rdev_events(rdev);
+ ret = rdev_suspend(rdev, NULL);
+ }
+ }
+ rtnl_unlock();
+
+ return ret;
+}
+
+static int wiphy_resume(struct device *dev)
+{
+ struct cfg80211_registered_device *rdev = dev_to_rdev(dev);
+ int ret = 0;
+
+ /* Age scan results with time spent in suspend */
+ cfg80211_bss_age(rdev, get_seconds() - rdev->suspend_at);
+
+ rtnl_lock();
+ if (rdev->wiphy.registered && rdev->ops->resume)
+ ret = rdev_resume(rdev);
+ rtnl_unlock();
+
+ return ret;
+}
+
+static SIMPLE_DEV_PM_OPS(wiphy_pm_ops, wiphy_suspend, wiphy_resume);
+#define WIPHY_PM_OPS (&wiphy_pm_ops)
+#else
+#define WIPHY_PM_OPS NULL
+#endif
+
+static const void *wiphy_namespace(struct device *d)
+{
+ struct wiphy *wiphy = container_of(d, struct wiphy, dev);
+
+ return wiphy_net(wiphy);
+}
+
+struct class ieee80211_class = {
+ .name = "ieee80211",
+ .owner = THIS_MODULE,
+ .dev_release = wiphy_dev_release,
+#if LINUX_VERSION_IS_GEQ(3,11,0)
+ .dev_groups = ieee80211_groups,
+#else
+ .dev_attrs = ieee80211_dev_attrs,
+#endif
+ .dev_uevent = wiphy_uevent,
+ .pm = WIPHY_PM_OPS,
+ .ns_type = &net_ns_type_operations,
+ .namespace = wiphy_namespace,
+};
+
+int wiphy_sysfs_init(void)
+{
+ init_ieee80211_attrs();
+ return class_register(&ieee80211_class);
+}
+
+void wiphy_sysfs_exit(void)
+{
+ class_unregister(&ieee80211_class);
+}
diff --git a/net/wireless/sysfs.h b/net/wireless/sysfs.h
new file mode 100644
index 0000000..b533ed7
--- /dev/null
+++ b/net/wireless/sysfs.h
@@ -0,0 +1,9 @@
+#ifndef __WIRELESS_SYSFS_H
+#define __WIRELESS_SYSFS_H
+
+int wiphy_sysfs_init(void);
+void wiphy_sysfs_exit(void);
+
+extern struct class ieee80211_class;
+
+#endif /* __WIRELESS_SYSFS_H */
diff --git a/net/wireless/trace.c b/net/wireless/trace.c
new file mode 100644
index 0000000..c435908
--- /dev/null
+++ b/net/wireless/trace.c
@@ -0,0 +1,10 @@
+#include <linux/module.h>
+#if LINUX_VERSION_IS_LESS(3,1,0)
+#include <linux/interrupt.h>
+#endif
+
+#ifndef __CHECKER__
+#define CREATE_TRACE_POINTS
+#include "trace.h"
+
+#endif
diff --git a/net/wireless/trace.h b/net/wireless/trace.h
new file mode 100644
index 0000000..894ba13
--- /dev/null
+++ b/net/wireless/trace.h
@@ -0,0 +1,3181 @@
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM cfg80211
+
+#if !defined(__RDEV_OPS_TRACE) || defined(TRACE_HEADER_MULTI_READ)
+#define __RDEV_OPS_TRACE
+
+#include <linux/tracepoint.h>
+
+#include <linux/rtnetlink.h>
+#include <linux/etherdevice.h>
+#include <net/cfg80211.h>
+#include "core.h"
+
+#define MAC_ENTRY(entry_mac) __array(u8, entry_mac, ETH_ALEN)
+#define MAC_ASSIGN(entry_mac, given_mac) do { \
+ if (given_mac) \
+ memcpy(__entry->entry_mac, given_mac, ETH_ALEN); \
+ else \
+ eth_zero_addr(__entry->entry_mac); \
+ } while (0)
+#define MAC_PR_FMT "%pM"
+#define MAC_PR_ARG(entry_mac) (__entry->entry_mac)
+
+#define MAXNAME 32
+#define WIPHY_ENTRY __array(char, wiphy_name, 32)
+#define WIPHY_ASSIGN strlcpy(__entry->wiphy_name, wiphy_name(wiphy), MAXNAME)
+#define WIPHY_PR_FMT "%s"
+#define WIPHY_PR_ARG __entry->wiphy_name
+
+#define WDEV_ENTRY __field(u32, id)
+#define WDEV_ASSIGN (__entry->id) = (!IS_ERR_OR_NULL(wdev) \
+ ? wdev->identifier : 0)
+#define WDEV_PR_FMT "wdev(%u)"
+#define WDEV_PR_ARG (__entry->id)
+
+#define NETDEV_ENTRY __array(char, name, IFNAMSIZ) \
+ __field(int, ifindex)
+#define NETDEV_ASSIGN \
+ do { \
+ memcpy(__entry->name, netdev->name, IFNAMSIZ); \
+ (__entry->ifindex) = (netdev->ifindex); \
+ } while (0)
+#define NETDEV_PR_FMT "netdev:%s(%d)"
+#define NETDEV_PR_ARG __entry->name, __entry->ifindex
+
+#define MESH_CFG_ENTRY __field(u16, dot11MeshRetryTimeout) \
+ __field(u16, dot11MeshConfirmTimeout) \
+ __field(u16, dot11MeshHoldingTimeout) \
+ __field(u16, dot11MeshMaxPeerLinks) \
+ __field(u8, dot11MeshMaxRetries) \
+ __field(u8, dot11MeshTTL) \
+ __field(u8, element_ttl) \
+ __field(bool, auto_open_plinks) \
+ __field(u32, dot11MeshNbrOffsetMaxNeighbor) \
+ __field(u8, dot11MeshHWMPmaxPREQretries) \
+ __field(u32, path_refresh_time) \
+ __field(u32, dot11MeshHWMPactivePathTimeout) \
+ __field(u16, min_discovery_timeout) \
+ __field(u16, dot11MeshHWMPpreqMinInterval) \
+ __field(u16, dot11MeshHWMPperrMinInterval) \
+ __field(u16, dot11MeshHWMPnetDiameterTraversalTime) \
+ __field(u8, dot11MeshHWMPRootMode) \
+ __field(u16, dot11MeshHWMPRannInterval) \
+ __field(bool, dot11MeshGateAnnouncementProtocol) \
+ __field(bool, dot11MeshForwarding) \
+ __field(s32, rssi_threshold) \
+ __field(u16, ht_opmode) \
+ __field(u32, dot11MeshHWMPactivePathToRootTimeout) \
+ __field(u16, dot11MeshHWMProotInterval) \
+ __field(u16, dot11MeshHWMPconfirmationInterval)
+#define MESH_CFG_ASSIGN \
+ do { \
+ __entry->dot11MeshRetryTimeout = conf->dot11MeshRetryTimeout; \
+ __entry->dot11MeshConfirmTimeout = \
+ conf->dot11MeshConfirmTimeout; \
+ __entry->dot11MeshHoldingTimeout = \
+ conf->dot11MeshHoldingTimeout; \
+ __entry->dot11MeshMaxPeerLinks = conf->dot11MeshMaxPeerLinks; \
+ __entry->dot11MeshMaxRetries = conf->dot11MeshMaxRetries; \
+ __entry->dot11MeshTTL = conf->dot11MeshTTL; \
+ __entry->element_ttl = conf->element_ttl; \
+ __entry->auto_open_plinks = conf->auto_open_plinks; \
+ __entry->dot11MeshNbrOffsetMaxNeighbor = \
+ conf->dot11MeshNbrOffsetMaxNeighbor; \
+ __entry->dot11MeshHWMPmaxPREQretries = \
+ conf->dot11MeshHWMPmaxPREQretries; \
+ __entry->path_refresh_time = conf->path_refresh_time; \
+ __entry->dot11MeshHWMPactivePathTimeout = \
+ conf->dot11MeshHWMPactivePathTimeout; \
+ __entry->min_discovery_timeout = conf->min_discovery_timeout; \
+ __entry->dot11MeshHWMPpreqMinInterval = \
+ conf->dot11MeshHWMPpreqMinInterval; \
+ __entry->dot11MeshHWMPperrMinInterval = \
+ conf->dot11MeshHWMPperrMinInterval; \
+ __entry->dot11MeshHWMPnetDiameterTraversalTime = \
+ conf->dot11MeshHWMPnetDiameterTraversalTime; \
+ __entry->dot11MeshHWMPRootMode = conf->dot11MeshHWMPRootMode; \
+ __entry->dot11MeshHWMPRannInterval = \
+ conf->dot11MeshHWMPRannInterval; \
+ __entry->dot11MeshGateAnnouncementProtocol = \
+ conf->dot11MeshGateAnnouncementProtocol; \
+ __entry->dot11MeshForwarding = conf->dot11MeshForwarding; \
+ __entry->rssi_threshold = conf->rssi_threshold; \
+ __entry->ht_opmode = conf->ht_opmode; \
+ __entry->dot11MeshHWMPactivePathToRootTimeout = \
+ conf->dot11MeshHWMPactivePathToRootTimeout; \
+ __entry->dot11MeshHWMProotInterval = \
+ conf->dot11MeshHWMProotInterval; \
+ __entry->dot11MeshHWMPconfirmationInterval = \
+ conf->dot11MeshHWMPconfirmationInterval; \
+ } while (0)
+
+#define CHAN_ENTRY __field(enum nl80211_band, band) \
+ __field(u16, center_freq)
+#define CHAN_ASSIGN(chan) \
+ do { \
+ if (chan) { \
+ __entry->band = chan->band; \
+ __entry->center_freq = chan->center_freq; \
+ } else { \
+ __entry->band = 0; \
+ __entry->center_freq = 0; \
+ } \
+ } while (0)
+#define CHAN_PR_FMT "band: %d, freq: %u"
+#define CHAN_PR_ARG __entry->band, __entry->center_freq
+
+#define CHAN_DEF_ENTRY __field(enum nl80211_band, band) \
+ __field(u32, control_freq) \
+ __field(u32, width) \
+ __field(u32, center_freq1) \
+ __field(u32, center_freq2)
+#define CHAN_DEF_ASSIGN(chandef) \
+ do { \
+ if ((chandef) && (chandef)->chan) { \
+ __entry->band = (chandef)->chan->band; \
+ __entry->control_freq = \
+ (chandef)->chan->center_freq; \
+ __entry->width = (chandef)->width; \
+ __entry->center_freq1 = (chandef)->center_freq1;\
+ __entry->center_freq2 = (chandef)->center_freq2;\
+ } else { \
+ __entry->band = 0; \
+ __entry->control_freq = 0; \
+ __entry->width = 0; \
+ __entry->center_freq1 = 0; \
+ __entry->center_freq2 = 0; \
+ } \
+ } while (0)
+#define CHAN_DEF_PR_FMT \
+ "band: %d, control freq: %u, width: %d, cf1: %u, cf2: %u"
+#define CHAN_DEF_PR_ARG __entry->band, __entry->control_freq, \
+ __entry->width, __entry->center_freq1, \
+ __entry->center_freq2
+
+#define SINFO_ENTRY __field(int, generation) \
+ __field(u32, connected_time) \
+ __field(u32, inactive_time) \
+ __field(u32, rx_bytes) \
+ __field(u32, tx_bytes) \
+ __field(u32, rx_packets) \
+ __field(u32, tx_packets) \
+ __field(u32, tx_retries) \
+ __field(u32, tx_failed) \
+ __field(u32, rx_dropped_misc) \
+ __field(u32, beacon_loss_count) \
+ __field(u16, llid) \
+ __field(u16, plid) \
+ __field(u8, plink_state)
+#define SINFO_ASSIGN \
+ do { \
+ __entry->generation = sinfo->generation; \
+ __entry->connected_time = sinfo->connected_time; \
+ __entry->inactive_time = sinfo->inactive_time; \
+ __entry->rx_bytes = sinfo->rx_bytes; \
+ __entry->tx_bytes = sinfo->tx_bytes; \
+ __entry->rx_packets = sinfo->rx_packets; \
+ __entry->tx_packets = sinfo->tx_packets; \
+ __entry->tx_retries = sinfo->tx_retries; \
+ __entry->tx_failed = sinfo->tx_failed; \
+ __entry->rx_dropped_misc = sinfo->rx_dropped_misc; \
+ __entry->beacon_loss_count = sinfo->beacon_loss_count; \
+ __entry->llid = sinfo->llid; \
+ __entry->plid = sinfo->plid; \
+ __entry->plink_state = sinfo->plink_state; \
+ } while (0)
+
+#define BOOL_TO_STR(bo) (bo) ? "true" : "false"
+
+#define QOS_MAP_ENTRY __field(u8, num_des) \
+ __array(u8, dscp_exception, \
+ 2 * IEEE80211_QOS_MAP_MAX_EX) \
+ __array(u8, up, IEEE80211_QOS_MAP_LEN_MIN)
+#define QOS_MAP_ASSIGN(qos_map) \
+ do { \
+ if ((qos_map)) { \
+ __entry->num_des = (qos_map)->num_des; \
+ memcpy(__entry->dscp_exception, \
+ &(qos_map)->dscp_exception, \
+ 2 * IEEE80211_QOS_MAP_MAX_EX); \
+ memcpy(__entry->up, &(qos_map)->up, \
+ IEEE80211_QOS_MAP_LEN_MIN); \
+ } else { \
+ __entry->num_des = 0; \
+ memset(__entry->dscp_exception, 0, \
+ 2 * IEEE80211_QOS_MAP_MAX_EX); \
+ memset(__entry->up, 0, \
+ IEEE80211_QOS_MAP_LEN_MIN); \
+ } \
+ } while (0)
+
+/*************************************************************
+ * rdev->ops traces *
+ *************************************************************/
+
+TRACE_EVENT(rdev_suspend,
+ TP_PROTO(struct wiphy *wiphy, struct cfg80211_wowlan *wow),
+ TP_ARGS(wiphy, wow),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ __field(bool, any)
+ __field(bool, disconnect)
+ __field(bool, magic_pkt)
+ __field(bool, gtk_rekey_failure)
+ __field(bool, eap_identity_req)
+ __field(bool, four_way_handshake)
+ __field(bool, rfkill_release)
+ __field(bool, valid_wow)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ if (wow) {
+ __entry->any = wow->any;
+ __entry->disconnect = wow->disconnect;
+ __entry->magic_pkt = wow->magic_pkt;
+ __entry->gtk_rekey_failure = wow->gtk_rekey_failure;
+ __entry->eap_identity_req = wow->eap_identity_req;
+ __entry->four_way_handshake = wow->four_way_handshake;
+ __entry->rfkill_release = wow->rfkill_release;
+ __entry->valid_wow = true;
+ } else {
+ __entry->valid_wow = false;
+ }
+ ),
+ TP_printk(WIPHY_PR_FMT ", wow%s - any: %d, disconnect: %d, "
+ "magic pkt: %d, gtk rekey failure: %d, eap identify req: %d, "
+ "four way handshake: %d, rfkill release: %d.",
+ WIPHY_PR_ARG, __entry->valid_wow ? "" : "(Not configured!)",
+ __entry->any, __entry->disconnect, __entry->magic_pkt,
+ __entry->gtk_rekey_failure, __entry->eap_identity_req,
+ __entry->four_way_handshake, __entry->rfkill_release)
+);
+
+TRACE_EVENT(rdev_return_int,
+ TP_PROTO(struct wiphy *wiphy, int ret),
+ TP_ARGS(wiphy, ret),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ __field(int, ret)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ __entry->ret = ret;
+ ),
+ TP_printk(WIPHY_PR_FMT ", returned: %d", WIPHY_PR_ARG, __entry->ret)
+);
+
+TRACE_EVENT(rdev_scan,
+ TP_PROTO(struct wiphy *wiphy, struct cfg80211_scan_request *request),
+ TP_ARGS(wiphy, request),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ ),
+ TP_printk(WIPHY_PR_FMT, WIPHY_PR_ARG)
+);
+
+DECLARE_EVENT_CLASS(wiphy_only_evt,
+ TP_PROTO(struct wiphy *wiphy),
+ TP_ARGS(wiphy),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ ),
+ TP_printk(WIPHY_PR_FMT, WIPHY_PR_ARG)
+);
+
+DEFINE_EVENT(wiphy_only_evt, rdev_resume,
+ TP_PROTO(struct wiphy *wiphy),
+ TP_ARGS(wiphy)
+);
+
+DEFINE_EVENT(wiphy_only_evt, rdev_return_void,
+ TP_PROTO(struct wiphy *wiphy),
+ TP_ARGS(wiphy)
+);
+
+DEFINE_EVENT(wiphy_only_evt, rdev_get_antenna,
+ TP_PROTO(struct wiphy *wiphy),
+ TP_ARGS(wiphy)
+);
+
+DEFINE_EVENT(wiphy_only_evt, rdev_rfkill_poll,
+ TP_PROTO(struct wiphy *wiphy),
+ TP_ARGS(wiphy)
+);
+
+DECLARE_EVENT_CLASS(wiphy_enabled_evt,
+ TP_PROTO(struct wiphy *wiphy, bool enabled),
+ TP_ARGS(wiphy, enabled),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ __field(bool, enabled)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ __entry->enabled = enabled;
+ ),
+ TP_printk(WIPHY_PR_FMT ", %senabled ",
+ WIPHY_PR_ARG, __entry->enabled ? "" : "not ")
+);
+
+DEFINE_EVENT(wiphy_enabled_evt, rdev_set_wakeup,
+ TP_PROTO(struct wiphy *wiphy, bool enabled),
+ TP_ARGS(wiphy, enabled)
+);
+
+TRACE_EVENT(rdev_add_virtual_intf,
+ TP_PROTO(struct wiphy *wiphy, char *name, enum nl80211_iftype type),
+ TP_ARGS(wiphy, name, type),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ __string(vir_intf_name, name ? name : "<noname>")
+ __field(enum nl80211_iftype, type)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ __assign_str(vir_intf_name, name ? name : "<noname>");
+ __entry->type = type;
+ ),
+ TP_printk(WIPHY_PR_FMT ", virtual intf name: %s, type: %d",
+ WIPHY_PR_ARG, __get_str(vir_intf_name), __entry->type)
+);
+
+DECLARE_EVENT_CLASS(wiphy_wdev_evt,
+ TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev),
+ TP_ARGS(wiphy, wdev),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ WDEV_ENTRY
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ WDEV_ASSIGN;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT, WIPHY_PR_ARG, WDEV_PR_ARG)
+);
+
+DEFINE_EVENT(wiphy_wdev_evt, rdev_return_wdev,
+ TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev),
+ TP_ARGS(wiphy, wdev)
+);
+
+DEFINE_EVENT(wiphy_wdev_evt, rdev_del_virtual_intf,
+ TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev),
+ TP_ARGS(wiphy, wdev)
+);
+
+TRACE_EVENT(rdev_change_virtual_intf,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ enum nl80211_iftype type),
+ TP_ARGS(wiphy, netdev, type),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ __field(enum nl80211_iftype, type)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ __entry->type = type;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", type: %d",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->type)
+);
+
+DECLARE_EVENT_CLASS(key_handle,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 key_index,
+ bool pairwise, const u8 *mac_addr),
+ TP_ARGS(wiphy, netdev, key_index, pairwise, mac_addr),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ MAC_ENTRY(mac_addr)
+ __field(u8, key_index)
+ __field(bool, pairwise)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(mac_addr, mac_addr);
+ __entry->key_index = key_index;
+ __entry->pairwise = pairwise;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", key_index: %u, pairwise: %s, mac addr: " MAC_PR_FMT,
+ WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->key_index,
+ BOOL_TO_STR(__entry->pairwise), MAC_PR_ARG(mac_addr))
+);
+
+DEFINE_EVENT(key_handle, rdev_add_key,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 key_index,
+ bool pairwise, const u8 *mac_addr),
+ TP_ARGS(wiphy, netdev, key_index, pairwise, mac_addr)
+);
+
+DEFINE_EVENT(key_handle, rdev_get_key,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 key_index,
+ bool pairwise, const u8 *mac_addr),
+ TP_ARGS(wiphy, netdev, key_index, pairwise, mac_addr)
+);
+
+DEFINE_EVENT(key_handle, rdev_del_key,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 key_index,
+ bool pairwise, const u8 *mac_addr),
+ TP_ARGS(wiphy, netdev, key_index, pairwise, mac_addr)
+);
+
+TRACE_EVENT(rdev_set_default_key,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 key_index,
+ bool unicast, bool multicast),
+ TP_ARGS(wiphy, netdev, key_index, unicast, multicast),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ __field(u8, key_index)
+ __field(bool, unicast)
+ __field(bool, multicast)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ __entry->key_index = key_index;
+ __entry->unicast = unicast;
+ __entry->multicast = multicast;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", key index: %u, unicast: %s, multicast: %s",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->key_index,
+ BOOL_TO_STR(__entry->unicast),
+ BOOL_TO_STR(__entry->multicast))
+);
+
+TRACE_EVENT(rdev_set_default_mgmt_key,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 key_index),
+ TP_ARGS(wiphy, netdev, key_index),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ __field(u8, key_index)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ __entry->key_index = key_index;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", key index: %u",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->key_index)
+);
+
+TRACE_EVENT(rdev_start_ap,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ struct cfg80211_ap_settings *settings),
+ TP_ARGS(wiphy, netdev, settings),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ CHAN_DEF_ENTRY
+ __field(int, beacon_interval)
+ __field(int, dtim_period)
+ __array(char, ssid, IEEE80211_MAX_SSID_LEN + 1)
+ __field(enum nl80211_hidden_ssid, hidden_ssid)
+ __field(u32, wpa_ver)
+ __field(bool, privacy)
+ __field(enum nl80211_auth_type, auth_type)
+ __field(int, inactivity_timeout)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ CHAN_DEF_ASSIGN(&settings->chandef);
+ __entry->beacon_interval = settings->beacon_interval;
+ __entry->dtim_period = settings->dtim_period;
+ __entry->hidden_ssid = settings->hidden_ssid;
+ __entry->wpa_ver = settings->crypto.wpa_versions;
+ __entry->privacy = settings->privacy;
+ __entry->auth_type = settings->auth_type;
+ __entry->inactivity_timeout = settings->inactivity_timeout;
+ memset(__entry->ssid, 0, IEEE80211_MAX_SSID_LEN + 1);
+ memcpy(__entry->ssid, settings->ssid, settings->ssid_len);
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", AP settings - ssid: %s, "
+ CHAN_DEF_PR_FMT ", beacon interval: %d, dtim period: %d, "
+ "hidden ssid: %d, wpa versions: %u, privacy: %s, "
+ "auth type: %d, inactivity timeout: %d",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->ssid, CHAN_DEF_PR_ARG,
+ __entry->beacon_interval, __entry->dtim_period,
+ __entry->hidden_ssid, __entry->wpa_ver,
+ BOOL_TO_STR(__entry->privacy), __entry->auth_type,
+ __entry->inactivity_timeout)
+);
+
+TRACE_EVENT(rdev_change_beacon,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ struct cfg80211_beacon_data *info),
+ TP_ARGS(wiphy, netdev, info),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ __dynamic_array(u8, head, info ? info->head_len : 0)
+ __dynamic_array(u8, tail, info ? info->tail_len : 0)
+ __dynamic_array(u8, beacon_ies, info ? info->beacon_ies_len : 0)
+ __dynamic_array(u8, proberesp_ies,
+ info ? info->proberesp_ies_len : 0)
+ __dynamic_array(u8, assocresp_ies,
+ info ? info->assocresp_ies_len : 0)
+ __dynamic_array(u8, probe_resp, info ? info->probe_resp_len : 0)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ if (info) {
+ if (info->head)
+ memcpy(__get_dynamic_array(head), info->head,
+ info->head_len);
+ if (info->tail)
+ memcpy(__get_dynamic_array(tail), info->tail,
+ info->tail_len);
+ if (info->beacon_ies)
+ memcpy(__get_dynamic_array(beacon_ies),
+ info->beacon_ies, info->beacon_ies_len);
+ if (info->proberesp_ies)
+ memcpy(__get_dynamic_array(proberesp_ies),
+ info->proberesp_ies,
+ info->proberesp_ies_len);
+ if (info->assocresp_ies)
+ memcpy(__get_dynamic_array(assocresp_ies),
+ info->assocresp_ies,
+ info->assocresp_ies_len);
+ if (info->probe_resp)
+ memcpy(__get_dynamic_array(probe_resp),
+ info->probe_resp, info->probe_resp_len);
+ }
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT, WIPHY_PR_ARG, NETDEV_PR_ARG)
+);
+
+DECLARE_EVENT_CLASS(wiphy_netdev_evt,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev),
+ TP_ARGS(wiphy, netdev),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT, WIPHY_PR_ARG, NETDEV_PR_ARG)
+);
+
+DEFINE_EVENT(wiphy_netdev_evt, rdev_stop_ap,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev),
+ TP_ARGS(wiphy, netdev)
+);
+
+DEFINE_EVENT(wiphy_netdev_evt, rdev_set_rekey_data,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev),
+ TP_ARGS(wiphy, netdev)
+);
+
+DEFINE_EVENT(wiphy_netdev_evt, rdev_get_mesh_config,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev),
+ TP_ARGS(wiphy, netdev)
+);
+
+DEFINE_EVENT(wiphy_netdev_evt, rdev_leave_mesh,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev),
+ TP_ARGS(wiphy, netdev)
+);
+
+DEFINE_EVENT(wiphy_netdev_evt, rdev_leave_ibss,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev),
+ TP_ARGS(wiphy, netdev)
+);
+
+DEFINE_EVENT(wiphy_netdev_evt, rdev_leave_ocb,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev),
+ TP_ARGS(wiphy, netdev)
+);
+
+DEFINE_EVENT(wiphy_netdev_evt, rdev_flush_pmksa,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev),
+ TP_ARGS(wiphy, netdev)
+);
+
+DECLARE_EVENT_CLASS(station_add_change,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 *mac,
+ struct station_parameters *params),
+ TP_ARGS(wiphy, netdev, mac, params),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ MAC_ENTRY(sta_mac)
+ __field(u32, sta_flags_mask)
+ __field(u32, sta_flags_set)
+ __field(u32, sta_modify_mask)
+ __field(int, listen_interval)
+ __field(u16, capability)
+ __field(u16, aid)
+ __field(u8, plink_action)
+ __field(u8, plink_state)
+ __field(u8, uapsd_queues)
+ __field(u8, max_sp)
+ __field(u8, opmode_notif)
+ __field(bool, opmode_notif_used)
+ __array(u8, ht_capa, (int)sizeof(struct ieee80211_ht_cap))
+ __array(u8, vht_capa, (int)sizeof(struct ieee80211_vht_cap))
+ __array(char, vlan, IFNAMSIZ)
+ __dynamic_array(u8, supported_rates,
+ params->supported_rates_len)
+ __dynamic_array(u8, ext_capab, params->ext_capab_len)
+ __dynamic_array(u8, supported_channels,
+ params->supported_channels_len)
+ __dynamic_array(u8, supported_oper_classes,
+ params->supported_oper_classes_len)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(sta_mac, mac);
+ __entry->sta_flags_mask = params->sta_flags_mask;
+ __entry->sta_flags_set = params->sta_flags_set;
+ __entry->sta_modify_mask = params->sta_modify_mask;
+ __entry->listen_interval = params->listen_interval;
+ __entry->aid = params->aid;
+ __entry->plink_action = params->plink_action;
+ __entry->plink_state = params->plink_state;
+ __entry->uapsd_queues = params->uapsd_queues;
+ memset(__entry->ht_capa, 0, sizeof(struct ieee80211_ht_cap));
+ if (params->ht_capa)
+ memcpy(__entry->ht_capa, params->ht_capa,
+ sizeof(struct ieee80211_ht_cap));
+ memset(__entry->vht_capa, 0, sizeof(struct ieee80211_vht_cap));
+ if (params->vht_capa)
+ memcpy(__entry->vht_capa, params->vht_capa,
+ sizeof(struct ieee80211_vht_cap));
+ memset(__entry->vlan, 0, sizeof(__entry->vlan));
+ if (params->vlan)
+ memcpy(__entry->vlan, params->vlan->name, IFNAMSIZ);
+ if (params->supported_rates && params->supported_rates_len)
+ memcpy(__get_dynamic_array(supported_rates),
+ params->supported_rates,
+ params->supported_rates_len);
+ if (params->ext_capab && params->ext_capab_len)
+ memcpy(__get_dynamic_array(ext_capab),
+ params->ext_capab,
+ params->ext_capab_len);
+ if (params->supported_channels &&
+ params->supported_channels_len)
+ memcpy(__get_dynamic_array(supported_channels),
+ params->supported_channels,
+ params->supported_channels_len);
+ if (params->supported_oper_classes &&
+ params->supported_oper_classes_len)
+ memcpy(__get_dynamic_array(supported_oper_classes),
+ params->supported_oper_classes,
+ params->supported_oper_classes_len);
+ __entry->max_sp = params->max_sp;
+ __entry->capability = params->capability;
+ __entry->opmode_notif = params->opmode_notif;
+ __entry->opmode_notif_used = params->opmode_notif_used;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", station mac: " MAC_PR_FMT
+ ", station flags mask: %u, station flags set: %u, "
+ "station modify mask: %u, listen interval: %d, aid: %u, "
+ "plink action: %u, plink state: %u, uapsd queues: %u, vlan:%s",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(sta_mac),
+ __entry->sta_flags_mask, __entry->sta_flags_set,
+ __entry->sta_modify_mask, __entry->listen_interval,
+ __entry->aid, __entry->plink_action, __entry->plink_state,
+ __entry->uapsd_queues, __entry->vlan)
+);
+
+DEFINE_EVENT(station_add_change, rdev_add_station,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 *mac,
+ struct station_parameters *params),
+ TP_ARGS(wiphy, netdev, mac, params)
+);
+
+DEFINE_EVENT(station_add_change, rdev_change_station,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 *mac,
+ struct station_parameters *params),
+ TP_ARGS(wiphy, netdev, mac, params)
+);
+
+DECLARE_EVENT_CLASS(wiphy_netdev_mac_evt,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, const u8 *mac),
+ TP_ARGS(wiphy, netdev, mac),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ MAC_ENTRY(sta_mac)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(sta_mac, mac);
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", mac: " MAC_PR_FMT,
+ WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(sta_mac))
+);
+
+DECLARE_EVENT_CLASS(station_del,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ struct station_del_parameters *params),
+ TP_ARGS(wiphy, netdev, params),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ MAC_ENTRY(sta_mac)
+ __field(u8, subtype)
+ __field(u16, reason_code)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(sta_mac, params->mac);
+ __entry->subtype = params->subtype;
+ __entry->reason_code = params->reason_code;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", station mac: " MAC_PR_FMT
+ ", subtype: %u, reason_code: %u",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(sta_mac),
+ __entry->subtype, __entry->reason_code)
+);
+
+DEFINE_EVENT(station_del, rdev_del_station,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ struct station_del_parameters *params),
+ TP_ARGS(wiphy, netdev, params)
+);
+
+DEFINE_EVENT(wiphy_netdev_mac_evt, rdev_get_station,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, const u8 *mac),
+ TP_ARGS(wiphy, netdev, mac)
+);
+
+DEFINE_EVENT(wiphy_netdev_mac_evt, rdev_del_mpath,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, const u8 *mac),
+ TP_ARGS(wiphy, netdev, mac)
+);
+
+DEFINE_EVENT(wiphy_netdev_mac_evt, rdev_set_wds_peer,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, const u8 *mac),
+ TP_ARGS(wiphy, netdev, mac)
+);
+
+TRACE_EVENT(rdev_dump_station,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, int idx,
+ u8 *mac),
+ TP_ARGS(wiphy, netdev, idx, mac),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ MAC_ENTRY(sta_mac)
+ __field(int, idx)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(sta_mac, mac);
+ __entry->idx = idx;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", station mac: " MAC_PR_FMT ", idx: %d",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(sta_mac),
+ __entry->idx)
+);
+
+TRACE_EVENT(rdev_return_int_station_info,
+ TP_PROTO(struct wiphy *wiphy, int ret, struct station_info *sinfo),
+ TP_ARGS(wiphy, ret, sinfo),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ __field(int, ret)
+ SINFO_ENTRY
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ __entry->ret = ret;
+ SINFO_ASSIGN;
+ ),
+ TP_printk(WIPHY_PR_FMT ", returned %d" ,
+ WIPHY_PR_ARG, __entry->ret)
+);
+
+DECLARE_EVENT_CLASS(mpath_evt,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 *dst,
+ u8 *next_hop),
+ TP_ARGS(wiphy, netdev, dst, next_hop),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ MAC_ENTRY(dst)
+ MAC_ENTRY(next_hop)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(dst, dst);
+ MAC_ASSIGN(next_hop, next_hop);
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", destination: " MAC_PR_FMT ", next hop: " MAC_PR_FMT,
+ WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(dst),
+ MAC_PR_ARG(next_hop))
+);
+
+DEFINE_EVENT(mpath_evt, rdev_add_mpath,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 *dst,
+ u8 *next_hop),
+ TP_ARGS(wiphy, netdev, dst, next_hop)
+);
+
+DEFINE_EVENT(mpath_evt, rdev_change_mpath,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 *dst,
+ u8 *next_hop),
+ TP_ARGS(wiphy, netdev, dst, next_hop)
+);
+
+DEFINE_EVENT(mpath_evt, rdev_get_mpath,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 *dst,
+ u8 *next_hop),
+ TP_ARGS(wiphy, netdev, dst, next_hop)
+);
+
+TRACE_EVENT(rdev_dump_mpath,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, int idx,
+ u8 *dst, u8 *next_hop),
+ TP_ARGS(wiphy, netdev, idx, dst, next_hop),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ MAC_ENTRY(dst)
+ MAC_ENTRY(next_hop)
+ __field(int, idx)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(dst, dst);
+ MAC_ASSIGN(next_hop, next_hop);
+ __entry->idx = idx;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", index: %d, destination: "
+ MAC_PR_FMT ", next hop: " MAC_PR_FMT,
+ WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->idx, MAC_PR_ARG(dst),
+ MAC_PR_ARG(next_hop))
+);
+
+TRACE_EVENT(rdev_get_mpp,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ u8 *dst, u8 *mpp),
+ TP_ARGS(wiphy, netdev, dst, mpp),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ MAC_ENTRY(dst)
+ MAC_ENTRY(mpp)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(dst, dst);
+ MAC_ASSIGN(mpp, mpp);
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", destination: " MAC_PR_FMT
+ ", mpp: " MAC_PR_FMT, WIPHY_PR_ARG, NETDEV_PR_ARG,
+ MAC_PR_ARG(dst), MAC_PR_ARG(mpp))
+);
+
+TRACE_EVENT(rdev_dump_mpp,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, int idx,
+ u8 *dst, u8 *mpp),
+ TP_ARGS(wiphy, netdev, idx, mpp, dst),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ MAC_ENTRY(dst)
+ MAC_ENTRY(mpp)
+ __field(int, idx)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(dst, dst);
+ MAC_ASSIGN(mpp, mpp);
+ __entry->idx = idx;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", index: %d, destination: "
+ MAC_PR_FMT ", mpp: " MAC_PR_FMT,
+ WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->idx, MAC_PR_ARG(dst),
+ MAC_PR_ARG(mpp))
+);
+
+TRACE_EVENT(rdev_return_int_mpath_info,
+ TP_PROTO(struct wiphy *wiphy, int ret, struct mpath_info *pinfo),
+ TP_ARGS(wiphy, ret, pinfo),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ __field(int, ret)
+ __field(int, generation)
+ __field(u32, filled)
+ __field(u32, frame_qlen)
+ __field(u32, sn)
+ __field(u32, metric)
+ __field(u32, exptime)
+ __field(u32, discovery_timeout)
+ __field(u8, discovery_retries)
+ __field(u8, flags)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ __entry->ret = ret;
+ __entry->generation = pinfo->generation;
+ __entry->filled = pinfo->filled;
+ __entry->frame_qlen = pinfo->frame_qlen;
+ __entry->sn = pinfo->sn;
+ __entry->metric = pinfo->metric;
+ __entry->exptime = pinfo->exptime;
+ __entry->discovery_timeout = pinfo->discovery_timeout;
+ __entry->discovery_retries = pinfo->discovery_retries;
+ __entry->flags = pinfo->flags;
+ ),
+ TP_printk(WIPHY_PR_FMT ", returned %d. mpath info - generation: %d, "
+ "filled: %u, frame qlen: %u, sn: %u, metric: %u, exptime: %u,"
+ " discovery timeout: %u, discovery retries: %u, flags: %u",
+ WIPHY_PR_ARG, __entry->ret, __entry->generation,
+ __entry->filled, __entry->frame_qlen, __entry->sn,
+ __entry->metric, __entry->exptime, __entry->discovery_timeout,
+ __entry->discovery_retries, __entry->flags)
+);
+
+TRACE_EVENT(rdev_return_int_mesh_config,
+ TP_PROTO(struct wiphy *wiphy, int ret, struct mesh_config *conf),
+ TP_ARGS(wiphy, ret, conf),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ MESH_CFG_ENTRY
+ __field(int, ret)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ MESH_CFG_ASSIGN;
+ __entry->ret = ret;
+ ),
+ TP_printk(WIPHY_PR_FMT ", returned: %d",
+ WIPHY_PR_ARG, __entry->ret)
+);
+
+TRACE_EVENT(rdev_update_mesh_config,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u32 mask,
+ const struct mesh_config *conf),
+ TP_ARGS(wiphy, netdev, mask, conf),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ MESH_CFG_ENTRY
+ __field(u32, mask)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ MESH_CFG_ASSIGN;
+ __entry->mask = mask;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", mask: %u",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->mask)
+);
+
+TRACE_EVENT(rdev_join_mesh,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ const struct mesh_config *conf,
+ const struct mesh_setup *setup),
+ TP_ARGS(wiphy, netdev, conf, setup),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ MESH_CFG_ENTRY
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ MESH_CFG_ASSIGN;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT,
+ WIPHY_PR_ARG, NETDEV_PR_ARG)
+);
+
+TRACE_EVENT(rdev_change_bss,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ struct bss_parameters *params),
+ TP_ARGS(wiphy, netdev, params),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ __field(int, use_cts_prot)
+ __field(int, use_short_preamble)
+ __field(int, use_short_slot_time)
+ __field(int, ap_isolate)
+ __field(int, ht_opmode)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ __entry->use_cts_prot = params->use_cts_prot;
+ __entry->use_short_preamble = params->use_short_preamble;
+ __entry->use_short_slot_time = params->use_short_slot_time;
+ __entry->ap_isolate = params->ap_isolate;
+ __entry->ht_opmode = params->ht_opmode;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", use cts prot: %d, "
+ "use short preamble: %d, use short slot time: %d, "
+ "ap isolate: %d, ht opmode: %d",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->use_cts_prot,
+ __entry->use_short_preamble, __entry->use_short_slot_time,
+ __entry->ap_isolate, __entry->ht_opmode)
+);
+
+TRACE_EVENT(rdev_set_txq_params,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ struct ieee80211_txq_params *params),
+ TP_ARGS(wiphy, netdev, params),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ __field(enum nl80211_ac, ac)
+ __field(u16, txop)
+ __field(u16, cwmin)
+ __field(u16, cwmax)
+ __field(u8, aifs)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ __entry->ac = params->ac;
+ __entry->txop = params->txop;
+ __entry->cwmin = params->cwmin;
+ __entry->cwmax = params->cwmax;
+ __entry->aifs = params->aifs;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", ac: %d, txop: %u, cwmin: %u, cwmax: %u, aifs: %u",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->ac, __entry->txop,
+ __entry->cwmin, __entry->cwmax, __entry->aifs)
+);
+
+TRACE_EVENT(rdev_libertas_set_mesh_channel,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ struct ieee80211_channel *chan),
+ TP_ARGS(wiphy, netdev, chan),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ CHAN_ENTRY
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ CHAN_ASSIGN(chan);
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " CHAN_PR_FMT, WIPHY_PR_ARG,
+ NETDEV_PR_ARG, CHAN_PR_ARG)
+);
+
+TRACE_EVENT(rdev_set_monitor_channel,
+ TP_PROTO(struct wiphy *wiphy,
+ struct cfg80211_chan_def *chandef),
+ TP_ARGS(wiphy, chandef),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ CHAN_DEF_ENTRY
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ CHAN_DEF_ASSIGN(chandef);
+ ),
+ TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT,
+ WIPHY_PR_ARG, CHAN_DEF_PR_ARG)
+);
+
+TRACE_EVENT(rdev_auth,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ struct cfg80211_auth_request *req),
+ TP_ARGS(wiphy, netdev, req),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ MAC_ENTRY(bssid)
+ __field(enum nl80211_auth_type, auth_type)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ if (req->bss)
+ MAC_ASSIGN(bssid, req->bss->bssid);
+ else
+ eth_zero_addr(__entry->bssid);
+ __entry->auth_type = req->auth_type;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", auth type: %d, bssid: " MAC_PR_FMT,
+ WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->auth_type,
+ MAC_PR_ARG(bssid))
+);
+
+TRACE_EVENT(rdev_assoc,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ struct cfg80211_assoc_request *req),
+ TP_ARGS(wiphy, netdev, req),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ MAC_ENTRY(bssid)
+ MAC_ENTRY(prev_bssid)
+ __field(bool, use_mfp)
+ __field(u32, flags)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ if (req->bss)
+ MAC_ASSIGN(bssid, req->bss->bssid);
+ else
+ eth_zero_addr(__entry->bssid);
+ MAC_ASSIGN(prev_bssid, req->prev_bssid);
+ __entry->use_mfp = req->use_mfp;
+ __entry->flags = req->flags;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", bssid: " MAC_PR_FMT
+ ", previous bssid: " MAC_PR_FMT ", use mfp: %s, flags: %u",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(bssid),
+ MAC_PR_ARG(prev_bssid), BOOL_TO_STR(__entry->use_mfp),
+ __entry->flags)
+);
+
+TRACE_EVENT(rdev_deauth,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ struct cfg80211_deauth_request *req),
+ TP_ARGS(wiphy, netdev, req),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ MAC_ENTRY(bssid)
+ __field(u16, reason_code)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(bssid, req->bssid);
+ __entry->reason_code = req->reason_code;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", bssid: " MAC_PR_FMT ", reason: %u",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(bssid),
+ __entry->reason_code)
+);
+
+TRACE_EVENT(rdev_disassoc,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ struct cfg80211_disassoc_request *req),
+ TP_ARGS(wiphy, netdev, req),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ MAC_ENTRY(bssid)
+ __field(u16, reason_code)
+ __field(bool, local_state_change)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ if (req->bss)
+ MAC_ASSIGN(bssid, req->bss->bssid);
+ else
+ eth_zero_addr(__entry->bssid);
+ __entry->reason_code = req->reason_code;
+ __entry->local_state_change = req->local_state_change;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", bssid: " MAC_PR_FMT
+ ", reason: %u, local state change: %s",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(bssid),
+ __entry->reason_code,
+ BOOL_TO_STR(__entry->local_state_change))
+);
+
+TRACE_EVENT(rdev_mgmt_tx_cancel_wait,
+ TP_PROTO(struct wiphy *wiphy,
+ struct wireless_dev *wdev, u64 cookie),
+ TP_ARGS(wiphy, wdev, cookie),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ WDEV_ENTRY
+ __field(u64, cookie)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ WDEV_ASSIGN;
+ __entry->cookie = cookie;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", cookie: %llu ",
+ WIPHY_PR_ARG, WDEV_PR_ARG, __entry->cookie)
+);
+
+TRACE_EVENT(rdev_set_power_mgmt,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ bool enabled, int timeout),
+ TP_ARGS(wiphy, netdev, enabled, timeout),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ __field(bool, enabled)
+ __field(int, timeout)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ __entry->enabled = enabled;
+ __entry->timeout = timeout;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", %senabled, timeout: %d ",
+ WIPHY_PR_ARG, NETDEV_PR_ARG,
+ __entry->enabled ? "" : "not ", __entry->timeout)
+);
+
+TRACE_EVENT(rdev_connect,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ struct cfg80211_connect_params *sme),
+ TP_ARGS(wiphy, netdev, sme),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ MAC_ENTRY(bssid)
+ __array(char, ssid, IEEE80211_MAX_SSID_LEN + 1)
+ __field(enum nl80211_auth_type, auth_type)
+ __field(bool, privacy)
+ __field(u32, wpa_versions)
+ __field(u32, flags)
+ MAC_ENTRY(prev_bssid)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(bssid, sme->bssid);
+ memset(__entry->ssid, 0, IEEE80211_MAX_SSID_LEN + 1);
+ memcpy(__entry->ssid, sme->ssid, sme->ssid_len);
+ __entry->auth_type = sme->auth_type;
+ __entry->privacy = sme->privacy;
+ __entry->wpa_versions = sme->crypto.wpa_versions;
+ __entry->flags = sme->flags;
+ MAC_ASSIGN(prev_bssid, sme->prev_bssid);
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", bssid: " MAC_PR_FMT
+ ", ssid: %s, auth type: %d, privacy: %s, wpa versions: %u, "
+ "flags: %u, previous bssid: " MAC_PR_FMT,
+ WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(bssid), __entry->ssid,
+ __entry->auth_type, BOOL_TO_STR(__entry->privacy),
+ __entry->wpa_versions, __entry->flags, MAC_PR_ARG(prev_bssid))
+);
+
+TRACE_EVENT(rdev_update_connect_params,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ struct cfg80211_connect_params *sme, u32 changed),
+ TP_ARGS(wiphy, netdev, sme, changed),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ __field(u32, changed)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ __entry->changed = changed;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", parameters changed: %u",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->changed)
+);
+
+TRACE_EVENT(rdev_set_cqm_rssi_config,
+ TP_PROTO(struct wiphy *wiphy,
+ struct net_device *netdev, s32 rssi_thold,
+ u32 rssi_hyst),
+ TP_ARGS(wiphy, netdev, rssi_thold, rssi_hyst),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ __field(s32, rssi_thold)
+ __field(u32, rssi_hyst)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ __entry->rssi_thold = rssi_thold;
+ __entry->rssi_hyst = rssi_hyst;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT
+ ", rssi_thold: %d, rssi_hyst: %u ",
+ WIPHY_PR_ARG, NETDEV_PR_ARG,
+ __entry->rssi_thold, __entry->rssi_hyst)
+);
+
+TRACE_EVENT(rdev_set_cqm_rssi_range_config,
+ TP_PROTO(struct wiphy *wiphy,
+ struct net_device *netdev, s32 low, s32 high),
+ TP_ARGS(wiphy, netdev, low, high),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ __field(s32, rssi_low)
+ __field(s32, rssi_high)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ __entry->rssi_low = low;
+ __entry->rssi_high = high;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT
+ ", range: %d - %d ",
+ WIPHY_PR_ARG, NETDEV_PR_ARG,
+ __entry->rssi_low, __entry->rssi_high)
+);
+
+TRACE_EVENT(rdev_set_cqm_txe_config,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u32 rate,
+ u32 pkts, u32 intvl),
+ TP_ARGS(wiphy, netdev, rate, pkts, intvl),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ __field(u32, rate)
+ __field(u32, pkts)
+ __field(u32, intvl)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ __entry->rate = rate;
+ __entry->pkts = pkts;
+ __entry->intvl = intvl;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", rate: %u, packets: %u, interval: %u",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->rate, __entry->pkts,
+ __entry->intvl)
+);
+
+TRACE_EVENT(rdev_disconnect,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ u16 reason_code),
+ TP_ARGS(wiphy, netdev, reason_code),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ __field(u16, reason_code)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ __entry->reason_code = reason_code;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", reason code: %u", WIPHY_PR_ARG,
+ NETDEV_PR_ARG, __entry->reason_code)
+);
+
+TRACE_EVENT(rdev_join_ibss,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ struct cfg80211_ibss_params *params),
+ TP_ARGS(wiphy, netdev, params),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ MAC_ENTRY(bssid)
+ __array(char, ssid, IEEE80211_MAX_SSID_LEN + 1)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(bssid, params->bssid);
+ memset(__entry->ssid, 0, IEEE80211_MAX_SSID_LEN + 1);
+ memcpy(__entry->ssid, params->ssid, params->ssid_len);
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", bssid: " MAC_PR_FMT ", ssid: %s",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(bssid), __entry->ssid)
+);
+
+TRACE_EVENT(rdev_join_ocb,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ const struct ocb_setup *setup),
+ TP_ARGS(wiphy, netdev, setup),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT,
+ WIPHY_PR_ARG, NETDEV_PR_ARG)
+);
+
+TRACE_EVENT(rdev_set_wiphy_params,
+ TP_PROTO(struct wiphy *wiphy, u32 changed),
+ TP_ARGS(wiphy, changed),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ __field(u32, changed)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ __entry->changed = changed;
+ ),
+ TP_printk(WIPHY_PR_FMT ", changed: %u",
+ WIPHY_PR_ARG, __entry->changed)
+);
+
+DEFINE_EVENT(wiphy_wdev_evt, rdev_get_tx_power,
+ TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev),
+ TP_ARGS(wiphy, wdev)
+);
+
+TRACE_EVENT(rdev_set_tx_power,
+ TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev,
+ enum nl80211_tx_power_setting type, int mbm),
+ TP_ARGS(wiphy, wdev, type, mbm),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ WDEV_ENTRY
+ __field(enum nl80211_tx_power_setting, type)
+ __field(int, mbm)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ WDEV_ASSIGN;
+ __entry->type = type;
+ __entry->mbm = mbm;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", type: %u, mbm: %d",
+ WIPHY_PR_ARG, WDEV_PR_ARG,__entry->type, __entry->mbm)
+);
+
+TRACE_EVENT(rdev_return_int_int,
+ TP_PROTO(struct wiphy *wiphy, int func_ret, int func_fill),
+ TP_ARGS(wiphy, func_ret, func_fill),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ __field(int, func_ret)
+ __field(int, func_fill)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ __entry->func_ret = func_ret;
+ __entry->func_fill = func_fill;
+ ),
+ TP_printk(WIPHY_PR_FMT ", function returns: %d, function filled: %d",
+ WIPHY_PR_ARG, __entry->func_ret, __entry->func_fill)
+);
+
+#ifdef CPTCFG_NL80211_TESTMODE
+TRACE_EVENT(rdev_testmode_cmd,
+ TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev),
+ TP_ARGS(wiphy, wdev),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ WDEV_ENTRY
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ WDEV_ASSIGN;
+ ),
+ TP_printk(WIPHY_PR_FMT WDEV_PR_FMT, WIPHY_PR_ARG, WDEV_PR_ARG)
+);
+
+TRACE_EVENT(rdev_testmode_dump,
+ TP_PROTO(struct wiphy *wiphy),
+ TP_ARGS(wiphy),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ ),
+ TP_printk(WIPHY_PR_FMT, WIPHY_PR_ARG)
+);
+#endif /* CPTCFG_NL80211_TESTMODE */
+
+TRACE_EVENT(rdev_set_bitrate_mask,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ const u8 *peer, const struct cfg80211_bitrate_mask *mask),
+ TP_ARGS(wiphy, netdev, peer, mask),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ MAC_ENTRY(peer)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(peer, peer);
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", peer: " MAC_PR_FMT,
+ WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer))
+);
+
+TRACE_EVENT(rdev_mgmt_frame_register,
+ TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev,
+ u16 frame_type, bool reg),
+ TP_ARGS(wiphy, wdev, frame_type, reg),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ WDEV_ENTRY
+ __field(u16, frame_type)
+ __field(bool, reg)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ WDEV_ASSIGN;
+ __entry->frame_type = frame_type;
+ __entry->reg = reg;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", frame_type: 0x%.2x, reg: %s ",
+ WIPHY_PR_ARG, WDEV_PR_ARG, __entry->frame_type,
+ __entry->reg ? "true" : "false")
+);
+
+TRACE_EVENT(rdev_return_int_tx_rx,
+ TP_PROTO(struct wiphy *wiphy, int ret, u32 tx, u32 rx),
+ TP_ARGS(wiphy, ret, tx, rx),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ __field(int, ret)
+ __field(u32, tx)
+ __field(u32, rx)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ __entry->ret = ret;
+ __entry->tx = tx;
+ __entry->rx = rx;
+ ),
+ TP_printk(WIPHY_PR_FMT ", returned %d, tx: %u, rx: %u",
+ WIPHY_PR_ARG, __entry->ret, __entry->tx, __entry->rx)
+);
+
+TRACE_EVENT(rdev_return_void_tx_rx,
+ TP_PROTO(struct wiphy *wiphy, u32 tx, u32 tx_max,
+ u32 rx, u32 rx_max),
+ TP_ARGS(wiphy, tx, tx_max, rx, rx_max),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ __field(u32, tx)
+ __field(u32, tx_max)
+ __field(u32, rx)
+ __field(u32, rx_max)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ __entry->tx = tx;
+ __entry->tx_max = tx_max;
+ __entry->rx = rx;
+ __entry->rx_max = rx_max;
+ ),
+ TP_printk(WIPHY_PR_FMT ", tx: %u, tx_max: %u, rx: %u, rx_max: %u ",
+ WIPHY_PR_ARG, __entry->tx, __entry->tx_max, __entry->rx,
+ __entry->rx_max)
+);
+
+DECLARE_EVENT_CLASS(tx_rx_evt,
+ TP_PROTO(struct wiphy *wiphy, u32 tx, u32 rx),
+ TP_ARGS(wiphy, rx, tx),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ __field(u32, tx)
+ __field(u32, rx)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ __entry->tx = tx;
+ __entry->rx = rx;
+ ),
+ TP_printk(WIPHY_PR_FMT ", tx: %u, rx: %u ",
+ WIPHY_PR_ARG, __entry->tx, __entry->rx)
+);
+
+DEFINE_EVENT(tx_rx_evt, rdev_set_antenna,
+ TP_PROTO(struct wiphy *wiphy, u32 tx, u32 rx),
+ TP_ARGS(wiphy, rx, tx)
+);
+
+DECLARE_EVENT_CLASS(wiphy_netdev_id_evt,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u64 id),
+ TP_ARGS(wiphy, netdev, id),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ __field(u64, id)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ __entry->id = id;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", id: %llu",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->id)
+);
+
+DEFINE_EVENT(wiphy_netdev_id_evt, rdev_sched_scan_start,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u64 id),
+ TP_ARGS(wiphy, netdev, id)
+);
+
+DEFINE_EVENT(wiphy_netdev_id_evt, rdev_sched_scan_stop,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u64 id),
+ TP_ARGS(wiphy, netdev, id)
+);
+
+TRACE_EVENT(rdev_tdls_mgmt,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ u8 *peer, u8 action_code, u8 dialog_token,
+ u16 status_code, u32 peer_capability,
+ bool initiator, const u8 *buf, size_t len),
+ TP_ARGS(wiphy, netdev, peer, action_code, dialog_token, status_code,
+ peer_capability, initiator, buf, len),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ MAC_ENTRY(peer)
+ __field(u8, action_code)
+ __field(u8, dialog_token)
+ __field(u16, status_code)
+ __field(u32, peer_capability)
+ __field(bool, initiator)
+ __dynamic_array(u8, buf, len)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(peer, peer);
+ __entry->action_code = action_code;
+ __entry->dialog_token = dialog_token;
+ __entry->status_code = status_code;
+ __entry->peer_capability = peer_capability;
+ __entry->initiator = initiator;
+ memcpy(__get_dynamic_array(buf), buf, len);
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " MAC_PR_FMT ", action_code: %u, "
+ "dialog_token: %u, status_code: %u, peer_capability: %u "
+ "initiator: %s buf: %#.2x ",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer),
+ __entry->action_code, __entry->dialog_token,
+ __entry->status_code, __entry->peer_capability,
+ BOOL_TO_STR(__entry->initiator),
+ ((u8 *)__get_dynamic_array(buf))[0])
+);
+
+TRACE_EVENT(rdev_dump_survey,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, int idx),
+ TP_ARGS(wiphy, netdev, idx),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ __field(int, idx)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ __entry->idx = idx;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", index: %d",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->idx)
+);
+
+TRACE_EVENT(rdev_return_int_survey_info,
+ TP_PROTO(struct wiphy *wiphy, int ret, struct survey_info *info),
+ TP_ARGS(wiphy, ret, info),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ CHAN_ENTRY
+ __field(int, ret)
+ __field(u64, time)
+ __field(u64, time_busy)
+ __field(u64, time_ext_busy)
+ __field(u64, time_rx)
+ __field(u64, time_tx)
+ __field(u64, time_scan)
+ __field(u32, filled)
+ __field(s8, noise)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ CHAN_ASSIGN(info->channel);
+ __entry->ret = ret;
+ __entry->time = info->time;
+ __entry->time_busy = info->time_busy;
+ __entry->time_ext_busy = info->time_ext_busy;
+ __entry->time_rx = info->time_rx;
+ __entry->time_tx = info->time_tx;
+ __entry->time_scan = info->time_scan;
+ __entry->filled = info->filled;
+ __entry->noise = info->noise;
+ ),
+ TP_printk(WIPHY_PR_FMT ", returned: %d, " CHAN_PR_FMT
+ ", channel time: %llu, channel time busy: %llu, "
+ "channel time extension busy: %llu, channel time rx: %llu, "
+ "channel time tx: %llu, scan time: %llu, filled: %u, noise: %d",
+ WIPHY_PR_ARG, __entry->ret, CHAN_PR_ARG,
+ __entry->time, __entry->time_busy,
+ __entry->time_ext_busy, __entry->time_rx,
+ __entry->time_tx, __entry->time_scan,
+ __entry->filled, __entry->noise)
+);
+
+TRACE_EVENT(rdev_tdls_oper,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ u8 *peer, enum nl80211_tdls_operation oper),
+ TP_ARGS(wiphy, netdev, peer, oper),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ MAC_ENTRY(peer)
+ __field(enum nl80211_tdls_operation, oper)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(peer, peer);
+ __entry->oper = oper;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " MAC_PR_FMT ", oper: %d",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer), __entry->oper)
+);
+
+DECLARE_EVENT_CLASS(rdev_pmksa,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ struct cfg80211_pmksa *pmksa),
+ TP_ARGS(wiphy, netdev, pmksa),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ MAC_ENTRY(bssid)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(bssid, pmksa->bssid);
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", bssid: " MAC_PR_FMT,
+ WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(bssid))
+);
+
+TRACE_EVENT(rdev_probe_client,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ const u8 *peer),
+ TP_ARGS(wiphy, netdev, peer),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ MAC_ENTRY(peer)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(peer, peer);
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " MAC_PR_FMT,
+ WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer))
+);
+
+DEFINE_EVENT(rdev_pmksa, rdev_set_pmksa,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ struct cfg80211_pmksa *pmksa),
+ TP_ARGS(wiphy, netdev, pmksa)
+);
+
+DEFINE_EVENT(rdev_pmksa, rdev_del_pmksa,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ struct cfg80211_pmksa *pmksa),
+ TP_ARGS(wiphy, netdev, pmksa)
+);
+
+TRACE_EVENT(rdev_remain_on_channel,
+ TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev,
+ struct ieee80211_channel *chan,
+ unsigned int duration),
+ TP_ARGS(wiphy, wdev, chan, duration),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ WDEV_ENTRY
+ CHAN_ENTRY
+ __field(unsigned int, duration)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ WDEV_ASSIGN;
+ CHAN_ASSIGN(chan);
+ __entry->duration = duration;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", " CHAN_PR_FMT ", duration: %u",
+ WIPHY_PR_ARG, WDEV_PR_ARG, CHAN_PR_ARG, __entry->duration)
+);
+
+TRACE_EVENT(rdev_return_int_cookie,
+ TP_PROTO(struct wiphy *wiphy, int ret, u64 cookie),
+ TP_ARGS(wiphy, ret, cookie),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ __field(int, ret)
+ __field(u64, cookie)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ __entry->ret = ret;
+ __entry->cookie = cookie;
+ ),
+ TP_printk(WIPHY_PR_FMT ", returned %d, cookie: %llu",
+ WIPHY_PR_ARG, __entry->ret, __entry->cookie)
+);
+
+TRACE_EVENT(rdev_cancel_remain_on_channel,
+ TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, u64 cookie),
+ TP_ARGS(wiphy, wdev, cookie),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ WDEV_ENTRY
+ __field(u64, cookie)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ WDEV_ASSIGN;
+ __entry->cookie = cookie;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", cookie: %llu",
+ WIPHY_PR_ARG, WDEV_PR_ARG, __entry->cookie)
+);
+
+TRACE_EVENT(rdev_mgmt_tx,
+ TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev,
+ struct cfg80211_mgmt_tx_params *params),
+ TP_ARGS(wiphy, wdev, params),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ WDEV_ENTRY
+ CHAN_ENTRY
+ __field(bool, offchan)
+ __field(unsigned int, wait)
+ __field(bool, no_cck)
+ __field(bool, dont_wait_for_ack)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ WDEV_ASSIGN;
+ CHAN_ASSIGN(params->chan);
+ __entry->offchan = params->offchan;
+ __entry->wait = params->wait;
+ __entry->no_cck = params->no_cck;
+ __entry->dont_wait_for_ack = params->dont_wait_for_ack;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", " CHAN_PR_FMT ", offchan: %s,"
+ " wait: %u, no cck: %s, dont wait for ack: %s",
+ WIPHY_PR_ARG, WDEV_PR_ARG, CHAN_PR_ARG,
+ BOOL_TO_STR(__entry->offchan), __entry->wait,
+ BOOL_TO_STR(__entry->no_cck),
+ BOOL_TO_STR(__entry->dont_wait_for_ack))
+);
+
+TRACE_EVENT(rdev_set_noack_map,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ u16 noack_map),
+ TP_ARGS(wiphy, netdev, noack_map),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ __field(u16, noack_map)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ __entry->noack_map = noack_map;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", noack_map: %u",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->noack_map)
+);
+
+DEFINE_EVENT(wiphy_wdev_evt, rdev_get_channel,
+ TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev),
+ TP_ARGS(wiphy, wdev)
+);
+
+TRACE_EVENT(rdev_return_chandef,
+ TP_PROTO(struct wiphy *wiphy, int ret,
+ struct cfg80211_chan_def *chandef),
+ TP_ARGS(wiphy, ret, chandef),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ __field(int, ret)
+ CHAN_DEF_ENTRY
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ if (ret == 0)
+ CHAN_DEF_ASSIGN(chandef);
+ else
+ CHAN_DEF_ASSIGN((struct cfg80211_chan_def *)NULL);
+ __entry->ret = ret;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT ", ret: %d",
+ WIPHY_PR_ARG, CHAN_DEF_PR_ARG, __entry->ret)
+);
+
+DEFINE_EVENT(wiphy_wdev_evt, rdev_start_p2p_device,
+ TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev),
+ TP_ARGS(wiphy, wdev)
+);
+
+DEFINE_EVENT(wiphy_wdev_evt, rdev_stop_p2p_device,
+ TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev),
+ TP_ARGS(wiphy, wdev)
+);
+
+TRACE_EVENT(rdev_start_nan,
+ TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev,
+ struct cfg80211_nan_conf *conf),
+ TP_ARGS(wiphy, wdev, conf),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ WDEV_ENTRY
+ __field(u8, master_pref)
+ __field(u8, bands);
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ WDEV_ASSIGN;
+ __entry->master_pref = conf->master_pref;
+ __entry->bands = conf->bands;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT
+ ", master preference: %u, bands: 0x%0x",
+ WIPHY_PR_ARG, WDEV_PR_ARG, __entry->master_pref,
+ __entry->bands)
+);
+
+TRACE_EVENT(rdev_nan_change_conf,
+ TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev,
+ struct cfg80211_nan_conf *conf, u32 changes),
+ TP_ARGS(wiphy, wdev, conf, changes),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ WDEV_ENTRY
+ __field(u8, master_pref)
+ __field(u8, bands);
+ __field(u32, changes);
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ WDEV_ASSIGN;
+ __entry->master_pref = conf->master_pref;
+ __entry->bands = conf->bands;
+ __entry->changes = changes;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT
+ ", master preference: %u, bands: 0x%0x, changes: %x",
+ WIPHY_PR_ARG, WDEV_PR_ARG, __entry->master_pref,
+ __entry->bands, __entry->changes)
+);
+
+DEFINE_EVENT(wiphy_wdev_evt, rdev_stop_nan,
+ TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev),
+ TP_ARGS(wiphy, wdev)
+);
+
+TRACE_EVENT(rdev_add_nan_func,
+ TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev,
+ const struct cfg80211_nan_func *func),
+ TP_ARGS(wiphy, wdev, func),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ WDEV_ENTRY
+ __field(u8, func_type)
+ __field(u64, cookie)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ WDEV_ASSIGN;
+ __entry->func_type = func->type;
+ __entry->cookie = func->cookie
+ ),
+ TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", type=%u, cookie=%llu",
+ WIPHY_PR_ARG, WDEV_PR_ARG, __entry->func_type,
+ __entry->cookie)
+);
+
+TRACE_EVENT(rdev_del_nan_func,
+ TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev,
+ u64 cookie),
+ TP_ARGS(wiphy, wdev, cookie),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ WDEV_ENTRY
+ __field(u64, cookie)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ WDEV_ASSIGN;
+ __entry->cookie = cookie;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", cookie=%llu",
+ WIPHY_PR_ARG, WDEV_PR_ARG, __entry->cookie)
+);
+
+TRACE_EVENT(rdev_set_mac_acl,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ struct cfg80211_acl_data *params),
+ TP_ARGS(wiphy, netdev, params),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ __field(u32, acl_policy)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ __entry->acl_policy = params->acl_policy;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", acl policy: %d",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->acl_policy)
+);
+
+TRACE_EVENT(rdev_update_ft_ies,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ struct cfg80211_update_ft_ies_params *ftie),
+ TP_ARGS(wiphy, netdev, ftie),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ __field(u16, md)
+ __dynamic_array(u8, ie, ftie->ie_len)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ __entry->md = ftie->md;
+ memcpy(__get_dynamic_array(ie), ftie->ie, ftie->ie_len);
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", md: 0x%x",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->md)
+);
+
+TRACE_EVENT(rdev_crit_proto_start,
+ TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev,
+ enum nl80211_crit_proto_id protocol, u16 duration),
+ TP_ARGS(wiphy, wdev, protocol, duration),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ WDEV_ENTRY
+ __field(u16, proto)
+ __field(u16, duration)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ WDEV_ASSIGN;
+ __entry->proto = protocol;
+ __entry->duration = duration;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", proto=%x, duration=%u",
+ WIPHY_PR_ARG, WDEV_PR_ARG, __entry->proto, __entry->duration)
+);
+
+TRACE_EVENT(rdev_crit_proto_stop,
+ TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev),
+ TP_ARGS(wiphy, wdev),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ WDEV_ENTRY
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ WDEV_ASSIGN;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT,
+ WIPHY_PR_ARG, WDEV_PR_ARG)
+);
+
+TRACE_EVENT(rdev_channel_switch,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ struct cfg80211_csa_settings *params),
+ TP_ARGS(wiphy, netdev, params),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ CHAN_DEF_ENTRY
+ __field(bool, radar_required)
+ __field(bool, block_tx)
+ __field(u8, count)
+ __dynamic_array(u16, bcn_ofs, params->n_counter_offsets_beacon)
+ __dynamic_array(u16, pres_ofs, params->n_counter_offsets_presp)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ CHAN_DEF_ASSIGN(¶ms->chandef);
+ __entry->radar_required = params->radar_required;
+ __entry->block_tx = params->block_tx;
+ __entry->count = params->count;
+ memcpy(__get_dynamic_array(bcn_ofs),
+ params->counter_offsets_beacon,
+ params->n_counter_offsets_beacon * sizeof(u16));
+
+ /* probe response offsets are optional */
+ if (params->n_counter_offsets_presp)
+ memcpy(__get_dynamic_array(pres_ofs),
+ params->counter_offsets_presp,
+ params->n_counter_offsets_presp * sizeof(u16));
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " CHAN_DEF_PR_FMT
+ ", block_tx: %d, count: %u, radar_required: %d",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, CHAN_DEF_PR_ARG,
+ __entry->block_tx, __entry->count, __entry->radar_required)
+);
+
+TRACE_EVENT(rdev_set_qos_map,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ struct cfg80211_qos_map *qos_map),
+ TP_ARGS(wiphy, netdev, qos_map),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ QOS_MAP_ENTRY
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ QOS_MAP_ASSIGN(qos_map);
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", num_des: %u",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->num_des)
+);
+
+TRACE_EVENT(rdev_set_ap_chanwidth,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ struct cfg80211_chan_def *chandef),
+ TP_ARGS(wiphy, netdev, chandef),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ CHAN_DEF_ENTRY
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ CHAN_DEF_ASSIGN(chandef);
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " CHAN_DEF_PR_FMT,
+ WIPHY_PR_ARG, NETDEV_PR_ARG, CHAN_DEF_PR_ARG)
+);
+
+TRACE_EVENT(rdev_add_tx_ts,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ u8 tsid, const u8 *peer, u8 user_prio, u16 admitted_time),
+ TP_ARGS(wiphy, netdev, tsid, peer, user_prio, admitted_time),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ MAC_ENTRY(peer)
+ __field(u8, tsid)
+ __field(u8, user_prio)
+ __field(u16, admitted_time)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(peer, peer);
+ __entry->tsid = tsid;
+ __entry->user_prio = user_prio;
+ __entry->admitted_time = admitted_time;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " MAC_PR_FMT ", TSID %d, UP %d, time %d",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer),
+ __entry->tsid, __entry->user_prio, __entry->admitted_time)
+);
+
+TRACE_EVENT(rdev_del_tx_ts,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ u8 tsid, const u8 *peer),
+ TP_ARGS(wiphy, netdev, tsid, peer),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ MAC_ENTRY(peer)
+ __field(u8, tsid)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(peer, peer);
+ __entry->tsid = tsid;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " MAC_PR_FMT ", TSID %d",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer), __entry->tsid)
+);
+
+TRACE_EVENT(rdev_tdls_channel_switch,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ const u8 *addr, u8 oper_class,
+ struct cfg80211_chan_def *chandef),
+ TP_ARGS(wiphy, netdev, addr, oper_class, chandef),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ MAC_ENTRY(addr)
+ __field(u8, oper_class)
+ CHAN_DEF_ENTRY
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(addr, addr);
+ CHAN_DEF_ASSIGN(chandef);
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " MAC_PR_FMT
+ " oper class %d, " CHAN_DEF_PR_FMT,
+ WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(addr),
+ __entry->oper_class, CHAN_DEF_PR_ARG)
+);
+
+TRACE_EVENT(rdev_tdls_cancel_channel_switch,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ const u8 *addr),
+ TP_ARGS(wiphy, netdev, addr),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ MAC_ENTRY(addr)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(addr, addr);
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " MAC_PR_FMT,
+ WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(addr))
+);
+
+TRACE_EVENT(rdev_set_pmk,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ struct cfg80211_pmk_conf *pmk_conf),
+
+ TP_ARGS(wiphy, netdev, pmk_conf),
+
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ MAC_ENTRY(aa)
+ __field(u8, pmk_len)
+ __field(u8, pmk_r0_name_len)
+ __dynamic_array(u8, pmk, pmk_conf->pmk_len)
+ __dynamic_array(u8, pmk_r0_name, WLAN_PMK_NAME_LEN)
+ ),
+
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(aa, pmk_conf->aa);
+ __entry->pmk_len = pmk_conf->pmk_len;
+ __entry->pmk_r0_name_len =
+ pmk_conf->pmk_r0_name ? WLAN_PMK_NAME_LEN : 0;
+ memcpy(__get_dynamic_array(pmk), pmk_conf->pmk,
+ pmk_conf->pmk_len);
+ memcpy(__get_dynamic_array(pmk_r0_name), pmk_conf->pmk_r0_name,
+ pmk_conf->pmk_r0_name ? WLAN_PMK_NAME_LEN : 0);
+ ),
+
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " MAC_PR_FMT
+ "pmk_len=%u, pmk: %s pmk_r0_name: %s", WIPHY_PR_ARG,
+ NETDEV_PR_ARG, MAC_PR_ARG(aa), __entry->pmk_len,
+ __print_array(__get_dynamic_array(pmk),
+ __get_dynamic_array_len(pmk), 1),
+ __entry->pmk_r0_name_len ?
+ __print_array(__get_dynamic_array(pmk_r0_name),
+ __get_dynamic_array_len(pmk_r0_name), 1) : "")
+);
+
+TRACE_EVENT(rdev_del_pmk,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, const u8 *aa),
+
+ TP_ARGS(wiphy, netdev, aa),
+
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ MAC_ENTRY(aa)
+ ),
+
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(aa, aa);
+ ),
+
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " MAC_PR_FMT,
+ WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(aa))
+);
+
+/*************************************************************
+ * cfg80211 exported functions traces *
+ *************************************************************/
+
+TRACE_EVENT(cfg80211_return_bool,
+ TP_PROTO(bool ret),
+ TP_ARGS(ret),
+ TP_STRUCT__entry(
+ __field(bool, ret)
+ ),
+ TP_fast_assign(
+ __entry->ret = ret;
+ ),
+ TP_printk("returned %s", BOOL_TO_STR(__entry->ret))
+);
+
+DECLARE_EVENT_CLASS(cfg80211_netdev_mac_evt,
+ TP_PROTO(struct net_device *netdev, const u8 *macaddr),
+ TP_ARGS(netdev, macaddr),
+ TP_STRUCT__entry(
+ NETDEV_ENTRY
+ MAC_ENTRY(macaddr)
+ ),
+ TP_fast_assign(
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(macaddr, macaddr);
+ ),
+ TP_printk(NETDEV_PR_FMT ", mac: " MAC_PR_FMT,
+ NETDEV_PR_ARG, MAC_PR_ARG(macaddr))
+);
+
+DEFINE_EVENT(cfg80211_netdev_mac_evt, cfg80211_notify_new_peer_candidate,
+ TP_PROTO(struct net_device *netdev, const u8 *macaddr),
+ TP_ARGS(netdev, macaddr)
+);
+
+DECLARE_EVENT_CLASS(netdev_evt_only,
+ TP_PROTO(struct net_device *netdev),
+ TP_ARGS(netdev),
+ TP_STRUCT__entry(
+ NETDEV_ENTRY
+ ),
+ TP_fast_assign(
+ NETDEV_ASSIGN;
+ ),
+ TP_printk(NETDEV_PR_FMT , NETDEV_PR_ARG)
+);
+
+DEFINE_EVENT(netdev_evt_only, cfg80211_send_rx_auth,
+ TP_PROTO(struct net_device *netdev),
+ TP_ARGS(netdev)
+);
+
+TRACE_EVENT(cfg80211_send_rx_assoc,
+ TP_PROTO(struct net_device *netdev, struct cfg80211_bss *bss),
+ TP_ARGS(netdev, bss),
+ TP_STRUCT__entry(
+ NETDEV_ENTRY
+ MAC_ENTRY(bssid)
+ CHAN_ENTRY
+ ),
+ TP_fast_assign(
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(bssid, bss->bssid);
+ CHAN_ASSIGN(bss->channel);
+ ),
+ TP_printk(NETDEV_PR_FMT ", " MAC_PR_FMT ", " CHAN_PR_FMT,
+ NETDEV_PR_ARG, MAC_PR_ARG(bssid), CHAN_PR_ARG)
+);
+
+DECLARE_EVENT_CLASS(netdev_frame_event,
+ TP_PROTO(struct net_device *netdev, const u8 *buf, int len),
+ TP_ARGS(netdev, buf, len),
+ TP_STRUCT__entry(
+ NETDEV_ENTRY
+ __dynamic_array(u8, frame, len)
+ ),
+ TP_fast_assign(
+ NETDEV_ASSIGN;
+ memcpy(__get_dynamic_array(frame), buf, len);
+ ),
+ TP_printk(NETDEV_PR_FMT ", ftype:0x%.2x",
+ NETDEV_PR_ARG,
+ le16_to_cpup((__le16 *)__get_dynamic_array(frame)))
+);
+
+DEFINE_EVENT(netdev_frame_event, cfg80211_rx_unprot_mlme_mgmt,
+ TP_PROTO(struct net_device *netdev, const u8 *buf, int len),
+ TP_ARGS(netdev, buf, len)
+);
+
+DEFINE_EVENT(netdev_frame_event, cfg80211_rx_mlme_mgmt,
+ TP_PROTO(struct net_device *netdev, const u8 *buf, int len),
+ TP_ARGS(netdev, buf, len)
+);
+
+TRACE_EVENT(cfg80211_tx_mlme_mgmt,
+ TP_PROTO(struct net_device *netdev, const u8 *buf, int len),
+ TP_ARGS(netdev, buf, len),
+ TP_STRUCT__entry(
+ NETDEV_ENTRY
+ __dynamic_array(u8, frame, len)
+ ),
+ TP_fast_assign(
+ NETDEV_ASSIGN;
+ memcpy(__get_dynamic_array(frame), buf, len);
+ ),
+ TP_printk(NETDEV_PR_FMT ", ftype:0x%.2x",
+ NETDEV_PR_ARG,
+ le16_to_cpup((__le16 *)__get_dynamic_array(frame)))
+);
+
+DECLARE_EVENT_CLASS(netdev_mac_evt,
+ TP_PROTO(struct net_device *netdev, const u8 *mac),
+ TP_ARGS(netdev, mac),
+ TP_STRUCT__entry(
+ NETDEV_ENTRY
+ MAC_ENTRY(mac)
+ ),
+ TP_fast_assign(
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(mac, mac)
+ ),
+ TP_printk(NETDEV_PR_FMT ", mac: " MAC_PR_FMT,
+ NETDEV_PR_ARG, MAC_PR_ARG(mac))
+);
+
+DEFINE_EVENT(netdev_mac_evt, cfg80211_send_auth_timeout,
+ TP_PROTO(struct net_device *netdev, const u8 *mac),
+ TP_ARGS(netdev, mac)
+);
+
+DEFINE_EVENT(netdev_mac_evt, cfg80211_send_assoc_timeout,
+ TP_PROTO(struct net_device *netdev, const u8 *mac),
+ TP_ARGS(netdev, mac)
+);
+
+TRACE_EVENT(cfg80211_michael_mic_failure,
+ TP_PROTO(struct net_device *netdev, const u8 *addr,
+ enum nl80211_key_type key_type, int key_id, const u8 *tsc),
+ TP_ARGS(netdev, addr, key_type, key_id, tsc),
+ TP_STRUCT__entry(
+ NETDEV_ENTRY
+ MAC_ENTRY(addr)
+ __field(enum nl80211_key_type, key_type)
+ __field(int, key_id)
+ __array(u8, tsc, 6)
+ ),
+ TP_fast_assign(
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(addr, addr);
+ __entry->key_type = key_type;
+ __entry->key_id = key_id;
+ if (tsc)
+ memcpy(__entry->tsc, tsc, 6);
+ ),
+ TP_printk(NETDEV_PR_FMT ", " MAC_PR_FMT ", key type: %d, key id: %d, tsc: %pm",
+ NETDEV_PR_ARG, MAC_PR_ARG(addr), __entry->key_type,
+ __entry->key_id, __entry->tsc)
+);
+
+TRACE_EVENT(cfg80211_ready_on_channel,
+ TP_PROTO(struct wireless_dev *wdev, u64 cookie,
+ struct ieee80211_channel *chan,
+ unsigned int duration),
+ TP_ARGS(wdev, cookie, chan, duration),
+ TP_STRUCT__entry(
+ WDEV_ENTRY
+ __field(u64, cookie)
+ CHAN_ENTRY
+ __field(unsigned int, duration)
+ ),
+ TP_fast_assign(
+ WDEV_ASSIGN;
+ __entry->cookie = cookie;
+ CHAN_ASSIGN(chan);
+ __entry->duration = duration;
+ ),
+ TP_printk(WDEV_PR_FMT ", cookie: %llu, " CHAN_PR_FMT ", duration: %u",
+ WDEV_PR_ARG, __entry->cookie, CHAN_PR_ARG,
+ __entry->duration)
+);
+
+TRACE_EVENT(cfg80211_ready_on_channel_expired,
+ TP_PROTO(struct wireless_dev *wdev, u64 cookie,
+ struct ieee80211_channel *chan),
+ TP_ARGS(wdev, cookie, chan),
+ TP_STRUCT__entry(
+ WDEV_ENTRY
+ __field(u64, cookie)
+ CHAN_ENTRY
+ ),
+ TP_fast_assign(
+ WDEV_ASSIGN;
+ __entry->cookie = cookie;
+ CHAN_ASSIGN(chan);
+ ),
+ TP_printk(WDEV_PR_FMT ", cookie: %llu, " CHAN_PR_FMT,
+ WDEV_PR_ARG, __entry->cookie, CHAN_PR_ARG)
+);
+
+TRACE_EVENT(cfg80211_new_sta,
+ TP_PROTO(struct net_device *netdev, const u8 *mac_addr,
+ struct station_info *sinfo),
+ TP_ARGS(netdev, mac_addr, sinfo),
+ TP_STRUCT__entry(
+ NETDEV_ENTRY
+ MAC_ENTRY(mac_addr)
+ SINFO_ENTRY
+ ),
+ TP_fast_assign(
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(mac_addr, mac_addr);
+ SINFO_ASSIGN;
+ ),
+ TP_printk(NETDEV_PR_FMT ", " MAC_PR_FMT,
+ NETDEV_PR_ARG, MAC_PR_ARG(mac_addr))
+);
+
+DEFINE_EVENT(cfg80211_netdev_mac_evt, cfg80211_del_sta,
+ TP_PROTO(struct net_device *netdev, const u8 *macaddr),
+ TP_ARGS(netdev, macaddr)
+);
+
+TRACE_EVENT(cfg80211_rx_mgmt,
+ TP_PROTO(struct wireless_dev *wdev, int freq, int sig_mbm),
+ TP_ARGS(wdev, freq, sig_mbm),
+ TP_STRUCT__entry(
+ WDEV_ENTRY
+ __field(int, freq)
+ __field(int, sig_mbm)
+ ),
+ TP_fast_assign(
+ WDEV_ASSIGN;
+ __entry->freq = freq;
+ __entry->sig_mbm = sig_mbm;
+ ),
+ TP_printk(WDEV_PR_FMT ", freq: %d, sig mbm: %d",
+ WDEV_PR_ARG, __entry->freq, __entry->sig_mbm)
+);
+
+TRACE_EVENT(cfg80211_mgmt_tx_status,
+ TP_PROTO(struct wireless_dev *wdev, u64 cookie, bool ack),
+ TP_ARGS(wdev, cookie, ack),
+ TP_STRUCT__entry(
+ WDEV_ENTRY
+ __field(u64, cookie)
+ __field(bool, ack)
+ ),
+ TP_fast_assign(
+ WDEV_ASSIGN;
+ __entry->cookie = cookie;
+ __entry->ack = ack;
+ ),
+ TP_printk(WDEV_PR_FMT", cookie: %llu, ack: %s",
+ WDEV_PR_ARG, __entry->cookie, BOOL_TO_STR(__entry->ack))
+);
+
+TRACE_EVENT(cfg80211_cqm_rssi_notify,
+ TP_PROTO(struct net_device *netdev,
+ enum nl80211_cqm_rssi_threshold_event rssi_event,
+ s32 rssi_level),
+ TP_ARGS(netdev, rssi_event, rssi_level),
+ TP_STRUCT__entry(
+ NETDEV_ENTRY
+ __field(enum nl80211_cqm_rssi_threshold_event, rssi_event)
+ __field(s32, rssi_level)
+ ),
+ TP_fast_assign(
+ NETDEV_ASSIGN;
+ __entry->rssi_event = rssi_event;
+ __entry->rssi_level = rssi_level;
+ ),
+ TP_printk(NETDEV_PR_FMT ", rssi event: %d, level: %d",
+ NETDEV_PR_ARG, __entry->rssi_event, __entry->rssi_level)
+);
+
+TRACE_EVENT(cfg80211_reg_can_beacon,
+ TP_PROTO(struct wiphy *wiphy, struct cfg80211_chan_def *chandef,
+ enum nl80211_iftype iftype, bool check_no_ir),
+ TP_ARGS(wiphy, chandef, iftype, check_no_ir),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ CHAN_DEF_ENTRY
+ __field(enum nl80211_iftype, iftype)
+ __field(bool, check_no_ir)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ CHAN_DEF_ASSIGN(chandef);
+ __entry->iftype = iftype;
+ __entry->check_no_ir = check_no_ir;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT ", iftype=%d check_no_ir=%s",
+ WIPHY_PR_ARG, CHAN_DEF_PR_ARG, __entry->iftype,
+ BOOL_TO_STR(__entry->check_no_ir))
+);
+
+TRACE_EVENT(cfg80211_chandef_dfs_required,
+ TP_PROTO(struct wiphy *wiphy, struct cfg80211_chan_def *chandef),
+ TP_ARGS(wiphy, chandef),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ CHAN_DEF_ENTRY
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ CHAN_DEF_ASSIGN(chandef);
+ ),
+ TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT,
+ WIPHY_PR_ARG, CHAN_DEF_PR_ARG)
+);
+
+TRACE_EVENT(cfg80211_ch_switch_notify,
+ TP_PROTO(struct net_device *netdev,
+ struct cfg80211_chan_def *chandef),
+ TP_ARGS(netdev, chandef),
+ TP_STRUCT__entry(
+ NETDEV_ENTRY
+ CHAN_DEF_ENTRY
+ ),
+ TP_fast_assign(
+ NETDEV_ASSIGN;
+ CHAN_DEF_ASSIGN(chandef);
+ ),
+ TP_printk(NETDEV_PR_FMT ", " CHAN_DEF_PR_FMT,
+ NETDEV_PR_ARG, CHAN_DEF_PR_ARG)
+);
+
+TRACE_EVENT(cfg80211_ch_switch_started_notify,
+ TP_PROTO(struct net_device *netdev,
+ struct cfg80211_chan_def *chandef),
+ TP_ARGS(netdev, chandef),
+ TP_STRUCT__entry(
+ NETDEV_ENTRY
+ CHAN_DEF_ENTRY
+ ),
+ TP_fast_assign(
+ NETDEV_ASSIGN;
+ CHAN_DEF_ASSIGN(chandef);
+ ),
+ TP_printk(NETDEV_PR_FMT ", " CHAN_DEF_PR_FMT,
+ NETDEV_PR_ARG, CHAN_DEF_PR_ARG)
+);
+
+TRACE_EVENT(cfg80211_radar_event,
+ TP_PROTO(struct wiphy *wiphy, struct cfg80211_chan_def *chandef),
+ TP_ARGS(wiphy, chandef),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ CHAN_DEF_ENTRY
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ CHAN_DEF_ASSIGN(chandef);
+ ),
+ TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT,
+ WIPHY_PR_ARG, CHAN_DEF_PR_ARG)
+);
+
+TRACE_EVENT(cfg80211_cac_event,
+ TP_PROTO(struct net_device *netdev, enum nl80211_radar_event evt),
+ TP_ARGS(netdev, evt),
+ TP_STRUCT__entry(
+ NETDEV_ENTRY
+ __field(enum nl80211_radar_event, evt)
+ ),
+ TP_fast_assign(
+ NETDEV_ASSIGN;
+ __entry->evt = evt;
+ ),
+ TP_printk(NETDEV_PR_FMT ", event: %d",
+ NETDEV_PR_ARG, __entry->evt)
+);
+
+DECLARE_EVENT_CLASS(cfg80211_rx_evt,
+ TP_PROTO(struct net_device *netdev, const u8 *addr),
+ TP_ARGS(netdev, addr),
+ TP_STRUCT__entry(
+ NETDEV_ENTRY
+ MAC_ENTRY(addr)
+ ),
+ TP_fast_assign(
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(addr, addr);
+ ),
+ TP_printk(NETDEV_PR_FMT ", " MAC_PR_FMT, NETDEV_PR_ARG, MAC_PR_ARG(addr))
+);
+
+DEFINE_EVENT(cfg80211_rx_evt, cfg80211_rx_spurious_frame,
+ TP_PROTO(struct net_device *netdev, const u8 *addr),
+ TP_ARGS(netdev, addr)
+);
+
+DEFINE_EVENT(cfg80211_rx_evt, cfg80211_rx_unexpected_4addr_frame,
+ TP_PROTO(struct net_device *netdev, const u8 *addr),
+ TP_ARGS(netdev, addr)
+);
+
+TRACE_EVENT(cfg80211_ibss_joined,
+ TP_PROTO(struct net_device *netdev, const u8 *bssid,
+ struct ieee80211_channel *channel),
+ TP_ARGS(netdev, bssid, channel),
+ TP_STRUCT__entry(
+ NETDEV_ENTRY
+ MAC_ENTRY(bssid)
+ CHAN_ENTRY
+ ),
+ TP_fast_assign(
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(bssid, bssid);
+ CHAN_ASSIGN(channel);
+ ),
+ TP_printk(NETDEV_PR_FMT ", bssid: " MAC_PR_FMT ", " CHAN_PR_FMT,
+ NETDEV_PR_ARG, MAC_PR_ARG(bssid), CHAN_PR_ARG)
+);
+
+TRACE_EVENT(cfg80211_probe_status,
+ TP_PROTO(struct net_device *netdev, const u8 *addr, u64 cookie,
+ bool acked),
+ TP_ARGS(netdev, addr, cookie, acked),
+ TP_STRUCT__entry(
+ NETDEV_ENTRY
+ MAC_ENTRY(addr)
+ __field(u64, cookie)
+ __field(bool, acked)
+ ),
+ TP_fast_assign(
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(addr, addr);
+ __entry->cookie = cookie;
+ __entry->acked = acked;
+ ),
+ TP_printk(NETDEV_PR_FMT " addr:" MAC_PR_FMT ", cookie: %llu, acked: %s",
+ NETDEV_PR_ARG, MAC_PR_ARG(addr), __entry->cookie,
+ BOOL_TO_STR(__entry->acked))
+);
+
+TRACE_EVENT(cfg80211_cqm_pktloss_notify,
+ TP_PROTO(struct net_device *netdev, const u8 *peer, u32 num_packets),
+ TP_ARGS(netdev, peer, num_packets),
+ TP_STRUCT__entry(
+ NETDEV_ENTRY
+ MAC_ENTRY(peer)
+ __field(u32, num_packets)
+ ),
+ TP_fast_assign(
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(peer, peer);
+ __entry->num_packets = num_packets;
+ ),
+ TP_printk(NETDEV_PR_FMT ", peer: " MAC_PR_FMT ", num of lost packets: %u",
+ NETDEV_PR_ARG, MAC_PR_ARG(peer), __entry->num_packets)
+);
+
+DEFINE_EVENT(cfg80211_netdev_mac_evt, cfg80211_gtk_rekey_notify,
+ TP_PROTO(struct net_device *netdev, const u8 *macaddr),
+ TP_ARGS(netdev, macaddr)
+);
+
+TRACE_EVENT(cfg80211_pmksa_candidate_notify,
+ TP_PROTO(struct net_device *netdev, int index, const u8 *bssid,
+ bool preauth),
+ TP_ARGS(netdev, index, bssid, preauth),
+ TP_STRUCT__entry(
+ NETDEV_ENTRY
+ __field(int, index)
+ MAC_ENTRY(bssid)
+ __field(bool, preauth)
+ ),
+ TP_fast_assign(
+ NETDEV_ASSIGN;
+ __entry->index = index;
+ MAC_ASSIGN(bssid, bssid);
+ __entry->preauth = preauth;
+ ),
+ TP_printk(NETDEV_PR_FMT ", index:%d, bssid: " MAC_PR_FMT ", pre auth: %s",
+ NETDEV_PR_ARG, __entry->index, MAC_PR_ARG(bssid),
+ BOOL_TO_STR(__entry->preauth))
+);
+
+TRACE_EVENT(cfg80211_report_obss_beacon,
+ TP_PROTO(struct wiphy *wiphy, const u8 *frame, size_t len,
+ int freq, int sig_dbm),
+ TP_ARGS(wiphy, frame, len, freq, sig_dbm),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ __field(int, freq)
+ __field(int, sig_dbm)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ __entry->freq = freq;
+ __entry->sig_dbm = sig_dbm;
+ ),
+ TP_printk(WIPHY_PR_FMT ", freq: %d, sig_dbm: %d",
+ WIPHY_PR_ARG, __entry->freq, __entry->sig_dbm)
+);
+
+TRACE_EVENT(cfg80211_tdls_oper_request,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, const u8 *peer,
+ enum nl80211_tdls_operation oper, u16 reason_code),
+ TP_ARGS(wiphy, netdev, peer, oper, reason_code),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ MAC_ENTRY(peer)
+ __field(enum nl80211_tdls_operation, oper)
+ __field(u16, reason_code)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(peer, peer);
+ __entry->oper = oper;
+ __entry->reason_code = reason_code;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", peer: " MAC_PR_FMT ", oper: %d, reason_code %u",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer), __entry->oper,
+ __entry->reason_code)
+ );
+
+TRACE_EVENT(cfg80211_scan_done,
+ TP_PROTO(struct cfg80211_scan_request *request,
+ struct cfg80211_scan_info *info),
+ TP_ARGS(request, info),
+ TP_STRUCT__entry(
+ __field(u32, n_channels)
+ __dynamic_array(u8, ie, request ? request->ie_len : 0)
+ __array(u32, rates, NUM_NL80211_BANDS)
+ __field(u32, wdev_id)
+ MAC_ENTRY(wiphy_mac)
+ __field(bool, no_cck)
+ __field(bool, aborted)
+ __field(u64, scan_start_tsf)
+ MAC_ENTRY(tsf_bssid)
+ ),
+ TP_fast_assign(
+ if (request) {
+ memcpy(__get_dynamic_array(ie), request->ie,
+ request->ie_len);
+ memcpy(__entry->rates, request->rates,
+ NUM_NL80211_BANDS);
+ __entry->wdev_id = request->wdev ?
+ request->wdev->identifier : 0;
+ if (request->wiphy)
+ MAC_ASSIGN(wiphy_mac,
+ request->wiphy->perm_addr);
+ __entry->no_cck = request->no_cck;
+ }
+ if (info) {
+ __entry->aborted = info->aborted;
+ __entry->scan_start_tsf = info->scan_start_tsf;
+ MAC_ASSIGN(tsf_bssid, info->tsf_bssid);
+ }
+ ),
+ TP_printk("aborted: %s, scan start (TSF): %llu, tsf_bssid: " MAC_PR_FMT,
+ BOOL_TO_STR(__entry->aborted),
+ (unsigned long long)__entry->scan_start_tsf,
+ MAC_PR_ARG(tsf_bssid))
+);
+
+DECLARE_EVENT_CLASS(wiphy_id_evt,
+ TP_PROTO(struct wiphy *wiphy, u64 id),
+ TP_ARGS(wiphy, id),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ __field(u64, id)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ __entry->id = id;
+ ),
+ TP_printk(WIPHY_PR_FMT ", id: %llu", WIPHY_PR_ARG, __entry->id)
+);
+
+DEFINE_EVENT(wiphy_id_evt, cfg80211_sched_scan_stopped,
+ TP_PROTO(struct wiphy *wiphy, u64 id),
+ TP_ARGS(wiphy, id)
+);
+
+DEFINE_EVENT(wiphy_id_evt, cfg80211_sched_scan_results,
+ TP_PROTO(struct wiphy *wiphy, u64 id),
+ TP_ARGS(wiphy, id)
+);
+
+TRACE_EVENT(cfg80211_get_bss,
+ TP_PROTO(struct wiphy *wiphy, struct ieee80211_channel *channel,
+ const u8 *bssid, const u8 *ssid, size_t ssid_len,
+ enum ieee80211_bss_type bss_type,
+ enum ieee80211_privacy privacy),
+ TP_ARGS(wiphy, channel, bssid, ssid, ssid_len, bss_type, privacy),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ CHAN_ENTRY
+ MAC_ENTRY(bssid)
+ __dynamic_array(u8, ssid, ssid_len)
+ __field(enum ieee80211_bss_type, bss_type)
+ __field(enum ieee80211_privacy, privacy)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ CHAN_ASSIGN(channel);
+ MAC_ASSIGN(bssid, bssid);
+ memcpy(__get_dynamic_array(ssid), ssid, ssid_len);
+ __entry->bss_type = bss_type;
+ __entry->privacy = privacy;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " CHAN_PR_FMT ", " MAC_PR_FMT
+ ", buf: %#.2x, bss_type: %d, privacy: %d",
+ WIPHY_PR_ARG, CHAN_PR_ARG, MAC_PR_ARG(bssid),
+ ((u8 *)__get_dynamic_array(ssid))[0], __entry->bss_type,
+ __entry->privacy)
+);
+
+TRACE_EVENT(cfg80211_inform_bss_frame,
+ TP_PROTO(struct wiphy *wiphy, struct cfg80211_inform_bss *data,
+ struct ieee80211_mgmt *mgmt, size_t len),
+ TP_ARGS(wiphy, data, mgmt, len),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ CHAN_ENTRY
+ __field(enum nl80211_bss_scan_width, scan_width)
+ __dynamic_array(u8, mgmt, len)
+ __field(s32, signal)
+ __field(u64, ts_boottime)
+ __field(u64, parent_tsf)
+ MAC_ENTRY(parent_bssid)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ CHAN_ASSIGN(data->chan);
+ __entry->scan_width = data->scan_width;
+ if (mgmt)
+ memcpy(__get_dynamic_array(mgmt), mgmt, len);
+ __entry->signal = data->signal;
+ __entry->ts_boottime = data->boottime_ns;
+ __entry->parent_tsf = data->parent_tsf;
+ MAC_ASSIGN(parent_bssid, data->parent_bssid);
+ ),
+ TP_printk(WIPHY_PR_FMT ", " CHAN_PR_FMT
+ "(scan_width: %d) signal: %d, tsb:%llu, detect_tsf:%llu, tsf_bssid: "
+ MAC_PR_FMT, WIPHY_PR_ARG, CHAN_PR_ARG, __entry->scan_width,
+ __entry->signal, (unsigned long long)__entry->ts_boottime,
+ (unsigned long long)__entry->parent_tsf,
+ MAC_PR_ARG(parent_bssid))
+);
+
+DECLARE_EVENT_CLASS(cfg80211_bss_evt,
+ TP_PROTO(struct cfg80211_bss *pub),
+ TP_ARGS(pub),
+ TP_STRUCT__entry(
+ MAC_ENTRY(bssid)
+ CHAN_ENTRY
+ ),
+ TP_fast_assign(
+ MAC_ASSIGN(bssid, pub->bssid);
+ CHAN_ASSIGN(pub->channel);
+ ),
+ TP_printk(MAC_PR_FMT ", " CHAN_PR_FMT, MAC_PR_ARG(bssid), CHAN_PR_ARG)
+);
+
+DEFINE_EVENT(cfg80211_bss_evt, cfg80211_return_bss,
+ TP_PROTO(struct cfg80211_bss *pub),
+ TP_ARGS(pub)
+);
+
+TRACE_EVENT(cfg80211_return_uint,
+ TP_PROTO(unsigned int ret),
+ TP_ARGS(ret),
+ TP_STRUCT__entry(
+ __field(unsigned int, ret)
+ ),
+ TP_fast_assign(
+ __entry->ret = ret;
+ ),
+ TP_printk("ret: %d", __entry->ret)
+);
+
+TRACE_EVENT(cfg80211_return_u32,
+ TP_PROTO(u32 ret),
+ TP_ARGS(ret),
+ TP_STRUCT__entry(
+ __field(u32, ret)
+ ),
+ TP_fast_assign(
+ __entry->ret = ret;
+ ),
+ TP_printk("ret: %u", __entry->ret)
+);
+
+TRACE_EVENT(cfg80211_report_wowlan_wakeup,
+ TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev,
+ struct cfg80211_wowlan_wakeup *wakeup),
+ TP_ARGS(wiphy, wdev, wakeup),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ WDEV_ENTRY
+ __field(bool, non_wireless)
+ __field(bool, disconnect)
+ __field(bool, magic_pkt)
+ __field(bool, gtk_rekey_failure)
+ __field(bool, eap_identity_req)
+ __field(bool, four_way_handshake)
+ __field(bool, rfkill_release)
+ __field(s32, pattern_idx)
+ __field(u32, packet_len)
+ __dynamic_array(u8, packet,
+ wakeup ? wakeup->packet_present_len : 0)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ WDEV_ASSIGN;
+ __entry->non_wireless = !wakeup;
+ __entry->disconnect = wakeup ? wakeup->disconnect : false;
+ __entry->magic_pkt = wakeup ? wakeup->magic_pkt : false;
+ __entry->gtk_rekey_failure = wakeup ? wakeup->gtk_rekey_failure : false;
+ __entry->eap_identity_req = wakeup ? wakeup->eap_identity_req : false;
+ __entry->four_way_handshake = wakeup ? wakeup->four_way_handshake : false;
+ __entry->rfkill_release = wakeup ? wakeup->rfkill_release : false;
+ __entry->pattern_idx = wakeup ? wakeup->pattern_idx : false;
+ __entry->packet_len = wakeup ? wakeup->packet_len : false;
+ if (wakeup && wakeup->packet && wakeup->packet_present_len)
+ memcpy(__get_dynamic_array(packet), wakeup->packet,
+ wakeup->packet_present_len);
+ ),
+ TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT, WIPHY_PR_ARG, WDEV_PR_ARG)
+);
+
+TRACE_EVENT(cfg80211_ft_event,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ struct cfg80211_ft_event_params *ft_event),
+ TP_ARGS(wiphy, netdev, ft_event),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ __dynamic_array(u8, ies, ft_event->ies_len)
+ MAC_ENTRY(target_ap)
+ __dynamic_array(u8, ric_ies, ft_event->ric_ies_len)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ if (ft_event->ies)
+ memcpy(__get_dynamic_array(ies), ft_event->ies,
+ ft_event->ies_len);
+ MAC_ASSIGN(target_ap, ft_event->target_ap);
+ if (ft_event->ric_ies)
+ memcpy(__get_dynamic_array(ric_ies), ft_event->ric_ies,
+ ft_event->ric_ies_len);
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", target_ap: " MAC_PR_FMT,
+ WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(target_ap))
+);
+
+TRACE_EVENT(cfg80211_stop_iface,
+ TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev),
+ TP_ARGS(wiphy, wdev),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ WDEV_ENTRY
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ WDEV_ASSIGN;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT,
+ WIPHY_PR_ARG, WDEV_PR_ARG)
+);
+
+TRACE_EVENT(rdev_start_radar_detection,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ struct cfg80211_chan_def *chandef,
+ u32 cac_time_ms),
+ TP_ARGS(wiphy, netdev, chandef, cac_time_ms),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ CHAN_DEF_ENTRY
+ __field(u32, cac_time_ms)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ CHAN_DEF_ASSIGN(chandef);
+ __entry->cac_time_ms = cac_time_ms;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " CHAN_DEF_PR_FMT
+ ", cac_time_ms=%u",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, CHAN_DEF_PR_ARG,
+ __entry->cac_time_ms)
+);
+
+TRACE_EVENT(rdev_set_mcast_rate,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ int mcast_rate[NUM_NL80211_BANDS]),
+ TP_ARGS(wiphy, netdev, mcast_rate),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ __array(int, mcast_rate, NUM_NL80211_BANDS)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ memcpy(__entry->mcast_rate, mcast_rate,
+ sizeof(int) * NUM_NL80211_BANDS);
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", "
+ "mcast_rates [2.4GHz=0x%x, 5.2GHz=0x%x, 60GHz=0x%x]",
+ WIPHY_PR_ARG, NETDEV_PR_ARG,
+ __entry->mcast_rate[NL80211_BAND_2GHZ],
+ __entry->mcast_rate[NL80211_BAND_5GHZ],
+ __entry->mcast_rate[NL80211_BAND_60GHZ])
+);
+
+TRACE_EVENT(rdev_set_coalesce,
+ TP_PROTO(struct wiphy *wiphy, struct cfg80211_coalesce *coalesce),
+ TP_ARGS(wiphy, coalesce),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ __field(int, n_rules)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ __entry->n_rules = coalesce ? coalesce->n_rules : 0;
+ ),
+ TP_printk(WIPHY_PR_FMT ", n_rules=%d",
+ WIPHY_PR_ARG, __entry->n_rules)
+);
+
+DEFINE_EVENT(wiphy_wdev_evt, rdev_abort_scan,
+ TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev),
+ TP_ARGS(wiphy, wdev)
+);
+
+TRACE_EVENT(rdev_set_multicast_to_unicast,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ const bool enabled),
+ TP_ARGS(wiphy, netdev, enabled),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ __field(bool, enabled)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ __entry->enabled = enabled;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", unicast: %s",
+ WIPHY_PR_ARG, NETDEV_PR_ARG,
+ BOOL_TO_STR(__entry->enabled))
+);
+#endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE trace
+#include <trace/define_trace.h>
diff --git a/net/wireless/util.c b/net/wireless/util.c
new file mode 100644
index 0000000..931c4d7
--- /dev/null
+++ b/net/wireless/util.c
@@ -0,0 +1,1878 @@
+/*
+ * Wireless utility functions
+ *
+ * Copyright 2007-2009 Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2013-2014 Intel Mobile Communications GmbH
+ */
+#include <linux/export.h>
+#include <linux/bitops.h>
+#include <linux/etherdevice.h>
+#include <linux/slab.h>
+#include <net/cfg80211.h>
+#include <net/ip.h>
+#include <net/dsfield.h>
+#include <linux/if_vlan.h>
+#if LINUX_VERSION_IS_GEQ(3,15,0)
+#include <linux/mpls.h>
+#endif
+#include <linux/gcd.h>
+#include "core.h"
+#include "rdev-ops.h"
+
+
+struct ieee80211_rate *
+ieee80211_get_response_rate(struct ieee80211_supported_band *sband,
+ u32 basic_rates, int bitrate)
+{
+ struct ieee80211_rate *result = &sband->bitrates[0];
+ int i;
+
+ for (i = 0; i < sband->n_bitrates; i++) {
+ if (!(basic_rates & BIT(i)))
+ continue;
+ if (sband->bitrates[i].bitrate > bitrate)
+ continue;
+ result = &sband->bitrates[i];
+ }
+
+ return result;
+}
+EXPORT_SYMBOL(ieee80211_get_response_rate);
+
+u32 ieee80211_mandatory_rates(struct ieee80211_supported_band *sband,
+ enum nl80211_bss_scan_width scan_width)
+{
+ struct ieee80211_rate *bitrates;
+ u32 mandatory_rates = 0;
+ enum ieee80211_rate_flags mandatory_flag;
+ int i;
+
+ if (WARN_ON(!sband))
+ return 1;
+
+ if (sband->band == NL80211_BAND_2GHZ) {
+ if (scan_width == NL80211_BSS_CHAN_WIDTH_5 ||
+ scan_width == NL80211_BSS_CHAN_WIDTH_10)
+ mandatory_flag = IEEE80211_RATE_MANDATORY_G;
+ else
+ mandatory_flag = IEEE80211_RATE_MANDATORY_B;
+ } else {
+ mandatory_flag = IEEE80211_RATE_MANDATORY_A;
+ }
+
+ bitrates = sband->bitrates;
+ for (i = 0; i < sband->n_bitrates; i++)
+ if (bitrates[i].flags & mandatory_flag)
+ mandatory_rates |= BIT(i);
+ return mandatory_rates;
+}
+EXPORT_SYMBOL(ieee80211_mandatory_rates);
+
+int ieee80211_channel_to_frequency(int chan, enum nl80211_band band)
+{
+ /* see 802.11 17.3.8.3.2 and Annex J
+ * there are overlapping channel numbers in 5GHz and 2GHz bands */
+ if (chan <= 0)
+ return 0; /* not supported */
+ switch (band) {
+ case NL80211_BAND_2GHZ:
+ if (chan == 14)
+ return 2484;
+ else if (chan < 14)
+ return 2407 + chan * 5;
+ break;
+ case NL80211_BAND_5GHZ:
+ if (chan >= 182 && chan <= 196)
+ return 4000 + chan * 5;
+ else
+ return 5000 + chan * 5;
+ break;
+ case NL80211_BAND_60GHZ:
+ if (chan < 5)
+ return 56160 + chan * 2160;
+ break;
+ default:
+ ;
+ }
+ return 0; /* not supported */
+}
+EXPORT_SYMBOL(ieee80211_channel_to_frequency);
+
+int ieee80211_frequency_to_channel(int freq)
+{
+ /* see 802.11 17.3.8.3.2 and Annex J */
+ if (freq == 2484)
+ return 14;
+ else if (freq < 2484)
+ return (freq - 2407) / 5;
+ else if (freq >= 4910 && freq <= 4980)
+ return (freq - 4000) / 5;
+ else if (freq <= 45000) /* DMG band lower limit */
+ return (freq - 5000) / 5;
+ else if (freq >= 58320 && freq <= 64800)
+ return (freq - 56160) / 2160;
+ else
+ return 0;
+}
+EXPORT_SYMBOL(ieee80211_frequency_to_channel);
+
+struct ieee80211_channel *ieee80211_get_channel(struct wiphy *wiphy, int freq)
+{
+ enum nl80211_band band;
+ struct ieee80211_supported_band *sband;
+ int i;
+
+ for (band = 0; band < NUM_NL80211_BANDS; band++) {
+ sband = wiphy->bands[band];
+
+ if (!sband)
+ continue;
+
+ for (i = 0; i < sband->n_channels; i++) {
+ if (sband->channels[i].center_freq == freq)
+ return &sband->channels[i];
+ }
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL(ieee80211_get_channel);
+
+static void set_mandatory_flags_band(struct ieee80211_supported_band *sband)
+{
+ int i, want;
+
+ switch (sband->band) {
+ case NL80211_BAND_5GHZ:
+ want = 3;
+ for (i = 0; i < sband->n_bitrates; i++) {
+ if (sband->bitrates[i].bitrate == 60 ||
+ sband->bitrates[i].bitrate == 120 ||
+ sband->bitrates[i].bitrate == 240) {
+ sband->bitrates[i].flags |=
+ IEEE80211_RATE_MANDATORY_A;
+ want--;
+ }
+ }
+ WARN_ON(want);
+ break;
+ case NL80211_BAND_2GHZ:
+ want = 7;
+ for (i = 0; i < sband->n_bitrates; i++) {
+ if (sband->bitrates[i].bitrate == 10) {
+ sband->bitrates[i].flags |=
+ IEEE80211_RATE_MANDATORY_B |
+ IEEE80211_RATE_MANDATORY_G;
+ want--;
+ }
+
+ if (sband->bitrates[i].bitrate == 20 ||
+ sband->bitrates[i].bitrate == 55 ||
+ sband->bitrates[i].bitrate == 110 ||
+ sband->bitrates[i].bitrate == 60 ||
+ sband->bitrates[i].bitrate == 120 ||
+ sband->bitrates[i].bitrate == 240) {
+ sband->bitrates[i].flags |=
+ IEEE80211_RATE_MANDATORY_G;
+ want--;
+ }
+
+ if (sband->bitrates[i].bitrate != 10 &&
+ sband->bitrates[i].bitrate != 20 &&
+ sband->bitrates[i].bitrate != 55 &&
+ sband->bitrates[i].bitrate != 110)
+ sband->bitrates[i].flags |=
+ IEEE80211_RATE_ERP_G;
+ }
+ WARN_ON(want != 0 && want != 3 && want != 6);
+ break;
+ case NL80211_BAND_60GHZ:
+ /* check for mandatory HT MCS 1..4 */
+ WARN_ON(!sband->ht_cap.ht_supported);
+ WARN_ON((sband->ht_cap.mcs.rx_mask[0] & 0x1e) != 0x1e);
+ break;
+ case NUM_NL80211_BANDS:
+ default:
+ WARN_ON(1);
+ break;
+ }
+}
+
+void ieee80211_set_bitrate_flags(struct wiphy *wiphy)
+{
+ enum nl80211_band band;
+
+ for (band = 0; band < NUM_NL80211_BANDS; band++)
+ if (wiphy->bands[band])
+ set_mandatory_flags_band(wiphy->bands[band]);
+}
+
+bool cfg80211_supported_cipher_suite(struct wiphy *wiphy, u32 cipher)
+{
+ int i;
+ for (i = 0; i < wiphy->n_cipher_suites; i++)
+ if (cipher == wiphy->cipher_suites[i])
+ return true;
+ return false;
+}
+
+int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev,
+ struct key_params *params, int key_idx,
+ bool pairwise, const u8 *mac_addr)
+{
+ if (key_idx < 0 || key_idx > 5)
+ return -EINVAL;
+
+ if (!pairwise && mac_addr && !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN))
+ return -EINVAL;
+
+ if (pairwise && !mac_addr)
+ return -EINVAL;
+
+ switch (params->cipher) {
+ case WLAN_CIPHER_SUITE_TKIP:
+ case WLAN_CIPHER_SUITE_CCMP:
+ case WLAN_CIPHER_SUITE_CCMP_256:
+ case WLAN_CIPHER_SUITE_GCMP:
+ case WLAN_CIPHER_SUITE_GCMP_256:
+ /* Disallow pairwise keys with non-zero index unless it's WEP
+ * or a vendor specific cipher (because current deployments use
+ * pairwise WEP keys with non-zero indices and for vendor
+ * specific ciphers this should be validated in the driver or
+ * hardware level - but 802.11i clearly specifies to use zero)
+ */
+ if (pairwise && key_idx)
+ return -EINVAL;
+ break;
+ case WLAN_CIPHER_SUITE_AES_CMAC:
+ case WLAN_CIPHER_SUITE_BIP_CMAC_256:
+ case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+ case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+ /* Disallow BIP (group-only) cipher as pairwise cipher */
+ if (pairwise)
+ return -EINVAL;
+ if (key_idx < 4)
+ return -EINVAL;
+ break;
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ if (key_idx > 3)
+ return -EINVAL;
+ default:
+ break;
+ }
+
+ switch (params->cipher) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ if (params->key_len != WLAN_KEY_LEN_WEP40)
+ return -EINVAL;
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ if (params->key_len != WLAN_KEY_LEN_TKIP)
+ return -EINVAL;
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ if (params->key_len != WLAN_KEY_LEN_CCMP)
+ return -EINVAL;
+ break;
+ case WLAN_CIPHER_SUITE_CCMP_256:
+ if (params->key_len != WLAN_KEY_LEN_CCMP_256)
+ return -EINVAL;
+ break;
+ case WLAN_CIPHER_SUITE_GCMP:
+ if (params->key_len != WLAN_KEY_LEN_GCMP)
+ return -EINVAL;
+ break;
+ case WLAN_CIPHER_SUITE_GCMP_256:
+ if (params->key_len != WLAN_KEY_LEN_GCMP_256)
+ return -EINVAL;
+ break;
+ case WLAN_CIPHER_SUITE_WEP104:
+ if (params->key_len != WLAN_KEY_LEN_WEP104)
+ return -EINVAL;
+ break;
+ case WLAN_CIPHER_SUITE_AES_CMAC:
+ if (params->key_len != WLAN_KEY_LEN_AES_CMAC)
+ return -EINVAL;
+ break;
+ case WLAN_CIPHER_SUITE_BIP_CMAC_256:
+ if (params->key_len != WLAN_KEY_LEN_BIP_CMAC_256)
+ return -EINVAL;
+ break;
+ case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+ if (params->key_len != WLAN_KEY_LEN_BIP_GMAC_128)
+ return -EINVAL;
+ break;
+ case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+ if (params->key_len != WLAN_KEY_LEN_BIP_GMAC_256)
+ return -EINVAL;
+ break;
+ default:
+ /*
+ * We don't know anything about this algorithm,
+ * allow using it -- but the driver must check
+ * all parameters! We still check below whether
+ * or not the driver supports this algorithm,
+ * of course.
+ */
+ break;
+ }
+
+ if (params->seq) {
+ switch (params->cipher) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ /* These ciphers do not use key sequence */
+ return -EINVAL;
+ case WLAN_CIPHER_SUITE_TKIP:
+ case WLAN_CIPHER_SUITE_CCMP:
+ case WLAN_CIPHER_SUITE_CCMP_256:
+ case WLAN_CIPHER_SUITE_GCMP:
+ case WLAN_CIPHER_SUITE_GCMP_256:
+ case WLAN_CIPHER_SUITE_AES_CMAC:
+ case WLAN_CIPHER_SUITE_BIP_CMAC_256:
+ case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+ case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+ if (params->seq_len != 6)
+ return -EINVAL;
+ break;
+ }
+ }
+
+ if (!cfg80211_supported_cipher_suite(&rdev->wiphy, params->cipher))
+ return -EINVAL;
+
+ return 0;
+}
+
+unsigned int __attribute_const__ ieee80211_hdrlen(__le16 fc)
+{
+ unsigned int hdrlen = 24;
+
+ if (ieee80211_is_data(fc)) {
+ if (ieee80211_has_a4(fc))
+ hdrlen = 30;
+ if (ieee80211_is_data_qos(fc)) {
+ hdrlen += IEEE80211_QOS_CTL_LEN;
+ if (ieee80211_has_order(fc))
+ hdrlen += IEEE80211_HT_CTL_LEN;
+ }
+ goto out;
+ }
+
+ if (ieee80211_is_mgmt(fc)) {
+ if (ieee80211_has_order(fc))
+ hdrlen += IEEE80211_HT_CTL_LEN;
+ goto out;
+ }
+
+ if (ieee80211_is_ctl(fc)) {
+ /*
+ * ACK and CTS are 10 bytes, all others 16. To see how
+ * to get this condition consider
+ * subtype mask: 0b0000000011110000 (0x00F0)
+ * ACK subtype: 0b0000000011010000 (0x00D0)
+ * CTS subtype: 0b0000000011000000 (0x00C0)
+ * bits that matter: ^^^ (0x00E0)
+ * value of those: 0b0000000011000000 (0x00C0)
+ */
+ if ((fc & cpu_to_le16(0x00E0)) == cpu_to_le16(0x00C0))
+ hdrlen = 10;
+ else
+ hdrlen = 16;
+ }
+out:
+ return hdrlen;
+}
+EXPORT_SYMBOL(ieee80211_hdrlen);
+
+unsigned int ieee80211_get_hdrlen_from_skb(const struct sk_buff *skb)
+{
+ const struct ieee80211_hdr *hdr =
+ (const struct ieee80211_hdr *)skb->data;
+ unsigned int hdrlen;
+
+ if (unlikely(skb->len < 10))
+ return 0;
+ hdrlen = ieee80211_hdrlen(hdr->frame_control);
+ if (unlikely(hdrlen > skb->len))
+ return 0;
+ return hdrlen;
+}
+EXPORT_SYMBOL(ieee80211_get_hdrlen_from_skb);
+
+static unsigned int __ieee80211_get_mesh_hdrlen(u8 flags)
+{
+ int ae = flags & MESH_FLAGS_AE;
+ /* 802.11-2012, 8.2.4.7.3 */
+ switch (ae) {
+ default:
+ case 0:
+ return 6;
+ case MESH_FLAGS_AE_A4:
+ return 12;
+ case MESH_FLAGS_AE_A5_A6:
+ return 18;
+ }
+}
+
+unsigned int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr)
+{
+ return __ieee80211_get_mesh_hdrlen(meshhdr->flags);
+}
+EXPORT_SYMBOL(ieee80211_get_mesh_hdrlen);
+
+int ieee80211_data_to_8023_exthdr(struct sk_buff *skb, struct ethhdr *ehdr,
+ const u8 *addr, enum nl80211_iftype iftype)
+{
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+ struct {
+ u8 hdr[ETH_ALEN] __aligned(2);
+ __be16 proto;
+ } payload;
+ struct ethhdr tmp;
+ u16 hdrlen;
+ u8 mesh_flags = 0;
+
+ if (unlikely(!ieee80211_is_data_present(hdr->frame_control)))
+ return -1;
+
+ hdrlen = ieee80211_hdrlen(hdr->frame_control);
+ if (skb->len < hdrlen + 8)
+ return -1;
+
+ /* convert IEEE 802.11 header + possible LLC headers into Ethernet
+ * header
+ * IEEE 802.11 address fields:
+ * ToDS FromDS Addr1 Addr2 Addr3 Addr4
+ * 0 0 DA SA BSSID n/a
+ * 0 1 DA BSSID SA n/a
+ * 1 0 BSSID SA DA n/a
+ * 1 1 RA TA DA SA
+ */
+ memcpy(tmp.h_dest, ieee80211_get_DA(hdr), ETH_ALEN);
+ memcpy(tmp.h_source, ieee80211_get_SA(hdr), ETH_ALEN);
+
+ if (iftype == NL80211_IFTYPE_MESH_POINT)
+ skb_copy_bits(skb, hdrlen, &mesh_flags, 1);
+
+ mesh_flags &= MESH_FLAGS_AE;
+
+ switch (hdr->frame_control &
+ cpu_to_le16(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) {
+ case cpu_to_le16(IEEE80211_FCTL_TODS):
+ if (unlikely(iftype != NL80211_IFTYPE_AP &&
+ iftype != NL80211_IFTYPE_AP_VLAN &&
+ iftype != NL80211_IFTYPE_P2P_GO))
+ return -1;
+ break;
+ case cpu_to_le16(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS):
+ if (unlikely(iftype != NL80211_IFTYPE_WDS &&
+ iftype != NL80211_IFTYPE_MESH_POINT &&
+ iftype != NL80211_IFTYPE_AP_VLAN &&
+ iftype != NL80211_IFTYPE_STATION))
+ return -1;
+ if (iftype == NL80211_IFTYPE_MESH_POINT) {
+ if (mesh_flags == MESH_FLAGS_AE_A4)
+ return -1;
+ if (mesh_flags == MESH_FLAGS_AE_A5_A6) {
+ skb_copy_bits(skb, hdrlen +
+ offsetof(struct ieee80211s_hdr, eaddr1),
+ tmp.h_dest, 2 * ETH_ALEN);
+ }
+ hdrlen += __ieee80211_get_mesh_hdrlen(mesh_flags);
+ }
+ break;
+ case cpu_to_le16(IEEE80211_FCTL_FROMDS):
+ if ((iftype != NL80211_IFTYPE_STATION &&
+ iftype != NL80211_IFTYPE_P2P_CLIENT &&
+ iftype != NL80211_IFTYPE_MESH_POINT) ||
+ (is_multicast_ether_addr(tmp.h_dest) &&
+ ether_addr_equal(tmp.h_source, addr)))
+ return -1;
+ if (iftype == NL80211_IFTYPE_MESH_POINT) {
+ if (mesh_flags == MESH_FLAGS_AE_A5_A6)
+ return -1;
+ if (mesh_flags == MESH_FLAGS_AE_A4)
+ skb_copy_bits(skb, hdrlen +
+ offsetof(struct ieee80211s_hdr, eaddr1),
+ tmp.h_source, ETH_ALEN);
+ hdrlen += __ieee80211_get_mesh_hdrlen(mesh_flags);
+ }
+ break;
+ case cpu_to_le16(0):
+ if (iftype != NL80211_IFTYPE_ADHOC &&
+ iftype != NL80211_IFTYPE_STATION &&
+ iftype != NL80211_IFTYPE_OCB)
+ return -1;
+ break;
+ }
+
+ skb_copy_bits(skb, hdrlen, &payload, sizeof(payload));
+ tmp.h_proto = payload.proto;
+
+ if (likely((ether_addr_equal(payload.hdr, rfc1042_header) &&
+ tmp.h_proto != htons(ETH_P_AARP) &&
+ tmp.h_proto != htons(ETH_P_IPX)) ||
+ ether_addr_equal(payload.hdr, bridge_tunnel_header)))
+ /* remove RFC1042 or Bridge-Tunnel encapsulation and
+ * replace EtherType */
+ hdrlen += ETH_ALEN + 2;
+ else
+ tmp.h_proto = htons(skb->len - hdrlen);
+
+ pskb_pull(skb, hdrlen);
+
+ if (!ehdr)
+ ehdr = (struct ethhdr *) skb_push(skb, sizeof(struct ethhdr));
+ memcpy(ehdr, &tmp, sizeof(tmp));
+
+ return 0;
+}
+EXPORT_SYMBOL(ieee80211_data_to_8023_exthdr);
+
+int ieee80211_data_from_8023(struct sk_buff *skb, const u8 *addr,
+ enum nl80211_iftype iftype,
+ const u8 *bssid, bool qos)
+{
+ struct ieee80211_hdr hdr;
+ u16 hdrlen, ethertype;
+ __le16 fc;
+ const u8 *encaps_data;
+ int encaps_len, skip_header_bytes;
+ int nh_pos, h_pos;
+ int head_need;
+
+ if (unlikely(skb->len < ETH_HLEN))
+ return -EINVAL;
+
+ nh_pos = skb_network_header(skb) - skb->data;
+ h_pos = skb_transport_header(skb) - skb->data;
+
+ /* convert Ethernet header to proper 802.11 header (based on
+ * operation mode) */
+ ethertype = (skb->data[12] << 8) | skb->data[13];
+ fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA);
+
+ switch (iftype) {
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_AP_VLAN:
+ case NL80211_IFTYPE_P2P_GO:
+ fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS);
+ /* DA BSSID SA */
+ memcpy(hdr.addr1, skb->data, ETH_ALEN);
+ memcpy(hdr.addr2, addr, ETH_ALEN);
+ memcpy(hdr.addr3, skb->data + ETH_ALEN, ETH_ALEN);
+ hdrlen = 24;
+ break;
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_P2P_CLIENT:
+ fc |= cpu_to_le16(IEEE80211_FCTL_TODS);
+ /* BSSID SA DA */
+ memcpy(hdr.addr1, bssid, ETH_ALEN);
+ memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
+ memcpy(hdr.addr3, skb->data, ETH_ALEN);
+ hdrlen = 24;
+ break;
+ case NL80211_IFTYPE_OCB:
+ case NL80211_IFTYPE_ADHOC:
+ /* DA SA BSSID */
+ memcpy(hdr.addr1, skb->data, ETH_ALEN);
+ memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
+ memcpy(hdr.addr3, bssid, ETH_ALEN);
+ hdrlen = 24;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ if (qos) {
+ fc |= cpu_to_le16(IEEE80211_STYPE_QOS_DATA);
+ hdrlen += 2;
+ }
+
+ hdr.frame_control = fc;
+ hdr.duration_id = 0;
+ hdr.seq_ctrl = 0;
+
+ skip_header_bytes = ETH_HLEN;
+ if (ethertype == ETH_P_AARP || ethertype == ETH_P_IPX) {
+ encaps_data = bridge_tunnel_header;
+ encaps_len = sizeof(bridge_tunnel_header);
+ skip_header_bytes -= 2;
+ } else if (ethertype >= ETH_P_802_3_MIN) {
+ encaps_data = rfc1042_header;
+ encaps_len = sizeof(rfc1042_header);
+ skip_header_bytes -= 2;
+ } else {
+ encaps_data = NULL;
+ encaps_len = 0;
+ }
+
+ skb_pull(skb, skip_header_bytes);
+ nh_pos -= skip_header_bytes;
+ h_pos -= skip_header_bytes;
+
+ head_need = hdrlen + encaps_len - skb_headroom(skb);
+
+ if (head_need > 0 || skb_cloned(skb)) {
+ head_need = max(head_need, 0);
+ if (head_need)
+ skb_orphan(skb);
+
+ if (pskb_expand_head(skb, head_need, 0, GFP_ATOMIC))
+ return -ENOMEM;
+ }
+
+ if (encaps_data) {
+ memcpy(skb_push(skb, encaps_len), encaps_data, encaps_len);
+ nh_pos += encaps_len;
+ h_pos += encaps_len;
+ }
+
+ memcpy(skb_push(skb, hdrlen), &hdr, hdrlen);
+
+ nh_pos += hdrlen;
+ h_pos += hdrlen;
+
+ /* Update skb pointers to various headers since this modified frame
+ * is going to go through Linux networking code that may potentially
+ * need things like pointer to IP header. */
+ skb_reset_mac_header(skb);
+ skb_set_network_header(skb, nh_pos);
+ skb_set_transport_header(skb, h_pos);
+
+ return 0;
+}
+EXPORT_SYMBOL(ieee80211_data_from_8023);
+
+static void
+__frame_add_frag(struct sk_buff *skb, struct page *page,
+ void *ptr, int len, int size)
+{
+ struct skb_shared_info *sh = skb_shinfo(skb);
+ int page_offset;
+
+ page_ref_inc(page);
+ page_offset = ptr - page_address(page);
+ skb_add_rx_frag(skb, sh->nr_frags, page, page_offset, len, size);
+}
+
+static void
+__ieee80211_amsdu_copy_frag(struct sk_buff *skb, struct sk_buff *frame,
+ int offset, int len)
+{
+ struct skb_shared_info *sh = skb_shinfo(skb);
+ const skb_frag_t *frag = &sh->frags[0];
+ struct page *frag_page;
+ void *frag_ptr;
+ int frag_len, frag_size;
+ int head_size = skb->len - skb->data_len;
+ int cur_len;
+
+ frag_page = virt_to_head_page(skb->head);
+ frag_ptr = skb->data;
+ frag_size = head_size;
+
+ while (offset >= frag_size) {
+ offset -= frag_size;
+ frag_page = skb_frag_page(frag);
+ frag_ptr = skb_frag_address(frag);
+ frag_size = skb_frag_size(frag);
+ frag++;
+ }
+
+ frag_ptr += offset;
+ frag_len = frag_size - offset;
+
+ cur_len = min(len, frag_len);
+
+ __frame_add_frag(frame, frag_page, frag_ptr, cur_len, frag_size);
+ len -= cur_len;
+
+ while (len > 0) {
+ frag_len = skb_frag_size(frag);
+ cur_len = min(len, frag_len);
+ __frame_add_frag(frame, skb_frag_page(frag),
+ skb_frag_address(frag), cur_len, frag_len);
+ len -= cur_len;
+ frag++;
+ }
+}
+
+static struct sk_buff *
+__ieee80211_amsdu_copy(struct sk_buff *skb, unsigned int hlen,
+ int offset, int len, bool reuse_frag)
+{
+ struct sk_buff *frame;
+ int cur_len = len;
+
+ if (skb->len - offset < len)
+ return NULL;
+
+ /*
+ * When reusing framents, copy some data to the head to simplify
+ * ethernet header handling and speed up protocol header processing
+ * in the stack later.
+ */
+ if (reuse_frag)
+ cur_len = min_t(int, len, 32);
+
+ /*
+ * Allocate and reserve two bytes more for payload
+ * alignment since sizeof(struct ethhdr) is 14.
+ */
+ frame = dev_alloc_skb(hlen + sizeof(struct ethhdr) + 2 + cur_len);
+ if (!frame)
+ return NULL;
+
+ skb_reserve(frame, hlen + sizeof(struct ethhdr) + 2);
+ skb_copy_bits(skb, offset, skb_put(frame, cur_len), cur_len);
+
+ len -= cur_len;
+ if (!len)
+ return frame;
+
+ offset += cur_len;
+ __ieee80211_amsdu_copy_frag(skb, frame, offset, len);
+
+ return frame;
+}
+
+void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list,
+ const u8 *addr, enum nl80211_iftype iftype,
+ const unsigned int extra_headroom,
+ const u8 *check_da, const u8 *check_sa)
+{
+ unsigned int hlen = ALIGN(extra_headroom, 4);
+ struct sk_buff *frame = NULL;
+ u16 ethertype;
+ u8 *payload;
+ int offset = 0, remaining;
+ struct ethhdr eth;
+#if LINUX_VERSION_IS_LESS(3,5,0)
+ bool reuse_frag = 0;
+#else
+ bool reuse_frag = skb->head_frag && !skb_has_frag_list(skb);
+#endif
+ bool reuse_skb = false;
+ bool last = false;
+
+ while (!last) {
+ unsigned int subframe_len;
+ int len;
+ u8 padding;
+
+ skb_copy_bits(skb, offset, ð, sizeof(eth));
+ len = ntohs(eth.h_proto);
+ subframe_len = sizeof(struct ethhdr) + len;
+ padding = (4 - subframe_len) & 0x3;
+
+ /* the last MSDU has no padding */
+ remaining = skb->len - offset;
+ if (subframe_len > remaining)
+ goto purge;
+
+ offset += sizeof(struct ethhdr);
+ last = remaining <= subframe_len + padding;
+
+ /* FIXME: should we really accept multicast DA? */
+ if ((check_da && !is_multicast_ether_addr(eth.h_dest) &&
+ !ether_addr_equal(check_da, eth.h_dest)) ||
+ (check_sa && !ether_addr_equal(check_sa, eth.h_source))) {
+ offset += len + padding;
+ continue;
+ }
+
+ /* reuse skb for the last subframe */
+ if (!skb_is_nonlinear(skb) && !reuse_frag && last) {
+ skb_pull(skb, offset);
+ frame = skb;
+ reuse_skb = true;
+ } else {
+ frame = __ieee80211_amsdu_copy(skb, hlen, offset, len,
+ reuse_frag);
+ if (!frame)
+ goto purge;
+
+ offset += len + padding;
+ }
+
+ skb_reset_network_header(frame);
+ frame->dev = skb->dev;
+ frame->priority = skb->priority;
+
+ payload = frame->data;
+ ethertype = (payload[6] << 8) | payload[7];
+ if (likely((ether_addr_equal(payload, rfc1042_header) &&
+ ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) ||
+ ether_addr_equal(payload, bridge_tunnel_header))) {
+ eth.h_proto = htons(ethertype);
+ skb_pull(frame, ETH_ALEN + 2);
+ }
+
+ memcpy(skb_push(frame, sizeof(eth)), ð, sizeof(eth));
+ __skb_queue_tail(list, frame);
+ }
+
+ if (!reuse_skb)
+ dev_kfree_skb(skb);
+
+ return;
+
+ purge:
+ __skb_queue_purge(list);
+ dev_kfree_skb(skb);
+}
+EXPORT_SYMBOL(ieee80211_amsdu_to_8023s);
+
+/* Given a data frame determine the 802.1p/1d tag to use. */
+unsigned int cfg80211_classify8021d(struct sk_buff *skb,
+ struct cfg80211_qos_map *qos_map)
+{
+ unsigned int dscp;
+ unsigned char vlan_priority;
+
+ /* skb->priority values from 256->263 are magic values to
+ * directly indicate a specific 802.1d priority. This is used
+ * to allow 802.1d priority to be passed directly in from VLAN
+ * tags, etc.
+ */
+ if (skb->priority >= 256 && skb->priority <= 263)
+ return skb->priority - 256;
+
+ if (skb_vlan_tag_present(skb)) {
+ vlan_priority = (skb_vlan_tag_get(skb) & VLAN_PRIO_MASK)
+ >> VLAN_PRIO_SHIFT;
+ if (vlan_priority > 0)
+ return vlan_priority;
+ }
+
+ switch (skb->protocol) {
+ case htons(ETH_P_IP):
+ dscp = ipv4_get_dsfield(ip_hdr(skb)) & 0xfc;
+ break;
+ case htons(ETH_P_IPV6):
+ dscp = ipv6_get_dsfield(ipv6_hdr(skb)) & 0xfc;
+ break;
+#if LINUX_VERSION_IS_GEQ(3,15,0)
+ case htons(ETH_P_MPLS_UC):
+ case htons(ETH_P_MPLS_MC): {
+ struct mpls_label mpls_tmp, *mpls;
+
+ mpls = skb_header_pointer(skb, sizeof(struct ethhdr),
+ sizeof(*mpls), &mpls_tmp);
+ if (!mpls)
+ return 0;
+
+ return (ntohl(mpls->entry) & MPLS_LS_TC_MASK)
+ >> MPLS_LS_TC_SHIFT;
+ }
+#endif
+ case htons(ETH_P_80221):
+ /* 802.21 is always network control traffic */
+ return 7;
+ default:
+ return 0;
+ }
+
+ if (qos_map) {
+ unsigned int i, tmp_dscp = dscp >> 2;
+
+ for (i = 0; i < qos_map->num_des; i++) {
+ if (tmp_dscp == qos_map->dscp_exception[i].dscp)
+ return qos_map->dscp_exception[i].up;
+ }
+
+ for (i = 0; i < 8; i++) {
+ if (tmp_dscp >= qos_map->up[i].low &&
+ tmp_dscp <= qos_map->up[i].high)
+ return i;
+ }
+ }
+
+ return dscp >> 5;
+}
+EXPORT_SYMBOL(cfg80211_classify8021d);
+
+const u8 *ieee80211_bss_get_ie(struct cfg80211_bss *bss, u8 ie)
+{
+ const struct cfg80211_bss_ies *ies;
+
+ ies = rcu_dereference(bss->ies);
+ if (!ies)
+ return NULL;
+
+ return cfg80211_find_ie(ie, ies->data, ies->len);
+}
+EXPORT_SYMBOL(ieee80211_bss_get_ie);
+
+void cfg80211_upload_connect_keys(struct wireless_dev *wdev)
+{
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+ struct net_device *dev = wdev->netdev;
+ int i;
+
+ if (!wdev->connect_keys)
+ return;
+
+ for (i = 0; i < CFG80211_MAX_WEP_KEYS; i++) {
+ if (!wdev->connect_keys->params[i].cipher)
+ continue;
+ if (rdev_add_key(rdev, dev, i, false, NULL,
+ &wdev->connect_keys->params[i])) {
+ netdev_err(dev, "failed to set key %d\n", i);
+ continue;
+ }
+ if (wdev->connect_keys->def == i &&
+ rdev_set_default_key(rdev, dev, i, true, true)) {
+ netdev_err(dev, "failed to set defkey %d\n", i);
+ continue;
+ }
+ }
+
+ kzfree(wdev->connect_keys);
+ wdev->connect_keys = NULL;
+}
+
+void cfg80211_process_wdev_events(struct wireless_dev *wdev)
+{
+ struct cfg80211_event *ev;
+ unsigned long flags;
+
+ spin_lock_irqsave(&wdev->event_lock, flags);
+ while (!list_empty(&wdev->event_list)) {
+ ev = list_first_entry(&wdev->event_list,
+ struct cfg80211_event, list);
+ list_del(&ev->list);
+ spin_unlock_irqrestore(&wdev->event_lock, flags);
+
+ wdev_lock(wdev);
+ switch (ev->type) {
+ case EVENT_CONNECT_RESULT:
+ __cfg80211_connect_result(
+ wdev->netdev,
+ &ev->cr,
+ ev->cr.status == WLAN_STATUS_SUCCESS);
+ break;
+ case EVENT_ROAMED:
+ __cfg80211_roamed(wdev, &ev->rm);
+ break;
+ case EVENT_DISCONNECTED:
+ __cfg80211_disconnected(wdev->netdev,
+ ev->dc.ie, ev->dc.ie_len,
+ ev->dc.reason,
+ !ev->dc.locally_generated);
+ break;
+ case EVENT_IBSS_JOINED:
+ __cfg80211_ibss_joined(wdev->netdev, ev->ij.bssid,
+ ev->ij.channel);
+ break;
+ case EVENT_STOPPED:
+ __cfg80211_leave(wiphy_to_rdev(wdev->wiphy), wdev);
+ break;
+ }
+ wdev_unlock(wdev);
+
+ kfree(ev);
+
+ spin_lock_irqsave(&wdev->event_lock, flags);
+ }
+ spin_unlock_irqrestore(&wdev->event_lock, flags);
+}
+
+void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev)
+{
+ struct wireless_dev *wdev;
+
+ ASSERT_RTNL();
+
+ list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list)
+ cfg80211_process_wdev_events(wdev);
+}
+
+int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, enum nl80211_iftype ntype,
+ struct vif_params *params)
+{
+ int err;
+ enum nl80211_iftype otype = dev->ieee80211_ptr->iftype;
+
+ ASSERT_RTNL();
+
+ /* don't support changing VLANs, you just re-create them */
+ if (otype == NL80211_IFTYPE_AP_VLAN)
+ return -EOPNOTSUPP;
+
+ /* cannot change into P2P device or NAN */
+ if (ntype == NL80211_IFTYPE_P2P_DEVICE ||
+ ntype == NL80211_IFTYPE_NAN)
+ return -EOPNOTSUPP;
+
+ if (!rdev->ops->change_virtual_intf ||
+ !(rdev->wiphy.interface_modes & (1 << ntype)))
+ return -EOPNOTSUPP;
+
+ /* if it's part of a bridge, reject changing type to station/ibss */
+ if ((dev->priv_flags & IFF_BRIDGE_PORT) &&
+ (ntype == NL80211_IFTYPE_ADHOC ||
+ ntype == NL80211_IFTYPE_STATION ||
+ ntype == NL80211_IFTYPE_P2P_CLIENT))
+ return -EBUSY;
+
+ if (ntype != otype) {
+ dev->ieee80211_ptr->use_4addr = false;
+ dev->ieee80211_ptr->mesh_id_up_len = 0;
+ wdev_lock(dev->ieee80211_ptr);
+ rdev_set_qos_map(rdev, dev, NULL);
+ wdev_unlock(dev->ieee80211_ptr);
+
+ switch (otype) {
+ case NL80211_IFTYPE_AP:
+ cfg80211_stop_ap(rdev, dev, true);
+ break;
+ case NL80211_IFTYPE_ADHOC:
+ cfg80211_leave_ibss(rdev, dev, false);
+ break;
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_P2P_CLIENT:
+ wdev_lock(dev->ieee80211_ptr);
+ cfg80211_disconnect(rdev, dev,
+ WLAN_REASON_DEAUTH_LEAVING, true);
+ wdev_unlock(dev->ieee80211_ptr);
+ break;
+ case NL80211_IFTYPE_MESH_POINT:
+ /* mesh should be handled? */
+ break;
+ default:
+ break;
+ }
+
+ cfg80211_process_rdev_events(rdev);
+ }
+
+ err = rdev_change_virtual_intf(rdev, dev, ntype, params);
+
+ WARN_ON(!err && dev->ieee80211_ptr->iftype != ntype);
+
+ if (!err && params && params->use_4addr != -1)
+ dev->ieee80211_ptr->use_4addr = params->use_4addr;
+
+ if (!err) {
+ dev->priv_flags &= ~IFF_DONT_BRIDGE;
+ switch (ntype) {
+ case NL80211_IFTYPE_STATION:
+ if (dev->ieee80211_ptr->use_4addr)
+ break;
+ /* fall through */
+ case NL80211_IFTYPE_OCB:
+ case NL80211_IFTYPE_P2P_CLIENT:
+ case NL80211_IFTYPE_ADHOC:
+ dev->priv_flags |= IFF_DONT_BRIDGE;
+ break;
+ case NL80211_IFTYPE_P2P_GO:
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_AP_VLAN:
+ case NL80211_IFTYPE_WDS:
+ case NL80211_IFTYPE_MESH_POINT:
+ /* bridging OK */
+ break;
+ case NL80211_IFTYPE_MONITOR:
+ /* monitor can't bridge anyway */
+ break;
+ case NL80211_IFTYPE_UNSPECIFIED:
+ case NUM_NL80211_IFTYPES:
+ /* not happening */
+ break;
+ case NL80211_IFTYPE_P2P_DEVICE:
+ case NL80211_IFTYPE_NAN:
+ WARN_ON(1);
+ break;
+ }
+ }
+
+ if (!err && ntype != otype && netif_running(dev)) {
+ cfg80211_update_iface_num(rdev, ntype, 1);
+ cfg80211_update_iface_num(rdev, otype, -1);
+ }
+
+ return err;
+}
+
+static u32 cfg80211_calculate_bitrate_ht(struct rate_info *rate)
+{
+ int modulation, streams, bitrate;
+
+ /* the formula below does only work for MCS values smaller than 32 */
+ if (WARN_ON_ONCE(rate->mcs >= 32))
+ return 0;
+
+ modulation = rate->mcs & 7;
+ streams = (rate->mcs >> 3) + 1;
+
+ bitrate = (rate->bw == RATE_INFO_BW_40) ? 13500000 : 6500000;
+
+ if (modulation < 4)
+ bitrate *= (modulation + 1);
+ else if (modulation == 4)
+ bitrate *= (modulation + 2);
+ else
+ bitrate *= (modulation + 3);
+
+ bitrate *= streams;
+
+ if (rate->flags & RATE_INFO_FLAGS_SHORT_GI)
+ bitrate = (bitrate / 9) * 10;
+
+ /* do NOT round down here */
+ return (bitrate + 50000) / 100000;
+}
+
+static u32 cfg80211_calculate_bitrate_60g(struct rate_info *rate)
+{
+ static const u32 __mcs2bitrate[] = {
+ /* control PHY */
+ [0] = 275,
+ /* SC PHY */
+ [1] = 3850,
+ [2] = 7700,
+ [3] = 9625,
+ [4] = 11550,
+ [5] = 12512, /* 1251.25 mbps */
+ [6] = 15400,
+ [7] = 19250,
+ [8] = 23100,
+ [9] = 25025,
+ [10] = 30800,
+ [11] = 38500,
+ [12] = 46200,
+ /* OFDM PHY */
+ [13] = 6930,
+ [14] = 8662, /* 866.25 mbps */
+ [15] = 13860,
+ [16] = 17325,
+ [17] = 20790,
+ [18] = 27720,
+ [19] = 34650,
+ [20] = 41580,
+ [21] = 45045,
+ [22] = 51975,
+ [23] = 62370,
+ [24] = 67568, /* 6756.75 mbps */
+ /* LP-SC PHY */
+ [25] = 6260,
+ [26] = 8340,
+ [27] = 11120,
+ [28] = 12510,
+ [29] = 16680,
+ [30] = 22240,
+ [31] = 25030,
+ };
+
+ if (WARN_ON_ONCE(rate->mcs >= ARRAY_SIZE(__mcs2bitrate)))
+ return 0;
+
+ return __mcs2bitrate[rate->mcs];
+}
+
+static u32 cfg80211_calculate_bitrate_vht(struct rate_info *rate)
+{
+ static const u32 base[4][10] = {
+ { 6500000,
+ 13000000,
+ 19500000,
+ 26000000,
+ 39000000,
+ 52000000,
+ 58500000,
+ 65000000,
+ 78000000,
+ /* not in the spec, but some devices use this: */
+ 86500000,
+ },
+ { 13500000,
+ 27000000,
+ 40500000,
+ 54000000,
+ 81000000,
+ 108000000,
+ 121500000,
+ 135000000,
+ 162000000,
+ 180000000,
+ },
+ { 29300000,
+ 58500000,
+ 87800000,
+ 117000000,
+ 175500000,
+ 234000000,
+ 263300000,
+ 292500000,
+ 351000000,
+ 390000000,
+ },
+ { 58500000,
+ 117000000,
+ 175500000,
+ 234000000,
+ 351000000,
+ 468000000,
+ 526500000,
+ 585000000,
+ 702000000,
+ 780000000,
+ },
+ };
+ u32 bitrate;
+ int idx;
+
+ if (WARN_ON_ONCE(rate->mcs > 9))
+ return 0;
+
+ switch (rate->bw) {
+ case RATE_INFO_BW_160:
+ idx = 3;
+ break;
+ case RATE_INFO_BW_80:
+ idx = 2;
+ break;
+ case RATE_INFO_BW_40:
+ idx = 1;
+ break;
+ case RATE_INFO_BW_5:
+ case RATE_INFO_BW_10:
+ default:
+ WARN_ON(1);
+ /* fall through */
+ case RATE_INFO_BW_20:
+ idx = 0;
+ }
+
+ bitrate = base[idx][rate->mcs];
+ bitrate *= rate->nss;
+
+ if (rate->flags & RATE_INFO_FLAGS_SHORT_GI)
+ bitrate = (bitrate / 9) * 10;
+
+ /* do NOT round down here */
+ return (bitrate + 50000) / 100000;
+}
+
+u32 cfg80211_calculate_bitrate(struct rate_info *rate)
+{
+ if (rate->flags & RATE_INFO_FLAGS_MCS)
+ return cfg80211_calculate_bitrate_ht(rate);
+ if (rate->flags & RATE_INFO_FLAGS_60G)
+ return cfg80211_calculate_bitrate_60g(rate);
+ if (rate->flags & RATE_INFO_FLAGS_VHT_MCS)
+ return cfg80211_calculate_bitrate_vht(rate);
+
+ return rate->legacy;
+}
+EXPORT_SYMBOL(cfg80211_calculate_bitrate);
+
+int cfg80211_get_p2p_attr(const u8 *ies, unsigned int len,
+ enum ieee80211_p2p_attr_id attr,
+ u8 *buf, unsigned int bufsize)
+{
+ u8 *out = buf;
+ u16 attr_remaining = 0;
+ bool desired_attr = false;
+ u16 desired_len = 0;
+
+ while (len > 0) {
+ unsigned int iedatalen;
+ unsigned int copy;
+ const u8 *iedata;
+
+ if (len < 2)
+ return -EILSEQ;
+ iedatalen = ies[1];
+ if (iedatalen + 2 > len)
+ return -EILSEQ;
+
+ if (ies[0] != WLAN_EID_VENDOR_SPECIFIC)
+ goto cont;
+
+ if (iedatalen < 4)
+ goto cont;
+
+ iedata = ies + 2;
+
+ /* check WFA OUI, P2P subtype */
+ if (iedata[0] != 0x50 || iedata[1] != 0x6f ||
+ iedata[2] != 0x9a || iedata[3] != 0x09)
+ goto cont;
+
+ iedatalen -= 4;
+ iedata += 4;
+
+ /* check attribute continuation into this IE */
+ copy = min_t(unsigned int, attr_remaining, iedatalen);
+ if (copy && desired_attr) {
+ desired_len += copy;
+ if (out) {
+ memcpy(out, iedata, min(bufsize, copy));
+ out += min(bufsize, copy);
+ bufsize -= min(bufsize, copy);
+ }
+
+
+ if (copy == attr_remaining)
+ return desired_len;
+ }
+
+ attr_remaining -= copy;
+ if (attr_remaining)
+ goto cont;
+
+ iedatalen -= copy;
+ iedata += copy;
+
+ while (iedatalen > 0) {
+ u16 attr_len;
+
+ /* P2P attribute ID & size must fit */
+ if (iedatalen < 3)
+ return -EILSEQ;
+ desired_attr = iedata[0] == attr;
+ attr_len = get_unaligned_le16(iedata + 1);
+ iedatalen -= 3;
+ iedata += 3;
+
+ copy = min_t(unsigned int, attr_len, iedatalen);
+
+ if (desired_attr) {
+ desired_len += copy;
+ if (out) {
+ memcpy(out, iedata, min(bufsize, copy));
+ out += min(bufsize, copy);
+ bufsize -= min(bufsize, copy);
+ }
+
+ if (copy == attr_len)
+ return desired_len;
+ }
+
+ iedata += copy;
+ iedatalen -= copy;
+ attr_remaining = attr_len - copy;
+ }
+
+ cont:
+ len -= ies[1] + 2;
+ ies += ies[1] + 2;
+ }
+
+ if (attr_remaining && desired_attr)
+ return -EILSEQ;
+
+ return -ENOENT;
+}
+EXPORT_SYMBOL(cfg80211_get_p2p_attr);
+
+static bool ieee80211_id_in_list(const u8 *ids, int n_ids, u8 id)
+{
+ int i;
+
+ for (i = 0; i < n_ids; i++)
+ if (ids[i] == id)
+ return true;
+ return false;
+}
+
+static size_t skip_ie(const u8 *ies, size_t ielen, size_t pos)
+{
+ /* we assume a validly formed IEs buffer */
+ u8 len = ies[pos + 1];
+
+ pos += 2 + len;
+
+ /* the IE itself must have 255 bytes for fragments to follow */
+ if (len < 255)
+ return pos;
+
+ while (pos < ielen && ies[pos] == WLAN_EID_FRAGMENT) {
+ len = ies[pos + 1];
+ pos += 2 + len;
+ }
+
+ return pos;
+}
+
+size_t ieee80211_ie_split_ric(const u8 *ies, size_t ielen,
+ const u8 *ids, int n_ids,
+ const u8 *after_ric, int n_after_ric,
+ size_t offset)
+{
+ size_t pos = offset;
+
+ while (pos < ielen && ieee80211_id_in_list(ids, n_ids, ies[pos])) {
+ if (ies[pos] == WLAN_EID_RIC_DATA && n_after_ric) {
+ pos = skip_ie(ies, ielen, pos);
+
+ while (pos < ielen &&
+ !ieee80211_id_in_list(after_ric, n_after_ric,
+ ies[pos]))
+ pos = skip_ie(ies, ielen, pos);
+ } else {
+ pos = skip_ie(ies, ielen, pos);
+ }
+ }
+
+ return pos;
+}
+EXPORT_SYMBOL(ieee80211_ie_split_ric);
+
+bool ieee80211_operating_class_to_band(u8 operating_class,
+ enum nl80211_band *band)
+{
+ switch (operating_class) {
+ case 112:
+ case 115 ... 127:
+ case 128 ... 130:
+ *band = NL80211_BAND_5GHZ;
+ return true;
+ case 81:
+ case 82:
+ case 83:
+ case 84:
+ *band = NL80211_BAND_2GHZ;
+ return true;
+ case 180:
+ *band = NL80211_BAND_60GHZ;
+ return true;
+ }
+
+ return false;
+}
+EXPORT_SYMBOL(ieee80211_operating_class_to_band);
+
+bool ieee80211_chandef_to_operating_class(struct cfg80211_chan_def *chandef,
+ u8 *op_class)
+{
+ u8 vht_opclass;
+ u16 freq = chandef->center_freq1;
+
+ if (freq >= 2412 && freq <= 2472) {
+ if (chandef->width > NL80211_CHAN_WIDTH_40)
+ return false;
+
+ /* 2.407 GHz, channels 1..13 */
+ if (chandef->width == NL80211_CHAN_WIDTH_40) {
+ if (freq > chandef->chan->center_freq)
+ *op_class = 83; /* HT40+ */
+ else
+ *op_class = 84; /* HT40- */
+ } else {
+ *op_class = 81;
+ }
+
+ return true;
+ }
+
+ if (freq == 2484) {
+ if (chandef->width > NL80211_CHAN_WIDTH_40)
+ return false;
+
+ *op_class = 82; /* channel 14 */
+ return true;
+ }
+
+ switch (chandef->width) {
+ case NL80211_CHAN_WIDTH_80:
+ vht_opclass = 128;
+ break;
+ case NL80211_CHAN_WIDTH_160:
+ vht_opclass = 129;
+ break;
+ case NL80211_CHAN_WIDTH_80P80:
+ vht_opclass = 130;
+ break;
+ case NL80211_CHAN_WIDTH_10:
+ case NL80211_CHAN_WIDTH_5:
+ return false; /* unsupported for now */
+ default:
+ vht_opclass = 0;
+ break;
+ }
+
+ /* 5 GHz, channels 36..48 */
+ if (freq >= 5180 && freq <= 5240) {
+ if (vht_opclass) {
+ *op_class = vht_opclass;
+ } else if (chandef->width == NL80211_CHAN_WIDTH_40) {
+ if (freq > chandef->chan->center_freq)
+ *op_class = 116;
+ else
+ *op_class = 117;
+ } else {
+ *op_class = 115;
+ }
+
+ return true;
+ }
+
+ /* 5 GHz, channels 52..64 */
+ if (freq >= 5260 && freq <= 5320) {
+ if (vht_opclass) {
+ *op_class = vht_opclass;
+ } else if (chandef->width == NL80211_CHAN_WIDTH_40) {
+ if (freq > chandef->chan->center_freq)
+ *op_class = 119;
+ else
+ *op_class = 120;
+ } else {
+ *op_class = 118;
+ }
+
+ return true;
+ }
+
+ /* 5 GHz, channels 100..144 */
+ if (freq >= 5500 && freq <= 5720) {
+ if (vht_opclass) {
+ *op_class = vht_opclass;
+ } else if (chandef->width == NL80211_CHAN_WIDTH_40) {
+ if (freq > chandef->chan->center_freq)
+ *op_class = 122;
+ else
+ *op_class = 123;
+ } else {
+ *op_class = 121;
+ }
+
+ return true;
+ }
+
+ /* 5 GHz, channels 149..169 */
+ if (freq >= 5745 && freq <= 5845) {
+ if (vht_opclass) {
+ *op_class = vht_opclass;
+ } else if (chandef->width == NL80211_CHAN_WIDTH_40) {
+ if (freq > chandef->chan->center_freq)
+ *op_class = 126;
+ else
+ *op_class = 127;
+ } else if (freq <= 5805) {
+ *op_class = 124;
+ } else {
+ *op_class = 125;
+ }
+
+ return true;
+ }
+
+ /* 56.16 GHz, channel 1..4 */
+ if (freq >= 56160 + 2160 * 1 && freq <= 56160 + 2160 * 4) {
+ if (chandef->width >= NL80211_CHAN_WIDTH_40)
+ return false;
+
+ *op_class = 180;
+ return true;
+ }
+
+ /* not supported yet */
+ return false;
+}
+EXPORT_SYMBOL(ieee80211_chandef_to_operating_class);
+
+static void cfg80211_calculate_bi_data(struct wiphy *wiphy, u32 new_beacon_int,
+ u32 *beacon_int_gcd,
+ bool *beacon_int_different)
+{
+ struct wireless_dev *wdev;
+
+ *beacon_int_gcd = 0;
+ *beacon_int_different = false;
+
+ list_for_each_entry(wdev, &wiphy->wdev_list, list) {
+ if (!wdev->beacon_interval)
+ continue;
+
+ if (!*beacon_int_gcd) {
+ *beacon_int_gcd = wdev->beacon_interval;
+ continue;
+ }
+
+ if (wdev->beacon_interval == *beacon_int_gcd)
+ continue;
+
+ *beacon_int_different = true;
+ *beacon_int_gcd = gcd(*beacon_int_gcd, wdev->beacon_interval);
+ }
+
+ if (new_beacon_int && *beacon_int_gcd != new_beacon_int) {
+ if (*beacon_int_gcd)
+ *beacon_int_different = true;
+ *beacon_int_gcd = gcd(*beacon_int_gcd, new_beacon_int);
+ }
+}
+
+int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev,
+ enum nl80211_iftype iftype, u32 beacon_int)
+{
+ /*
+ * This is just a basic pre-condition check; if interface combinations
+ * are possible the driver must already be checking those with a call
+ * to cfg80211_check_combinations(), in which case we'll validate more
+ * through the cfg80211_calculate_bi_data() call and code in
+ * cfg80211_iter_combinations().
+ */
+
+ if (beacon_int < 10 || beacon_int > 10000)
+ return -EINVAL;
+
+ return 0;
+}
+
+int cfg80211_iter_combinations(struct wiphy *wiphy,
+ struct iface_combination_params *params,
+ void (*iter)(const struct ieee80211_iface_combination *c,
+ void *data),
+ void *data)
+{
+ const struct ieee80211_regdomain *regdom;
+ enum nl80211_dfs_regions region = 0;
+ int i, j, iftype;
+ int num_interfaces = 0;
+ u32 used_iftypes = 0;
+ u32 beacon_int_gcd;
+ bool beacon_int_different;
+
+ /*
+ * This is a bit strange, since the iteration used to rely only on
+ * the data given by the driver, but here it now relies on context,
+ * in form of the currently operating interfaces.
+ * This is OK for all current users, and saves us from having to
+ * push the GCD calculations into all the drivers.
+ * In the future, this should probably rely more on data that's in
+ * cfg80211 already - the only thing not would appear to be any new
+ * interfaces (while being brought up) and channel/radar data.
+ */
+ cfg80211_calculate_bi_data(wiphy, params->new_beacon_int,
+ &beacon_int_gcd, &beacon_int_different);
+
+ if (params->radar_detect) {
+ rcu_read_lock();
+ regdom = rcu_dereference(cfg80211_regdomain);
+ if (regdom)
+ region = regdom->dfs_region;
+ rcu_read_unlock();
+ }
+
+ for (iftype = 0; iftype < NUM_NL80211_IFTYPES; iftype++) {
+ num_interfaces += params->iftype_num[iftype];
+ if (params->iftype_num[iftype] > 0 &&
+ !(wiphy->software_iftypes & BIT(iftype)))
+ used_iftypes |= BIT(iftype);
+ }
+
+ for (i = 0; i < wiphy->n_iface_combinations; i++) {
+ const struct ieee80211_iface_combination *c;
+ struct ieee80211_iface_limit *limits;
+ u32 all_iftypes = 0;
+
+ c = &wiphy->iface_combinations[i];
+
+ if (num_interfaces > c->max_interfaces)
+ continue;
+ if (params->num_different_channels > c->num_different_channels)
+ continue;
+
+ limits = kmemdup(c->limits, sizeof(limits[0]) * c->n_limits,
+ GFP_KERNEL);
+ if (!limits)
+ return -ENOMEM;
+
+ for (iftype = 0; iftype < NUM_NL80211_IFTYPES; iftype++) {
+ if (wiphy->software_iftypes & BIT(iftype))
+ continue;
+ for (j = 0; j < c->n_limits; j++) {
+ all_iftypes |= limits[j].types;
+ if (!(limits[j].types & BIT(iftype)))
+ continue;
+ if (limits[j].max < params->iftype_num[iftype])
+ goto cont;
+ limits[j].max -= params->iftype_num[iftype];
+ }
+ }
+
+ if (params->radar_detect !=
+ (c->radar_detect_widths & params->radar_detect))
+ goto cont;
+
+ if (params->radar_detect && c->radar_detect_regions &&
+ !(c->radar_detect_regions & BIT(region)))
+ goto cont;
+
+ /* Finally check that all iftypes that we're currently
+ * using are actually part of this combination. If they
+ * aren't then we can't use this combination and have
+ * to continue to the next.
+ */
+ if ((all_iftypes & used_iftypes) != used_iftypes)
+ goto cont;
+
+ if (beacon_int_gcd) {
+ if (c->beacon_int_min_gcd &&
+ beacon_int_gcd < c->beacon_int_min_gcd)
+ goto cont;
+ if (!c->beacon_int_min_gcd && beacon_int_different)
+ goto cont;
+ }
+
+ /* This combination covered all interface types and
+ * supported the requested numbers, so we're good.
+ */
+
+ (*iter)(c, data);
+ cont:
+ kfree(limits);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(cfg80211_iter_combinations);
+
+static void
+cfg80211_iter_sum_ifcombs(const struct ieee80211_iface_combination *c,
+ void *data)
+{
+ int *num = data;
+ (*num)++;
+}
+
+int cfg80211_check_combinations(struct wiphy *wiphy,
+ struct iface_combination_params *params)
+{
+ int err, num = 0;
+
+ err = cfg80211_iter_combinations(wiphy, params,
+ cfg80211_iter_sum_ifcombs, &num);
+ if (err)
+ return err;
+ if (num == 0)
+ return -EBUSY;
+
+ return 0;
+}
+EXPORT_SYMBOL(cfg80211_check_combinations);
+
+int ieee80211_get_ratemask(struct ieee80211_supported_band *sband,
+ const u8 *rates, unsigned int n_rates,
+ u32 *mask)
+{
+ int i, j;
+
+ if (!sband)
+ return -EINVAL;
+
+ if (n_rates == 0 || n_rates > NL80211_MAX_SUPP_RATES)
+ return -EINVAL;
+
+ *mask = 0;
+
+ for (i = 0; i < n_rates; i++) {
+ int rate = (rates[i] & 0x7f) * 5;
+ bool found = false;
+
+ for (j = 0; j < sband->n_bitrates; j++) {
+ if (sband->bitrates[j].bitrate == rate) {
+ found = true;
+ *mask |= BIT(j);
+ break;
+ }
+ }
+ if (!found)
+ return -EINVAL;
+ }
+
+ /*
+ * mask must have at least one bit set here since we
+ * didn't accept a 0-length rates array nor allowed
+ * entries in the array that didn't exist
+ */
+
+ return 0;
+}
+
+unsigned int ieee80211_get_num_supported_channels(struct wiphy *wiphy)
+{
+ enum nl80211_band band;
+ unsigned int n_channels = 0;
+
+ for (band = 0; band < NUM_NL80211_BANDS; band++)
+ if (wiphy->bands[band])
+ n_channels += wiphy->bands[band]->n_channels;
+
+ return n_channels;
+}
+EXPORT_SYMBOL(ieee80211_get_num_supported_channels);
+
+int cfg80211_get_station(struct net_device *dev, const u8 *mac_addr,
+ struct station_info *sinfo)
+{
+ struct cfg80211_registered_device *rdev;
+ struct wireless_dev *wdev;
+
+ wdev = dev->ieee80211_ptr;
+ if (!wdev)
+ return -EOPNOTSUPP;
+
+ rdev = wiphy_to_rdev(wdev->wiphy);
+ if (!rdev->ops->get_station)
+ return -EOPNOTSUPP;
+
+ return rdev_get_station(rdev, dev, mac_addr, sinfo);
+}
+EXPORT_SYMBOL(cfg80211_get_station);
+
+void cfg80211_free_nan_func(struct cfg80211_nan_func *f)
+{
+ int i;
+
+ if (!f)
+ return;
+
+ kfree(f->serv_spec_info);
+ kfree(f->srf_bf);
+ kfree(f->srf_macs);
+ for (i = 0; i < f->num_rx_filters; i++)
+ kfree(f->rx_filters[i].filter);
+
+ for (i = 0; i < f->num_tx_filters; i++)
+ kfree(f->tx_filters[i].filter);
+
+ kfree(f->rx_filters);
+ kfree(f->tx_filters);
+ kfree(f);
+}
+EXPORT_SYMBOL(cfg80211_free_nan_func);
+
+bool cfg80211_does_bw_fit_range(const struct ieee80211_freq_range *freq_range,
+ u32 center_freq_khz, u32 bw_khz)
+{
+ u32 start_freq_khz, end_freq_khz;
+
+ start_freq_khz = center_freq_khz - (bw_khz / 2);
+ end_freq_khz = center_freq_khz + (bw_khz / 2);
+
+ if (start_freq_khz >= freq_range->start_freq_khz &&
+ end_freq_khz <= freq_range->end_freq_khz)
+ return true;
+
+ return false;
+}
+
+/* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */
+/* Ethernet-II snap header (RFC1042 for most EtherTypes) */
+const unsigned char rfc1042_header[] __aligned(2) =
+ { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
+EXPORT_SYMBOL(rfc1042_header);
+
+/* Bridge-Tunnel header (for EtherTypes ETH_P_AARP and ETH_P_IPX) */
+const unsigned char bridge_tunnel_header[] __aligned(2) =
+ { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 };
+EXPORT_SYMBOL(bridge_tunnel_header);
diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c
new file mode 100644
index 0000000..5d4a02c
--- /dev/null
+++ b/net/wireless/wext-compat.c
@@ -0,0 +1,1509 @@
+/*
+ * cfg80211 - wext compat code
+ *
+ * This is temporary code until all wireless functionality is migrated
+ * into cfg80211, when that happens all the exports here go away and
+ * we directly assign the wireless handlers of wireless interfaces.
+ *
+ * Copyright 2008-2009 Johannes Berg <johannes@sipsolutions.net>
+ */
+
+#include <linux/export.h>
+#include <linux/wireless.h>
+#include <linux/nl80211.h>
+#include <linux/if_arp.h>
+#include <linux/etherdevice.h>
+#include <linux/slab.h>
+#include <net/iw_handler.h>
+#include <net/cfg80211.h>
+#include <net/cfg80211-wext.h>
+#include "wext-compat.h"
+#include "core.h"
+#include "rdev-ops.h"
+
+int cfg80211_wext_giwname(struct net_device *dev,
+ struct iw_request_info *info,
+ char *name, char *extra)
+{
+ strcpy(name, "IEEE 802.11");
+ return 0;
+}
+EXPORT_WEXT_HANDLER(cfg80211_wext_giwname);
+
+int cfg80211_wext_siwmode(struct net_device *dev, struct iw_request_info *info,
+ u32 *mode, char *extra)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_registered_device *rdev;
+ struct vif_params vifparams;
+ enum nl80211_iftype type;
+
+ rdev = wiphy_to_rdev(wdev->wiphy);
+
+ switch (*mode) {
+ case IW_MODE_INFRA:
+ type = NL80211_IFTYPE_STATION;
+ break;
+ case IW_MODE_ADHOC:
+ type = NL80211_IFTYPE_ADHOC;
+ break;
+ case IW_MODE_REPEAT:
+ type = NL80211_IFTYPE_WDS;
+ break;
+ case IW_MODE_MONITOR:
+ type = NL80211_IFTYPE_MONITOR;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (type == wdev->iftype)
+ return 0;
+
+ memset(&vifparams, 0, sizeof(vifparams));
+
+ return cfg80211_change_iface(rdev, dev, type, &vifparams);
+}
+EXPORT_WEXT_HANDLER(cfg80211_wext_siwmode);
+
+int cfg80211_wext_giwmode(struct net_device *dev, struct iw_request_info *info,
+ u32 *mode, char *extra)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+
+ if (!wdev)
+ return -EOPNOTSUPP;
+
+ switch (wdev->iftype) {
+ case NL80211_IFTYPE_AP:
+ *mode = IW_MODE_MASTER;
+ break;
+ case NL80211_IFTYPE_STATION:
+ *mode = IW_MODE_INFRA;
+ break;
+ case NL80211_IFTYPE_ADHOC:
+ *mode = IW_MODE_ADHOC;
+ break;
+ case NL80211_IFTYPE_MONITOR:
+ *mode = IW_MODE_MONITOR;
+ break;
+ case NL80211_IFTYPE_WDS:
+ *mode = IW_MODE_REPEAT;
+ break;
+ case NL80211_IFTYPE_AP_VLAN:
+ *mode = IW_MODE_SECOND; /* FIXME */
+ break;
+ default:
+ *mode = IW_MODE_AUTO;
+ break;
+ }
+ return 0;
+}
+EXPORT_WEXT_HANDLER(cfg80211_wext_giwmode);
+
+
+int cfg80211_wext_giwrange(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *data, char *extra)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct iw_range *range = (struct iw_range *) extra;
+ enum nl80211_band band;
+ int i, c = 0;
+
+ if (!wdev)
+ return -EOPNOTSUPP;
+
+ data->length = sizeof(struct iw_range);
+ memset(range, 0, sizeof(struct iw_range));
+
+ range->we_version_compiled = WIRELESS_EXT;
+ range->we_version_source = 21;
+ range->retry_capa = IW_RETRY_LIMIT;
+ range->retry_flags = IW_RETRY_LIMIT;
+ range->min_retry = 0;
+ range->max_retry = 255;
+ range->min_rts = 0;
+ range->max_rts = 2347;
+ range->min_frag = 256;
+ range->max_frag = 2346;
+
+ range->max_encoding_tokens = 4;
+
+ range->max_qual.updated = IW_QUAL_NOISE_INVALID;
+
+ switch (wdev->wiphy->signal_type) {
+ case CFG80211_SIGNAL_TYPE_NONE:
+ break;
+ case CFG80211_SIGNAL_TYPE_MBM:
+ range->max_qual.level = (u8)-110;
+ range->max_qual.qual = 70;
+ range->avg_qual.qual = 35;
+ range->max_qual.updated |= IW_QUAL_DBM;
+ range->max_qual.updated |= IW_QUAL_QUAL_UPDATED;
+ range->max_qual.updated |= IW_QUAL_LEVEL_UPDATED;
+ break;
+ case CFG80211_SIGNAL_TYPE_UNSPEC:
+ range->max_qual.level = 100;
+ range->max_qual.qual = 100;
+ range->avg_qual.qual = 50;
+ range->max_qual.updated |= IW_QUAL_QUAL_UPDATED;
+ range->max_qual.updated |= IW_QUAL_LEVEL_UPDATED;
+ break;
+ }
+
+ range->avg_qual.level = range->max_qual.level / 2;
+ range->avg_qual.noise = range->max_qual.noise / 2;
+ range->avg_qual.updated = range->max_qual.updated;
+
+ for (i = 0; i < wdev->wiphy->n_cipher_suites; i++) {
+ switch (wdev->wiphy->cipher_suites[i]) {
+ case WLAN_CIPHER_SUITE_TKIP:
+ range->enc_capa |= (IW_ENC_CAPA_CIPHER_TKIP |
+ IW_ENC_CAPA_WPA);
+ break;
+
+ case WLAN_CIPHER_SUITE_CCMP:
+ range->enc_capa |= (IW_ENC_CAPA_CIPHER_CCMP |
+ IW_ENC_CAPA_WPA2);
+ break;
+
+ case WLAN_CIPHER_SUITE_WEP40:
+ range->encoding_size[range->num_encoding_sizes++] =
+ WLAN_KEY_LEN_WEP40;
+ break;
+
+ case WLAN_CIPHER_SUITE_WEP104:
+ range->encoding_size[range->num_encoding_sizes++] =
+ WLAN_KEY_LEN_WEP104;
+ break;
+ }
+ }
+
+ for (band = 0; band < NUM_NL80211_BANDS; band ++) {
+ struct ieee80211_supported_band *sband;
+
+ sband = wdev->wiphy->bands[band];
+
+ if (!sband)
+ continue;
+
+ for (i = 0; i < sband->n_channels && c < IW_MAX_FREQUENCIES; i++) {
+ struct ieee80211_channel *chan = &sband->channels[i];
+
+ if (!(chan->flags & IEEE80211_CHAN_DISABLED)) {
+ range->freq[c].i =
+ ieee80211_frequency_to_channel(
+ chan->center_freq);
+ range->freq[c].m = chan->center_freq;
+ range->freq[c].e = 6;
+ c++;
+ }
+ }
+ }
+ range->num_channels = c;
+ range->num_frequency = c;
+
+ IW_EVENT_CAPA_SET_KERNEL(range->event_capa);
+ IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWAP);
+ IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWSCAN);
+
+ if (wdev->wiphy->max_scan_ssids > 0)
+ range->scan_capa |= IW_SCAN_CAPA_ESSID;
+
+ return 0;
+}
+EXPORT_WEXT_HANDLER(cfg80211_wext_giwrange);
+
+
+/**
+ * cfg80211_wext_freq - get wext frequency for non-"auto"
+ * @dev: the net device
+ * @freq: the wext freq encoding
+ *
+ * Returns a frequency, or a negative error code, or 0 for auto.
+ */
+int cfg80211_wext_freq(struct iw_freq *freq)
+{
+ /*
+ * Parse frequency - return 0 for auto and
+ * -EINVAL for impossible things.
+ */
+ if (freq->e == 0) {
+ enum nl80211_band band = NL80211_BAND_2GHZ;
+ if (freq->m < 0)
+ return 0;
+ if (freq->m > 14)
+ band = NL80211_BAND_5GHZ;
+ return ieee80211_channel_to_frequency(freq->m, band);
+ } else {
+ int i, div = 1000000;
+ for (i = 0; i < freq->e; i++)
+ div /= 10;
+ if (div <= 0)
+ return -EINVAL;
+ return freq->m / div;
+ }
+}
+
+int cfg80211_wext_siwrts(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *rts, char *extra)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+ u32 orts = wdev->wiphy->rts_threshold;
+ int err;
+
+ if (rts->disabled || !rts->fixed)
+ wdev->wiphy->rts_threshold = (u32) -1;
+ else if (rts->value < 0)
+ return -EINVAL;
+ else
+ wdev->wiphy->rts_threshold = rts->value;
+
+ err = rdev_set_wiphy_params(rdev, WIPHY_PARAM_RTS_THRESHOLD);
+ if (err)
+ wdev->wiphy->rts_threshold = orts;
+
+ return err;
+}
+EXPORT_WEXT_HANDLER(cfg80211_wext_siwrts);
+
+int cfg80211_wext_giwrts(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *rts, char *extra)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+
+ rts->value = wdev->wiphy->rts_threshold;
+ rts->disabled = rts->value == (u32) -1;
+ rts->fixed = 1;
+
+ return 0;
+}
+EXPORT_WEXT_HANDLER(cfg80211_wext_giwrts);
+
+int cfg80211_wext_siwfrag(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *frag, char *extra)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+ u32 ofrag = wdev->wiphy->frag_threshold;
+ int err;
+
+ if (frag->disabled || !frag->fixed)
+ wdev->wiphy->frag_threshold = (u32) -1;
+ else if (frag->value < 256)
+ return -EINVAL;
+ else {
+ /* Fragment length must be even, so strip LSB. */
+ wdev->wiphy->frag_threshold = frag->value & ~0x1;
+ }
+
+ err = rdev_set_wiphy_params(rdev, WIPHY_PARAM_FRAG_THRESHOLD);
+ if (err)
+ wdev->wiphy->frag_threshold = ofrag;
+
+ return err;
+}
+EXPORT_WEXT_HANDLER(cfg80211_wext_siwfrag);
+
+int cfg80211_wext_giwfrag(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *frag, char *extra)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+
+ frag->value = wdev->wiphy->frag_threshold;
+ frag->disabled = frag->value == (u32) -1;
+ frag->fixed = 1;
+
+ return 0;
+}
+EXPORT_WEXT_HANDLER(cfg80211_wext_giwfrag);
+
+static int cfg80211_wext_siwretry(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *retry, char *extra)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+ u32 changed = 0;
+ u8 olong = wdev->wiphy->retry_long;
+ u8 oshort = wdev->wiphy->retry_short;
+ int err;
+
+ if (retry->disabled || retry->value < 1 || retry->value > 255 ||
+ (retry->flags & IW_RETRY_TYPE) != IW_RETRY_LIMIT)
+ return -EINVAL;
+
+ if (retry->flags & IW_RETRY_LONG) {
+ wdev->wiphy->retry_long = retry->value;
+ changed |= WIPHY_PARAM_RETRY_LONG;
+ } else if (retry->flags & IW_RETRY_SHORT) {
+ wdev->wiphy->retry_short = retry->value;
+ changed |= WIPHY_PARAM_RETRY_SHORT;
+ } else {
+ wdev->wiphy->retry_short = retry->value;
+ wdev->wiphy->retry_long = retry->value;
+ changed |= WIPHY_PARAM_RETRY_LONG;
+ changed |= WIPHY_PARAM_RETRY_SHORT;
+ }
+
+ if (!changed)
+ return 0;
+
+ err = rdev_set_wiphy_params(rdev, changed);
+ if (err) {
+ wdev->wiphy->retry_short = oshort;
+ wdev->wiphy->retry_long = olong;
+ }
+
+ return err;
+}
+
+int cfg80211_wext_giwretry(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *retry, char *extra)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+
+ retry->disabled = 0;
+
+ if (retry->flags == 0 || (retry->flags & IW_RETRY_SHORT)) {
+ /*
+ * First return short value, iwconfig will ask long value
+ * later if needed
+ */
+ retry->flags |= IW_RETRY_LIMIT | IW_RETRY_SHORT;
+ retry->value = wdev->wiphy->retry_short;
+ if (wdev->wiphy->retry_long == wdev->wiphy->retry_short)
+ retry->flags |= IW_RETRY_LONG;
+
+ return 0;
+ }
+
+ if (retry->flags & IW_RETRY_LONG) {
+ retry->flags = IW_RETRY_LIMIT | IW_RETRY_LONG;
+ retry->value = wdev->wiphy->retry_long;
+ }
+
+ return 0;
+}
+EXPORT_WEXT_HANDLER(cfg80211_wext_giwretry);
+
+static int __cfg80211_set_encryption(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, bool pairwise,
+ const u8 *addr, bool remove, bool tx_key,
+ int idx, struct key_params *params)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ int err, i;
+ bool rejoin = false;
+
+ if (pairwise && !addr)
+ return -EINVAL;
+
+ /*
+ * In many cases we won't actually need this, but it's better
+ * to do it first in case the allocation fails. Don't use wext.
+ */
+ if (!wdev->wext.keys) {
+ wdev->wext.keys = kzalloc(sizeof(*wdev->wext.keys),
+ GFP_KERNEL);
+ if (!wdev->wext.keys)
+ return -ENOMEM;
+ for (i = 0; i < CFG80211_MAX_WEP_KEYS; i++)
+ wdev->wext.keys->params[i].key =
+ wdev->wext.keys->data[i];
+ }
+
+ if (wdev->iftype != NL80211_IFTYPE_ADHOC &&
+ wdev->iftype != NL80211_IFTYPE_STATION)
+ return -EOPNOTSUPP;
+
+ if (params->cipher == WLAN_CIPHER_SUITE_AES_CMAC) {
+ if (!wdev->current_bss)
+ return -ENOLINK;
+
+ if (!rdev->ops->set_default_mgmt_key)
+ return -EOPNOTSUPP;
+
+ if (idx < 4 || idx > 5)
+ return -EINVAL;
+ } else if (idx < 0 || idx > 3)
+ return -EINVAL;
+
+ if (remove) {
+ err = 0;
+ if (wdev->current_bss) {
+ /*
+ * If removing the current TX key, we will need to
+ * join a new IBSS without the privacy bit clear.
+ */
+ if (idx == wdev->wext.default_key &&
+ wdev->iftype == NL80211_IFTYPE_ADHOC) {
+ __cfg80211_leave_ibss(rdev, wdev->netdev, true);
+ rejoin = true;
+ }
+
+ if (!pairwise && addr &&
+ !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN))
+ err = -ENOENT;
+ else
+ err = rdev_del_key(rdev, dev, idx, pairwise,
+ addr);
+ }
+ wdev->wext.connect.privacy = false;
+ /*
+ * Applications using wireless extensions expect to be
+ * able to delete keys that don't exist, so allow that.
+ */
+ if (err == -ENOENT)
+ err = 0;
+ if (!err) {
+ if (!addr && idx < 4) {
+ memset(wdev->wext.keys->data[idx], 0,
+ sizeof(wdev->wext.keys->data[idx]));
+ wdev->wext.keys->params[idx].key_len = 0;
+ wdev->wext.keys->params[idx].cipher = 0;
+ }
+ if (idx == wdev->wext.default_key)
+ wdev->wext.default_key = -1;
+ else if (idx == wdev->wext.default_mgmt_key)
+ wdev->wext.default_mgmt_key = -1;
+ }
+
+ if (!err && rejoin)
+ err = cfg80211_ibss_wext_join(rdev, wdev);
+
+ return err;
+ }
+
+ if (addr)
+ tx_key = false;
+
+ if (cfg80211_validate_key_settings(rdev, params, idx, pairwise, addr))
+ return -EINVAL;
+
+ err = 0;
+ if (wdev->current_bss)
+ err = rdev_add_key(rdev, dev, idx, pairwise, addr, params);
+ else if (params->cipher != WLAN_CIPHER_SUITE_WEP40 &&
+ params->cipher != WLAN_CIPHER_SUITE_WEP104)
+ return -EINVAL;
+ if (err)
+ return err;
+
+ /*
+ * We only need to store WEP keys, since they're the only keys that
+ * can be be set before a connection is established and persist after
+ * disconnecting.
+ */
+ if (!addr && (params->cipher == WLAN_CIPHER_SUITE_WEP40 ||
+ params->cipher == WLAN_CIPHER_SUITE_WEP104)) {
+ wdev->wext.keys->params[idx] = *params;
+ memcpy(wdev->wext.keys->data[idx],
+ params->key, params->key_len);
+ wdev->wext.keys->params[idx].key =
+ wdev->wext.keys->data[idx];
+ }
+
+ if ((params->cipher == WLAN_CIPHER_SUITE_WEP40 ||
+ params->cipher == WLAN_CIPHER_SUITE_WEP104) &&
+ (tx_key || (!addr && wdev->wext.default_key == -1))) {
+ if (wdev->current_bss) {
+ /*
+ * If we are getting a new TX key from not having
+ * had one before we need to join a new IBSS with
+ * the privacy bit set.
+ */
+ if (wdev->iftype == NL80211_IFTYPE_ADHOC &&
+ wdev->wext.default_key == -1) {
+ __cfg80211_leave_ibss(rdev, wdev->netdev, true);
+ rejoin = true;
+ }
+ err = rdev_set_default_key(rdev, dev, idx, true, true);
+ }
+ if (!err) {
+ wdev->wext.default_key = idx;
+ if (rejoin)
+ err = cfg80211_ibss_wext_join(rdev, wdev);
+ }
+ return err;
+ }
+
+ if (params->cipher == WLAN_CIPHER_SUITE_AES_CMAC &&
+ (tx_key || (!addr && wdev->wext.default_mgmt_key == -1))) {
+ if (wdev->current_bss)
+ err = rdev_set_default_mgmt_key(rdev, dev, idx);
+ if (!err)
+ wdev->wext.default_mgmt_key = idx;
+ return err;
+ }
+
+ return 0;
+}
+
+static int cfg80211_set_encryption(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, bool pairwise,
+ const u8 *addr, bool remove, bool tx_key,
+ int idx, struct key_params *params)
+{
+ int err;
+
+ wdev_lock(dev->ieee80211_ptr);
+ err = __cfg80211_set_encryption(rdev, dev, pairwise, addr,
+ remove, tx_key, idx, params);
+ wdev_unlock(dev->ieee80211_ptr);
+
+ return err;
+}
+
+static int cfg80211_wext_siwencode(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *erq, char *keybuf)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+ int idx, err;
+ bool remove = false;
+ struct key_params params;
+
+ if (wdev->iftype != NL80211_IFTYPE_STATION &&
+ wdev->iftype != NL80211_IFTYPE_ADHOC)
+ return -EOPNOTSUPP;
+
+ /* no use -- only MFP (set_default_mgmt_key) is optional */
+ if (!rdev->ops->del_key ||
+ !rdev->ops->add_key ||
+ !rdev->ops->set_default_key)
+ return -EOPNOTSUPP;
+
+ idx = erq->flags & IW_ENCODE_INDEX;
+ if (idx == 0) {
+ idx = wdev->wext.default_key;
+ if (idx < 0)
+ idx = 0;
+ } else if (idx < 1 || idx > 4)
+ return -EINVAL;
+ else
+ idx--;
+
+ if (erq->flags & IW_ENCODE_DISABLED)
+ remove = true;
+ else if (erq->length == 0) {
+ /* No key data - just set the default TX key index */
+ err = 0;
+ wdev_lock(wdev);
+ if (wdev->current_bss)
+ err = rdev_set_default_key(rdev, dev, idx, true,
+ true);
+ if (!err)
+ wdev->wext.default_key = idx;
+ wdev_unlock(wdev);
+ return err;
+ }
+
+ memset(¶ms, 0, sizeof(params));
+ params.key = keybuf;
+ params.key_len = erq->length;
+ if (erq->length == 5)
+ params.cipher = WLAN_CIPHER_SUITE_WEP40;
+ else if (erq->length == 13)
+ params.cipher = WLAN_CIPHER_SUITE_WEP104;
+ else if (!remove)
+ return -EINVAL;
+
+ return cfg80211_set_encryption(rdev, dev, false, NULL, remove,
+ wdev->wext.default_key == -1,
+ idx, ¶ms);
+}
+
+static int cfg80211_wext_siwencodeext(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *erq, char *extra)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+ struct iw_encode_ext *ext = (struct iw_encode_ext *) extra;
+ const u8 *addr;
+ int idx;
+ bool remove = false;
+ struct key_params params;
+ u32 cipher;
+
+ if (wdev->iftype != NL80211_IFTYPE_STATION &&
+ wdev->iftype != NL80211_IFTYPE_ADHOC)
+ return -EOPNOTSUPP;
+
+ /* no use -- only MFP (set_default_mgmt_key) is optional */
+ if (!rdev->ops->del_key ||
+ !rdev->ops->add_key ||
+ !rdev->ops->set_default_key)
+ return -EOPNOTSUPP;
+
+ switch (ext->alg) {
+ case IW_ENCODE_ALG_NONE:
+ remove = true;
+ cipher = 0;
+ break;
+ case IW_ENCODE_ALG_WEP:
+ if (ext->key_len == 5)
+ cipher = WLAN_CIPHER_SUITE_WEP40;
+ else if (ext->key_len == 13)
+ cipher = WLAN_CIPHER_SUITE_WEP104;
+ else
+ return -EINVAL;
+ break;
+ case IW_ENCODE_ALG_TKIP:
+ cipher = WLAN_CIPHER_SUITE_TKIP;
+ break;
+ case IW_ENCODE_ALG_CCMP:
+ cipher = WLAN_CIPHER_SUITE_CCMP;
+ break;
+ case IW_ENCODE_ALG_AES_CMAC:
+ cipher = WLAN_CIPHER_SUITE_AES_CMAC;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ if (erq->flags & IW_ENCODE_DISABLED)
+ remove = true;
+
+ idx = erq->flags & IW_ENCODE_INDEX;
+ if (cipher == WLAN_CIPHER_SUITE_AES_CMAC) {
+ if (idx < 4 || idx > 5) {
+ idx = wdev->wext.default_mgmt_key;
+ if (idx < 0)
+ return -EINVAL;
+ } else
+ idx--;
+ } else {
+ if (idx < 1 || idx > 4) {
+ idx = wdev->wext.default_key;
+ if (idx < 0)
+ return -EINVAL;
+ } else
+ idx--;
+ }
+
+ addr = ext->addr.sa_data;
+ if (is_broadcast_ether_addr(addr))
+ addr = NULL;
+
+ memset(¶ms, 0, sizeof(params));
+ params.key = ext->key;
+ params.key_len = ext->key_len;
+ params.cipher = cipher;
+
+ if (ext->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID) {
+ params.seq = ext->rx_seq;
+ params.seq_len = 6;
+ }
+
+ return cfg80211_set_encryption(
+ rdev, dev,
+ !(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY),
+ addr, remove,
+ ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY,
+ idx, ¶ms);
+}
+
+static int cfg80211_wext_giwencode(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *erq, char *keybuf)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ int idx;
+
+ if (wdev->iftype != NL80211_IFTYPE_STATION &&
+ wdev->iftype != NL80211_IFTYPE_ADHOC)
+ return -EOPNOTSUPP;
+
+ idx = erq->flags & IW_ENCODE_INDEX;
+ if (idx == 0) {
+ idx = wdev->wext.default_key;
+ if (idx < 0)
+ idx = 0;
+ } else if (idx < 1 || idx > 4)
+ return -EINVAL;
+ else
+ idx--;
+
+ erq->flags = idx + 1;
+
+ if (!wdev->wext.keys || !wdev->wext.keys->params[idx].cipher) {
+ erq->flags |= IW_ENCODE_DISABLED;
+ erq->length = 0;
+ return 0;
+ }
+
+ erq->length = min_t(size_t, erq->length,
+ wdev->wext.keys->params[idx].key_len);
+ memcpy(keybuf, wdev->wext.keys->params[idx].key, erq->length);
+ erq->flags |= IW_ENCODE_ENABLED;
+
+ return 0;
+}
+
+static int cfg80211_wext_siwfreq(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_freq *wextfreq, char *extra)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+ struct cfg80211_chan_def chandef = {
+ .width = NL80211_CHAN_WIDTH_20_NOHT,
+ };
+ int freq;
+
+ switch (wdev->iftype) {
+ case NL80211_IFTYPE_STATION:
+ return cfg80211_mgd_wext_siwfreq(dev, info, wextfreq, extra);
+ case NL80211_IFTYPE_ADHOC:
+ return cfg80211_ibss_wext_siwfreq(dev, info, wextfreq, extra);
+ case NL80211_IFTYPE_MONITOR:
+ freq = cfg80211_wext_freq(wextfreq);
+ if (freq < 0)
+ return freq;
+ if (freq == 0)
+ return -EINVAL;
+ chandef.center_freq1 = freq;
+ chandef.chan = ieee80211_get_channel(&rdev->wiphy, freq);
+ if (!chandef.chan)
+ return -EINVAL;
+ return cfg80211_set_monitor_channel(rdev, &chandef);
+ case NL80211_IFTYPE_MESH_POINT:
+ freq = cfg80211_wext_freq(wextfreq);
+ if (freq < 0)
+ return freq;
+ if (freq == 0)
+ return -EINVAL;
+ chandef.center_freq1 = freq;
+ chandef.chan = ieee80211_get_channel(&rdev->wiphy, freq);
+ if (!chandef.chan)
+ return -EINVAL;
+ return cfg80211_set_mesh_channel(rdev, wdev, &chandef);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int cfg80211_wext_giwfreq(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_freq *freq, char *extra)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+ struct cfg80211_chan_def chandef;
+ int ret;
+
+ switch (wdev->iftype) {
+ case NL80211_IFTYPE_STATION:
+ return cfg80211_mgd_wext_giwfreq(dev, info, freq, extra);
+ case NL80211_IFTYPE_ADHOC:
+ return cfg80211_ibss_wext_giwfreq(dev, info, freq, extra);
+ case NL80211_IFTYPE_MONITOR:
+ if (!rdev->ops->get_channel)
+ return -EINVAL;
+
+ ret = rdev_get_channel(rdev, wdev, &chandef);
+ if (ret)
+ return ret;
+ freq->m = chandef.chan->center_freq;
+ freq->e = 6;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int cfg80211_wext_siwtxpower(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *data, char *extra)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+ enum nl80211_tx_power_setting type;
+ int dbm = 0;
+
+ if ((data->txpower.flags & IW_TXPOW_TYPE) != IW_TXPOW_DBM)
+ return -EINVAL;
+ if (data->txpower.flags & IW_TXPOW_RANGE)
+ return -EINVAL;
+
+ if (!rdev->ops->set_tx_power)
+ return -EOPNOTSUPP;
+
+ /* only change when not disabling */
+ if (!data->txpower.disabled) {
+ rfkill_set_sw_state(rdev->rfkill, false);
+
+ if (data->txpower.fixed) {
+ /*
+ * wext doesn't support negative values, see
+ * below where it's for automatic
+ */
+ if (data->txpower.value < 0)
+ return -EINVAL;
+ dbm = data->txpower.value;
+ type = NL80211_TX_POWER_FIXED;
+ /* TODO: do regulatory check! */
+ } else {
+ /*
+ * Automatic power level setting, max being the value
+ * passed in from userland.
+ */
+ if (data->txpower.value < 0) {
+ type = NL80211_TX_POWER_AUTOMATIC;
+ } else {
+ dbm = data->txpower.value;
+ type = NL80211_TX_POWER_LIMITED;
+ }
+ }
+ } else {
+ rfkill_set_sw_state(rdev->rfkill, true);
+ schedule_work(&rdev->rfkill_sync);
+ return 0;
+ }
+
+ return rdev_set_tx_power(rdev, wdev, type, DBM_TO_MBM(dbm));
+}
+
+static int cfg80211_wext_giwtxpower(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *data, char *extra)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+ int err, val;
+
+ if ((data->txpower.flags & IW_TXPOW_TYPE) != IW_TXPOW_DBM)
+ return -EINVAL;
+ if (data->txpower.flags & IW_TXPOW_RANGE)
+ return -EINVAL;
+
+ if (!rdev->ops->get_tx_power)
+ return -EOPNOTSUPP;
+
+ err = rdev_get_tx_power(rdev, wdev, &val);
+ if (err)
+ return err;
+
+ /* well... oh well */
+ data->txpower.fixed = 1;
+ data->txpower.disabled = rfkill_blocked(rdev->rfkill);
+ data->txpower.value = val;
+ data->txpower.flags = IW_TXPOW_DBM;
+
+ return 0;
+}
+
+static int cfg80211_set_auth_alg(struct wireless_dev *wdev,
+ s32 auth_alg)
+{
+ int nr_alg = 0;
+
+ if (!auth_alg)
+ return -EINVAL;
+
+ if (auth_alg & ~(IW_AUTH_ALG_OPEN_SYSTEM |
+ IW_AUTH_ALG_SHARED_KEY |
+ IW_AUTH_ALG_LEAP))
+ return -EINVAL;
+
+ if (auth_alg & IW_AUTH_ALG_OPEN_SYSTEM) {
+ nr_alg++;
+ wdev->wext.connect.auth_type = NL80211_AUTHTYPE_OPEN_SYSTEM;
+ }
+
+ if (auth_alg & IW_AUTH_ALG_SHARED_KEY) {
+ nr_alg++;
+ wdev->wext.connect.auth_type = NL80211_AUTHTYPE_SHARED_KEY;
+ }
+
+ if (auth_alg & IW_AUTH_ALG_LEAP) {
+ nr_alg++;
+ wdev->wext.connect.auth_type = NL80211_AUTHTYPE_NETWORK_EAP;
+ }
+
+ if (nr_alg > 1)
+ wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
+
+ return 0;
+}
+
+static int cfg80211_set_wpa_version(struct wireless_dev *wdev, u32 wpa_versions)
+{
+ if (wpa_versions & ~(IW_AUTH_WPA_VERSION_WPA |
+ IW_AUTH_WPA_VERSION_WPA2|
+ IW_AUTH_WPA_VERSION_DISABLED))
+ return -EINVAL;
+
+ if ((wpa_versions & IW_AUTH_WPA_VERSION_DISABLED) &&
+ (wpa_versions & (IW_AUTH_WPA_VERSION_WPA|
+ IW_AUTH_WPA_VERSION_WPA2)))
+ return -EINVAL;
+
+ if (wpa_versions & IW_AUTH_WPA_VERSION_DISABLED)
+ wdev->wext.connect.crypto.wpa_versions &=
+ ~(NL80211_WPA_VERSION_1|NL80211_WPA_VERSION_2);
+
+ if (wpa_versions & IW_AUTH_WPA_VERSION_WPA)
+ wdev->wext.connect.crypto.wpa_versions |=
+ NL80211_WPA_VERSION_1;
+
+ if (wpa_versions & IW_AUTH_WPA_VERSION_WPA2)
+ wdev->wext.connect.crypto.wpa_versions |=
+ NL80211_WPA_VERSION_2;
+
+ return 0;
+}
+
+static int cfg80211_set_cipher_group(struct wireless_dev *wdev, u32 cipher)
+{
+ if (cipher & IW_AUTH_CIPHER_WEP40)
+ wdev->wext.connect.crypto.cipher_group =
+ WLAN_CIPHER_SUITE_WEP40;
+ else if (cipher & IW_AUTH_CIPHER_WEP104)
+ wdev->wext.connect.crypto.cipher_group =
+ WLAN_CIPHER_SUITE_WEP104;
+ else if (cipher & IW_AUTH_CIPHER_TKIP)
+ wdev->wext.connect.crypto.cipher_group =
+ WLAN_CIPHER_SUITE_TKIP;
+ else if (cipher & IW_AUTH_CIPHER_CCMP)
+ wdev->wext.connect.crypto.cipher_group =
+ WLAN_CIPHER_SUITE_CCMP;
+ else if (cipher & IW_AUTH_CIPHER_AES_CMAC)
+ wdev->wext.connect.crypto.cipher_group =
+ WLAN_CIPHER_SUITE_AES_CMAC;
+ else if (cipher & IW_AUTH_CIPHER_NONE)
+ wdev->wext.connect.crypto.cipher_group = 0;
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
+static int cfg80211_set_cipher_pairwise(struct wireless_dev *wdev, u32 cipher)
+{
+ int nr_ciphers = 0;
+ u32 *ciphers_pairwise = wdev->wext.connect.crypto.ciphers_pairwise;
+
+ if (cipher & IW_AUTH_CIPHER_WEP40) {
+ ciphers_pairwise[nr_ciphers] = WLAN_CIPHER_SUITE_WEP40;
+ nr_ciphers++;
+ }
+
+ if (cipher & IW_AUTH_CIPHER_WEP104) {
+ ciphers_pairwise[nr_ciphers] = WLAN_CIPHER_SUITE_WEP104;
+ nr_ciphers++;
+ }
+
+ if (cipher & IW_AUTH_CIPHER_TKIP) {
+ ciphers_pairwise[nr_ciphers] = WLAN_CIPHER_SUITE_TKIP;
+ nr_ciphers++;
+ }
+
+ if (cipher & IW_AUTH_CIPHER_CCMP) {
+ ciphers_pairwise[nr_ciphers] = WLAN_CIPHER_SUITE_CCMP;
+ nr_ciphers++;
+ }
+
+ if (cipher & IW_AUTH_CIPHER_AES_CMAC) {
+ ciphers_pairwise[nr_ciphers] = WLAN_CIPHER_SUITE_AES_CMAC;
+ nr_ciphers++;
+ }
+
+ BUILD_BUG_ON(NL80211_MAX_NR_CIPHER_SUITES < 5);
+
+ wdev->wext.connect.crypto.n_ciphers_pairwise = nr_ciphers;
+
+ return 0;
+}
+
+
+static int cfg80211_set_key_mgt(struct wireless_dev *wdev, u32 key_mgt)
+{
+ int nr_akm_suites = 0;
+
+ if (key_mgt & ~(IW_AUTH_KEY_MGMT_802_1X |
+ IW_AUTH_KEY_MGMT_PSK))
+ return -EINVAL;
+
+ if (key_mgt & IW_AUTH_KEY_MGMT_802_1X) {
+ wdev->wext.connect.crypto.akm_suites[nr_akm_suites] =
+ WLAN_AKM_SUITE_8021X;
+ nr_akm_suites++;
+ }
+
+ if (key_mgt & IW_AUTH_KEY_MGMT_PSK) {
+ wdev->wext.connect.crypto.akm_suites[nr_akm_suites] =
+ WLAN_AKM_SUITE_PSK;
+ nr_akm_suites++;
+ }
+
+ wdev->wext.connect.crypto.n_akm_suites = nr_akm_suites;
+
+ return 0;
+}
+
+static int cfg80211_wext_siwauth(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *data, char *extra)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+
+ if (wdev->iftype != NL80211_IFTYPE_STATION)
+ return -EOPNOTSUPP;
+
+ switch (data->flags & IW_AUTH_INDEX) {
+ case IW_AUTH_PRIVACY_INVOKED:
+ wdev->wext.connect.privacy = data->value;
+ return 0;
+ case IW_AUTH_WPA_VERSION:
+ return cfg80211_set_wpa_version(wdev, data->value);
+ case IW_AUTH_CIPHER_GROUP:
+ return cfg80211_set_cipher_group(wdev, data->value);
+ case IW_AUTH_KEY_MGMT:
+ return cfg80211_set_key_mgt(wdev, data->value);
+ case IW_AUTH_CIPHER_PAIRWISE:
+ return cfg80211_set_cipher_pairwise(wdev, data->value);
+ case IW_AUTH_80211_AUTH_ALG:
+ return cfg80211_set_auth_alg(wdev, data->value);
+ case IW_AUTH_WPA_ENABLED:
+ case IW_AUTH_RX_UNENCRYPTED_EAPOL:
+ case IW_AUTH_DROP_UNENCRYPTED:
+ case IW_AUTH_MFP:
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int cfg80211_wext_giwauth(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *data, char *extra)
+{
+ /* XXX: what do we need? */
+
+ return -EOPNOTSUPP;
+}
+
+static int cfg80211_wext_siwpower(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *wrq, char *extra)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+ bool ps = wdev->ps;
+ int timeout = wdev->ps_timeout;
+ int err;
+
+ if (wdev->iftype != NL80211_IFTYPE_STATION)
+ return -EINVAL;
+
+ if (!rdev->ops->set_power_mgmt)
+ return -EOPNOTSUPP;
+
+ if (wrq->disabled) {
+ ps = false;
+ } else {
+ switch (wrq->flags & IW_POWER_MODE) {
+ case IW_POWER_ON: /* If not specified */
+ case IW_POWER_MODE: /* If set all mask */
+ case IW_POWER_ALL_R: /* If explicitely state all */
+ ps = true;
+ break;
+ default: /* Otherwise we ignore */
+ return -EINVAL;
+ }
+
+ if (wrq->flags & ~(IW_POWER_MODE | IW_POWER_TIMEOUT))
+ return -EINVAL;
+
+ if (wrq->flags & IW_POWER_TIMEOUT)
+ timeout = wrq->value / 1000;
+ }
+
+ err = rdev_set_power_mgmt(rdev, dev, ps, timeout);
+ if (err)
+ return err;
+
+ wdev->ps = ps;
+ wdev->ps_timeout = timeout;
+
+ return 0;
+
+}
+
+static int cfg80211_wext_giwpower(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *wrq, char *extra)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+
+ wrq->disabled = !wdev->ps;
+
+ return 0;
+}
+
+static int cfg80211_wds_wext_siwap(struct net_device *dev,
+ struct iw_request_info *info,
+ struct sockaddr *addr, char *extra)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+ int err;
+
+ if (WARN_ON(wdev->iftype != NL80211_IFTYPE_WDS))
+ return -EINVAL;
+
+ if (addr->sa_family != ARPHRD_ETHER)
+ return -EINVAL;
+
+ if (netif_running(dev))
+ return -EBUSY;
+
+ if (!rdev->ops->set_wds_peer)
+ return -EOPNOTSUPP;
+
+ err = rdev_set_wds_peer(rdev, dev, (u8 *)&addr->sa_data);
+ if (err)
+ return err;
+
+ memcpy(&wdev->wext.bssid, (u8 *) &addr->sa_data, ETH_ALEN);
+
+ return 0;
+}
+
+static int cfg80211_wds_wext_giwap(struct net_device *dev,
+ struct iw_request_info *info,
+ struct sockaddr *addr, char *extra)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+
+ if (WARN_ON(wdev->iftype != NL80211_IFTYPE_WDS))
+ return -EINVAL;
+
+ addr->sa_family = ARPHRD_ETHER;
+ memcpy(&addr->sa_data, wdev->wext.bssid, ETH_ALEN);
+
+ return 0;
+}
+
+static int cfg80211_wext_siwrate(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *rate, char *extra)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+ struct cfg80211_bitrate_mask mask;
+ u32 fixed, maxrate;
+ struct ieee80211_supported_band *sband;
+ int band, ridx;
+ bool match = false;
+
+ if (!rdev->ops->set_bitrate_mask)
+ return -EOPNOTSUPP;
+
+ memset(&mask, 0, sizeof(mask));
+ fixed = 0;
+ maxrate = (u32)-1;
+
+ if (rate->value < 0) {
+ /* nothing */
+ } else if (rate->fixed) {
+ fixed = rate->value / 100000;
+ } else {
+ maxrate = rate->value / 100000;
+ }
+
+ for (band = 0; band < NUM_NL80211_BANDS; band++) {
+ sband = wdev->wiphy->bands[band];
+ if (sband == NULL)
+ continue;
+ for (ridx = 0; ridx < sband->n_bitrates; ridx++) {
+ struct ieee80211_rate *srate = &sband->bitrates[ridx];
+ if (fixed == srate->bitrate) {
+ mask.control[band].legacy = 1 << ridx;
+ match = true;
+ break;
+ }
+ if (srate->bitrate <= maxrate) {
+ mask.control[band].legacy |= 1 << ridx;
+ match = true;
+ }
+ }
+ }
+
+ if (!match)
+ return -EINVAL;
+
+ return rdev_set_bitrate_mask(rdev, dev, NULL, &mask);
+}
+
+static int cfg80211_wext_giwrate(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *rate, char *extra)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+ /* we are under RTNL - globally locked - so can use a static struct */
+ static struct station_info sinfo;
+ u8 addr[ETH_ALEN];
+ int err;
+
+ if (wdev->iftype != NL80211_IFTYPE_STATION)
+ return -EOPNOTSUPP;
+
+ if (!rdev->ops->get_station)
+ return -EOPNOTSUPP;
+
+ err = 0;
+ wdev_lock(wdev);
+ if (wdev->current_bss)
+ memcpy(addr, wdev->current_bss->pub.bssid, ETH_ALEN);
+ else
+ err = -EOPNOTSUPP;
+ wdev_unlock(wdev);
+ if (err)
+ return err;
+
+ err = rdev_get_station(rdev, dev, addr, &sinfo);
+ if (err)
+ return err;
+
+ if (!(sinfo.filled & BIT(NL80211_STA_INFO_TX_BITRATE)))
+ return -EOPNOTSUPP;
+
+ rate->value = 100000 * cfg80211_calculate_bitrate(&sinfo.txrate);
+
+ return 0;
+}
+
+/* Get wireless statistics. Called by /proc/net/wireless and by SIOCGIWSTATS */
+static struct iw_statistics *cfg80211_wireless_stats(struct net_device *dev)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+ /* we are under RTNL - globally locked - so can use static structs */
+ static struct iw_statistics wstats;
+ static struct station_info sinfo;
+ u8 bssid[ETH_ALEN];
+
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION)
+ return NULL;
+
+ if (!rdev->ops->get_station)
+ return NULL;
+
+ /* Grab BSSID of current BSS, if any */
+ wdev_lock(wdev);
+ if (!wdev->current_bss) {
+ wdev_unlock(wdev);
+ return NULL;
+ }
+ memcpy(bssid, wdev->current_bss->pub.bssid, ETH_ALEN);
+ wdev_unlock(wdev);
+
+ memset(&sinfo, 0, sizeof(sinfo));
+
+ if (rdev_get_station(rdev, dev, bssid, &sinfo))
+ return NULL;
+
+ memset(&wstats, 0, sizeof(wstats));
+
+ switch (rdev->wiphy.signal_type) {
+ case CFG80211_SIGNAL_TYPE_MBM:
+ if (sinfo.filled & BIT(NL80211_STA_INFO_SIGNAL)) {
+ int sig = sinfo.signal;
+ wstats.qual.updated |= IW_QUAL_LEVEL_UPDATED;
+ wstats.qual.updated |= IW_QUAL_QUAL_UPDATED;
+ wstats.qual.updated |= IW_QUAL_DBM;
+ wstats.qual.level = sig;
+ if (sig < -110)
+ sig = -110;
+ else if (sig > -40)
+ sig = -40;
+ wstats.qual.qual = sig + 110;
+ break;
+ }
+ case CFG80211_SIGNAL_TYPE_UNSPEC:
+ if (sinfo.filled & BIT(NL80211_STA_INFO_SIGNAL)) {
+ wstats.qual.updated |= IW_QUAL_LEVEL_UPDATED;
+ wstats.qual.updated |= IW_QUAL_QUAL_UPDATED;
+ wstats.qual.level = sinfo.signal;
+ wstats.qual.qual = sinfo.signal;
+ break;
+ }
+ default:
+ wstats.qual.updated |= IW_QUAL_LEVEL_INVALID;
+ wstats.qual.updated |= IW_QUAL_QUAL_INVALID;
+ }
+
+ wstats.qual.updated |= IW_QUAL_NOISE_INVALID;
+ if (sinfo.filled & BIT(NL80211_STA_INFO_RX_DROP_MISC))
+ wstats.discard.misc = sinfo.rx_dropped_misc;
+ if (sinfo.filled & BIT(NL80211_STA_INFO_TX_FAILED))
+ wstats.discard.retries = sinfo.tx_failed;
+
+ return &wstats;
+}
+
+static int cfg80211_wext_siwap(struct net_device *dev,
+ struct iw_request_info *info,
+ struct sockaddr *ap_addr, char *extra)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+
+ switch (wdev->iftype) {
+ case NL80211_IFTYPE_ADHOC:
+ return cfg80211_ibss_wext_siwap(dev, info, ap_addr, extra);
+ case NL80211_IFTYPE_STATION:
+ return cfg80211_mgd_wext_siwap(dev, info, ap_addr, extra);
+ case NL80211_IFTYPE_WDS:
+ return cfg80211_wds_wext_siwap(dev, info, ap_addr, extra);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int cfg80211_wext_giwap(struct net_device *dev,
+ struct iw_request_info *info,
+ struct sockaddr *ap_addr, char *extra)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+
+ switch (wdev->iftype) {
+ case NL80211_IFTYPE_ADHOC:
+ return cfg80211_ibss_wext_giwap(dev, info, ap_addr, extra);
+ case NL80211_IFTYPE_STATION:
+ return cfg80211_mgd_wext_giwap(dev, info, ap_addr, extra);
+ case NL80211_IFTYPE_WDS:
+ return cfg80211_wds_wext_giwap(dev, info, ap_addr, extra);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int cfg80211_wext_siwessid(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *data, char *ssid)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+
+ switch (wdev->iftype) {
+ case NL80211_IFTYPE_ADHOC:
+ return cfg80211_ibss_wext_siwessid(dev, info, data, ssid);
+ case NL80211_IFTYPE_STATION:
+ return cfg80211_mgd_wext_siwessid(dev, info, data, ssid);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int cfg80211_wext_giwessid(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *data, char *ssid)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+
+ data->flags = 0;
+ data->length = 0;
+
+ switch (wdev->iftype) {
+ case NL80211_IFTYPE_ADHOC:
+ return cfg80211_ibss_wext_giwessid(dev, info, data, ssid);
+ case NL80211_IFTYPE_STATION:
+ return cfg80211_mgd_wext_giwessid(dev, info, data, ssid);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int cfg80211_wext_siwpmksa(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *data, char *extra)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+ struct cfg80211_pmksa cfg_pmksa;
+ struct iw_pmksa *pmksa = (struct iw_pmksa *)extra;
+
+ memset(&cfg_pmksa, 0, sizeof(struct cfg80211_pmksa));
+
+ if (wdev->iftype != NL80211_IFTYPE_STATION)
+ return -EINVAL;
+
+ cfg_pmksa.bssid = pmksa->bssid.sa_data;
+ cfg_pmksa.pmkid = pmksa->pmkid;
+
+ switch (pmksa->cmd) {
+ case IW_PMKSA_ADD:
+ if (!rdev->ops->set_pmksa)
+ return -EOPNOTSUPP;
+
+ return rdev_set_pmksa(rdev, dev, &cfg_pmksa);
+
+ case IW_PMKSA_REMOVE:
+ if (!rdev->ops->del_pmksa)
+ return -EOPNOTSUPP;
+
+ return rdev_del_pmksa(rdev, dev, &cfg_pmksa);
+
+ case IW_PMKSA_FLUSH:
+ if (!rdev->ops->flush_pmksa)
+ return -EOPNOTSUPP;
+
+ return rdev_flush_pmksa(rdev, dev);
+
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static const iw_handler cfg80211_handlers[] = {
+ [IW_IOCTL_IDX(SIOCGIWNAME)] = (iw_handler) cfg80211_wext_giwname,
+ [IW_IOCTL_IDX(SIOCSIWFREQ)] = (iw_handler) cfg80211_wext_siwfreq,
+ [IW_IOCTL_IDX(SIOCGIWFREQ)] = (iw_handler) cfg80211_wext_giwfreq,
+ [IW_IOCTL_IDX(SIOCSIWMODE)] = (iw_handler) cfg80211_wext_siwmode,
+ [IW_IOCTL_IDX(SIOCGIWMODE)] = (iw_handler) cfg80211_wext_giwmode,
+ [IW_IOCTL_IDX(SIOCGIWRANGE)] = (iw_handler) cfg80211_wext_giwrange,
+ [IW_IOCTL_IDX(SIOCSIWAP)] = (iw_handler) cfg80211_wext_siwap,
+ [IW_IOCTL_IDX(SIOCGIWAP)] = (iw_handler) cfg80211_wext_giwap,
+ [IW_IOCTL_IDX(SIOCSIWMLME)] = (iw_handler) cfg80211_wext_siwmlme,
+ [IW_IOCTL_IDX(SIOCSIWSCAN)] = (iw_handler) cfg80211_wext_siwscan,
+ [IW_IOCTL_IDX(SIOCGIWSCAN)] = (iw_handler) cfg80211_wext_giwscan,
+ [IW_IOCTL_IDX(SIOCSIWESSID)] = (iw_handler) cfg80211_wext_siwessid,
+ [IW_IOCTL_IDX(SIOCGIWESSID)] = (iw_handler) cfg80211_wext_giwessid,
+ [IW_IOCTL_IDX(SIOCSIWRATE)] = (iw_handler) cfg80211_wext_siwrate,
+ [IW_IOCTL_IDX(SIOCGIWRATE)] = (iw_handler) cfg80211_wext_giwrate,
+ [IW_IOCTL_IDX(SIOCSIWRTS)] = (iw_handler) cfg80211_wext_siwrts,
+ [IW_IOCTL_IDX(SIOCGIWRTS)] = (iw_handler) cfg80211_wext_giwrts,
+ [IW_IOCTL_IDX(SIOCSIWFRAG)] = (iw_handler) cfg80211_wext_siwfrag,
+ [IW_IOCTL_IDX(SIOCGIWFRAG)] = (iw_handler) cfg80211_wext_giwfrag,
+ [IW_IOCTL_IDX(SIOCSIWTXPOW)] = (iw_handler) cfg80211_wext_siwtxpower,
+ [IW_IOCTL_IDX(SIOCGIWTXPOW)] = (iw_handler) cfg80211_wext_giwtxpower,
+ [IW_IOCTL_IDX(SIOCSIWRETRY)] = (iw_handler) cfg80211_wext_siwretry,
+ [IW_IOCTL_IDX(SIOCGIWRETRY)] = (iw_handler) cfg80211_wext_giwretry,
+ [IW_IOCTL_IDX(SIOCSIWENCODE)] = (iw_handler) cfg80211_wext_siwencode,
+ [IW_IOCTL_IDX(SIOCGIWENCODE)] = (iw_handler) cfg80211_wext_giwencode,
+ [IW_IOCTL_IDX(SIOCSIWPOWER)] = (iw_handler) cfg80211_wext_siwpower,
+ [IW_IOCTL_IDX(SIOCGIWPOWER)] = (iw_handler) cfg80211_wext_giwpower,
+ [IW_IOCTL_IDX(SIOCSIWGENIE)] = (iw_handler) cfg80211_wext_siwgenie,
+ [IW_IOCTL_IDX(SIOCSIWAUTH)] = (iw_handler) cfg80211_wext_siwauth,
+ [IW_IOCTL_IDX(SIOCGIWAUTH)] = (iw_handler) cfg80211_wext_giwauth,
+ [IW_IOCTL_IDX(SIOCSIWENCODEEXT)]= (iw_handler) cfg80211_wext_siwencodeext,
+ [IW_IOCTL_IDX(SIOCSIWPMKSA)] = (iw_handler) cfg80211_wext_siwpmksa,
+};
+
+const struct iw_handler_def cfg80211_wext_handler = {
+ .num_standard = ARRAY_SIZE(cfg80211_handlers),
+ .standard = cfg80211_handlers,
+ .get_wireless_stats = cfg80211_wireless_stats,
+};
diff --git a/net/wireless/wext-compat.h b/net/wireless/wext-compat.h
new file mode 100644
index 0000000..046a3d3
--- /dev/null
+++ b/net/wireless/wext-compat.h
@@ -0,0 +1,63 @@
+#ifndef __WEXT_COMPAT
+#define __WEXT_COMPAT
+
+#include <net/iw_handler.h>
+#include <linux/wireless.h>
+
+#ifdef CPTCFG_CFG80211_WEXT_EXPORT
+#define EXPORT_WEXT_HANDLER(h) EXPORT_SYMBOL_GPL(h)
+#else
+#define EXPORT_WEXT_HANDLER(h)
+#endif /* CPTCFG_CFG80211_WEXT_EXPORT */
+
+int cfg80211_ibss_wext_siwfreq(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_freq *freq, char *extra);
+int cfg80211_ibss_wext_giwfreq(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_freq *freq, char *extra);
+int cfg80211_ibss_wext_siwap(struct net_device *dev,
+ struct iw_request_info *info,
+ struct sockaddr *ap_addr, char *extra);
+int cfg80211_ibss_wext_giwap(struct net_device *dev,
+ struct iw_request_info *info,
+ struct sockaddr *ap_addr, char *extra);
+int cfg80211_ibss_wext_siwessid(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *data, char *ssid);
+int cfg80211_ibss_wext_giwessid(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *data, char *ssid);
+
+int cfg80211_mgd_wext_siwfreq(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_freq *freq, char *extra);
+int cfg80211_mgd_wext_giwfreq(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_freq *freq, char *extra);
+int cfg80211_mgd_wext_siwap(struct net_device *dev,
+ struct iw_request_info *info,
+ struct sockaddr *ap_addr, char *extra);
+int cfg80211_mgd_wext_giwap(struct net_device *dev,
+ struct iw_request_info *info,
+ struct sockaddr *ap_addr, char *extra);
+int cfg80211_mgd_wext_siwessid(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *data, char *ssid);
+int cfg80211_mgd_wext_giwessid(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *data, char *ssid);
+
+int cfg80211_wext_siwmlme(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *data, char *extra);
+int cfg80211_wext_siwgenie(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *data, char *extra);
+
+
+int cfg80211_wext_freq(struct iw_freq *freq);
+
+
+extern const struct iw_handler_def cfg80211_wext_handler;
+#endif /* __WEXT_COMPAT */
diff --git a/net/wireless/wext-core.c b/net/wireless/wext-core.c
new file mode 100644
index 0000000..d276a2e
--- /dev/null
+++ b/net/wireless/wext-core.c
@@ -0,0 +1,1184 @@
+/*
+ * This file implement the Wireless Extensions core API.
+ *
+ * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com>
+ * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved.
+ * Copyright 2009 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * (As all part of the Linux kernel, this file is GPL)
+ */
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/rtnetlink.h>
+#include <linux/slab.h>
+#include <linux/wireless.h>
+#include <linux/uaccess.h>
+#include <linux/export.h>
+#include <net/cfg80211.h>
+#include <net/iw_handler.h>
+#include <net/netlink.h>
+#include <net/wext.h>
+#include <net/net_namespace.h>
+
+typedef int (*wext_ioctl_func)(struct net_device *, struct iwreq *,
+ unsigned int, struct iw_request_info *,
+ iw_handler);
+
+
+/*
+ * Meta-data about all the standard Wireless Extension request we
+ * know about.
+ */
+static const struct iw_ioctl_description standard_ioctl[] = {
+ [IW_IOCTL_IDX(SIOCSIWCOMMIT)] = {
+ .header_type = IW_HEADER_TYPE_NULL,
+ },
+ [IW_IOCTL_IDX(SIOCGIWNAME)] = {
+ .header_type = IW_HEADER_TYPE_CHAR,
+ .flags = IW_DESCR_FLAG_DUMP,
+ },
+ [IW_IOCTL_IDX(SIOCSIWNWID)] = {
+ .header_type = IW_HEADER_TYPE_PARAM,
+ .flags = IW_DESCR_FLAG_EVENT,
+ },
+ [IW_IOCTL_IDX(SIOCGIWNWID)] = {
+ .header_type = IW_HEADER_TYPE_PARAM,
+ .flags = IW_DESCR_FLAG_DUMP,
+ },
+ [IW_IOCTL_IDX(SIOCSIWFREQ)] = {
+ .header_type = IW_HEADER_TYPE_FREQ,
+ .flags = IW_DESCR_FLAG_EVENT,
+ },
+ [IW_IOCTL_IDX(SIOCGIWFREQ)] = {
+ .header_type = IW_HEADER_TYPE_FREQ,
+ .flags = IW_DESCR_FLAG_DUMP,
+ },
+ [IW_IOCTL_IDX(SIOCSIWMODE)] = {
+ .header_type = IW_HEADER_TYPE_UINT,
+ .flags = IW_DESCR_FLAG_EVENT,
+ },
+ [IW_IOCTL_IDX(SIOCGIWMODE)] = {
+ .header_type = IW_HEADER_TYPE_UINT,
+ .flags = IW_DESCR_FLAG_DUMP,
+ },
+ [IW_IOCTL_IDX(SIOCSIWSENS)] = {
+ .header_type = IW_HEADER_TYPE_PARAM,
+ },
+ [IW_IOCTL_IDX(SIOCGIWSENS)] = {
+ .header_type = IW_HEADER_TYPE_PARAM,
+ },
+ [IW_IOCTL_IDX(SIOCSIWRANGE)] = {
+ .header_type = IW_HEADER_TYPE_NULL,
+ },
+ [IW_IOCTL_IDX(SIOCGIWRANGE)] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = 1,
+ .max_tokens = sizeof(struct iw_range),
+ .flags = IW_DESCR_FLAG_DUMP,
+ },
+ [IW_IOCTL_IDX(SIOCSIWPRIV)] = {
+ .header_type = IW_HEADER_TYPE_NULL,
+ },
+ [IW_IOCTL_IDX(SIOCGIWPRIV)] = { /* (handled directly by us) */
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = sizeof(struct iw_priv_args),
+ .max_tokens = 16,
+ .flags = IW_DESCR_FLAG_NOMAX,
+ },
+ [IW_IOCTL_IDX(SIOCSIWSTATS)] = {
+ .header_type = IW_HEADER_TYPE_NULL,
+ },
+ [IW_IOCTL_IDX(SIOCGIWSTATS)] = { /* (handled directly by us) */
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = 1,
+ .max_tokens = sizeof(struct iw_statistics),
+ .flags = IW_DESCR_FLAG_DUMP,
+ },
+ [IW_IOCTL_IDX(SIOCSIWSPY)] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = sizeof(struct sockaddr),
+ .max_tokens = IW_MAX_SPY,
+ },
+ [IW_IOCTL_IDX(SIOCGIWSPY)] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = sizeof(struct sockaddr) +
+ sizeof(struct iw_quality),
+ .max_tokens = IW_MAX_SPY,
+ },
+ [IW_IOCTL_IDX(SIOCSIWTHRSPY)] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = sizeof(struct iw_thrspy),
+ .min_tokens = 1,
+ .max_tokens = 1,
+ },
+ [IW_IOCTL_IDX(SIOCGIWTHRSPY)] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = sizeof(struct iw_thrspy),
+ .min_tokens = 1,
+ .max_tokens = 1,
+ },
+ [IW_IOCTL_IDX(SIOCSIWAP)] = {
+ .header_type = IW_HEADER_TYPE_ADDR,
+ },
+ [IW_IOCTL_IDX(SIOCGIWAP)] = {
+ .header_type = IW_HEADER_TYPE_ADDR,
+ .flags = IW_DESCR_FLAG_DUMP,
+ },
+ [IW_IOCTL_IDX(SIOCSIWMLME)] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = 1,
+ .min_tokens = sizeof(struct iw_mlme),
+ .max_tokens = sizeof(struct iw_mlme),
+ },
+ [IW_IOCTL_IDX(SIOCGIWAPLIST)] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = sizeof(struct sockaddr) +
+ sizeof(struct iw_quality),
+ .max_tokens = IW_MAX_AP,
+ .flags = IW_DESCR_FLAG_NOMAX,
+ },
+ [IW_IOCTL_IDX(SIOCSIWSCAN)] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = 1,
+ .min_tokens = 0,
+ .max_tokens = sizeof(struct iw_scan_req),
+ },
+ [IW_IOCTL_IDX(SIOCGIWSCAN)] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = 1,
+ .max_tokens = IW_SCAN_MAX_DATA,
+ .flags = IW_DESCR_FLAG_NOMAX,
+ },
+ [IW_IOCTL_IDX(SIOCSIWESSID)] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = 1,
+ .max_tokens = IW_ESSID_MAX_SIZE,
+ .flags = IW_DESCR_FLAG_EVENT,
+ },
+ [IW_IOCTL_IDX(SIOCGIWESSID)] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = 1,
+ .max_tokens = IW_ESSID_MAX_SIZE,
+ .flags = IW_DESCR_FLAG_DUMP,
+ },
+ [IW_IOCTL_IDX(SIOCSIWNICKN)] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = 1,
+ .max_tokens = IW_ESSID_MAX_SIZE,
+ },
+ [IW_IOCTL_IDX(SIOCGIWNICKN)] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = 1,
+ .max_tokens = IW_ESSID_MAX_SIZE,
+ },
+ [IW_IOCTL_IDX(SIOCSIWRATE)] = {
+ .header_type = IW_HEADER_TYPE_PARAM,
+ },
+ [IW_IOCTL_IDX(SIOCGIWRATE)] = {
+ .header_type = IW_HEADER_TYPE_PARAM,
+ },
+ [IW_IOCTL_IDX(SIOCSIWRTS)] = {
+ .header_type = IW_HEADER_TYPE_PARAM,
+ },
+ [IW_IOCTL_IDX(SIOCGIWRTS)] = {
+ .header_type = IW_HEADER_TYPE_PARAM,
+ },
+ [IW_IOCTL_IDX(SIOCSIWFRAG)] = {
+ .header_type = IW_HEADER_TYPE_PARAM,
+ },
+ [IW_IOCTL_IDX(SIOCGIWFRAG)] = {
+ .header_type = IW_HEADER_TYPE_PARAM,
+ },
+ [IW_IOCTL_IDX(SIOCSIWTXPOW)] = {
+ .header_type = IW_HEADER_TYPE_PARAM,
+ },
+ [IW_IOCTL_IDX(SIOCGIWTXPOW)] = {
+ .header_type = IW_HEADER_TYPE_PARAM,
+ },
+ [IW_IOCTL_IDX(SIOCSIWRETRY)] = {
+ .header_type = IW_HEADER_TYPE_PARAM,
+ },
+ [IW_IOCTL_IDX(SIOCGIWRETRY)] = {
+ .header_type = IW_HEADER_TYPE_PARAM,
+ },
+ [IW_IOCTL_IDX(SIOCSIWENCODE)] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = 1,
+ .max_tokens = IW_ENCODING_TOKEN_MAX,
+ .flags = IW_DESCR_FLAG_EVENT | IW_DESCR_FLAG_RESTRICT,
+ },
+ [IW_IOCTL_IDX(SIOCGIWENCODE)] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = 1,
+ .max_tokens = IW_ENCODING_TOKEN_MAX,
+ .flags = IW_DESCR_FLAG_DUMP | IW_DESCR_FLAG_RESTRICT,
+ },
+ [IW_IOCTL_IDX(SIOCSIWPOWER)] = {
+ .header_type = IW_HEADER_TYPE_PARAM,
+ },
+ [IW_IOCTL_IDX(SIOCGIWPOWER)] = {
+ .header_type = IW_HEADER_TYPE_PARAM,
+ },
+ [IW_IOCTL_IDX(SIOCSIWGENIE)] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = 1,
+ .max_tokens = IW_GENERIC_IE_MAX,
+ },
+ [IW_IOCTL_IDX(SIOCGIWGENIE)] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = 1,
+ .max_tokens = IW_GENERIC_IE_MAX,
+ },
+ [IW_IOCTL_IDX(SIOCSIWAUTH)] = {
+ .header_type = IW_HEADER_TYPE_PARAM,
+ },
+ [IW_IOCTL_IDX(SIOCGIWAUTH)] = {
+ .header_type = IW_HEADER_TYPE_PARAM,
+ },
+ [IW_IOCTL_IDX(SIOCSIWENCODEEXT)] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = 1,
+ .min_tokens = sizeof(struct iw_encode_ext),
+ .max_tokens = sizeof(struct iw_encode_ext) +
+ IW_ENCODING_TOKEN_MAX,
+ },
+ [IW_IOCTL_IDX(SIOCGIWENCODEEXT)] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = 1,
+ .min_tokens = sizeof(struct iw_encode_ext),
+ .max_tokens = sizeof(struct iw_encode_ext) +
+ IW_ENCODING_TOKEN_MAX,
+ },
+ [IW_IOCTL_IDX(SIOCSIWPMKSA)] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = 1,
+ .min_tokens = sizeof(struct iw_pmksa),
+ .max_tokens = sizeof(struct iw_pmksa),
+ },
+};
+static const unsigned int standard_ioctl_num = ARRAY_SIZE(standard_ioctl);
+
+/*
+ * Meta-data about all the additional standard Wireless Extension events
+ * we know about.
+ */
+static const struct iw_ioctl_description standard_event[] = {
+ [IW_EVENT_IDX(IWEVTXDROP)] = {
+ .header_type = IW_HEADER_TYPE_ADDR,
+ },
+ [IW_EVENT_IDX(IWEVQUAL)] = {
+ .header_type = IW_HEADER_TYPE_QUAL,
+ },
+ [IW_EVENT_IDX(IWEVCUSTOM)] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = 1,
+ .max_tokens = IW_CUSTOM_MAX,
+ },
+ [IW_EVENT_IDX(IWEVREGISTERED)] = {
+ .header_type = IW_HEADER_TYPE_ADDR,
+ },
+ [IW_EVENT_IDX(IWEVEXPIRED)] = {
+ .header_type = IW_HEADER_TYPE_ADDR,
+ },
+ [IW_EVENT_IDX(IWEVGENIE)] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = 1,
+ .max_tokens = IW_GENERIC_IE_MAX,
+ },
+ [IW_EVENT_IDX(IWEVMICHAELMICFAILURE)] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = 1,
+ .max_tokens = sizeof(struct iw_michaelmicfailure),
+ },
+ [IW_EVENT_IDX(IWEVASSOCREQIE)] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = 1,
+ .max_tokens = IW_GENERIC_IE_MAX,
+ },
+ [IW_EVENT_IDX(IWEVASSOCRESPIE)] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = 1,
+ .max_tokens = IW_GENERIC_IE_MAX,
+ },
+ [IW_EVENT_IDX(IWEVPMKIDCAND)] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = 1,
+ .max_tokens = sizeof(struct iw_pmkid_cand),
+ },
+};
+static const unsigned int standard_event_num = ARRAY_SIZE(standard_event);
+
+/* Size (in bytes) of various events */
+static const int event_type_size[] = {
+ IW_EV_LCP_LEN, /* IW_HEADER_TYPE_NULL */
+ 0,
+ IW_EV_CHAR_LEN, /* IW_HEADER_TYPE_CHAR */
+ 0,
+ IW_EV_UINT_LEN, /* IW_HEADER_TYPE_UINT */
+ IW_EV_FREQ_LEN, /* IW_HEADER_TYPE_FREQ */
+ IW_EV_ADDR_LEN, /* IW_HEADER_TYPE_ADDR */
+ 0,
+ IW_EV_POINT_LEN, /* Without variable payload */
+ IW_EV_PARAM_LEN, /* IW_HEADER_TYPE_PARAM */
+ IW_EV_QUAL_LEN, /* IW_HEADER_TYPE_QUAL */
+};
+
+#ifdef CONFIG_COMPAT
+static const int compat_event_type_size[] = {
+ IW_EV_COMPAT_LCP_LEN, /* IW_HEADER_TYPE_NULL */
+ 0,
+ IW_EV_COMPAT_CHAR_LEN, /* IW_HEADER_TYPE_CHAR */
+ 0,
+ IW_EV_COMPAT_UINT_LEN, /* IW_HEADER_TYPE_UINT */
+ IW_EV_COMPAT_FREQ_LEN, /* IW_HEADER_TYPE_FREQ */
+ IW_EV_COMPAT_ADDR_LEN, /* IW_HEADER_TYPE_ADDR */
+ 0,
+ IW_EV_COMPAT_POINT_LEN, /* Without variable payload */
+ IW_EV_COMPAT_PARAM_LEN, /* IW_HEADER_TYPE_PARAM */
+ IW_EV_COMPAT_QUAL_LEN, /* IW_HEADER_TYPE_QUAL */
+};
+#endif
+
+
+/* IW event code */
+
+void wireless_nlevent_flush(void)
+{
+ struct sk_buff *skb;
+ struct net *net;
+
+ ASSERT_RTNL();
+
+ for_each_net(net) {
+ while ((skb = skb_dequeue(&net->wext_nlevents)))
+ rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL,
+ GFP_KERNEL);
+ }
+}
+EXPORT_SYMBOL_GPL(wireless_nlevent_flush);
+
+static int wext_netdev_notifier_call(struct notifier_block *nb,
+ unsigned long state, void *ptr)
+{
+ /*
+ * When a netdev changes state in any way, flush all pending messages
+ * to avoid them going out in a strange order, e.g. RTM_NEWLINK after
+ * RTM_DELLINK, or with IFF_UP after without IFF_UP during dev_close()
+ * or similar - all of which could otherwise happen due to delays from
+ * schedule_work().
+ */
+ wireless_nlevent_flush();
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block wext_netdev_notifier = {
+ .notifier_call = wext_netdev_notifier_call,
+};
+
+static int __net_init wext_pernet_init(struct net *net)
+{
+ skb_queue_head_init(&net->wext_nlevents);
+ return 0;
+}
+
+static void __net_exit wext_pernet_exit(struct net *net)
+{
+ skb_queue_purge(&net->wext_nlevents);
+}
+
+static struct pernet_operations wext_pernet_ops = {
+ .init = wext_pernet_init,
+ .exit = wext_pernet_exit,
+};
+
+static int __init wireless_nlevent_init(void)
+{
+ int err = register_pernet_subsys(&wext_pernet_ops);
+
+ if (err)
+ return err;
+
+ err = register_netdevice_notifier(&wext_netdev_notifier);
+ if (err)
+ unregister_pernet_subsys(&wext_pernet_ops);
+ return err;
+}
+
+subsys_initcall(wireless_nlevent_init);
+
+/* Process events generated by the wireless layer or the driver. */
+static void wireless_nlevent_process(struct work_struct *work)
+{
+ rtnl_lock();
+ wireless_nlevent_flush();
+ rtnl_unlock();
+}
+
+static DECLARE_WORK(wireless_nlevent_work, wireless_nlevent_process);
+
+static struct nlmsghdr *rtnetlink_ifinfo_prep(struct net_device *dev,
+ struct sk_buff *skb)
+{
+ struct ifinfomsg *r;
+ struct nlmsghdr *nlh;
+
+ nlh = nlmsg_put(skb, 0, 0, RTM_NEWLINK, sizeof(*r), 0);
+ if (!nlh)
+ return NULL;
+
+ r = nlmsg_data(nlh);
+ r->ifi_family = AF_UNSPEC;
+ r->__ifi_pad = 0;
+ r->ifi_type = dev->type;
+ r->ifi_index = dev->ifindex;
+ r->ifi_flags = dev_get_flags(dev);
+ r->ifi_change = 0; /* Wireless changes don't affect those flags */
+
+ if (nla_put_string(skb, IFLA_IFNAME, dev->name))
+ goto nla_put_failure;
+
+ return nlh;
+ nla_put_failure:
+ nlmsg_cancel(skb, nlh);
+ return NULL;
+}
+
+
+/*
+ * Main event dispatcher. Called from other parts and drivers.
+ * Send the event on the appropriate channels.
+ * May be called from interrupt context.
+ */
+void wireless_send_event(struct net_device * dev,
+ unsigned int cmd,
+ union iwreq_data * wrqu,
+ const char * extra)
+{
+ const struct iw_ioctl_description * descr = NULL;
+ int extra_len = 0;
+ struct iw_event *event; /* Mallocated whole event */
+ int event_len; /* Its size */
+ int hdr_len; /* Size of the event header */
+ int wrqu_off = 0; /* Offset in wrqu */
+ /* Don't "optimise" the following variable, it will crash */
+ unsigned int cmd_index; /* *MUST* be unsigned */
+ struct sk_buff *skb;
+ struct nlmsghdr *nlh;
+ struct nlattr *nla;
+#ifdef CONFIG_COMPAT
+ struct __compat_iw_event *compat_event;
+ struct compat_iw_point compat_wrqu;
+ struct sk_buff *compskb;
+#endif
+
+ /*
+ * Nothing in the kernel sends scan events with data, be safe.
+ * This is necessary because we cannot fix up scan event data
+ * for compat, due to being contained in 'extra', but normally
+ * applications are required to retrieve the scan data anyway
+ * and no data is included in the event, this codifies that
+ * practice.
+ */
+ if (WARN_ON(cmd == SIOCGIWSCAN && extra))
+ extra = NULL;
+
+ /* Get the description of the Event */
+ if (cmd <= SIOCIWLAST) {
+ cmd_index = IW_IOCTL_IDX(cmd);
+ if (cmd_index < standard_ioctl_num)
+ descr = &(standard_ioctl[cmd_index]);
+ } else {
+ cmd_index = IW_EVENT_IDX(cmd);
+ if (cmd_index < standard_event_num)
+ descr = &(standard_event[cmd_index]);
+ }
+ /* Don't accept unknown events */
+ if (descr == NULL) {
+ /* Note : we don't return an error to the driver, because
+ * the driver would not know what to do about it. It can't
+ * return an error to the user, because the event is not
+ * initiated by a user request.
+ * The best the driver could do is to log an error message.
+ * We will do it ourselves instead...
+ */
+ netdev_err(dev, "(WE) : Invalid/Unknown Wireless Event (0x%04X)\n",
+ cmd);
+ return;
+ }
+
+ /* Check extra parameters and set extra_len */
+ if (descr->header_type == IW_HEADER_TYPE_POINT) {
+ /* Check if number of token fits within bounds */
+ if (wrqu->data.length > descr->max_tokens) {
+ netdev_err(dev, "(WE) : Wireless Event (cmd=0x%04X) too big (%d)\n",
+ cmd, wrqu->data.length);
+ return;
+ }
+ if (wrqu->data.length < descr->min_tokens) {
+ netdev_err(dev, "(WE) : Wireless Event (cmd=0x%04X) too small (%d)\n",
+ cmd, wrqu->data.length);
+ return;
+ }
+ /* Calculate extra_len - extra is NULL for restricted events */
+ if (extra != NULL)
+ extra_len = wrqu->data.length * descr->token_size;
+ /* Always at an offset in wrqu */
+ wrqu_off = IW_EV_POINT_OFF;
+ }
+
+ /* Total length of the event */
+ hdr_len = event_type_size[descr->header_type];
+ event_len = hdr_len + extra_len;
+
+ /*
+ * The problem for 64/32 bit.
+ *
+ * On 64-bit, a regular event is laid out as follows:
+ * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
+ * | event.len | event.cmd | p a d d i n g |
+ * | wrqu data ... (with the correct size) |
+ *
+ * This padding exists because we manipulate event->u,
+ * and 'event' is not packed.
+ *
+ * An iw_point event is laid out like this instead:
+ * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
+ * | event.len | event.cmd | p a d d i n g |
+ * | iwpnt.len | iwpnt.flg | p a d d i n g |
+ * | extra data ...
+ *
+ * The second padding exists because struct iw_point is extended,
+ * but this depends on the platform...
+ *
+ * On 32-bit, all the padding shouldn't be there.
+ */
+
+ skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
+ if (!skb)
+ return;
+
+ /* Send via the RtNetlink event channel */
+ nlh = rtnetlink_ifinfo_prep(dev, skb);
+ if (WARN_ON(!nlh)) {
+ kfree_skb(skb);
+ return;
+ }
+
+ /* Add the wireless events in the netlink packet */
+ nla = nla_reserve(skb, IFLA_WIRELESS, event_len);
+ if (!nla) {
+ kfree_skb(skb);
+ return;
+ }
+ event = nla_data(nla);
+
+ /* Fill event - first clear to avoid data leaking */
+ memset(event, 0, hdr_len);
+ event->len = event_len;
+ event->cmd = cmd;
+ memcpy(&event->u, ((char *) wrqu) + wrqu_off, hdr_len - IW_EV_LCP_LEN);
+ if (extra_len)
+ memcpy(((char *) event) + hdr_len, extra, extra_len);
+
+ nlmsg_end(skb, nlh);
+#ifdef CONFIG_COMPAT
+ hdr_len = compat_event_type_size[descr->header_type];
+ event_len = hdr_len + extra_len;
+
+ compskb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
+ if (!compskb) {
+ kfree_skb(skb);
+ return;
+ }
+
+ /* Send via the RtNetlink event channel */
+ nlh = rtnetlink_ifinfo_prep(dev, compskb);
+ if (WARN_ON(!nlh)) {
+ kfree_skb(skb);
+ kfree_skb(compskb);
+ return;
+ }
+
+ /* Add the wireless events in the netlink packet */
+ nla = nla_reserve(compskb, IFLA_WIRELESS, event_len);
+ if (!nla) {
+ kfree_skb(skb);
+ kfree_skb(compskb);
+ return;
+ }
+ compat_event = nla_data(nla);
+
+ compat_event->len = event_len;
+ compat_event->cmd = cmd;
+ if (descr->header_type == IW_HEADER_TYPE_POINT) {
+ compat_wrqu.length = wrqu->data.length;
+ compat_wrqu.flags = wrqu->data.flags;
+ memcpy(&compat_event->pointer,
+ ((char *) &compat_wrqu) + IW_EV_COMPAT_POINT_OFF,
+ hdr_len - IW_EV_COMPAT_LCP_LEN);
+ if (extra_len)
+ memcpy(((char *) compat_event) + hdr_len,
+ extra, extra_len);
+ } else {
+ /* extra_len must be zero, so no if (extra) needed */
+ memcpy(&compat_event->pointer, wrqu,
+ hdr_len - IW_EV_COMPAT_LCP_LEN);
+ }
+
+ nlmsg_end(compskb, nlh);
+
+ skb_shinfo(skb)->frag_list = compskb;
+#endif
+ skb_queue_tail(&dev_net(dev)->wext_nlevents, skb);
+ schedule_work(&wireless_nlevent_work);
+}
+EXPORT_SYMBOL(wireless_send_event);
+
+
+
+/* IW handlers */
+
+struct iw_statistics *get_wireless_stats(struct net_device *dev)
+{
+#ifdef CONFIG_WIRELESS_EXT
+ if ((dev->wireless_handlers != NULL) &&
+ (dev->wireless_handlers->get_wireless_stats != NULL))
+ return dev->wireless_handlers->get_wireless_stats(dev);
+#endif
+
+#ifdef CPTCFG_CFG80211_WEXT
+ if (dev->ieee80211_ptr &&
+ dev->ieee80211_ptr->wiphy &&
+ dev->ieee80211_ptr->wiphy->wext &&
+ dev->ieee80211_ptr->wiphy->wext->get_wireless_stats)
+ return dev->ieee80211_ptr->wiphy->wext->get_wireless_stats(dev);
+#endif
+
+ /* not found */
+ return NULL;
+}
+
+static int iw_handler_get_iwstats(struct net_device * dev,
+ struct iw_request_info * info,
+ union iwreq_data * wrqu,
+ char * extra)
+{
+ /* Get stats from the driver */
+ struct iw_statistics *stats;
+
+ stats = get_wireless_stats(dev);
+ if (stats) {
+ /* Copy statistics to extra */
+ memcpy(extra, stats, sizeof(struct iw_statistics));
+ wrqu->data.length = sizeof(struct iw_statistics);
+
+ /* Check if we need to clear the updated flag */
+ if (wrqu->data.flags != 0)
+ stats->qual.updated &= ~IW_QUAL_ALL_UPDATED;
+ return 0;
+ } else
+ return -EOPNOTSUPP;
+}
+
+static iw_handler get_handler(struct net_device *dev, unsigned int cmd)
+{
+ /* Don't "optimise" the following variable, it will crash */
+ unsigned int index; /* *MUST* be unsigned */
+ const struct iw_handler_def *handlers = NULL;
+
+#ifdef CPTCFG_CFG80211_WEXT
+ if (dev->ieee80211_ptr && dev->ieee80211_ptr->wiphy)
+ handlers = dev->ieee80211_ptr->wiphy->wext;
+#endif
+#ifdef CONFIG_WIRELESS_EXT
+ if (dev->wireless_handlers)
+ handlers = dev->wireless_handlers;
+#endif
+
+ if (!handlers)
+ return NULL;
+
+ /* Try as a standard command */
+ index = IW_IOCTL_IDX(cmd);
+ if (index < handlers->num_standard)
+ return handlers->standard[index];
+
+#ifdef CONFIG_WEXT_PRIV
+ /* Try as a private command */
+ index = cmd - SIOCIWFIRSTPRIV;
+ if (index < handlers->num_private)
+ return handlers->private[index];
+#endif
+
+ /* Not found */
+ return NULL;
+}
+
+static int ioctl_standard_iw_point(struct iw_point *iwp, unsigned int cmd,
+ const struct iw_ioctl_description *descr,
+ iw_handler handler, struct net_device *dev,
+ struct iw_request_info *info)
+{
+ int err, extra_size, user_length = 0, essid_compat = 0;
+ char *extra;
+
+ /* Calculate space needed by arguments. Always allocate
+ * for max space.
+ */
+ extra_size = descr->max_tokens * descr->token_size;
+
+ /* Check need for ESSID compatibility for WE < 21 */
+ switch (cmd) {
+ case SIOCSIWESSID:
+ case SIOCGIWESSID:
+ case SIOCSIWNICKN:
+ case SIOCGIWNICKN:
+ if (iwp->length == descr->max_tokens + 1)
+ essid_compat = 1;
+ else if (IW_IS_SET(cmd) && (iwp->length != 0)) {
+ char essid[IW_ESSID_MAX_SIZE + 1];
+ unsigned int len;
+ len = iwp->length * descr->token_size;
+
+ if (len > IW_ESSID_MAX_SIZE)
+ return -EFAULT;
+
+ err = copy_from_user(essid, iwp->pointer, len);
+ if (err)
+ return -EFAULT;
+
+ if (essid[iwp->length - 1] == '\0')
+ essid_compat = 1;
+ }
+ break;
+ default:
+ break;
+ }
+
+ iwp->length -= essid_compat;
+
+ /* Check what user space is giving us */
+ if (IW_IS_SET(cmd)) {
+ /* Check NULL pointer */
+ if (!iwp->pointer && iwp->length != 0)
+ return -EFAULT;
+ /* Check if number of token fits within bounds */
+ if (iwp->length > descr->max_tokens)
+ return -E2BIG;
+ if (iwp->length < descr->min_tokens)
+ return -EINVAL;
+ } else {
+ /* Check NULL pointer */
+ if (!iwp->pointer)
+ return -EFAULT;
+ /* Save user space buffer size for checking */
+ user_length = iwp->length;
+
+ /* Don't check if user_length > max to allow forward
+ * compatibility. The test user_length < min is
+ * implied by the test at the end.
+ */
+
+ /* Support for very large requests */
+ if ((descr->flags & IW_DESCR_FLAG_NOMAX) &&
+ (user_length > descr->max_tokens)) {
+ /* Allow userspace to GET more than max so
+ * we can support any size GET requests.
+ * There is still a limit : -ENOMEM.
+ */
+ extra_size = user_length * descr->token_size;
+
+ /* Note : user_length is originally a __u16,
+ * and token_size is controlled by us,
+ * so extra_size won't get negative and
+ * won't overflow...
+ */
+ }
+ }
+
+ /* kzalloc() ensures NULL-termination for essid_compat. */
+ extra = kzalloc(extra_size, GFP_KERNEL);
+ if (!extra)
+ return -ENOMEM;
+
+ /* If it is a SET, get all the extra data in here */
+ if (IW_IS_SET(cmd) && (iwp->length != 0)) {
+ if (copy_from_user(extra, iwp->pointer,
+ iwp->length *
+ descr->token_size)) {
+ err = -EFAULT;
+ goto out;
+ }
+
+ if (cmd == SIOCSIWENCODEEXT) {
+ struct iw_encode_ext *ee = (void *) extra;
+
+ if (iwp->length < sizeof(*ee) + ee->key_len) {
+ err = -EFAULT;
+ goto out;
+ }
+ }
+ }
+
+ if (IW_IS_GET(cmd) && !(descr->flags & IW_DESCR_FLAG_NOMAX)) {
+ /*
+ * If this is a GET, but not NOMAX, it means that the extra
+ * data is not bounded by userspace, but by max_tokens. Thus
+ * set the length to max_tokens. This matches the extra data
+ * allocation.
+ * The driver should fill it with the number of tokens it
+ * provided, and it may check iwp->length rather than having
+ * knowledge of max_tokens. If the driver doesn't change the
+ * iwp->length, this ioctl just copies back max_token tokens
+ * filled with zeroes. Hopefully the driver isn't claiming
+ * them to be valid data.
+ */
+ iwp->length = descr->max_tokens;
+ }
+
+ err = handler(dev, info, (union iwreq_data *) iwp, extra);
+
+ iwp->length += essid_compat;
+
+ /* If we have something to return to the user */
+ if (!err && IW_IS_GET(cmd)) {
+ /* Check if there is enough buffer up there */
+ if (user_length < iwp->length) {
+ err = -E2BIG;
+ goto out;
+ }
+
+ if (copy_to_user(iwp->pointer, extra,
+ iwp->length *
+ descr->token_size)) {
+ err = -EFAULT;
+ goto out;
+ }
+ }
+
+ /* Generate an event to notify listeners of the change */
+ if ((descr->flags & IW_DESCR_FLAG_EVENT) &&
+ ((err == 0) || (err == -EIWCOMMIT))) {
+ union iwreq_data *data = (union iwreq_data *) iwp;
+
+ if (descr->flags & IW_DESCR_FLAG_RESTRICT)
+ /* If the event is restricted, don't
+ * export the payload.
+ */
+ wireless_send_event(dev, cmd, data, NULL);
+ else
+ wireless_send_event(dev, cmd, data, extra);
+ }
+
+out:
+ kfree(extra);
+ return err;
+}
+
+/*
+ * Call the commit handler in the driver
+ * (if exist and if conditions are right)
+ *
+ * Note : our current commit strategy is currently pretty dumb,
+ * but we will be able to improve on that...
+ * The goal is to try to agreagate as many changes as possible
+ * before doing the commit. Drivers that will define a commit handler
+ * are usually those that need a reset after changing parameters, so
+ * we want to minimise the number of reset.
+ * A cool idea is to use a timer : at each "set" command, we re-set the
+ * timer, when the timer eventually fires, we call the driver.
+ * Hopefully, more on that later.
+ *
+ * Also, I'm waiting to see how many people will complain about the
+ * netif_running(dev) test. I'm open on that one...
+ * Hopefully, the driver will remember to do a commit in "open()" ;-)
+ */
+int call_commit_handler(struct net_device *dev)
+{
+#ifdef CONFIG_WIRELESS_EXT
+ if ((netif_running(dev)) &&
+ (dev->wireless_handlers->standard[0] != NULL))
+ /* Call the commit handler on the driver */
+ return dev->wireless_handlers->standard[0](dev, NULL,
+ NULL, NULL);
+ else
+ return 0; /* Command completed successfully */
+#else
+ /* cfg80211 has no commit */
+ return 0;
+#endif
+}
+
+/*
+ * Main IOCTl dispatcher.
+ * Check the type of IOCTL and call the appropriate wrapper...
+ */
+static int wireless_process_ioctl(struct net *net, struct iwreq *iwr,
+ unsigned int cmd,
+ struct iw_request_info *info,
+ wext_ioctl_func standard,
+ wext_ioctl_func private)
+{
+ struct net_device *dev;
+ iw_handler handler;
+
+ /* Permissions are already checked in dev_ioctl() before calling us.
+ * The copy_to/from_user() of ifr is also dealt with in there */
+
+ /* Make sure the device exist */
+ if ((dev = __dev_get_by_name(net, iwr->ifr_name)) == NULL)
+ return -ENODEV;
+
+ /* A bunch of special cases, then the generic case...
+ * Note that 'cmd' is already filtered in dev_ioctl() with
+ * (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) */
+ if (cmd == SIOCGIWSTATS)
+ return standard(dev, iwr, cmd, info,
+ &iw_handler_get_iwstats);
+
+#ifdef CONFIG_WEXT_PRIV
+ if (cmd == SIOCGIWPRIV && dev->wireless_handlers)
+ return standard(dev, iwr, cmd, info,
+ iw_handler_get_private);
+#endif
+
+ /* Basic check */
+ if (!netif_device_present(dev))
+ return -ENODEV;
+
+ /* New driver API : try to find the handler */
+ handler = get_handler(dev, cmd);
+ if (handler) {
+ /* Standard and private are not the same */
+ if (cmd < SIOCIWFIRSTPRIV)
+ return standard(dev, iwr, cmd, info, handler);
+ else if (private)
+ return private(dev, iwr, cmd, info, handler);
+ }
+ return -EOPNOTSUPP;
+}
+
+/* If command is `set a parameter', or `get the encoding parameters',
+ * check if the user has the right to do it.
+ */
+static int wext_permission_check(unsigned int cmd)
+{
+ if ((IW_IS_SET(cmd) || cmd == SIOCGIWENCODE ||
+ cmd == SIOCGIWENCODEEXT) &&
+ !capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ return 0;
+}
+
+/* entry point from dev ioctl */
+static int wext_ioctl_dispatch(struct net *net, struct iwreq *iwr,
+ unsigned int cmd, struct iw_request_info *info,
+ wext_ioctl_func standard,
+ wext_ioctl_func private)
+{
+ int ret = wext_permission_check(cmd);
+
+ if (ret)
+ return ret;
+
+ dev_load(net, iwr->ifr_name);
+ rtnl_lock();
+ ret = wireless_process_ioctl(net, iwr, cmd, info, standard, private);
+ rtnl_unlock();
+
+ return ret;
+}
+
+/*
+ * Wrapper to call a standard Wireless Extension handler.
+ * We do various checks and also take care of moving data between
+ * user space and kernel space.
+ */
+static int ioctl_standard_call(struct net_device * dev,
+ struct iwreq *iwr,
+ unsigned int cmd,
+ struct iw_request_info *info,
+ iw_handler handler)
+{
+ const struct iw_ioctl_description * descr;
+ int ret = -EINVAL;
+
+ /* Get the description of the IOCTL */
+ if (IW_IOCTL_IDX(cmd) >= standard_ioctl_num)
+ return -EOPNOTSUPP;
+ descr = &(standard_ioctl[IW_IOCTL_IDX(cmd)]);
+
+ /* Check if we have a pointer to user space data or not */
+ if (descr->header_type != IW_HEADER_TYPE_POINT) {
+
+ /* No extra arguments. Trivial to handle */
+ ret = handler(dev, info, &(iwr->u), NULL);
+
+ /* Generate an event to notify listeners of the change */
+ if ((descr->flags & IW_DESCR_FLAG_EVENT) &&
+ ((ret == 0) || (ret == -EIWCOMMIT)))
+ wireless_send_event(dev, cmd, &(iwr->u), NULL);
+ } else {
+ ret = ioctl_standard_iw_point(&iwr->u.data, cmd, descr,
+ handler, dev, info);
+ }
+
+ /* Call commit handler if needed and defined */
+ if (ret == -EIWCOMMIT)
+ ret = call_commit_handler(dev);
+
+ /* Here, we will generate the appropriate event if needed */
+
+ return ret;
+}
+
+
+int wext_handle_ioctl(struct net *net, struct iwreq *iwr, unsigned int cmd,
+ void __user *arg)
+{
+ struct iw_request_info info = { .cmd = cmd, .flags = 0 };
+ int ret;
+
+ ret = wext_ioctl_dispatch(net, iwr, cmd, &info,
+ ioctl_standard_call,
+ ioctl_private_call);
+ if (ret >= 0 &&
+ IW_IS_GET(cmd) &&
+ copy_to_user(arg, iwr, sizeof(struct iwreq)))
+ return -EFAULT;
+
+ return ret;
+}
+
+#ifdef CONFIG_COMPAT
+static int compat_standard_call(struct net_device *dev,
+ struct iwreq *iwr,
+ unsigned int cmd,
+ struct iw_request_info *info,
+ iw_handler handler)
+{
+ const struct iw_ioctl_description *descr;
+ struct compat_iw_point *iwp_compat;
+ struct iw_point iwp;
+ int err;
+
+ descr = standard_ioctl + IW_IOCTL_IDX(cmd);
+
+ if (descr->header_type != IW_HEADER_TYPE_POINT)
+ return ioctl_standard_call(dev, iwr, cmd, info, handler);
+
+ iwp_compat = (struct compat_iw_point *) &iwr->u.data;
+ iwp.pointer = compat_ptr(iwp_compat->pointer);
+ iwp.length = iwp_compat->length;
+ iwp.flags = iwp_compat->flags;
+
+ err = ioctl_standard_iw_point(&iwp, cmd, descr, handler, dev, info);
+
+ iwp_compat->pointer = ptr_to_compat(iwp.pointer);
+ iwp_compat->length = iwp.length;
+ iwp_compat->flags = iwp.flags;
+
+ return err;
+}
+
+int compat_wext_handle_ioctl(struct net *net, unsigned int cmd,
+ unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ struct iw_request_info info;
+ struct iwreq iwr;
+ char *colon;
+ int ret;
+
+ if (copy_from_user(&iwr, argp, sizeof(struct iwreq)))
+ return -EFAULT;
+
+ iwr.ifr_name[IFNAMSIZ-1] = 0;
+ colon = strchr(iwr.ifr_name, ':');
+ if (colon)
+ *colon = 0;
+
+ info.cmd = cmd;
+ info.flags = IW_REQUEST_FLAG_COMPAT;
+
+ ret = wext_ioctl_dispatch(net, &iwr, cmd, &info,
+ compat_standard_call,
+ compat_private_call);
+
+ if (ret >= 0 &&
+ IW_IS_GET(cmd) &&
+ copy_to_user(argp, &iwr, sizeof(struct iwreq)))
+ return -EFAULT;
+
+ return ret;
+}
+#endif
+
+char *iwe_stream_add_event(struct iw_request_info *info, char *stream,
+ char *ends, struct iw_event *iwe, int event_len)
+{
+ int lcp_len = iwe_stream_lcp_len(info);
+
+ event_len = iwe_stream_event_len_adjust(info, event_len);
+
+ /* Check if it's possible */
+ if (likely((stream + event_len) < ends)) {
+ iwe->len = event_len;
+ /* Beware of alignement issues on 64 bits */
+ memcpy(stream, (char *) iwe, IW_EV_LCP_PK_LEN);
+ memcpy(stream + lcp_len, &iwe->u,
+ event_len - lcp_len);
+ stream += event_len;
+ }
+
+ return stream;
+}
+EXPORT_SYMBOL(iwe_stream_add_event);
+
+char *iwe_stream_add_point(struct iw_request_info *info, char *stream,
+ char *ends, struct iw_event *iwe, char *extra)
+{
+ int event_len = iwe_stream_point_len(info) + iwe->u.data.length;
+ int point_len = iwe_stream_point_len(info);
+ int lcp_len = iwe_stream_lcp_len(info);
+
+ /* Check if it's possible */
+ if (likely((stream + event_len) < ends)) {
+ iwe->len = event_len;
+ memcpy(stream, (char *) iwe, IW_EV_LCP_PK_LEN);
+ memcpy(stream + lcp_len,
+ ((char *) &iwe->u) + IW_EV_POINT_OFF,
+ IW_EV_POINT_PK_LEN - IW_EV_LCP_PK_LEN);
+ if (iwe->u.data.length && extra)
+ memcpy(stream + point_len, extra, iwe->u.data.length);
+ stream += event_len;
+ }
+
+ return stream;
+}
+EXPORT_SYMBOL(iwe_stream_add_point);
+
+char *iwe_stream_add_value(struct iw_request_info *info, char *event,
+ char *value, char *ends, struct iw_event *iwe,
+ int event_len)
+{
+ int lcp_len = iwe_stream_lcp_len(info);
+
+ /* Don't duplicate LCP */
+ event_len -= IW_EV_LCP_LEN;
+
+ /* Check if it's possible */
+ if (likely((value + event_len) < ends)) {
+ /* Add new value */
+ memcpy(value, &iwe->u, event_len);
+ value += event_len;
+ /* Patch LCP */
+ iwe->len = value - event;
+ memcpy(event, (char *) iwe, lcp_len);
+ }
+
+ return value;
+}
+EXPORT_SYMBOL(iwe_stream_add_value);
diff --git a/net/wireless/wext-priv.c b/net/wireless/wext-priv.c
new file mode 100644
index 0000000..674d426
--- /dev/null
+++ b/net/wireless/wext-priv.c
@@ -0,0 +1,249 @@
+/*
+ * This file implement the Wireless Extensions priv API.
+ *
+ * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com>
+ * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved.
+ * Copyright 2009 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * (As all part of the Linux kernel, this file is GPL)
+ */
+#include <linux/slab.h>
+#include <linux/wireless.h>
+#include <linux/netdevice.h>
+#include <net/iw_handler.h>
+#include <net/wext.h>
+
+int iw_handler_get_private(struct net_device * dev,
+ struct iw_request_info * info,
+ union iwreq_data * wrqu,
+ char * extra)
+{
+ /* Check if the driver has something to export */
+ if ((dev->wireless_handlers->num_private_args == 0) ||
+ (dev->wireless_handlers->private_args == NULL))
+ return -EOPNOTSUPP;
+
+ /* Check if there is enough buffer up there */
+ if (wrqu->data.length < dev->wireless_handlers->num_private_args) {
+ /* User space can't know in advance how large the buffer
+ * needs to be. Give it a hint, so that we can support
+ * any size buffer we want somewhat efficiently... */
+ wrqu->data.length = dev->wireless_handlers->num_private_args;
+ return -E2BIG;
+ }
+
+ /* Set the number of available ioctls. */
+ wrqu->data.length = dev->wireless_handlers->num_private_args;
+
+ /* Copy structure to the user buffer. */
+ memcpy(extra, dev->wireless_handlers->private_args,
+ sizeof(struct iw_priv_args) * wrqu->data.length);
+
+ return 0;
+}
+
+/* Size (in bytes) of the various private data types */
+static const char iw_priv_type_size[] = {
+ 0, /* IW_PRIV_TYPE_NONE */
+ 1, /* IW_PRIV_TYPE_BYTE */
+ 1, /* IW_PRIV_TYPE_CHAR */
+ 0, /* Not defined */
+ sizeof(__u32), /* IW_PRIV_TYPE_INT */
+ sizeof(struct iw_freq), /* IW_PRIV_TYPE_FLOAT */
+ sizeof(struct sockaddr), /* IW_PRIV_TYPE_ADDR */
+ 0, /* Not defined */
+};
+
+static int get_priv_size(__u16 args)
+{
+ int num = args & IW_PRIV_SIZE_MASK;
+ int type = (args & IW_PRIV_TYPE_MASK) >> 12;
+
+ return num * iw_priv_type_size[type];
+}
+
+static int adjust_priv_size(__u16 args, struct iw_point *iwp)
+{
+ int num = iwp->length;
+ int max = args & IW_PRIV_SIZE_MASK;
+ int type = (args & IW_PRIV_TYPE_MASK) >> 12;
+
+ /* Make sure the driver doesn't goof up */
+ if (max < num)
+ num = max;
+
+ return num * iw_priv_type_size[type];
+}
+
+/*
+ * Wrapper to call a private Wireless Extension handler.
+ * We do various checks and also take care of moving data between
+ * user space and kernel space.
+ * It's not as nice and slimline as the standard wrapper. The cause
+ * is struct iw_priv_args, which was not really designed for the
+ * job we are going here.
+ *
+ * IMPORTANT : This function prevent to set and get data on the same
+ * IOCTL and enforce the SET/GET convention. Not doing it would be
+ * far too hairy...
+ * If you need to set and get data at the same time, please don't use
+ * a iw_handler but process it in your ioctl handler (i.e. use the
+ * old driver API).
+ */
+static int get_priv_descr_and_size(struct net_device *dev, unsigned int cmd,
+ const struct iw_priv_args **descrp)
+{
+ const struct iw_priv_args *descr;
+ int i, extra_size;
+
+ descr = NULL;
+ for (i = 0; i < dev->wireless_handlers->num_private_args; i++) {
+ if (cmd == dev->wireless_handlers->private_args[i].cmd) {
+ descr = &dev->wireless_handlers->private_args[i];
+ break;
+ }
+ }
+
+ extra_size = 0;
+ if (descr) {
+ if (IW_IS_SET(cmd)) {
+ int offset = 0; /* For sub-ioctls */
+ /* Check for sub-ioctl handler */
+ if (descr->name[0] == '\0')
+ /* Reserve one int for sub-ioctl index */
+ offset = sizeof(__u32);
+
+ /* Size of set arguments */
+ extra_size = get_priv_size(descr->set_args);
+
+ /* Does it fits in iwr ? */
+ if ((descr->set_args & IW_PRIV_SIZE_FIXED) &&
+ ((extra_size + offset) <= IFNAMSIZ))
+ extra_size = 0;
+ } else {
+ /* Size of get arguments */
+ extra_size = get_priv_size(descr->get_args);
+
+ /* Does it fits in iwr ? */
+ if ((descr->get_args & IW_PRIV_SIZE_FIXED) &&
+ (extra_size <= IFNAMSIZ))
+ extra_size = 0;
+ }
+ }
+ *descrp = descr;
+ return extra_size;
+}
+
+static int ioctl_private_iw_point(struct iw_point *iwp, unsigned int cmd,
+ const struct iw_priv_args *descr,
+ iw_handler handler, struct net_device *dev,
+ struct iw_request_info *info, int extra_size)
+{
+ char *extra;
+ int err;
+
+ /* Check what user space is giving us */
+ if (IW_IS_SET(cmd)) {
+ if (!iwp->pointer && iwp->length != 0)
+ return -EFAULT;
+
+ if (iwp->length > (descr->set_args & IW_PRIV_SIZE_MASK))
+ return -E2BIG;
+ } else if (!iwp->pointer)
+ return -EFAULT;
+
+ extra = kzalloc(extra_size, GFP_KERNEL);
+ if (!extra)
+ return -ENOMEM;
+
+ /* If it is a SET, get all the extra data in here */
+ if (IW_IS_SET(cmd) && (iwp->length != 0)) {
+ if (copy_from_user(extra, iwp->pointer, extra_size)) {
+ err = -EFAULT;
+ goto out;
+ }
+ }
+
+ /* Call the handler */
+ err = handler(dev, info, (union iwreq_data *) iwp, extra);
+
+ /* If we have something to return to the user */
+ if (!err && IW_IS_GET(cmd)) {
+ /* Adjust for the actual length if it's variable,
+ * avoid leaking kernel bits outside.
+ */
+ if (!(descr->get_args & IW_PRIV_SIZE_FIXED))
+ extra_size = adjust_priv_size(descr->get_args, iwp);
+
+ if (copy_to_user(iwp->pointer, extra, extra_size))
+ err = -EFAULT;
+ }
+
+out:
+ kfree(extra);
+ return err;
+}
+
+int ioctl_private_call(struct net_device *dev, struct iwreq *iwr,
+ unsigned int cmd, struct iw_request_info *info,
+ iw_handler handler)
+{
+ int extra_size = 0, ret = -EINVAL;
+ const struct iw_priv_args *descr;
+
+ extra_size = get_priv_descr_and_size(dev, cmd, &descr);
+
+ /* Check if we have a pointer to user space data or not. */
+ if (extra_size == 0) {
+ /* No extra arguments. Trivial to handle */
+ ret = handler(dev, info, &(iwr->u), (char *) &(iwr->u));
+ } else {
+ ret = ioctl_private_iw_point(&iwr->u.data, cmd, descr,
+ handler, dev, info, extra_size);
+ }
+
+ /* Call commit handler if needed and defined */
+ if (ret == -EIWCOMMIT)
+ ret = call_commit_handler(dev);
+
+ return ret;
+}
+
+#ifdef CONFIG_COMPAT
+int compat_private_call(struct net_device *dev, struct iwreq *iwr,
+ unsigned int cmd, struct iw_request_info *info,
+ iw_handler handler)
+{
+ const struct iw_priv_args *descr;
+ int ret, extra_size;
+
+ extra_size = get_priv_descr_and_size(dev, cmd, &descr);
+
+ /* Check if we have a pointer to user space data or not. */
+ if (extra_size == 0) {
+ /* No extra arguments. Trivial to handle */
+ ret = handler(dev, info, &(iwr->u), (char *) &(iwr->u));
+ } else {
+ struct compat_iw_point *iwp_compat;
+ struct iw_point iwp;
+
+ iwp_compat = (struct compat_iw_point *) &iwr->u.data;
+ iwp.pointer = compat_ptr(iwp_compat->pointer);
+ iwp.length = iwp_compat->length;
+ iwp.flags = iwp_compat->flags;
+
+ ret = ioctl_private_iw_point(&iwp, cmd, descr,
+ handler, dev, info, extra_size);
+
+ iwp_compat->pointer = ptr_to_compat(iwp.pointer);
+ iwp_compat->length = iwp.length;
+ iwp_compat->flags = iwp.flags;
+ }
+
+ /* Call commit handler if needed and defined */
+ if (ret == -EIWCOMMIT)
+ ret = call_commit_handler(dev);
+
+ return ret;
+}
+#endif
diff --git a/net/wireless/wext-proc.c b/net/wireless/wext-proc.c
new file mode 100644
index 0000000..b65984f
--- /dev/null
+++ b/net/wireless/wext-proc.c
@@ -0,0 +1,156 @@
+/*
+ * This file implement the Wireless Extensions proc API.
+ *
+ * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com>
+ * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved.
+ *
+ * (As all part of the Linux kernel, this file is GPL)
+ */
+
+/*
+ * The /proc/net/wireless file is a human readable user-space interface
+ * exporting various wireless specific statistics from the wireless devices.
+ * This is the most popular part of the Wireless Extensions ;-)
+ *
+ * This interface is a pure clone of /proc/net/dev (in net/core/dev.c).
+ * The content of the file is basically the content of "struct iw_statistics".
+ */
+
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/wireless.h>
+#include <linux/netdevice.h>
+#include <linux/rtnetlink.h>
+#include <net/iw_handler.h>
+#include <net/wext.h>
+
+
+static void wireless_seq_printf_stats(struct seq_file *seq,
+ struct net_device *dev)
+{
+ /* Get stats from the driver */
+ struct iw_statistics *stats = get_wireless_stats(dev);
+ static struct iw_statistics nullstats = {};
+
+ /* show device if it's wireless regardless of current stats */
+ if (!stats) {
+#ifdef CONFIG_WIRELESS_EXT
+ if (dev->wireless_handlers)
+ stats = &nullstats;
+#endif
+#ifdef CPTCFG_CFG80211
+ if (dev->ieee80211_ptr)
+ stats = &nullstats;
+#endif
+ }
+
+ if (stats) {
+ seq_printf(seq, "%6s: %04x %3d%c %3d%c %3d%c %6d %6d %6d "
+ "%6d %6d %6d\n",
+ dev->name, stats->status, stats->qual.qual,
+ stats->qual.updated & IW_QUAL_QUAL_UPDATED
+ ? '.' : ' ',
+ ((__s32) stats->qual.level) -
+ ((stats->qual.updated & IW_QUAL_DBM) ? 0x100 : 0),
+ stats->qual.updated & IW_QUAL_LEVEL_UPDATED
+ ? '.' : ' ',
+ ((__s32) stats->qual.noise) -
+ ((stats->qual.updated & IW_QUAL_DBM) ? 0x100 : 0),
+ stats->qual.updated & IW_QUAL_NOISE_UPDATED
+ ? '.' : ' ',
+ stats->discard.nwid, stats->discard.code,
+ stats->discard.fragment, stats->discard.retries,
+ stats->discard.misc, stats->miss.beacon);
+
+ if (stats != &nullstats)
+ stats->qual.updated &= ~IW_QUAL_ALL_UPDATED;
+ }
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Print info for /proc/net/wireless (print all entries)
+ */
+static int wireless_dev_seq_show(struct seq_file *seq, void *v)
+{
+ might_sleep();
+
+ if (v == SEQ_START_TOKEN)
+ seq_printf(seq, "Inter-| sta-| Quality | Discarded "
+ "packets | Missed | WE\n"
+ " face | tus | link level noise | nwid "
+ "crypt frag retry misc | beacon | %d\n",
+ WIRELESS_EXT);
+ else
+ wireless_seq_printf_stats(seq, v);
+ return 0;
+}
+
+static void *wireless_dev_seq_start(struct seq_file *seq, loff_t *pos)
+{
+ struct net *net = seq_file_net(seq);
+ loff_t off;
+ struct net_device *dev;
+
+ rtnl_lock();
+ if (!*pos)
+ return SEQ_START_TOKEN;
+
+ off = 1;
+ for_each_netdev(net, dev)
+ if (off++ == *pos)
+ return dev;
+ return NULL;
+}
+
+static void *wireless_dev_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+ struct net *net = seq_file_net(seq);
+
+ ++*pos;
+
+ return v == SEQ_START_TOKEN ?
+ first_net_device(net) : next_net_device(v);
+}
+
+static void wireless_dev_seq_stop(struct seq_file *seq, void *v)
+{
+ rtnl_unlock();
+}
+
+static const struct seq_operations wireless_seq_ops = {
+ .start = wireless_dev_seq_start,
+ .next = wireless_dev_seq_next,
+ .stop = wireless_dev_seq_stop,
+ .show = wireless_dev_seq_show,
+};
+
+static int seq_open_wireless(struct inode *inode, struct file *file)
+{
+ return seq_open_net(inode, file, &wireless_seq_ops,
+ sizeof(struct seq_net_private));
+}
+
+static const struct file_operations wireless_seq_fops = {
+ .owner = THIS_MODULE,
+ .open = seq_open_wireless,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release_net,
+};
+
+int __net_init wext_proc_init(struct net *net)
+{
+ /* Create /proc/net/wireless entry */
+ if (!proc_create("wireless", S_IRUGO, net->proc_net,
+ &wireless_seq_fops))
+ return -ENOMEM;
+
+ return 0;
+}
+
+void __net_exit wext_proc_exit(struct net *net)
+{
+ remove_proc_entry("wireless", net->proc_net);
+}
diff --git a/net/wireless/wext-sme.c b/net/wireless/wext-sme.c
new file mode 100644
index 0000000..c434f19
--- /dev/null
+++ b/net/wireless/wext-sme.c
@@ -0,0 +1,390 @@
+/*
+ * cfg80211 wext compat for managed mode.
+ *
+ * Copyright 2009 Johannes Berg <johannes@sipsolutions.net>
+ * Copyright (C) 2009 Intel Corporation. All rights reserved.
+ */
+
+#include <linux/export.h>
+#include <linux/etherdevice.h>
+#include <linux/if_arp.h>
+#include <linux/slab.h>
+#include <net/cfg80211.h>
+#include <net/cfg80211-wext.h>
+#include "wext-compat.h"
+#include "nl80211.h"
+
+int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev)
+{
+ struct cfg80211_cached_keys *ck = NULL;
+ const u8 *prev_bssid = NULL;
+ int err, i;
+
+ ASSERT_RTNL();
+ ASSERT_WDEV_LOCK(wdev);
+
+ if (!netif_running(wdev->netdev))
+ return 0;
+
+ wdev->wext.connect.ie = wdev->wext.ie;
+ wdev->wext.connect.ie_len = wdev->wext.ie_len;
+
+ /* Use default background scan period */
+ wdev->wext.connect.bg_scan_period = -1;
+
+ if (wdev->wext.keys) {
+ wdev->wext.keys->def = wdev->wext.default_key;
+ if (wdev->wext.default_key != -1)
+ wdev->wext.connect.privacy = true;
+ }
+
+ if (!wdev->wext.connect.ssid_len)
+ return 0;
+
+ if (wdev->wext.keys && wdev->wext.keys->def != -1) {
+ ck = kmemdup(wdev->wext.keys, sizeof(*ck), GFP_KERNEL);
+ if (!ck)
+ return -ENOMEM;
+ for (i = 0; i < CFG80211_MAX_WEP_KEYS; i++)
+ ck->params[i].key = ck->data[i];
+ }
+
+ if (wdev->wext.prev_bssid_valid)
+ prev_bssid = wdev->wext.prev_bssid;
+
+ err = cfg80211_connect(rdev, wdev->netdev,
+ &wdev->wext.connect, ck, prev_bssid);
+ if (err)
+ kzfree(ck);
+
+ return err;
+}
+
+int cfg80211_mgd_wext_siwfreq(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_freq *wextfreq, char *extra)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+ struct ieee80211_channel *chan = NULL;
+ int err, freq;
+
+ /* call only for station! */
+ if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
+ return -EINVAL;
+
+ freq = cfg80211_wext_freq(wextfreq);
+ if (freq < 0)
+ return freq;
+
+ if (freq) {
+ chan = ieee80211_get_channel(wdev->wiphy, freq);
+ if (!chan)
+ return -EINVAL;
+ if (chan->flags & IEEE80211_CHAN_DISABLED)
+ return -EINVAL;
+ }
+
+ wdev_lock(wdev);
+
+ if (wdev->conn) {
+ bool event = true;
+
+ if (wdev->wext.connect.channel == chan) {
+ err = 0;
+ goto out;
+ }
+
+ /* if SSID set, we'll try right again, avoid event */
+ if (wdev->wext.connect.ssid_len)
+ event = false;
+ err = cfg80211_disconnect(rdev, dev,
+ WLAN_REASON_DEAUTH_LEAVING, event);
+ if (err)
+ goto out;
+ }
+
+ wdev->wext.connect.channel = chan;
+ err = cfg80211_mgd_wext_connect(rdev, wdev);
+ out:
+ wdev_unlock(wdev);
+ return err;
+}
+
+int cfg80211_mgd_wext_giwfreq(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_freq *freq, char *extra)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct ieee80211_channel *chan = NULL;
+
+ /* call only for station! */
+ if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
+ return -EINVAL;
+
+ wdev_lock(wdev);
+ if (wdev->current_bss)
+ chan = wdev->current_bss->pub.channel;
+ else if (wdev->wext.connect.channel)
+ chan = wdev->wext.connect.channel;
+ wdev_unlock(wdev);
+
+ if (chan) {
+ freq->m = chan->center_freq;
+ freq->e = 6;
+ return 0;
+ }
+
+ /* no channel if not joining */
+ return -EINVAL;
+}
+
+int cfg80211_mgd_wext_siwessid(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *data, char *ssid)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+ size_t len = data->length;
+ int err;
+
+ /* call only for station! */
+ if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
+ return -EINVAL;
+
+ if (!data->flags)
+ len = 0;
+
+ /* iwconfig uses nul termination in SSID.. */
+ if (len > 0 && ssid[len - 1] == '\0')
+ len--;
+
+ wdev_lock(wdev);
+
+ err = 0;
+
+ if (wdev->conn) {
+ bool event = true;
+
+ if (wdev->wext.connect.ssid && len &&
+ len == wdev->wext.connect.ssid_len &&
+ memcmp(wdev->wext.connect.ssid, ssid, len) == 0)
+ goto out;
+
+ /* if SSID set now, we'll try to connect, avoid event */
+ if (len)
+ event = false;
+ err = cfg80211_disconnect(rdev, dev,
+ WLAN_REASON_DEAUTH_LEAVING, event);
+ if (err)
+ goto out;
+ }
+
+ wdev->wext.prev_bssid_valid = false;
+ wdev->wext.connect.ssid = wdev->wext.ssid;
+ memcpy(wdev->wext.ssid, ssid, len);
+ wdev->wext.connect.ssid_len = len;
+
+ wdev->wext.connect.crypto.control_port = false;
+ wdev->wext.connect.crypto.control_port_ethertype =
+ cpu_to_be16(ETH_P_PAE);
+
+ err = cfg80211_mgd_wext_connect(rdev, wdev);
+ out:
+ wdev_unlock(wdev);
+ return err;
+}
+
+int cfg80211_mgd_wext_giwessid(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *data, char *ssid)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+
+ /* call only for station! */
+ if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
+ return -EINVAL;
+
+ data->flags = 0;
+
+ wdev_lock(wdev);
+ if (wdev->current_bss) {
+ const u8 *ie;
+
+ rcu_read_lock();
+ ie = ieee80211_bss_get_ie(&wdev->current_bss->pub,
+ WLAN_EID_SSID);
+ if (ie) {
+ data->flags = 1;
+ data->length = ie[1];
+ memcpy(ssid, ie + 2, data->length);
+ }
+ rcu_read_unlock();
+ } else if (wdev->wext.connect.ssid && wdev->wext.connect.ssid_len) {
+ data->flags = 1;
+ data->length = wdev->wext.connect.ssid_len;
+ memcpy(ssid, wdev->wext.connect.ssid, data->length);
+ }
+ wdev_unlock(wdev);
+
+ return 0;
+}
+
+int cfg80211_mgd_wext_siwap(struct net_device *dev,
+ struct iw_request_info *info,
+ struct sockaddr *ap_addr, char *extra)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+ u8 *bssid = ap_addr->sa_data;
+ int err;
+
+ /* call only for station! */
+ if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
+ return -EINVAL;
+
+ if (ap_addr->sa_family != ARPHRD_ETHER)
+ return -EINVAL;
+
+ /* automatic mode */
+ if (is_zero_ether_addr(bssid) || is_broadcast_ether_addr(bssid))
+ bssid = NULL;
+
+ wdev_lock(wdev);
+
+ if (wdev->conn) {
+ err = 0;
+ /* both automatic */
+ if (!bssid && !wdev->wext.connect.bssid)
+ goto out;
+
+ /* fixed already - and no change */
+ if (wdev->wext.connect.bssid && bssid &&
+ ether_addr_equal(bssid, wdev->wext.connect.bssid))
+ goto out;
+
+ err = cfg80211_disconnect(rdev, dev,
+ WLAN_REASON_DEAUTH_LEAVING, false);
+ if (err)
+ goto out;
+ }
+
+ if (bssid) {
+ memcpy(wdev->wext.bssid, bssid, ETH_ALEN);
+ wdev->wext.connect.bssid = wdev->wext.bssid;
+ } else
+ wdev->wext.connect.bssid = NULL;
+
+ err = cfg80211_mgd_wext_connect(rdev, wdev);
+ out:
+ wdev_unlock(wdev);
+ return err;
+}
+
+int cfg80211_mgd_wext_giwap(struct net_device *dev,
+ struct iw_request_info *info,
+ struct sockaddr *ap_addr, char *extra)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+
+ /* call only for station! */
+ if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
+ return -EINVAL;
+
+ ap_addr->sa_family = ARPHRD_ETHER;
+
+ wdev_lock(wdev);
+ if (wdev->current_bss)
+ memcpy(ap_addr->sa_data, wdev->current_bss->pub.bssid, ETH_ALEN);
+ else
+ eth_zero_addr(ap_addr->sa_data);
+ wdev_unlock(wdev);
+
+ return 0;
+}
+
+int cfg80211_wext_siwgenie(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *data, char *extra)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+ u8 *ie = extra;
+ int ie_len = data->length, err;
+
+ if (wdev->iftype != NL80211_IFTYPE_STATION)
+ return -EOPNOTSUPP;
+
+ if (!ie_len)
+ ie = NULL;
+
+ wdev_lock(wdev);
+
+ /* no change */
+ err = 0;
+ if (wdev->wext.ie_len == ie_len &&
+ memcmp(wdev->wext.ie, ie, ie_len) == 0)
+ goto out;
+
+ if (ie_len) {
+ ie = kmemdup(extra, ie_len, GFP_KERNEL);
+ if (!ie) {
+ err = -ENOMEM;
+ goto out;
+ }
+ } else
+ ie = NULL;
+
+ kfree(wdev->wext.ie);
+ wdev->wext.ie = ie;
+ wdev->wext.ie_len = ie_len;
+
+ if (wdev->conn) {
+ err = cfg80211_disconnect(rdev, dev,
+ WLAN_REASON_DEAUTH_LEAVING, false);
+ if (err)
+ goto out;
+ }
+
+ /* userspace better not think we'll reconnect */
+ err = 0;
+ out:
+ wdev_unlock(wdev);
+ return err;
+}
+
+int cfg80211_wext_siwmlme(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *data, char *extra)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct iw_mlme *mlme = (struct iw_mlme *)extra;
+ struct cfg80211_registered_device *rdev;
+ int err;
+
+ if (!wdev)
+ return -EOPNOTSUPP;
+
+ rdev = wiphy_to_rdev(wdev->wiphy);
+
+ if (wdev->iftype != NL80211_IFTYPE_STATION)
+ return -EINVAL;
+
+ if (mlme->addr.sa_family != ARPHRD_ETHER)
+ return -EINVAL;
+
+ wdev_lock(wdev);
+ switch (mlme->cmd) {
+ case IW_MLME_DEAUTH:
+ case IW_MLME_DISASSOC:
+ err = cfg80211_disconnect(rdev, dev, mlme->reason_code, true);
+ break;
+ default:
+ err = -EOPNOTSUPP;
+ break;
+ }
+ wdev_unlock(wdev);
+
+ return err;
+}
diff --git a/net/wireless/wext-spy.c b/net/wireless/wext-spy.c
new file mode 100644
index 0000000..33bef22
--- /dev/null
+++ b/net/wireless/wext-spy.c
@@ -0,0 +1,232 @@
+/*
+ * This file implement the Wireless Extensions spy API.
+ *
+ * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com>
+ * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved.
+ *
+ * (As all part of the Linux kernel, this file is GPL)
+ */
+
+#include <linux/wireless.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/export.h>
+#include <net/iw_handler.h>
+#include <net/arp.h>
+#include <net/wext.h>
+
+static inline struct iw_spy_data *get_spydata(struct net_device *dev)
+{
+ /* This is the new way */
+ if (dev->wireless_data)
+ return dev->wireless_data->spy_data;
+ return NULL;
+}
+
+int iw_handler_set_spy(struct net_device * dev,
+ struct iw_request_info * info,
+ union iwreq_data * wrqu,
+ char * extra)
+{
+ struct iw_spy_data * spydata = get_spydata(dev);
+ struct sockaddr * address = (struct sockaddr *) extra;
+
+ /* Make sure driver is not buggy or using the old API */
+ if (!spydata)
+ return -EOPNOTSUPP;
+
+ /* Disable spy collection while we copy the addresses.
+ * While we copy addresses, any call to wireless_spy_update()
+ * will NOP. This is OK, as anyway the addresses are changing. */
+ spydata->spy_number = 0;
+
+ /* We want to operate without locking, because wireless_spy_update()
+ * most likely will happen in the interrupt handler, and therefore
+ * have its own locking constraints and needs performance.
+ * The rtnl_lock() make sure we don't race with the other iw_handlers.
+ * This make sure wireless_spy_update() "see" that the spy list
+ * is temporarily disabled. */
+ smp_wmb();
+
+ /* Are there are addresses to copy? */
+ if (wrqu->data.length > 0) {
+ int i;
+
+ /* Copy addresses */
+ for (i = 0; i < wrqu->data.length; i++)
+ memcpy(spydata->spy_address[i], address[i].sa_data,
+ ETH_ALEN);
+ /* Reset stats */
+ memset(spydata->spy_stat, 0,
+ sizeof(struct iw_quality) * IW_MAX_SPY);
+ }
+
+ /* Make sure above is updated before re-enabling */
+ smp_wmb();
+
+ /* Enable addresses */
+ spydata->spy_number = wrqu->data.length;
+
+ return 0;
+}
+EXPORT_SYMBOL(iw_handler_set_spy);
+
+int iw_handler_get_spy(struct net_device * dev,
+ struct iw_request_info * info,
+ union iwreq_data * wrqu,
+ char * extra)
+{
+ struct iw_spy_data * spydata = get_spydata(dev);
+ struct sockaddr * address = (struct sockaddr *) extra;
+ int i;
+
+ /* Make sure driver is not buggy or using the old API */
+ if (!spydata)
+ return -EOPNOTSUPP;
+
+ wrqu->data.length = spydata->spy_number;
+
+ /* Copy addresses. */
+ for (i = 0; i < spydata->spy_number; i++) {
+ memcpy(address[i].sa_data, spydata->spy_address[i], ETH_ALEN);
+ address[i].sa_family = AF_UNIX;
+ }
+ /* Copy stats to the user buffer (just after). */
+ if (spydata->spy_number > 0)
+ memcpy(extra + (sizeof(struct sockaddr) *spydata->spy_number),
+ spydata->spy_stat,
+ sizeof(struct iw_quality) * spydata->spy_number);
+ /* Reset updated flags. */
+ for (i = 0; i < spydata->spy_number; i++)
+ spydata->spy_stat[i].updated &= ~IW_QUAL_ALL_UPDATED;
+ return 0;
+}
+EXPORT_SYMBOL(iw_handler_get_spy);
+
+/*------------------------------------------------------------------*/
+/*
+ * Standard Wireless Handler : set spy threshold
+ */
+int iw_handler_set_thrspy(struct net_device * dev,
+ struct iw_request_info *info,
+ union iwreq_data * wrqu,
+ char * extra)
+{
+ struct iw_spy_data * spydata = get_spydata(dev);
+ struct iw_thrspy * threshold = (struct iw_thrspy *) extra;
+
+ /* Make sure driver is not buggy or using the old API */
+ if (!spydata)
+ return -EOPNOTSUPP;
+
+ /* Just do it */
+ memcpy(&(spydata->spy_thr_low), &(threshold->low),
+ 2 * sizeof(struct iw_quality));
+
+ /* Clear flag */
+ memset(spydata->spy_thr_under, '\0', sizeof(spydata->spy_thr_under));
+
+ return 0;
+}
+EXPORT_SYMBOL(iw_handler_set_thrspy);
+
+/*------------------------------------------------------------------*/
+/*
+ * Standard Wireless Handler : get spy threshold
+ */
+int iw_handler_get_thrspy(struct net_device * dev,
+ struct iw_request_info *info,
+ union iwreq_data * wrqu,
+ char * extra)
+{
+ struct iw_spy_data * spydata = get_spydata(dev);
+ struct iw_thrspy * threshold = (struct iw_thrspy *) extra;
+
+ /* Make sure driver is not buggy or using the old API */
+ if (!spydata)
+ return -EOPNOTSUPP;
+
+ /* Just do it */
+ memcpy(&(threshold->low), &(spydata->spy_thr_low),
+ 2 * sizeof(struct iw_quality));
+
+ return 0;
+}
+EXPORT_SYMBOL(iw_handler_get_thrspy);
+
+/*------------------------------------------------------------------*/
+/*
+ * Prepare and send a Spy Threshold event
+ */
+static void iw_send_thrspy_event(struct net_device * dev,
+ struct iw_spy_data * spydata,
+ unsigned char * address,
+ struct iw_quality * wstats)
+{
+ union iwreq_data wrqu;
+ struct iw_thrspy threshold;
+
+ /* Init */
+ wrqu.data.length = 1;
+ wrqu.data.flags = 0;
+ /* Copy address */
+ memcpy(threshold.addr.sa_data, address, ETH_ALEN);
+ threshold.addr.sa_family = ARPHRD_ETHER;
+ /* Copy stats */
+ memcpy(&(threshold.qual), wstats, sizeof(struct iw_quality));
+ /* Copy also thresholds */
+ memcpy(&(threshold.low), &(spydata->spy_thr_low),
+ 2 * sizeof(struct iw_quality));
+
+ /* Send event to user space */
+ wireless_send_event(dev, SIOCGIWTHRSPY, &wrqu, (char *) &threshold);
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Call for the driver to update the spy data.
+ * For now, the spy data is a simple array. As the size of the array is
+ * small, this is good enough. If we wanted to support larger number of
+ * spy addresses, we should use something more efficient...
+ */
+void wireless_spy_update(struct net_device * dev,
+ unsigned char * address,
+ struct iw_quality * wstats)
+{
+ struct iw_spy_data * spydata = get_spydata(dev);
+ int i;
+ int match = -1;
+
+ /* Make sure driver is not buggy or using the old API */
+ if (!spydata)
+ return;
+
+ /* Update all records that match */
+ for (i = 0; i < spydata->spy_number; i++)
+ if (ether_addr_equal(address, spydata->spy_address[i])) {
+ memcpy(&(spydata->spy_stat[i]), wstats,
+ sizeof(struct iw_quality));
+ match = i;
+ }
+
+ /* Generate an event if we cross the spy threshold.
+ * To avoid event storms, we have a simple hysteresis : we generate
+ * event only when we go under the low threshold or above the
+ * high threshold. */
+ if (match >= 0) {
+ if (spydata->spy_thr_under[match]) {
+ if (wstats->level > spydata->spy_thr_high.level) {
+ spydata->spy_thr_under[match] = 0;
+ iw_send_thrspy_event(dev, spydata,
+ address, wstats);
+ }
+ } else {
+ if (wstats->level < spydata->spy_thr_low.level) {
+ spydata->spy_thr_under[match] = 1;
+ iw_send_thrspy_event(dev, spydata,
+ address, wstats);
+ }
+ }
+ }
+}
+EXPORT_SYMBOL(wireless_spy_update);
diff --git a/scripts/blacklist.sh b/scripts/blacklist.sh
new file mode 100755
index 0000000..f941c4a
--- /dev/null
+++ b/scripts/blacklist.sh
@@ -0,0 +1,35 @@
+#!/bin/bash
+
+BLACKLIST_CONF="/etc/modprobe.d/backports.conf"
+BLACKLIST_MAP=".blacklist.map"
+
+MODULE_DIR=$1
+MODULE_UPDATES=$2
+
+if [[ ! -d $MODULE_DIR ]]; then
+ exit
+fi
+
+if [[ ! -d $MODULE_UPDATES ]]; then
+ exit
+fi
+
+mkdir -p $(dirname $BLACKLIST_CONF)
+rm -f $BLACKLIST_CONF
+
+echo "# To be used when using backported drivers" > $BLACKLIST_CONF
+
+for i in $(grep -v ^# $BLACKLIST_MAP | awk '{print $2}'); do
+ MODULE="${i}.ko"
+ MODULE_UPDATE="$(grep -v ^# $BLACKLIST_MAP | grep $i | awk '{print $1}' | head -1).ko"
+
+ COUNT=$(find $MODULE_DIR -type f -name ${MODULE} -or -name ${MODULE}.gz | wc -l)
+ COUNT_REPLACE=$(find $MODULE_UPDATES -type f -name ${MODULE_UPDATE} -or -name ${MODULE_UPDATE}.gz | wc -l)
+
+ if [ $COUNT -ne 0 ]; then
+ if [ $COUNT_REPLACE -ne 0 ]; then
+ echo "Blacklisting $MODULE ..."
+ echo blacklist $i >> $BLACKLIST_CONF
+ fi
+ fi
+done
diff --git a/scripts/check_depmod.sh b/scripts/check_depmod.sh
new file mode 100755
index 0000000..6123894
--- /dev/null
+++ b/scripts/check_depmod.sh
@@ -0,0 +1,78 @@
+#!/bin/bash
+# Copyright 2009-2013 Luis R. Rodriguez <mcgrof@do-not-panic.com>
+#
+# Ensures your distribution likes to prefer updates/ over the kernel/
+# search updates built-in
+
+# Seems Mandriva has an $DEPMOD_DIR but it doesn't have any files,
+# so lets deal with those distributions.
+DEPMOD_CONF="/etc/depmod.conf"
+DEPMOD_CONF_TMP="$DEPMOD_CONF.backports.old"
+DEPMOD_DIR="/etc/depmod.d/"
+BACKPORT_DEPMOD_FILE="backports.conf"
+GREP_REGEX_UPDATES="^[[:space:]]*search.*[[:space:]]updates\([[:space:]]\|$\)"
+GREP_REGEX_SEARCH="^[[:space:]]*search[[:space:]].\+$"
+DEPMOD_CMD="depmod"
+
+function add_compat_depmod_conf {
+ echo "NOTE: Your distribution lacks an $DEPMOD_DIR directory with "
+ echo "updates/ directory being prioritized for modules, we're adding "
+ echo "one for you."
+ mkdir -p $DEPMOD_DIR
+ FIRST_FILE=$(ls $DEPMOD_DIR|head -1)
+ [ -n "$FIRST_FILE" ] && while [[ $FIRST_FILE < $BACKPORT_DEPMOD_FILE ]]; do
+ BACKPORT_DEPMOD_FILE="0$BACKPORT_DEPMOD_FILE"
+ done
+ echo "search updates" > $DEPMOD_DIR/$BACKPORT_DEPMOD_FILE
+}
+
+function add_global_depmod_conf {
+ echo "NOTE: Your distribution lacks updates/ directory being"
+ echo "prioritized for modules, we're adding it to $DEPMOD_CONF."
+ rm -f $DEPMOD_CONF_TMP
+ [ -f $DEPMOD_CONF ] && cp -f $DEPMOD_CONF $DEPMOD_CONF_TMP
+ echo "search updates" > $DEPMOD_CONF
+ [ -f $DEPMOD_CONF_TMP ] && cat $DEPMOD_CONF_TMP >> $DEPMOD_CONF
+}
+
+function depmod_updates_ok {
+ echo "depmod will prefer updates/ over kernel/ -- OK!"
+}
+
+function add_depmod_conf {
+ if [ -f "$DEPMOD_CONF" ]; then
+ add_global_depmod_conf
+ else
+ DEPMOD_VERSION=$($DEPMOD_CMD --version | cut -d" " -f2 | sed "s/\.//")
+ if [[ $DEPMOD_VERSION -gt 36 ]]; then
+ add_compat_depmod_conf
+ else
+ add_global_depmod_conf
+ fi
+ fi
+}
+
+GREP_FILES=""
+[ -f $DEPMOD_CONF ] && GREP_FILES="$DEPMOD_CONF"
+if [ -d $DEPMOD_DIR ]; then
+ DEPMOD_FILE_COUNT=$(ls $DEPMOD_DIR | wc -l)
+ [[ $DEPMOD_FILE_COUNT -gt 0 ]] && GREP_FILES="$GREP_FILES $DEPMOD_DIR/*"
+fi
+
+if [ -n "$GREP_FILES" ]; then
+ grep -q "$GREP_REGEX_SEARCH" $GREP_FILES
+ if [[ $? -eq 0 ]]; then
+ grep -q "$GREP_REGEX_UPDATES" $GREP_FILES
+ if [[ $? -eq 0 ]]; then
+ depmod_updates_ok
+ else
+ add_depmod_conf
+ fi
+ else
+ depmod_updates_ok
+ fi
+else
+ depmod_updates_ok
+fi
+
+exit 0
diff --git a/scripts/compress_modules.sh b/scripts/compress_modules.sh
new file mode 100755
index 0000000..b2034c2
--- /dev/null
+++ b/scripts/compress_modules.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+set -e
+
+source ./scripts/mod_helpers.sh
+
+if test "$(mod_filename mac80211)" = "mac80211.ko.gz" ; then
+ for driver in $(find "$1" -type f -name *.ko); do
+ echo COMPRESS $driver
+ gzip -9 $driver
+ done
+fi
diff --git a/scripts/mod_helpers.sh b/scripts/mod_helpers.sh
new file mode 100644
index 0000000..0845b3e
--- /dev/null
+++ b/scripts/mod_helpers.sh
@@ -0,0 +1,11 @@
+function mod_filename()
+{
+ which modinfo > /dev/null 2>&1
+ if [[ $? -eq 0 ]]; then
+ MOD_QUERY="modinfo -F filename"
+ else
+ MOD_QUERY="modprobe -l"
+ fi
+ mod_path="$($MOD_QUERY $1 | tail -1)"
+ echo $(basename "$mod_path")
+}
diff --git a/scripts/uninstall.sh b/scripts/uninstall.sh
new file mode 100755
index 0000000..b46d3fe
--- /dev/null
+++ b/scripts/uninstall.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+set -e
+
+source ./scripts/mod_helpers.sh
+
+if test "$(mod_filename mac80211)" = "mac80211.ko.gz" ; then
+ compr=".gz"
+else
+ compr=""
+fi
+
+for driver in $(find ${BACKPORT_DIR} -type f -name *.ko); do
+ mod_name=${driver/${BACKPORT_DIR}/${KLIB}${KMODDIR}}${compr}
+ echo " uninstall" $mod_name
+ rm -f $mod_name
+done
diff --git a/scripts/update-initramfs.sh b/scripts/update-initramfs.sh
new file mode 100755
index 0000000..e0d7702
--- /dev/null
+++ b/scripts/update-initramfs.sh
@@ -0,0 +1,62 @@
+#!/bin/bash
+# Copyright 2009-2013 Luis R. Rodriguez <mcgrof@do-not-panic.com>
+#
+# Since we provide ssb, ethernet modules and most importantly
+# DRM drivers, people may want to update the initramfs image
+# of their distribution. This can also help people who may
+# want to wireless-boot their systems.
+
+KLIB="$1"
+ver=$(echo $KLIB | awk -F "/lib/modules/" '{print $2}' | awk -F"/" '{print $1}')
+dir=/boot/
+
+LSB_RED_ID=$(/usr/bin/lsb_release -i -s &> /dev/null)
+
+if [[ -z $LSB_RED_ID && -f "/etc/os-release" ]]; then
+ # Let's try with os-release. Fedora doesn't have
+ # lsb_release anymore.
+ LSB_RED_ID=$(sed -n '/^NAME/ s/^NAME=\(.*\)$/\1/p' /etc/os-release)
+fi
+
+case $LSB_RED_ID in
+"Ubuntu")
+ echo "Updating ${LSB_RED_ID}'s initramfs for $ver under $dir ..."
+ mkinitramfs -o $dir/initrd.img-$ver $ver
+ echo "Will now run update-grub to ensure grub will find the new initramfs ..."
+ update-grub
+ ;;
+"Debian")
+ echo "Updating ${LSB_RED_ID}'s initramfs for $ver under $dir ..."
+ mkinitramfs -o $dir/initrd.img-$ver $ver
+ echo "Will now run update-grub to ensure grub will find the new initramfs ..."
+ update-grub
+ ;;
+"Fedora")
+ # This adds a -compat-drivers suffixed initramfs with a new grub2
+ # entry to not override distribution's default stuff.
+ INITRAMFS=${dir}initramfs-$ver-compat-drivers.img
+ KERNEL=${dir}vmlinuz-$ver
+ GRUB_TITLE="Fedora ($ver) with compat-drivers"
+
+ echo "Updating ${LSB_RED_ID}'s initramfs for $ver under $dir ..."
+ mkinitrd --force $INITRAMFS $ver
+
+ # If a previous compat-drivers entry for the same kernel exists
+ # do not add it again.
+ grep -q "${GRUB_TITLE}" /etc/grub2.cfg &> /dev/null
+ if [[ "$?" == "1" ]]; then
+ echo "Will now run grubby to add a new kernel entry ..."
+ # Add a new kernel entry
+ grubby --grub2 --copy-default --add-kernel="$KERNEL" --initrd="$INITRAMFS" --title="$GRUB_TITLE"
+ fi
+ ;;
+*)
+ echo "Note:"
+ echo "You may or may not need to update your initramfs, you should if"
+ echo "any of the modules installed are part of your initramfs. To add"
+ echo "support for your distribution to do this automatically send a"
+ echo "patch against \"$(basename $0)\". If your distribution does not"
+ echo "require this send a patch with the '/usr/bin/lsb_release -i -s'"
+ echo "($LSB_RED_ID) tag for your distribution to avoid this warning."
+ ;;
+esac
diff --git a/versions b/versions
new file mode 100644
index 0000000..59864f7
--- /dev/null
+++ b/versions
@@ -0,0 +1,3 @@
+BACKPORTS_VERSION="backports-20160324-111-g97b8d7c"
+BACKPORTED_KERNEL_VERSION="v4.12-0-g6f7da29"
+BACKPORTED_KERNEL_NAME="Linux"