[rdkb][common][app][Add atenl tool for wifi test mode]
[Description]
Add atenl tool for wifi test mode
1. add atenl test mode tool
2. remove iwpriv from wireless tools, because of iwpriv command of wireless-tool is conflict with the iwpriv of atenl
[Release-log]
diff --git a/recipes-devtools/atenl/Makefile b/recipes-devtools/atenl/Makefile
new file mode 100644
index 0000000..d1c9d36
--- /dev/null
+++ b/recipes-devtools/atenl/Makefile
@@ -0,0 +1,35 @@
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=atenl
+PKG_RELEASE=1
+
+PKG_LICENSE:=GPLv2
+PKG_LICENSE_FILES:=
+
+PKG_MAINTAINER:=Shayne Chen <shayne.chen@mediatek.com>
+PKG_BUILD_PARALLEL:=1
+
+include $(INCLUDE_DIR)/package.mk
+include $(INCLUDE_DIR)/cmake.mk
+
+CMAKE_SOURCE_DIR:=$(PKG_BUILD_DIR)
+CMAKE_BINARY_DIR:=$(PKG_BUILD_DIR)
+
+define Package/atenl
+ SECTION:=MTK Properties
+ CATEGORY:=MTK Properties
+ TITLE:=testmode daemon for nl80211
+ SUBMENU:=Applications
+ DEPENDS:=+libnl-tiny
+endef
+
+TARGET_CFLAGS += -I$(STAGING_DIR)/usr/include/libnl-tiny
+
+define Package/atenl/install
+ mkdir -p $(1)/usr/sbin
+ $(INSTALL_BIN) $(PKG_BUILD_DIR)/atenl $(1)/usr/sbin
+ $(INSTALL_BIN) ./files/ated.sh $(1)/usr/sbin/ated
+ $(INSTALL_BIN) ./files/iwpriv.sh $(1)/usr/sbin/iwpriv
+endef
+
+$(eval $(call BuildPackage,atenl))
diff --git a/recipes-devtools/atenl/atenl.bb b/recipes-devtools/atenl/atenl.bb
new file mode 100644
index 0000000..ca5cb4e
--- /dev/null
+++ b/recipes-devtools/atenl/atenl.bb
@@ -0,0 +1,27 @@
+DESCRIPTION = "testmode daemon for nl80211"
+SECTION = "applications"
+LICENSE = "GPLv2"
+LIC_FILES_CHKSUM = "file://COPYING;md5=751419260aa954499f7abaabaa882bbe"
+
+DEPENDS += "libnl-tiny"
+RDEPENDS_${PN} += "busybox"
+inherit pkgconfig cmake
+
+SRC_URI = " \
+ file://COPYING;subdir=git/src \
+ file://src;subdir=git \
+ file://ated.sh;subdir=git \
+ file://iwpriv.sh;subdir=git \
+ "
+
+S = "${WORKDIR}/git/src"
+
+CFLAGS_append = " -I=${includedir}/libnl-tiny "
+
+
+do_install_append() {
+ install -d ${D}${sbindir}
+ install -m 0755 ${WORKDIR}/git/ated.sh ${D}${sbindir}/ated
+ install -m 0755 ${WORKDIR}/git/iwpriv.sh ${D}${sbindir}/iwpriv
+}
+
diff --git a/recipes-devtools/atenl/files/COPYING b/recipes-devtools/atenl/files/COPYING
new file mode 100644
index 0000000..d511905
--- /dev/null
+++ b/recipes-devtools/atenl/files/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/recipes-devtools/atenl/files/ated.sh b/recipes-devtools/atenl/files/ated.sh
new file mode 100644
index 0000000..1c24083
--- /dev/null
+++ b/recipes-devtools/atenl/files/ated.sh
@@ -0,0 +1,115 @@
+#!/bin/ash
+# This script is used for wrapping atenl daemon to ated
+# 0 is normal mode, 1 is used for doing specific commands such as "sync eeprom all"
+
+work_mode="RUN" # RUN/PRINT/DEBUG
+mode="0"
+add_quote="0"
+cmd="atenl"
+interface=""
+phy_idx=0
+ated_file="/tmp/interface"
+
+function do_cmd() {
+ case ${work_mode} in
+ "RUN")
+ eval "$1"
+ ;;
+ "PRINT")
+ echo "$1"
+ ;;
+ "DEBUG")
+ eval "$1"
+ echo "$1"
+ ;;
+ esac
+}
+
+function record_config() {
+ local tmp_file=$3
+ if [ -f ${tmp_file} ]; then
+ if grep -q $1 ${tmp_file}; then
+ sed -i "/$1/c\\$1=$2" ${tmp_file}
+ else
+ echo "$1=$2" >> ${tmp_file}
+ fi
+ else
+ echo "$1=$2" >> ${tmp_file}
+ fi
+}
+
+function get_config() {
+ local tmp_file=$2
+ if [ ! -f ${tmp_file} ]; then
+ echo ""
+ return
+ fi
+
+ if grep -q $1 ${tmp_file}; then
+ echo "$(cat ${tmp_file} | grep $1 | sed s/=/' '/g | cut -d " " -f 2)"
+ else
+ echo ""
+ fi
+}
+
+function convert_interface {
+ local start_idx_7986=$(get_config "STARTIDX" ${ated_file})
+ local eeprom_file=/sys/kernel/debug/ieee80211/phy0/mt76/eeprom
+ if [ -z "${start_idx_7986}" ]; then
+ if [ ! -z "$(head -c 2 ${eeprom_file} | hexdump | grep "7916")" ]; then
+ start_idx_7986="2"
+ elif [ ! -z "$(head -c 2 ${eeprom_file} | hexdump | grep "7915")" ]; then
+ start_idx_7986="1"
+ elif [ ! -z "$(head -c 2 ${eeprom_file} | hexdump | grep "7986")" ]; then
+ start_idx_7986="0"
+ else
+ echo "Interface conversion failed!"
+ echo "Please use ated -i <phy0/phy1/..> ... or configure the sku of your board manually by the following commands"
+ echo "For AX6000: echo STARTIDX=0 >> ${ated_file}"
+ echo "For AX7800: echo STARTIDX=2 >> ${ated_file}"
+ echo "For AX8400: echo STARTIDX=1 >> ${ated_file}"
+ return 0
+ fi
+ record_config "STARTIDX" ${start_idx_7986} ${ated_file}
+ fi
+
+ if [[ $1 == "raix"* ]]; then
+ interface="phy1"
+ phy_idx=1
+ elif [[ $1 == "rai"* ]]; then
+ interface="phy0"
+ phy_idx=0
+ elif [[ $1 == "rax"* ]]; then
+ phy_idx=$((start_idx_7986+1))
+ interface="phy${phy_idx}"
+ else
+ phy_idx=$start_idx_7986
+ interface="phy${phy_idx}"
+ fi
+}
+
+
+for i in "$@"
+do
+ if [ "$i" = "-c" ]; then
+ cmd="${cmd} -c"
+ mode="1"
+ add_quote="1"
+ elif [ "${add_quote}" = "1" ]; then
+ cmd="${cmd} \"${i}\""
+ add_quote="0"
+ else
+ if [[ ${i} == "ra"* ]]; then
+ convert_interface $i
+ cmd="${cmd} ${interface}"
+ else
+ cmd="${cmd} ${i}"
+ fi
+ fi
+done
+
+if [ "$mode" = "0" ]; then
+ killall atenl > /dev/null 2>&1
+fi
+
+do_cmd "${cmd}"
diff --git a/recipes-devtools/atenl/files/iwpriv.sh b/recipes-devtools/atenl/files/iwpriv.sh
new file mode 100644
index 0000000..910e314
--- /dev/null
+++ b/recipes-devtools/atenl/files/iwpriv.sh
@@ -0,0 +1,931 @@
+#!/bin/ash
+
+interface=$1 # phy0/phy1/ra0
+cmd_type=$2 # set/show/e2p/mac
+full_cmd=$3
+interface_ori=${interface}
+start_idx_7986="0"
+
+work_mode="RUN" # RUN/PRINT/DEBUG
+iwpriv_file="/tmp/iwpriv_wrapper"
+interface_file="/tmp/interface"
+phy_idx=$(echo ${interface} | tr -dc '0-9')
+
+function do_cmd() {
+ case ${work_mode} in
+ "RUN")
+ eval "$1"
+ ;;
+ "PRINT")
+ echo "$1"
+ ;;
+ "DEBUG")
+ eval "$1"
+ echo "$1"
+ ;;
+ esac
+}
+
+function print_debug() {
+ if [ "${work_mode}" = "DEBUG" ]; then
+ echo "$1"
+ fi
+}
+
+function write_dmesg() {
+ echo "$1" > /dev/kmsg
+}
+
+function record_config() {
+ local config=$1
+ local tmp_file=$3
+
+ # check is mt7986 or mt7915/7916, and write its config
+ if [ ${config} != "STARTIDX" ]; then
+ if [ $phy_idx -lt $start_idx_7986 ]; then
+ config="${config}_PCIE"
+ elif [ $phy_idx -ge $start_idx_7986 ]; then
+ config="${config}_7986"
+ fi
+ fi
+
+ if [ -f ${tmp_file} ]; then
+ if grep -q ${config} ${tmp_file}; then
+ sed -i "/${config}/c\\${config}=$2" ${tmp_file}
+ else
+ echo "${config}=$2" >> ${tmp_file}
+ fi
+ else
+ echo "${config}=$2" >> ${tmp_file}
+ fi
+}
+
+function get_config() {
+ local config=$1
+ local tmp_file=$2
+
+ if [ ! -f ${tmp_file} ]; then
+ echo ""
+ return
+ fi
+
+ # check is mt7986 or mt7915/7916, and load its config
+ if [ ${config} != "STARTIDX" ]; then
+ if [ $phy_idx -lt $start_idx_7986 ]; then
+ config="${config}_PCIE"
+ elif [ $phy_idx -ge $start_idx_7986 ]; then
+ config="${config}_7986"
+ fi
+ fi
+
+ if grep -q ${config} ${tmp_file}; then
+ echo "$(cat ${tmp_file} | grep ${config} | sed s/=/' '/g | cut -d " " -f 2)"
+ else
+ echo ""
+ fi
+}
+
+function convert_interface {
+ start_idx_7986=$(get_config "STARTIDX" ${interface_file})
+ local eeprom_file=/sys/kernel/debug/ieee80211/phy0/mt76/eeprom
+ if [ -z "${start_idx_7986}" ]; then
+ if [ ! -z "$(head -c 2 ${eeprom_file} | hexdump | grep "7916")" ]; then
+ start_idx_7986="2"
+ elif [ ! -z "$(head -c 2 ${eeprom_file} | hexdump | grep "7915")" ]; then
+ start_idx_7986="1"
+ elif [ ! -z "$(head -c 2 ${eeprom_file} | hexdump | grep "7986")" ]; then
+ start_idx_7986="0"
+ else
+ echo "Interface Conversion Failed!"
+ echo "Please use iwpriv <phy0/phy1/..> set <...> or configure the sku of your board manually by the following commands"
+ echo "For AX6000: echo STARTIDX=0 >> ${interface_file}"
+ echo "For AX7800: echo STARTIDX=2 >> ${interface_file}"
+ echo "For AX8400: echo STARTIDX=1 >> ${interface_file}"
+ exit 0
+ fi
+ record_config "STARTIDX" ${start_idx_7986} ${interface_file}
+ fi
+
+ if [[ $1 == "raix"* ]]; then
+ phy_idx=1
+ elif [[ $1 == "rai"* ]]; then
+ phy_idx=0
+ elif [[ $1 == "rax"* ]]; then
+ phy_idx=$((start_idx_7986+1))
+ else
+ phy_idx=$start_idx_7986
+ fi
+
+ # convert phy index according to band idx
+ local band_idx=$(get_config "ATECTRLBANDIDX" ${iwpriv_file})
+ if [ "${band_idx}" = "0" ]; then
+ if [[ $1 == "raix"* ]]; then
+ phy_idx=0
+ elif [[ $1 == "rax"* ]]; then
+ phy_idx=$start_idx_7986
+ fi
+ elif [ "${band_idx}" = "1" ]; then
+ if [[ $1 == "rai"* ]]; then
+ phy_idx=1
+ elif [[ $1 == "ra"* ]]; then
+ phy_idx=$((start_idx_7986+1))
+ fi
+ fi
+
+ interface="phy${phy_idx}"
+}
+
+function change_band_idx {
+ local new_idx=$1
+ local new_phy_idx=$phy_idx
+
+ local old_idx=$(get_config "ATECTRLBANDIDX" ${iwpriv_file})
+
+
+ if [[ ${interface_ori} == "ra"* ]]; then
+ if [ -z "${old_idx}" ] || [ "${old_idx}" != "${new_idx}" ]; then
+ if [ "${new_idx}" = "0" ]; then
+ # raix0 & rai0 becomes rai0
+ if [[ $interface_ori == "rai"* ]]; then
+ new_phy_idx=0
+ # rax0 & ra0 becomes ra0
+ elif [[ $interface_ori == "ra"* ]]; then
+ new_phy_idx=$start_idx_7986
+ fi
+ elif [ "${new_idx}" = "1" ]; then
+ # raix0 & rai0 becomes raix0
+ if [[ $interface_ori == "rai"* ]]; then
+ new_phy_idx=1
+ # rax0 & ra0 becomes rax0
+ elif [[ $interface_ori == "ra"* ]]; then
+ new_phy_idx=$((start_idx_7986+1))
+ fi
+ fi
+ fi
+
+ if [ ${new_phy_idx} != ${phy_idx} ]; then
+ do_ate_work "ATESTOP"
+ phy_idx=$new_phy_idx
+ interface="phy${phy_idx}"
+ do_ate_work "ATESTART"
+ fi
+ fi
+ record_config "ATECTRLBANDIDX" ${new_idx} ${iwpriv_file}
+}
+
+function simple_convert() {
+ if [ "$1" = "ATETXCNT" ]; then
+ echo "tx_count"
+ elif [ "$1" = "ATETXLEN" ]; then
+ echo "tx_length"
+ elif [ "$1" = "ATETXMCS" ]; then
+ echo "tx_rate_idx"
+ elif [ "$1" = "ATEVHTNSS" ]; then
+ echo "tx_rate_nss"
+ elif [ "$1" = "ATETXLDPC" ]; then
+ echo "tx_rate_ldpc"
+ elif [ "$1" = "ATETXSTBC" ]; then
+ echo "tx_rate_stbc"
+ elif [ "$1" = "ATEPKTTXTIME" ]; then
+ echo "tx_time"
+ elif [ "$1" = "ATEIPG" ]; then
+ echo "tx_ipg"
+ elif [ "$1" = "ATEDUTYCYCLE" ]; then
+ echo "tx_duty_cycle"
+ elif [ "$1" = "ATETXFREQOFFSET" ]; then
+ echo "freq_offset"
+ else
+ echo "undefined"
+ fi
+}
+
+function convert_tx_mode() {
+ if [ "$1" = "0" ]; then
+ echo "cck"
+ elif [ "$1" = "1" ]; then
+ echo "ofdm"
+ elif [ "$1" = "2" ]; then
+ echo "ht"
+ elif [ "$1" = "4" ]; then
+ echo "vht"
+ elif [ "$1" = "8" ]; then
+ echo "he_su"
+ elif [ "$1" = "9" ]; then
+ echo "he_er"
+ elif [ "$1" = "10" ]; then
+ echo "he_tb"
+ elif [ "$1" = "11" ]; then
+ echo "he_mu"
+ else
+ echo "undefined"
+ fi
+}
+
+function convert_gi {
+ local tx_mode=$1
+ local val=$2
+ local sgi="0"
+ local he_ltf="0"
+
+ case ${tx_mode} in
+ "ht"|"vht")
+ sgi=${val}
+ ;;
+ "he_su"|"he_er")
+ case ${val} in
+ "0")
+ ;;
+ "1")
+ he_ltf="1"
+ ;;
+ "2")
+ sgi="1"
+ he_ltf="1"
+ ;;
+ "3")
+ sgi="2"
+ he_ltf="2"
+ ;;
+ "4")
+ he_ltf="2"
+ ;;
+ *)
+ echo "unknown gi"
+ esac
+ ;;
+ "he_mu")
+ case ${val} in
+ "0")
+ he_ltf="2"
+ ;;
+ "1")
+ he_ltf="1"
+ ;;
+ "2")
+ sgi="1"
+ he_ltf="1"
+ ;;
+ "3")
+ sgi="2"
+ he_ltf="2"
+ ;;
+ *)
+ echo "unknown gi"
+ esac
+ ;;
+ "he_tb")
+ case ${val} in
+ "0")
+ sgi="1"
+ ;;
+ "1")
+ sgi="1"
+ he_ltf="1"
+ ;;
+ "2")
+ sgi="2"
+ he_ltf="2"
+ ;;
+ *)
+ echo "unknown gi"
+ esac
+ ;;
+ *)
+ print_debug "legacy mode no need gi"
+ esac
+
+ do_cmd "mt76-test ${interface} set tx_rate_sgi=${sgi} tx_ltf=${he_ltf}"
+}
+
+function convert_channel {
+ local ctrl_band_idx=$(get_config "ATECTRLBANDIDX" ${iwpriv_file})
+ local ch=$(echo $1 | sed s/:/' '/g | cut -d " " -f 1)
+ local bw=$(get_config "ATETXBW" ${iwpriv_file} | cut -d ":" -f 1)
+ local bw_str="HT20"
+ local base_chan=1
+ local control_freq=0
+ local base_freq=0
+
+ if [ -z ${ctrl_band_idx} ]; then
+ local band=$(echo $1 | sed s/:/' '/g | cut -d " " -f 2)
+ else
+ local band=$ctrl_band_idx
+ fi
+
+ if [[ $1 != *":"* ]] || [ "${band}" = "0" ]; then
+ case ${bw} in
+ "1")
+ if [ "${ch}" -lt "3" ] || [ "${ch}" -gt "12" ]; then
+ local bw_str="HT20"
+ else
+ local bw_str="HT40+"
+ ch=$(expr ${ch} - "2")
+ fi
+ ;;
+ esac
+ local base_freq=2412
+ elif [ "${band}" = "1" ]; then
+ case ${bw} in
+ "5")
+ bw_str="160MHz"
+ if [ ${ch} -lt "68" ]; then
+ ch="36"
+ elif [ ${ch} -lt "100" ]; then
+ ch="68"
+ elif [ ${ch} -lt "132" ]; then
+ ch="100"
+ elif [ ${ch} -lt "181" ]; then
+ ch="149"
+ fi
+ ;;
+ "2")
+ bw_str="80MHz"
+ if [ ${ch} -lt "52" ]; then
+ ch="36"
+ elif [ ${ch} -lt "68" ]; then
+ ch="52"
+ elif [ ${ch} -lt "84" ]; then
+ ch="68"
+ elif [ ${ch} -lt "100" ]; then
+ ch="84"
+ elif [ ${ch} -lt "116" ]; then
+ ch="100"
+ elif [ ${ch} -lt "132" ]; then
+ ch="116"
+ elif [ ${ch} -lt "149" ]; then
+ ch="132"
+ elif [ ${ch} -lt "165" ]; then
+ ch="149"
+ elif [ ${ch} -lt "181" ]; then
+ ch="165"
+ fi
+ ;;
+ "1")
+ if [ ${ch} -lt "44" ]; then
+ ch=$([ "${ch}" -lt "40" ] && echo "36" || echo "40")
+ bw_str=$([ "${ch}" -le "38" ] && echo "HT40+" || echo "HT40-")
+ elif [ ${ch} -lt "52" ]; then
+ ch=$([ "${ch}" -lt "48" ] && echo "44" || echo "48")
+ bw_str=$([ "${ch}" -le "46" ] && echo "HT40+" || echo "HT40-")
+ elif [ ${ch} -lt "60" ]; then
+ ch=$([ "${ch}" -lt "56" ] && echo "52" || echo "56")
+ bw_str=$([ "${ch}" -le "54" ] && echo "HT40+" || echo "HT40-")
+ elif [ ${ch} -lt "68" ]; then
+ ch=$([ "${ch}" -lt "64" ] && echo "60" || echo "64")
+ bw_str=$([ "${ch}" -le "62" ] && echo "HT40+" || echo "HT40-")
+ elif [ ${ch} -lt "76" ]; then
+ ch=$([ "${ch}" -lt "72" ] && echo "68" || echo "72")
+ bw_str=$([ "${ch}" -le "70" ] && echo "HT40+" || echo "HT40-")
+ elif [ ${ch} -lt "84" ]; then
+ ch=$([ "${ch}" -lt "80" ] && echo "76" || echo "80")
+ bw_str=$([ "${ch}" -le "78" ] && echo "HT40+" || echo "HT40-")
+ elif [ ${ch} -lt "92" ]; then
+ ch=$([ "${ch}" -lt "88" ] && echo "84" || echo "88")
+ bw_str=$([ "${ch}" -le "86" ] && echo "HT40+" || echo "HT40-")
+ elif [ ${ch} -lt "100" ]; then
+ ch=$([ "${ch}" -lt "96" ] && echo "92" || echo "96")
+ bw_str=$([ "${ch}" -le "94" ] && echo "HT40+" || echo "HT40-")
+ elif [ ${ch} -lt "108" ]; then
+ ch=$([ "${ch}" -lt "104" ] && echo "100" || echo "104")
+ bw_str=$([ "${ch}" -le "102" ] && echo "HT40+" || echo "HT40-")
+ elif [ ${ch} -lt "116" ]; then
+ ch=$([ "${ch}" -lt "112" ] && echo "108" || echo "112")
+ bw_str=$([ "${ch}" -le "110" ] && echo "HT40+" || echo "HT40-")
+ elif [ ${ch} -lt "124" ]; then
+ ch=$([ "${ch}" -lt "120" ] && echo "116" || echo "120")
+ bw_str=$([ "${ch}" -le "118" ] && echo "HT40+" || echo "HT40-")
+ elif [ ${ch} -lt "132" ]; then
+ ch=$([ "${ch}" -lt "128" ] && echo "124" || echo "128")
+ bw_str=$([ "${ch}" -le "126" ] && echo "HT40+" || echo "HT40-")
+ elif [ ${ch} -lt "140" ]; then
+ ch=$([ "${ch}" -lt "136" ] && echo "132" || echo "136")
+ bw_str=$([ "${ch}" -le "134" ] && echo "HT40+" || echo "HT40-")
+ elif [ ${ch} -lt "149" ]; then
+ ch=$([ "${ch}" -lt "144" ] && echo "140" || echo "144")
+ bw_str=$([ "${ch}" -le "142" ] && echo "HT40+" || echo "HT40-")
+ elif [ ${ch} -lt "157" ]; then
+ ch=$([ "${ch}" -lt "153" ] && echo "149" || echo "153")
+ bw_str=$([ "${ch}" -le "151" ] && echo "HT40+" || echo "HT40-")
+ elif [ ${ch} -lt "165" ]; then
+ ch=$([ "${ch}" -lt "161" ] && echo "157" || echo "161")
+ bw_str=$([ "${ch}" -le "159" ] && echo "HT40+" || echo "HT40-")
+ elif [ ${ch} -lt "173" ]; then
+ ch=$([ "${ch}" -lt "169" ] && echo "165" || echo "169")
+ bw_str=$([ "${ch}" -le "167" ] && echo "HT40+" || echo "HT40-")
+ elif [ ${ch} -lt "181" ]; then
+ ch=$([ "${ch}" -lt "177" ] && echo "173" || echo "177")
+ bw_str=$([ "${ch}" -le "175" ] && echo "HT40+" || echo "HT40-")
+ fi
+ ;;
+ "0")
+ local bw_str="HT20"
+ ;;
+ esac
+ local base_freq=5180
+ local base_chan=36
+ else
+ case ${bw} in
+ "5")
+ bw_str="160MHz"
+ if [ ${ch} -lt "33" ]; then
+ ch="1"
+ elif [ ${ch} -lt "65" ]; then
+ ch="33"
+ elif [ ${ch} -lt "97" ]; then
+ ch="65"
+ elif [ ${ch} -lt "129" ]; then
+ ch="97"
+ elif [ ${ch} -lt "161" ]; then
+ ch="129"
+ elif [ ${ch} -lt "193" ]; then
+ ch="161"
+ elif [ ${ch} -lt "225" ]; then
+ ch="193"
+ fi
+ ;;
+ "2")
+ bw_str="80MHz"
+ if [ ${ch} -lt "17" ]; then
+ ch="1"
+ elif [ ${ch} -lt "33" ]; then
+ ch="17"
+ elif [ ${ch} -lt "49" ]; then
+ ch="33"
+ elif [ ${ch} -lt "65" ]; then
+ ch="49"
+ elif [ ${ch} -lt "81" ]; then
+ ch="65"
+ elif [ ${ch} -lt "97" ]; then
+ ch="81"
+ elif [ ${ch} -lt "113" ]; then
+ ch="97"
+ elif [ ${ch} -lt "129" ]; then
+ ch="113"
+ elif [ ${ch} -lt "145" ]; then
+ ch="129"
+ elif [ ${ch} -lt "161" ]; then
+ ch="145"
+ elif [ ${ch} -lt "177" ]; then
+ ch="161"
+ elif [ ${ch} -lt "193" ]; then
+ ch="177"
+ elif [ ${ch} -lt "209" ]; then
+ ch="193"
+ elif [ ${ch} -lt "225" ]; then
+ ch="209"
+ fi
+ ;;
+ "1")
+ if [ ${ch} -lt "9" ]; then
+ ch=$([ "${ch}" -lt "5" ] && echo "1" || echo "5")
+ bw_str=$([ "${ch}" -le "3" ] && echo "HT40+" || echo "HT40-")
+ elif [ ${ch} -lt "17" ]; then
+ ch=$([ "${ch}" -lt "13" ] && echo "9" || echo "13")
+ bw_str=$([ "${ch}" -le "11" ] && echo "HT40+" || echo "HT40-")
+ elif [ ${ch} -lt "25" ]; then
+ ch=$([ "${ch}" -lt "21" ] && echo "17" || echo "21")
+ bw_str=$([ "${ch}" -le "19" ] && echo "HT40+" || echo "HT40-")
+ elif [ ${ch} -lt "33" ]; then
+ ch=$([ "${ch}" -lt "29" ] && echo "25" || echo "29")
+ bw_str=$([ "${ch}" -le "27" ] && echo "HT40+" || echo "HT40-")
+ elif [ ${ch} -lt "33" ]; then
+ ch=$([ "${ch}" -lt "29" ] && echo "25" || echo "29")
+ bw_str=$([ "${ch}" -le "27" ] && echo "HT40+" || echo "HT40-")
+ elif [ ${ch} -lt "41" ]; then
+ ch=$([ "${ch}" -lt "37" ] && echo "33" || echo "37")
+ bw_str=$([ "${ch}" -le "35" ] && echo "HT40+" || echo "HT40-")
+ elif [ ${ch} -lt "49" ]; then
+ ch=$([ "${ch}" -lt "45" ] && echo "41" || echo "45")
+ bw_str=$([ "${ch}" -le "43" ] && echo "HT40+" || echo "HT40-")
+ elif [ ${ch} -lt "57" ]; then
+ ch=$([ "${ch}" -lt "53" ] && echo "49" || echo "53")
+ bw_str=$([ "${ch}" -le "51" ] && echo "HT40+" || echo "HT40-")
+ elif [ ${ch} -lt "65" ]; then
+ ch=$([ "${ch}" -lt "61" ] && echo "57" || echo "61")
+ bw_str=$([ "${ch}" -le "59" ] && echo "HT40+" || echo "HT40-")
+ elif [ ${ch} -lt "73" ]; then
+ ch=$([ "${ch}" -lt "69" ] && echo "65" || echo "69")
+ bw_str=$([ "${ch}" -le "67" ] && echo "HT40+" || echo "HT40-")
+ elif [ ${ch} -lt "81" ]; then
+ ch=$([ "${ch}" -lt "77" ] && echo "73" || echo "77")
+ bw_str=$([ "${ch}" -le "75" ] && echo "HT40+" || echo "HT40-")
+ elif [ ${ch} -lt "89" ]; then
+ ch=$([ "${ch}" -lt "85" ] && echo "81" || echo "85")
+ bw_str=$([ "${ch}" -le "83" ] && echo "HT40+" || echo "HT40-")
+ elif [ ${ch} -lt "97" ]; then
+ ch=$([ "${ch}" -lt "93" ] && echo "89" || echo "93")
+ bw_str=$([ "${ch}" -le "91" ] && echo "HT40+" || echo "HT40-")
+ elif [ ${ch} -lt "105" ]; then
+ ch=$([ "${ch}" -lt "101" ] && echo "97" || echo "101")
+ bw_str=$([ "${ch}" -le "99" ] && echo "HT40+" || echo "HT40-")
+ elif [ ${ch} -lt "113" ]; then
+ ch=$([ "${ch}" -lt "109" ] && echo "105" || echo "109")
+ bw_str=$([ "${ch}" -le "107" ] && echo "HT40+" || echo "HT40-")
+ elif [ ${ch} -lt "121" ]; then
+ ch=$([ "${ch}" -lt "117" ] && echo "113" || echo "117")
+ bw_str=$([ "${ch}" -le "115" ] && echo "HT40+" || echo "HT40-")
+ elif [ ${ch} -lt "129" ]; then
+ ch=$([ "${ch}" -lt "125" ] && echo "121" || echo "125")
+ bw_str=$([ "${ch}" -le "123" ] && echo "HT40+" || echo "HT40-")
+ elif [ ${ch} -lt "137" ]; then
+ ch=$([ "${ch}" -lt "133" ] && echo "129" || echo "133")
+ bw_str=$([ "${ch}" -le "131" ] && echo "HT40+" || echo "HT40-")
+ elif [ ${ch} -lt "145" ]; then
+ ch=$([ "${ch}" -lt "141" ] && echo "137" || echo "141")
+ bw_str=$([ "${ch}" -le "139" ] && echo "HT40+" || echo "HT40-")
+ elif [ ${ch} -lt "153" ]; then
+ ch=$([ "${ch}" -lt "149" ] && echo "145" || echo "149")
+ bw_str=$([ "${ch}" -le "147" ] && echo "HT40+" || echo "HT40-")
+ elif [ ${ch} -lt "161" ]; then
+ ch=$([ "${ch}" -lt "157" ] && echo "153" || echo "157")
+ bw_str=$([ "${ch}" -le "155" ] && echo "HT40+" || echo "HT40-")
+ elif [ ${ch} -lt "169" ]; then
+ ch=$([ "${ch}" -lt "165" ] && echo "161" || echo "165")
+ bw_str=$([ "${ch}" -le "163" ] && echo "HT40+" || echo "HT40-")
+ elif [ ${ch} -lt "177" ]; then
+ ch=$([ "${ch}" -lt "173" ] && echo "169" || echo "173")
+ bw_str=$([ "${ch}" -le "171" ] && echo "HT40+" || echo "HT40-")
+ elif [ ${ch} -lt "185" ]; then
+ ch=$([ "${ch}" -lt "181" ] && echo "177" || echo "181")
+ bw_str=$([ "${ch}" -le "179" ] && echo "HT40+" || echo "HT40-")
+ elif [ ${ch} -lt "193" ]; then
+ ch=$([ "${ch}" -lt "189" ] && echo "185" || echo "189")
+ bw_str=$([ "${ch}" -le "187" ] && echo "HT40+" || echo "HT40-")
+ elif [ ${ch} -lt "201" ]; then
+ ch=$([ "${ch}" -lt "197" ] && echo "193" || echo "197")
+ bw_str=$([ "${ch}" -le "195" ] && echo "HT40+" || echo "HT40-")
+ elif [ ${ch} -lt "209" ]; then
+ ch=$([ "${ch}" -lt "205" ] && echo "201" || echo "205")
+ bw_str=$([ "${ch}" -le "203" ] && echo "HT40+" || echo "HT40-")
+ elif [ ${ch} -lt "217" ]; then
+ ch=$([ "${ch}" -lt "213" ] && echo "209" || echo "213")
+ bw_str=$([ "${ch}" -le "211" ] && echo "HT40+" || echo "HT40-")
+ elif [ ${ch} -lt "225" ]; then
+ ch=$([ "${ch}" -lt "221" ] && echo "217" || echo "221")
+ bw_str=$([ "${ch}" -le "219" ] && echo "HT40+" || echo "HT40-")
+ elif [ ${ch} -lt "233" ]; then
+ ch=$([ "${ch}" -lt "229" ] && echo "225" || echo "229")
+ bw_str=$([ "${ch}" -le "227" ] && echo "HT40+" || echo "HT40-")
+ fi
+ ;;
+ "0")
+ local bw_str="HT20"
+ ;;
+ esac
+ local base_freq=5955
+ fi
+
+ local control_freq=$(((ch - base_chan) * 5 + base_freq))
+ do_cmd "iw dev mon${phy_idx} set freq ${control_freq} ${bw_str}"
+}
+
+function convert_rxstat {
+ local res=$(do_cmd "mt76-test ${interface} dump stats")
+ local mdrdy=$(echo "${res}" | grep "rx_packets" | cut -d "=" -f 2)
+ local fcs_error=$(echo "${res}" | grep "rx_fcs_error" | cut -d "=" -f 2)
+ local rcpi=$(echo "${res}" | grep "last_rcpi" | cut -d "=" -f 2 | sed 's/,/ /g')
+ local ib_rssi=$(echo "${res}" | grep "last_ib_rssi" | cut -d "=" -f 2 | sed 's/,/ /g')
+ local wb_rssi=$(echo "${res}" | grep "last_wb_rssi" | cut -d "=" -f 2 | sed 's/,/ /g')
+ local rx_ok=$(expr ${mdrdy} - ${fcs_error})
+
+ write_dmesg "rcpi: ${rcpi}"
+ write_dmesg "fagc rssi ib: ${ib_rssi}"
+ write_dmesg "fagc rssi wb: ${wb_rssi}"
+ write_dmesg "all_mac_rx_mdrdy_cnt: ${mdrdy}"
+ write_dmesg "all_mac_rx_fcs_err_cnt: ${fcs_error}"
+ write_dmesg "all_mac_rx_ok_cnt : ${rx_ok}"
+}
+
+function set_mac_addr {
+ record_config ${cmd} ${param} ${iwpriv_file}
+
+ local addr1=$(get_config "ATEDA" ${iwpriv_file})
+ local addr2=$(get_config "ATESA" ${iwpriv_file})
+ local addr3=$(get_config "ATEBSSID" ${iwpriv_file})
+
+ if [ -z "${addr1}" ]; then
+ addr1="00:11:22:33:44:55"
+ fi
+ if [ -z "${addr2}" ]; then
+ addr2="00:11:22:33:44:55"
+ fi
+ if [ -z "${addr3}" ]; then
+ addr3="00:11:22:33:44:55"
+ fi
+
+ do_cmd "mt76-test phy${phy_idx} set mac_addrs=${addr1},${addr2},${addr3}"
+}
+
+function convert_ibf {
+ local cmd=$1
+ local param=$2
+ local new_cmd=""
+ local new_param=$(echo ${param} | sed s/":"/","/g)
+
+ case ${cmd} in
+ "ATETxBfInit")
+ new_cmd="init"
+ new_param=1
+ do_cmd "mt76-test phy${phy_idx} set state=idle"
+ ;;
+ "ATEIBFPhaseComp")
+ new_cmd="phase_comp"
+ new_param="${new_param} aid=1"
+ ;;
+ "ATEEBfProfileConfig")
+ new_cmd="ebf_prof_update"
+ ;;
+ "ATEIBfProfileConfig")
+ new_cmd="ibf_prof_update"
+ ;;
+ "ATEIBfInstCal")
+ new_cmd="phase_cal"
+ ;;
+ "ATEIBfGdCal")
+ new_cmd="phase_cal"
+ new_param="${new_param},00"
+ ;;
+ "TxBfTxApply")
+ new_cmd="apply_tx"
+ ;;
+ "ATETxPacketWithBf")
+ local bf_on=${new_param:0:2}
+ local aid="01"
+ local wlan_idx=${new_param:3:2}
+ local update="00"
+ local tx_len=${new_param:6}
+
+ new_cmd="tx_prep"
+ new_param="${bf_on},${aid},${wlan_idx},${update}"
+ if [ "${tx_len}" = "00" ]; then
+ new_param="${new_param} aid=1 tx_count=10000000 tx_length=1024"
+ else
+ new_param="${new_param} aid=1 tx_count=${tx_len} tx_length=1024"
+ fi
+ do_cmd "mt76-test phy${phy_idx} set state=idle"
+ ;;
+ "TxBfProfileData20MAllWrite")
+ new_cmd="prof_update_all"
+ ;;
+ "ATEIBFPhaseE2pUpdate")
+ new_cmd="e2p_update"
+ ;;
+ *)
+ esac
+
+ do_cmd "mt76-test phy${phy_idx} set txbf_act=${new_cmd} txbf_param=${new_param}"
+
+ if [ "${cmd}" = "ATETxPacketWithBf" ]; then
+ do_cmd "mt76-test phy${phy_idx} set state=tx_frames"
+ fi
+}
+
+function do_ate_work() {
+ local ate_cmd=$1
+
+ case ${ate_cmd} in
+ "ATESTART")
+ local if_str=$(ifconfig | grep mon${phy_idx})
+
+ if [ ! -z "${if_str}" -a "${if_str}" != " " ]; then
+ echo "ATE already starts."
+ else
+ do_cmd "iw phy ${interface} interface add mon${phy_idx} type monitor"
+ do_cmd "iw dev wlan${phy_idx} del"
+ do_cmd "ifconfig mon${phy_idx} up"
+ do_cmd "iw reg set VV"
+ fi
+ ;;
+ "ATESTOP")
+ local if_str=$(ifconfig | grep mon${phy_idx})
+
+ if [ -z "${if_str}" -a "${if_str}" != " " ]; then
+ echo "ATE does not start."
+ else
+ do_cmd "mt76-test ${interface} set state=off"
+ do_cmd "iw dev mon${phy_idx} del"
+ do_cmd "iw phy ${interface} interface add wlan${phy_idx} type managed"
+ do_cmd "mt76-test ${interface} set aid=0"
+ fi
+
+ if [ ${phy_idx} -lt ${start_idx_7986} ]; then
+ sed -i '/_PCIE=/d' ${iwpriv_file}
+ elif [ ${phy_idx} -ge ${start_idx_7986} ]; then
+ sed -i '/_7986=/d' ${iwpriv_file}
+ fi
+ ;;
+ "TXCOMMIT")
+ do_cmd "mt76-test ${interface} set aid=1"
+ ;;
+ "TXFRAME")
+ do_cmd "mt76-test ${interface} set state=tx_frames"
+ ;;
+ "TXSTOP"|"RXSTOP")
+ do_cmd "mt76-test ${interface} set state=idle"
+ ;;
+ "TXREVERT")
+ do_cmd "mt76-test ${interface} set aid=0"
+ ;;
+ "RXFRAME")
+ do_cmd "mt76-test ${interface} set state=rx_frames"
+ ;;
+ "TXCONT")
+ do_cmd "mt76-test ${interface} set state=tx_cont"
+ ;;
+ "GROUPREK")
+ do_cmd "mt76-test ${interface} set state=group_prek"
+ do_cmd "atenl -i ${interface} -c \"eeprom precal sync group\""
+ ;;
+ "GROUPREKDump")
+ do_cmd "mt76-test ${interface} set state=group_prek_dump"
+ ;;
+ "GROUPREKClean")
+ do_cmd "mt76-test ${interface} set state=group_prek_clean"
+ do_cmd "atenl -i ${interface} -c \"eeprom precal group clean\""
+ ;;
+ "DPD2G")
+ do_cmd "mt76-test ${interface} set state=dpd_2g"
+ do_cmd "atenl -i ${interface} -c \"eeprom precal sync dpd 2g\""
+ ;;
+ "DPD5G")
+ do_cmd "mt76-test ${interface} set state=dpd_5g"
+ do_cmd "atenl -i ${interface} -c \"eeprom precal sync dpd 5g\""
+ ;;
+ "DPD6G")
+ do_cmd "mt76-test ${interface} set state=dpd_6g"
+ do_cmd "atenl -i ${interface} -c \"eeprom precal sync dpd 6g\""
+ ;;
+ "DPDDump")
+ do_cmd "mt76-test ${interface} set state=dpd_dump"
+ ;;
+ "DPDClean")
+ do_cmd "mt76-test ${interface} set state=dpd_clean"
+ do_cmd "atenl -i ${interface} -c \"eeprom precal dpd clean\""
+ ;;
+ *)
+ print_debug "skip ${ate_cmd}"
+ ;;
+ esac
+}
+
+# main start here
+
+if [[ ${interface} == "ra"* ]]; then
+ convert_interface $interface
+fi
+
+tmp_work_mode=$(get_config "WORKMODE" ${iwpriv_file})
+
+if [ ! -z ${tmp_work_mode} ]; then
+ work_mode=${tmp_work_mode}
+fi
+
+cmd=$(echo ${full_cmd} | sed s/=/' '/g | cut -d " " -f 1)
+param=$(echo ${full_cmd} | sed s/=/' '/g | cut -d " " -f 2)
+
+if [ "${cmd_type}" = "set" ]; then
+ skip=0
+ case ${cmd} in
+ "ATE")
+ do_ate_work ${param}
+
+ skip=1
+ ;;
+ "ATETXCNT"|"ATETXLEN"|"ATETXMCS"|"ATEVHTNSS"|"ATETXLDPC"|"ATETXSTBC"| \
+ "ATEPKTTXTIME"|"ATEIPG"|"ATEDUTYCYCLE"|"ATETXFREQOFFSET")
+ cmd_new=$(simple_convert ${cmd})
+ if [ "${param_new}" = "undefined" ]; then
+ echo "unknown cmd: ${cmd}"
+ exit
+ fi
+ param_new=${param}
+ ;;
+ "ATETXANT"|"ATERXANT")
+ cmd_new="tx_antenna"
+ param_new=${param}
+ ;;
+ "ATETXGI")
+ tx_mode=$(convert_tx_mode $(get_config "ATETXMODE" ${iwpriv_file}))
+ convert_gi ${tx_mode} ${param}
+ skip=1
+ ;;
+ "ATETXMODE")
+ cmd_new="tx_rate_mode"
+ param_new=$(convert_tx_mode ${param})
+ if [ "${param_new}" = "undefined" ]; then
+ echo "unknown tx mode"
+ echo "0:cck, 1:ofdm, 2:ht, 4:vht, 8:he_su, 9:he_er, 10:he_tb, 11:he_mu"
+ exit
+ else
+ record_config ${cmd} ${param} ${iwpriv_file}
+ fi
+ ;;
+ "ATETXPOW0"|"ATETXPOW1"|"ATETXPOW2"|"ATETXPOW3")
+ cmd_new="tx_power"
+ param_new="${param},0,0,0"
+ ;;
+ "ATETXBW")
+ record_config ${cmd} ${param} ${iwpriv_file}
+ skip=1
+ ;;
+ "ATECHANNEL")
+ convert_channel ${param}
+ skip=1
+ ;;
+ "ATERXSTAT")
+ convert_rxstat
+ skip=1
+ ;;
+ "ATECTRLBANDIDX")
+ change_band_idx ${param}
+ skip=1
+ ;;
+ "ATEDA"|"ATESA"|"ATEBSSID")
+ set_mac_addr ${cmd} ${param}
+ skip=1
+ ;;
+ "ATETxBfInit"|"ATEIBFPhaseComp"|"ATEEBfProfileConfig"|"ATEIBfProfileConfig"| \
+ "TxBfTxApply"|"ATETxPacketWithBf"|"TxBfProfileData20MAllWrite"|"ATEIBfInstCal"|\
+ "ATEIBfGdCal"|"ATEIBFPhaseE2pUpdate")
+ convert_ibf ${cmd} ${param}
+ skip=1
+ ;;
+ "bufferMode")
+ if [ "${param}" = "2" ]; then
+ do_cmd "atenl -i ${interface} -c \"eeprom update buffermode\""
+ fi
+ skip=1
+ ;;
+ "ResetCounter"|"ATERXSTATRESET")
+ skip=1
+ ;;
+ "WORKMODE")
+ record_config "WORKMODE" ${param} ${iwpriv_file}
+ echo "Entering ${param} mode in iwpriv"
+ skip=1
+ ;;
+ *)
+ print_debug "Unknown command to set: ${cmd}"
+ skip=1
+ esac
+
+ if [ "${skip}" != "1" ]; then
+ do_cmd "mt76-test ${interface} set ${cmd_new}=${param_new}"
+ fi
+
+elif [ "${cmd_type}" = "show" ]; then
+ do_cmd "mt76-test ${interface} dump"
+ do_cmd "mt76-test ${interface} dump stats"
+
+elif [ "${cmd_type}" = "e2p" ]; then
+ offset=$(printf "0x%s" ${cmd})
+ val=$(printf "0x%s" ${param})
+
+ # eeprom offset write
+ if [[ ${full_cmd} == *"="* ]]; then
+ tmp=$((${val} & 0xff))
+ tmp=$(printf "0x%x" ${tmp})
+ do_cmd "atenl -i ${interface} -c \"eeprom set ${offset}=${tmp}\""
+
+ offset=$((${offset}))
+ offset=$(expr ${offset} + "1")
+ offset=$(printf "0x%x" ${offset})
+ tmp=$(((${val} >> 8) & 0xff))
+ tmp=$(printf "0x%x" ${tmp})
+ do_cmd "atenl -i ${interface} -c \"eeprom set ${offset}=${tmp}\""
+ else
+ v1=$(do_cmd "atenl -i ${interface} -c \"eeprom read ${param}\"")
+ v1=$(echo "${v1}" | grep "val =" | cut -d '(' -f 2 | grep -o -E '[0-9]+')
+
+ tmp=$(printf "0x%s" ${param})
+ tmp=$((${tmp}))
+ param2=$(expr ${tmp} + "1")
+ param2=$(printf "%x" ${param2})
+ v2=$(do_cmd "atenl -i ${interface} -c \"eeprom read ${param2}\"")
+ v2=$(echo "${v2}" | grep "val =" | cut -d '(' -f 2 | grep -o -E '[0-9]+')
+
+ param=$(printf "0x%s" ${param})
+ param=$(printf "%04x" ${param})
+ param=$(echo $param | tr 'a-z' 'A-Z')
+ printf "%s e2p:\n" ${interface_ori}
+ printf "[0x%s]:0x%02x%02x\n" ${param} ${v2} ${v1}
+ fi
+
+elif [ "${cmd_type}" = "mac" ]; then
+ regidx=/sys/kernel/debug/ieee80211/phy${phy_idx}/mt76/regidx
+ regval=/sys/kernel/debug/ieee80211/phy${phy_idx}/mt76/regval
+ offset=$(printf "0x%s" ${cmd})
+ val=$(printf "0x%s" ${param})
+
+ echo ${offset} > ${regidx}
+ # reg write
+ if [[ ${full_cmd} == *"="* ]]; then
+ echo ${val} > ${regval}
+ fi
+
+ res=$(cat ${regval} | cut -d 'x' -f 2)
+ printf "%s mac:[%s]:%s\n" ${interface_ori} ${offset} ${res}
+
+else
+ echo "Unknown command"
+fi
diff --git a/recipes-devtools/atenl/files/src/CMakeLists.txt b/recipes-devtools/atenl/files/src/CMakeLists.txt
new file mode 100644
index 0000000..cc91b05
--- /dev/null
+++ b/recipes-devtools/atenl/files/src/CMakeLists.txt
@@ -0,0 +1,13 @@
+cmake_minimum_required(VERSION 2.8)
+
+PROJECT(atenl C)
+ADD_DEFINITIONS(-Os -Wall --std=gnu99 -g3)
+
+ADD_EXECUTABLE(atenl main.c eth.c hqa.c nl.c eeprom.c util.c)
+TARGET_LINK_LIBRARIES(atenl nl-tiny)
+
+SET(CMAKE_INSTALL_PREFIX /usr)
+
+INSTALL(TARGETS atenl
+ RUNTIME DESTINATION sbin
+)
diff --git a/recipes-devtools/atenl/files/src/atenl.h b/recipes-devtools/atenl/files/src/atenl.h
new file mode 100644
index 0000000..75ee474
--- /dev/null
+++ b/recipes-devtools/atenl/files/src/atenl.h
@@ -0,0 +1,418 @@
+/* Copyright (C) 2021-2022 Mediatek Inc. */
+#ifndef __ATENL_H
+#define __ATENL_H
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <linux/nl80211.h>
+#include <net/if.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "nl.h"
+#include "util.h"
+#include "debug.h"
+
+#define BRIDGE_NAME_OPENWRT "br-lan"
+#define BRIDGE_NAME_RDKB "brlan0"
+#define ETH_P_RACFG 0x2880
+#define RACFG_PKT_MAX_SIZE 1600
+#define RACFG_HLEN 12
+#define RACFG_MAGIC_NO 0x18142880
+#define PRE_CAL_INFO 16
+#define DPD_INFO_CH_SHIFT 24
+#define DPD_INFO_2G_SHIFT 16
+#define DPD_INFO_5G_SHIFT 8
+#define DPD_INFO_6G_SHIFT 0
+#define DPD_INFO_MASK GENMASK(7, 0)
+#define MT_EE_CAL_UNIT 1024
+
+#define RACFG_CMD_TYPE_MASK GENMASK(14, 0)
+#define RACFG_CMD_TYPE_ETHREQ BIT(3)
+#define RACFG_CMD_TYPE_PLATFORM_MODULE GENMASK(4, 3)
+
+#define set_band_val(_an, _band, _field, _val) \
+ _an->anb[_band]._field = (_val)
+#define get_band_val(_an, _band, _field) \
+ (_an->anb[_band]._field)
+
+enum atenl_rf_mode {
+ ATENL_RF_MODE_NORMAL,
+ ATENL_RF_MODE_TEST,
+ ATENL_RF_MODE_ICAP,
+ ATENL_RF_MODE_ICAP_OVERLAP,
+
+ __ATENL_RF_MODE_MAX,
+};
+
+struct atenl_rx_stat {
+ u64 total;
+ u64 ok_cnt;
+ u64 err_cnt;
+ u64 len_mismatch;
+};
+
+struct atenl_band {
+ bool valid;
+ u8 phy_idx;
+ u8 cap;
+ u8 chainmask;
+
+ enum mt76_testmode_state cur_state;
+ s8 tx_power;
+ enum atenl_rf_mode rf_mode;
+
+ bool use_tx_time;
+ u32 tx_time;
+ u32 tx_mpdu_len;
+
+ bool reset_tx_cnt;
+ bool reset_rx_cnt;
+
+ /* history */
+ struct atenl_rx_stat rx_stat;
+};
+
+#define MAX_BAND_NUM 3
+
+struct atenl {
+ struct atenl_band anb[MAX_BAND_NUM];
+ u16 chip_id;
+ u16 adie_id;
+ u8 sub_chip_id;
+ u8 cur_band;
+
+ u8 mac_addr[ETH_ALEN];
+ char *bridge_name;
+ bool unicast;
+ int sock_eth;
+
+ const char *mtd_part;
+ u32 mtd_offset;
+ u8 is_main_phy;
+ u8 *eeprom_data;
+ int eeprom_fd;
+ u16 eeprom_size;
+ u32 eeprom_prek_offs;
+
+ u8 *cal;
+ u32 cal_info[5];
+
+ bool cmd_mode;
+
+ /* intermediate data */
+ u8 ibf_mcs;
+ u8 ibf_ant;
+};
+
+struct atenl_cmd_hdr {
+ __be32 magic_no;
+ __be16 cmd_type;
+ __be16 cmd_id;
+ __be16 len;
+ __be16 seq;
+ u8 data[2048];
+} __attribute__((packed));
+
+enum atenl_cmd {
+ HQA_CMD_UNKNOWN,
+ HQA_CMD_LEGACY, /* legacy or deprecated */
+
+ HQA_CMD_OPEN_ADAPTER,
+ HQA_CMD_CLOSE_ADAPTER,
+ HQA_CMD_GET_CHIP_ID,
+ HQA_CMD_GET_SUB_CHIP_ID,
+ HQA_CMD_SET_TX_BW,
+ HQA_CMD_SET_TX_PKT_BW,
+ HQA_CMD_SET_TX_PRI_BW,
+ HQA_CMD_GET_TX_INFO,
+ HQA_CMD_SET_TX_PATH,
+ HQA_CMD_SET_TX_POWER,
+ HQA_CMD_SET_TX_POWER_MANUAL,
+ HQA_CMD_SET_RF_MODE,
+ HQA_CMD_SET_RX_PATH,
+ HQA_CMD_SET_RX_PKT_LEN,
+ HQA_CMD_SET_FREQ_OFFSET,
+ HQA_CMD_SET_TSSI,
+ HQA_CMD_SET_CFG,
+ HQA_CMD_SET_RU,
+ HQA_CMD_SET_BAND,
+ HQA_CMD_SET_EEPROM_TO_FW,
+ HQA_CMD_READ_MAC_BBP_REG,
+ HQA_CMD_READ_MAC_BBP_REG_QA,
+ HQA_CMD_READ_RF_REG,
+ HQA_CMD_READ_EEPROM_BULK,
+ HQA_CMD_READ_TEMPERATURE,
+ HQA_CMD_WRITE_MAC_BBP_REG,
+ HQA_CMD_WRITE_RF_REG,
+ HQA_CMD_WRITE_EEPROM_BULK,
+ HQA_CMD_WRITE_BUFFER_DONE,
+ HQA_CMD_GET_BAND,
+ HQA_CMD_GET_CFG,
+ HQA_CMD_GET_TX_POWER,
+ HQA_CMD_GET_TX_TONE_POWER,
+ HQA_CMD_GET_EFUSE_FREE_BLOCK,
+ HQA_CMD_GET_FREQ_OFFSET,
+ HQA_CMD_GET_FW_INFO,
+ HQA_CMD_GET_RX_INFO,
+ HQA_CMD_GET_RF_CAP,
+ HQA_CMD_CHECK_EFUSE_MODE,
+ HQA_CMD_CHECK_EFUSE_MODE_TYPE,
+ HQA_CMD_CHECK_EFUSE_MODE_NATIVE,
+ HQA_CMD_ANT_SWAP_CAP,
+ HQA_CMD_RESET_TX_RX_COUNTER,
+ HQA_CMD_CONTINUOUS_TX,
+
+ HQA_CMD_EXT,
+ HQA_CMD_ERR,
+
+ __HQA_CMD_MAX_NUM,
+};
+
+enum atenl_ext_cmd {
+ HQA_EXT_CMD_UNSPEC,
+
+ HQA_EXT_CMD_SET_CHANNEL,
+ HQA_EXT_CMD_SET_TX,
+ HQA_EXT_CMD_START_TX,
+ HQA_EXT_CMD_START_RX,
+ HQA_EXT_CMD_STOP_TX,
+ HQA_EXT_CMD_STOP_RX,
+ HQA_EXT_CMD_SET_TX_TIME_OPT,
+
+ HQA_EXT_CMD_OFF_CH_SCAN,
+
+ HQA_EXT_CMD_IBF_SET_VAL,
+ HQA_EXT_CMD_IBF_GET_STATUS,
+ HQA_EXT_CMD_IBF_PROF_UPDATE_ALL,
+
+ HQA_EXT_CMD_ERR,
+
+ __HQA_EXT_CMD_MAX_NUM,
+};
+
+struct atenl_data {
+ u8 buf[RACFG_PKT_MAX_SIZE];
+ int len;
+ u16 cmd_id;
+ u8 ext_id;
+ enum atenl_cmd cmd;
+ enum atenl_ext_cmd ext_cmd;
+};
+
+struct atenl_ops {
+ int (*ops)(struct atenl *an, struct atenl_data *data);
+ u8 cmd;
+ u8 flags;
+ u16 cmd_id;
+ u16 resp_len;
+};
+
+#define ATENL_OPS_FLAG_EXT_CMD BIT(0)
+#define ATENL_OPS_FLAG_LEGACY BIT(1)
+#define ATENL_OPS_FLAG_SKIP BIT(2)
+
+static inline struct atenl_cmd_hdr * atenl_hdr(struct atenl_data *data)
+{
+ u8 *hqa_data = (u8 *)data->buf + ETH_HLEN;
+
+ return (struct atenl_cmd_hdr *)hqa_data;
+}
+
+enum atenl_phy_type {
+ ATENL_PHY_TYPE_CCK,
+ ATENL_PHY_TYPE_OFDM,
+ ATENL_PHY_TYPE_HT,
+ ATENL_PHY_TYPE_HT_GF,
+ ATENL_PHY_TYPE_VHT,
+ ATENL_PHY_TYPE_HE_SU = 8,
+ ATENL_PHY_TYPE_HE_EXT_SU,
+ ATENL_PHY_TYPE_HE_TB,
+ ATENL_PHY_TYPE_HE_MU,
+};
+
+enum atenl_e2p_mode {
+ E2P_EFUSE_MODE = 1,
+ E2P_FLASH_MODE,
+ E2P_EEPROM_MODE,
+ E2P_BIN_MODE,
+};
+
+enum atenl_band_type {
+ BAND_TYPE_UNUSE,
+ BAND_TYPE_2G,
+ BAND_TYPE_5G,
+ BAND_TYPE_2G_5G,
+ BAND_TYPE_6G,
+ BAND_TYPE_2G_6G,
+ BAND_TYPE_5G_6G,
+ BAND_TYPE_2G_5G_6G,
+};
+
+enum atenl_ch_band {
+ CH_BAND_2GHZ,
+ CH_BAND_5GHZ,
+ CH_BAND_6GHZ,
+};
+
+/* for mt7915 */
+enum {
+ MT_EE_BAND_SEL_DEFAULT,
+ MT_EE_BAND_SEL_5GHZ,
+ MT_EE_BAND_SEL_2GHZ,
+ MT_EE_BAND_SEL_DUAL,
+};
+
+/* for mt7916/mt7986 */
+enum {
+ MT_EE_BAND_SEL_2G,
+ MT_EE_BAND_SEL_5G,
+ MT_EE_BAND_SEL_6G,
+ MT_EE_BAND_SEL_5G_6G,
+};
+
+#define MT_EE_WIFI_CONF 0x190
+#define MT_EE_WIFI_CONF0_BAND_SEL GENMASK(7, 6)
+
+enum {
+ MT7976_ONE_ADIE_DBDC = 0x7,
+ MT7975_ONE_ADIE_SINGLE_BAND = 0x8, /* AX7800 */
+ MT7976_ONE_ADIE_SINGLE_BAND = 0xa, /* AX7800 */
+ MT7975_DUAL_ADIE_DBDC = 0xd, /* AX6000 */
+ MT7976_DUAL_ADIE_DBDC = 0xf, /* AX6000 */
+};
+
+enum {
+ TEST_CBW_20MHZ,
+ TEST_CBW_40MHZ,
+ TEST_CBW_80MHZ,
+ TEST_CBW_10MHZ,
+ TEST_CBW_5MHZ,
+ TEST_CBW_160MHZ,
+ TEST_CBW_8080MHZ,
+
+ TEST_CBW_MAX = TEST_CBW_8080MHZ - 1,
+};
+
+struct atenl_rx_info_hdr {
+ __be32 type;
+ __be32 ver;
+ __be32 val;
+ __be32 len;
+} __attribute__((packed));
+
+struct atenl_rx_info_band {
+ __be32 mac_rx_fcs_err_cnt;
+ __be32 mac_rx_mdrdy_cnt;
+ __be32 mac_rx_len_mismatch;
+ __be32 mac_rx_fcs_ok_cnt;
+ __be32 phy_rx_fcs_err_cnt_cck;
+ __be32 phy_rx_fcs_err_cnt_ofdm;
+ __be32 phy_rx_pd_cck;
+ __be32 phy_rx_pd_ofdm;
+ __be32 phy_rx_sig_err_cck;
+ __be32 phy_rx_sfd_err_cck;
+ __be32 phy_rx_sig_err_ofdm;
+ __be32 phy_rx_tag_err_ofdm;
+ __be32 phy_rx_mdrdy_cnt_cck;
+ __be32 phy_rx_mdrdy_cnt_ofdm;
+} __attribute__((packed));
+
+struct atenl_rx_info_path {
+ __be32 rcpi;
+ __be32 rssi;
+ __be32 fagc_ib_rssi;
+ __be32 fagc_wb_rssi;
+ __be32 inst_ib_rssi;
+ __be32 inst_wb_rssi;
+} __attribute__((packed));
+
+struct atenl_rx_info_user {
+ __be32 freq_offset;
+ __be32 snr;
+ __be32 fcs_error_cnt;
+} __attribute__((packed));
+
+struct atenl_rx_info_comm {
+ __be32 rx_fifo_full;
+ __be32 aci_hit_low;
+ __be32 aci_hit_high;
+ __be32 mu_pkt_count;
+ __be32 sig_mcs;
+ __be32 sinr;
+ __be32 driver_rx_count;
+} __attribute__((packed));
+
+enum atenl_ibf_action {
+ TXBF_ACT_INIT = 1,
+ TXBF_ACT_CHANNEL,
+ TXBF_ACT_MCS,
+ TXBF_ACT_POWER,
+ TXBF_ACT_TX_ANT,
+ TXBF_ACT_RX_START,
+ TXBF_ACT_RX_ANT,
+ TXBF_ACT_LNA_GAIN,
+ TXBF_ACT_IBF_PHASE_COMP,
+ TXBF_ACT_TX_PKT,
+ TXBF_ACT_IBF_PROF_UPDATE,
+ TXBF_ACT_EBF_PROF_UPDATE,
+ TXBF_ACT_IBF_PHASE_CAL,
+ TXBF_ACT_IBF_PHASE_E2P_UPDATE = 16,
+};
+
+enum prek_ops {
+ PREK_SYNC_ALL = 1,
+ PREK_SYNC_GROUP,
+ PREK_SYNC_DPD_2G,
+ PREK_SYNC_DPD_5G,
+ PREK_SYNC_DPD_6G,
+ PREK_CLEAN_GROUP,
+ PREK_CLEAN_DPD,
+};
+
+static inline bool is_mt7915(struct atenl *an)
+{
+ return an->chip_id == 0x7915;
+}
+
+static inline bool is_mt7916(struct atenl *an)
+{
+ return (an->chip_id == 0x7916) || (an->chip_id == 0x7906);
+}
+
+static inline bool is_mt7986(struct atenl *an)
+{
+ return an->chip_id == 0x7986;
+}
+
+int atenl_eth_init(struct atenl *an);
+int atenl_eth_recv(struct atenl *an, struct atenl_data *data);
+int atenl_eth_send(struct atenl *an, struct atenl_data *data);
+int atenl_hqa_proc_cmd(struct atenl *an);
+int atenl_nl_process(struct atenl *an, struct atenl_data *data);
+int atenl_nl_process_many(struct atenl *an, struct atenl_data *data);
+int atenl_nl_check_mtd(struct atenl *an);
+int atenl_nl_write_eeprom(struct atenl *an, u32 offset, u8 *val, int len);
+int atenl_nl_write_efuse_all(struct atenl *an);
+int atenl_nl_update_buffer_mode(struct atenl *an);
+int atenl_nl_set_state(struct atenl *an, u8 band,
+ enum mt76_testmode_state state);
+int atenl_nl_set_aid(struct atenl *an, u8 band, u8 aid);
+int atenl_nl_precal_sync_from_driver(struct atenl *an, enum prek_ops ops);
+int atenl_eeprom_init(struct atenl *an, u8 phy_idx);
+void atenl_eeprom_close(struct atenl *an);
+int atenl_eeprom_write_mtd(struct atenl *an);
+int atenl_eeprom_update_precal(struct atenl *an, int write_offs, int size);
+int atenl_eeprom_read_from_driver(struct atenl *an, u32 offset, int len);
+void atenl_eeprom_cmd_handler(struct atenl *an, u8 phy_idx, char *cmd);
+u16 atenl_get_center_channel(u8 bw, u8 ch_band, u16 ctrl_ch);
+int atenl_reg_read(struct atenl *an, u32 offset, u32 *res);
+int atenl_reg_write(struct atenl *an, u32 offset, u32 val);
+int atenl_rf_read(struct atenl *an, u32 wf_sel, u32 offset, u32 *res);
+int atenl_rf_write(struct atenl *an, u32 wf_sel, u32 offset, u32 val);
+
+#endif
diff --git a/recipes-devtools/atenl/files/src/debug.h b/recipes-devtools/atenl/files/src/debug.h
new file mode 100644
index 0000000..7621887
--- /dev/null
+++ b/recipes-devtools/atenl/files/src/debug.h
@@ -0,0 +1,31 @@
+/* Copyright (C) 2021-2022 Mediatek Inc. */
+#ifndef __ATENL_DEBUG_H
+#define __ATENL_DEBUG_H
+
+/* #define CONFIG_ATENL_DEBUG 1 */
+/* #define CONFIG_ATENL_DEBUG_VERBOSE 1 */
+
+#define atenl_info(fmt, ...) (void)fprintf(stdout, fmt, ##__VA_ARGS__)
+#define atenl_err(fmt, ...) (void)fprintf(stderr, fmt, ##__VA_ARGS__)
+#ifdef CONFIG_ATENL_DEBUG
+#define atenl_dbg(fmt, ...) atenl_info(fmt, ##__VA_ARGS__)
+#else
+#define atenl_dbg(fmt, ...)
+#endif
+
+static inline void
+atenl_dbg_print_data(const void *data, const char *func_name, u32 len)
+{
+#ifdef CONFIG_ATENL_DEBUG_VERBOSE
+ u32 *tmp = (u32 *)data;
+ int i;
+
+ for (i = 0; i < DIV_ROUND_UP(len, 4); i++)
+ atenl_dbg("%s: [%d] = 0x%08x\n", func_name, i, tmp[i]);
+#endif
+}
+
+/* #define debug_print(fmt, ...) \ */
+/* do { if (DEBUG) fprintf(stderr, fmt, __VA_ARGS__); } while (0) */
+
+#endif
diff --git a/recipes-devtools/atenl/files/src/eeprom.c b/recipes-devtools/atenl/files/src/eeprom.c
new file mode 100644
index 0000000..1c1cb6c
--- /dev/null
+++ b/recipes-devtools/atenl/files/src/eeprom.c
@@ -0,0 +1,563 @@
+#define _GNU_SOURCE
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include "atenl.h"
+
+#define EEPROM_PART_SIZE 0x64000
+char *eeprom_file;
+
+static FILE *mtd_open(const char *mtd)
+{
+ char line[128], name[64];
+ FILE *fp;
+ int i;
+
+ fp = fopen("/proc/mtd", "r");
+ if (!fp)
+ return NULL;
+
+ snprintf(name, sizeof(name), "\"%s\"", mtd);
+ while (fgets(line, sizeof(line), fp)) {
+ if (!sscanf(line, "mtd%d:", &i) || !strstr(line, name))
+ continue;
+
+ snprintf(line, sizeof(line), "/dev/mtd%d", i);
+ fclose(fp);
+ return fopen(line, "r");
+ }
+ fclose(fp);
+
+ return NULL;
+}
+
+static int
+atenl_flash_create_file(struct atenl *an)
+{
+#define READ_LEN_LIMIT 0x64000
+ char buf[1024];
+ ssize_t len, limit = 0;
+ FILE *f;
+ int fd, ret;
+
+ f = mtd_open(an->mtd_part);
+ if (!f) {
+ atenl_err("Failed to open MTD device\n");
+ return -1;
+ }
+ fseek(f, an->mtd_offset, SEEK_SET);
+
+ fd = open(eeprom_file, O_RDWR | O_CREAT | O_EXCL, 00644);
+ if (fd < 0)
+ goto out;
+
+ while ((len = fread(buf, 1, sizeof(buf), f)) > 0) {
+ ssize_t w;
+
+retry:
+ w = write(fd, buf, len);
+ if (w > 0) {
+ limit += len;
+
+ if (limit >= READ_LEN_LIMIT)
+ break;
+ continue;
+ }
+
+ if (errno == EINTR)
+ goto retry;
+
+ perror("write");
+ unlink(eeprom_file);
+ close(fd);
+ fd = -1;
+ goto out;
+ }
+
+ ret = lseek(fd, 0, SEEK_SET);
+ if (ret) {
+ fclose(f);
+ close(fd);
+ return ret;
+ }
+
+out:
+ fclose(f);
+ return fd;
+}
+
+static int
+atenl_efuse_create_file(struct atenl *an)
+{
+ char fname[64], buf[1024];
+ ssize_t len;
+ int fd_ori, fd, ret;
+
+ snprintf(fname, sizeof(fname),
+ "/sys/kernel/debug/ieee80211/phy%d/mt76/eeprom", get_band_val(an, 0, phy_idx));
+ fd_ori = open(fname, O_RDONLY);
+ if (fd_ori < 0)
+ return -1;
+
+ fd = open(eeprom_file, O_RDWR | O_CREAT | O_EXCL, 00644);
+ if (fd < 0)
+ goto out;
+
+ while ((len = read(fd_ori, buf, sizeof(buf))) > 0) {
+ ssize_t w;
+
+retry:
+ w = write(fd, buf, len);
+ if (w > 0)
+ continue;
+
+ if (errno == EINTR)
+ goto retry;
+
+ perror("write");
+ unlink(eeprom_file);
+ close(fd);
+ fd = -1;
+ goto out;
+ }
+
+ ret = lseek(fd, 0, SEEK_SET);
+ if (ret) {
+ close(fd_ori);
+ close(fd);
+ return ret;
+ }
+
+out:
+ close(fd_ori);
+ return fd;
+}
+
+static bool
+atenl_eeprom_file_exists(void)
+{
+ struct stat st;
+
+ return stat(eeprom_file, &st) == 0;
+}
+
+static int
+atenl_eeprom_init_file(struct atenl *an, bool flash_mode)
+{
+ int fd;
+
+ if (!atenl_eeprom_file_exists()) {
+ if (flash_mode)
+ atenl_dbg("%s: init eeprom with flash mode\n", __func__);
+ else
+ atenl_dbg("%s: init eeprom with efuse mode\n", __func__);
+
+ if (flash_mode)
+ return atenl_flash_create_file(an);
+
+ return atenl_efuse_create_file(an);
+ }
+
+ fd = open(eeprom_file, O_RDWR);
+ if (fd < 0)
+ perror("open");
+
+ return fd;
+}
+
+static void
+atenl_eeprom_init_chip_id(struct atenl *an)
+{
+ an->chip_id = *(u16 *)an->eeprom_data;
+
+ if (is_mt7915(an)) {
+ an->adie_id = 0x7975;
+ } else if (is_mt7916(an)) {
+ an->adie_id = 0x7976;
+ } else if (is_mt7986(an)) {
+ bool is_7975 = false;
+ u32 val;
+ u8 sub_id;
+
+ atenl_reg_read(an, 0x18050000, &val);
+
+ switch (val & 0xf) {
+ case MT7975_ONE_ADIE_SINGLE_BAND:
+ is_7975 = true;
+ /* fallthrough */
+ case MT7976_ONE_ADIE_SINGLE_BAND:
+ sub_id = 0xa;
+ break;
+ case MT7976_ONE_ADIE_DBDC:
+ sub_id = 0x7;
+ break;
+ case MT7975_DUAL_ADIE_DBDC:
+ is_7975 = true;
+ /* fallthrough */
+ case MT7976_DUAL_ADIE_DBDC:
+ default:
+ sub_id = 0xf;
+ break;
+ }
+
+ an->sub_chip_id = sub_id;
+ an->adie_id = is_7975 ? 0x7975 : 0x7976;
+ }
+}
+
+static void
+atenl_eeprom_init_max_size(struct atenl *an)
+{
+ switch (an->chip_id) {
+ case 0x7915:
+ an->eeprom_size = 3584;
+ an->eeprom_prek_offs = 0x62;
+ break;
+ case 0x7906:
+ case 0x7916:
+ case 0x7986:
+ an->eeprom_size = 4096;
+ an->eeprom_prek_offs = 0x19a;
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+atenl_eeprom_init_band_cap(struct atenl *an)
+{
+ u8 *eeprom = an->eeprom_data;
+
+ if (is_mt7915(an)) {
+ u8 val = eeprom[MT_EE_WIFI_CONF];
+ u8 band_sel = FIELD_GET(MT_EE_WIFI_CONF0_BAND_SEL, val);
+ struct atenl_band *anb = &an->anb[0];
+
+ /* MT7915A */
+ if (band_sel == MT_EE_BAND_SEL_DEFAULT) {
+ anb->valid = true;
+ anb->cap = BAND_TYPE_2G_5G;
+ return;
+ }
+
+ /* MT7915D */
+ if (band_sel == MT_EE_BAND_SEL_2GHZ) {
+ anb->valid = true;
+ anb->cap = BAND_TYPE_2G;
+ }
+
+ val = eeprom[MT_EE_WIFI_CONF + 1];
+ band_sel = FIELD_GET(MT_EE_WIFI_CONF0_BAND_SEL, val);
+ anb++;
+
+ if (band_sel == MT_EE_BAND_SEL_5GHZ) {
+ anb->valid = true;
+ anb->cap = BAND_TYPE_5G;
+ }
+ } else if (is_mt7916(an) || is_mt7986(an)) {
+ struct atenl_band *anb;
+ u8 val, band_sel;
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ val = eeprom[MT_EE_WIFI_CONF + i];
+ band_sel = FIELD_GET(MT_EE_WIFI_CONF0_BAND_SEL, val);
+ anb = &an->anb[i];
+
+ anb->valid = true;
+ switch (band_sel) {
+ case MT_EE_BAND_SEL_2G:
+ anb->cap = BAND_TYPE_2G;
+ break;
+ case MT_EE_BAND_SEL_5G:
+ anb->cap = BAND_TYPE_5G;
+ break;
+ case MT_EE_BAND_SEL_6G:
+ anb->cap = BAND_TYPE_6G;
+ break;
+ case MT_EE_BAND_SEL_5G_6G:
+ anb->cap = BAND_TYPE_5G_6G;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+}
+
+static void
+atenl_eeprom_init_antenna_cap(struct atenl *an)
+{
+ if (is_mt7915(an)) {
+ if (an->anb[0].cap == BAND_TYPE_2G_5G)
+ an->anb[0].chainmask = 0xf;
+ else {
+ an->anb[0].chainmask = 0x3;
+ an->anb[1].chainmask = 0xc;
+ }
+ } else if (is_mt7916(an)) {
+ an->anb[0].chainmask = 0x3;
+ an->anb[1].chainmask = 0x3;
+ } else if (is_mt7986(an)) {
+ an->anb[0].chainmask = 0xf;
+ an->anb[1].chainmask = 0xf;
+ }
+}
+
+int atenl_eeprom_init(struct atenl *an, u8 phy_idx)
+{
+ bool flash_mode;
+ int eeprom_fd;
+ char buf[30];
+ u8 main_phy_idx = phy_idx;
+
+ set_band_val(an, 0, phy_idx, phy_idx);
+ atenl_nl_check_mtd(an);
+ flash_mode = an->mtd_part != NULL;
+
+ if (flash_mode)
+ main_phy_idx = an->is_main_phy ? main_phy_idx : (main_phy_idx - 1);
+
+ snprintf(buf, sizeof(buf), "/tmp/atenl-eeprom-phy%u", main_phy_idx);
+ eeprom_file = strdup(buf);
+
+ eeprom_fd = atenl_eeprom_init_file(an, flash_mode);
+ if (eeprom_fd < 0)
+ return -1;
+
+ an->eeprom_data = mmap(NULL, EEPROM_PART_SIZE, PROT_READ | PROT_WRITE,
+ MAP_SHARED, eeprom_fd, 0);
+ if (!an->eeprom_data) {
+ perror("mmap");
+ close(eeprom_fd);
+ return -1;
+ }
+
+ an->eeprom_fd = eeprom_fd;
+ atenl_eeprom_init_chip_id(an);
+ atenl_eeprom_init_max_size(an);
+ atenl_eeprom_init_band_cap(an);
+ atenl_eeprom_init_antenna_cap(an);
+
+ if (get_band_val(an, 1, valid))
+ set_band_val(an, 1, phy_idx, phy_idx + 1);
+
+ return 0;
+}
+
+void atenl_eeprom_close(struct atenl *an)
+{
+ msync(an->eeprom_data, EEPROM_PART_SIZE, MS_SYNC);
+ munmap(an->eeprom_data, EEPROM_PART_SIZE);
+ close(an->eeprom_fd);
+
+ if (!an->cmd_mode) {
+ if (remove(eeprom_file))
+ perror("remove");
+ }
+
+ free(eeprom_file);
+}
+
+int atenl_eeprom_update_precal(struct atenl *an, int write_offs, int size)
+{
+ u32 offs = an->eeprom_prek_offs;
+ u8 cal_indicator, *eeprom, *pre_cal;
+
+ if (!an->cal && !an->cal_info)
+ return 0;
+
+ eeprom = an->eeprom_data;
+ pre_cal = eeprom + an->eeprom_size;
+ cal_indicator = an->cal_info[4];
+
+ memcpy(eeprom + offs, &cal_indicator, sizeof(u8));
+ memcpy(pre_cal, an->cal_info, PRE_CAL_INFO);
+ pre_cal += (PRE_CAL_INFO + write_offs);
+
+ if (an->cal)
+ memcpy(pre_cal, an->cal, size);
+ else
+ memset(pre_cal, 0, size);
+
+ return 0;
+}
+
+int atenl_eeprom_write_mtd(struct atenl *an)
+{
+ bool flash_mode = an->mtd_part != NULL;
+ pid_t pid;
+ char offset[10];
+
+ if (!flash_mode)
+ return 0;
+
+ pid = fork();
+ if (pid < 0) {
+ perror("Fork");
+ return EXIT_FAILURE;
+ } else if (pid == 0) {
+ int ret;
+ char *part = strdup(an->mtd_part);
+ snprintf(offset, sizeof(offset), "%d", an->mtd_offset);
+ char *cmd[] = {"mtd", "-p", offset, "write", eeprom_file, part, NULL};
+
+ ret = execvp("mtd", cmd);
+ if (ret < 0) {
+ atenl_err("%s: exec error\n", __func__);
+ exit(0);
+ }
+ } else {
+ wait(&pid);
+ }
+
+ return 0;
+}
+
+/* Directly read values from driver's eeprom.
+ * It's usally used to get calibrated data from driver.
+ */
+int atenl_eeprom_read_from_driver(struct atenl *an, u32 offset, int len)
+{
+ u8 *eeprom_data = an->eeprom_data + offset;
+ char fname[64], buf[1024];
+ int fd_ori, ret;
+ ssize_t rd;
+
+ snprintf(fname, sizeof(fname),
+ "/sys/kernel/debug/ieee80211/phy%d/mt76/eeprom",
+ get_band_val(an, 0, phy_idx));
+ fd_ori = open(fname, O_RDONLY);
+ if (fd_ori < 0)
+ return -1;
+
+ ret = lseek(fd_ori, offset, SEEK_SET);
+ if (ret < 0)
+ goto out;
+
+ while ((rd = read(fd_ori, buf, sizeof(buf))) > 0 && len) {
+ if (len < rd) {
+ memcpy(eeprom_data, buf, len);
+ break;
+ } else {
+ memcpy(eeprom_data, buf, rd);
+ eeprom_data += rd;
+ len -= rd;
+ }
+ }
+
+ ret = 0;
+out:
+ close(fd_ori);
+ return ret;
+}
+
+/* Update all eeprom values to driver before writing efuse */
+static void
+atenl_eeprom_sync_to_driver(struct atenl *an)
+{
+ int i;
+
+ for (i = 0; i < an->eeprom_size; i += 16)
+ atenl_nl_write_eeprom(an, i, &an->eeprom_data[i], 16);
+}
+
+void atenl_eeprom_cmd_handler(struct atenl *an, u8 phy_idx, char *cmd)
+{
+ bool flash_mode;
+
+ an->cmd_mode = true;
+
+ atenl_eeprom_init(an, phy_idx);
+ flash_mode = an->mtd_part != NULL;
+
+ if (!strncmp(cmd, "sync eeprom all", 15)) {
+ atenl_eeprom_write_mtd(an);
+ } else if (!strncmp(cmd, "eeprom", 6)) {
+ char *s = strchr(cmd, ' ');
+
+ if (!s) {
+ atenl_err("eeprom: please type a correct command\n");
+ return;
+ }
+
+ s++;
+ if (!strncmp(s, "reset", 5)) {
+ unlink(eeprom_file);
+ } else if (!strncmp(s, "file", 4)) {
+ atenl_info("%s\n", eeprom_file);
+ atenl_info("Flash mode: %d\n", flash_mode);
+ } else if (!strncmp(s, "set", 3)) {
+ u32 offset, val;
+
+ s = strchr(s, ' ');
+ if (!s)
+ return;
+ s++;
+
+ if (!sscanf(s, "%x=%x", &offset, &val) ||
+ offset > EEPROM_PART_SIZE)
+ return;
+
+ an->eeprom_data[offset] = val;
+ atenl_info("set offset 0x%x to 0x%x\n", offset, val);
+ } else if (!strncmp(s, "update buffermode", 17)) {
+ atenl_eeprom_sync_to_driver(an);
+ atenl_nl_update_buffer_mode(an);
+ } else if (!strncmp(s, "write", 5)) {
+ s = strchr(s, ' ');
+ if (!s)
+ return;
+ s++;
+
+ if (!strncmp(s, "flash", 5)) {
+ atenl_eeprom_write_mtd(an);
+ } else if (!strncmp(s, "to efuse", 8)) {
+ atenl_eeprom_sync_to_driver(an);
+ atenl_nl_write_efuse_all(an);
+ }
+ } else if (!strncmp(s, "read", 4)) {
+ u32 offset;
+
+ s = strchr(s, ' ');
+ if (!s)
+ return;
+ s++;
+
+ if (!sscanf(s, "%x", &offset) ||
+ offset > EEPROM_PART_SIZE)
+ return;
+
+ atenl_info("val = 0x%x (%u)\n", an->eeprom_data[offset],
+ an->eeprom_data[offset]);
+ } else if (!strncmp(s, "precal", 6)) {
+ s = strchr(s, ' ');
+ if (!s)
+ return;
+ s++;
+
+ if (!strncmp(s, "sync group", 10)) {
+ atenl_nl_precal_sync_from_driver(an, PREK_SYNC_GROUP);
+ } else if (!strncmp(s, "sync dpd 2g", 11)) {
+ atenl_nl_precal_sync_from_driver(an, PREK_SYNC_DPD_2G);
+ } else if (!strncmp(s, "sync dpd 5g", 11)) {
+ atenl_nl_precal_sync_from_driver(an, PREK_SYNC_DPD_5G);
+ } else if (!strncmp(s, "sync dpd 6g", 11)) {
+ atenl_nl_precal_sync_from_driver(an, PREK_SYNC_DPD_6G);
+ } else if (!strncmp(s, "group clean", 11)) {
+ atenl_nl_precal_sync_from_driver(an, PREK_CLEAN_GROUP);
+ } else if (!strncmp(s, "dpd clean", 9)) {
+ atenl_nl_precal_sync_from_driver(an, PREK_CLEAN_DPD);
+ } else if (!strncmp(s, "sync", 4)) {
+ atenl_nl_precal_sync_from_driver(an, PREK_SYNC_ALL);
+ }
+ } else {
+ atenl_err("Unknown eeprom command: %s\n", cmd);
+ }
+ } else {
+ atenl_err("Unknown command: %s\n", cmd);
+ }
+}
diff --git a/recipes-devtools/atenl/files/src/eth.c b/recipes-devtools/atenl/files/src/eth.c
new file mode 100644
index 0000000..cd32f71
--- /dev/null
+++ b/recipes-devtools/atenl/files/src/eth.c
@@ -0,0 +1,118 @@
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include "atenl.h"
+
+int atenl_eth_init(struct atenl *an)
+{
+ struct sockaddr_ll addr = {};
+ struct ifreq ifr = {};
+ int ret;
+
+ if (!an->bridge_name) {
+ perror("Bridge name not specified");
+ goto out;
+ }
+
+ memcpy(ifr.ifr_name, an->bridge_name, strlen(an->bridge_name));
+ ret = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_RACFG));
+ if (ret < 0) {
+ perror("socket");
+ goto out;
+ }
+ an->sock_eth = ret;
+
+ addr.sll_family = AF_PACKET;
+ addr.sll_ifindex = if_nametoindex(an->bridge_name);
+
+ ret = bind(an->sock_eth, (struct sockaddr *)&addr, sizeof(addr));
+ if (ret < 0) {
+ perror("bind");
+ goto out;
+ }
+
+ ret = ioctl(an->sock_eth, SIOCGIFHWADDR, &ifr);
+ if (ret < 0) {
+ perror("ioctl(SIOCGIFHWADDR)");
+ goto out;
+ }
+
+ memcpy(an->mac_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
+ atenl_info("Open Ethernet socket success on %s, mac addr = " MACSTR "\n",
+ an->bridge_name, MAC2STR(an->mac_addr));
+
+ ret = 0;
+out:
+ return ret;
+}
+
+int atenl_eth_recv(struct atenl *an, struct atenl_data *data)
+{
+ struct ethhdr *hdr;
+ int len;
+
+ len = recvfrom(an->sock_eth, data->buf, sizeof(data->buf), 0, NULL, NULL);
+
+ if (len < ETH_HLEN + RACFG_HLEN) {
+ atenl_err("packet len is too short: %d\n", len);
+ return -EINVAL;
+ }
+
+ hdr = (struct ethhdr *)data->buf;
+ if (hdr->h_proto != htons(ETH_P_RACFG)) {
+ atenl_err("invalid protocol type\n");
+ return -EINVAL;
+ }
+
+ if (!ether_addr_equal(an->mac_addr, hdr->h_dest) &&
+ !is_broadcast_ether_addr(hdr->h_dest)) {
+ atenl_err("invalid dest MAC\n");
+ return -EINVAL;
+ }
+
+ data->len = len;
+
+ return 0;
+}
+
+int atenl_eth_send(struct atenl *an, struct atenl_data *data)
+{
+ struct ethhdr *ehdr = (struct ethhdr *)data->buf;
+ struct sockaddr_ll addr = {};
+ int ret, len = data->len;
+
+ if (an->unicast)
+ ether_addr_copy(ehdr->h_dest, ehdr->h_source);
+ else
+ eth_broadcast_addr(ehdr->h_dest);
+
+ ether_addr_copy(ehdr->h_source, an->mac_addr);
+ ehdr->h_proto = htons(ETH_P_RACFG);
+
+ if (len < 60)
+ len = 60;
+ else if (len > 1514) {
+ atenl_err("response ethernet length is too long\n");
+ return -1;
+ }
+
+ atenl_dbg_print_data(data->buf, __func__, len);
+
+ addr.sll_family = PF_PACKET;
+ addr.sll_protocol = htons(ETH_P_RACFG);
+ addr.sll_ifindex = if_nametoindex(an->bridge_name);
+ addr.sll_pkttype = PACKET_BROADCAST;
+ addr.sll_hatype = ARPHRD_ETHER;
+ addr.sll_halen = ETH_ALEN;
+ memset(addr.sll_addr, 0, 8);
+ eth_broadcast_addr(addr.sll_addr);
+
+ ret = sendto(an->sock_eth, data->buf, len, 0,
+ (struct sockaddr *)&addr, sizeof(addr));
+ if (ret < 0) {
+ perror("sendto");
+ return ret;
+ }
+
+ return 0;
+}
diff --git a/recipes-devtools/atenl/files/src/hqa.c b/recipes-devtools/atenl/files/src/hqa.c
new file mode 100644
index 0000000..eabfce5
--- /dev/null
+++ b/recipes-devtools/atenl/files/src/hqa.c
@@ -0,0 +1,1236 @@
+/* Copyright (C) 2021-2022 Mediatek Inc. */
+#include "atenl.h"
+
+#define CHAN(_ch, _freq, _ch_80, _ch_160, ...) { \
+ .ch = _ch, \
+ .freq = _freq, \
+ .ch_80 = _ch_80, \
+ .ch_160 = _ch_160, \
+ __VA_ARGS__ \
+}
+
+struct atenl_channel {
+ /* ctrl ch */
+ u16 ch;
+ u16 freq;
+ /* center ch */
+ u16 ch_80;
+ u16 ch_160;
+ /* only use for channels that don't have 80 but has 40 */
+ u16 ch_40;
+};
+
+static const struct atenl_channel atenl_channels_5ghz[] = {
+ CHAN(8, 5040, 0, 0),
+ CHAN(12, 5060, 0, 0),
+ CHAN(16, 5080, 0, 0),
+
+ CHAN(36, 5180, 42, 50),
+ CHAN(40, 5200, 42, 50),
+ CHAN(44, 5220, 42, 50),
+ CHAN(48, 5240, 42, 50),
+
+ CHAN(52, 5260, 58, 50),
+ CHAN(56, 5280, 58, 50),
+ CHAN(60, 5300, 58, 50),
+ CHAN(64, 5320, 58, 50),
+
+ CHAN(68, 5340, 0, 0),
+ CHAN(80, 5400, 0, 0),
+ CHAN(84, 5420, 0, 0),
+ CHAN(88, 5440, 0, 0),
+ CHAN(92, 5460, 0, 0),
+ CHAN(96, 5480, 0, 0),
+
+ CHAN(100, 5500, 106, 114),
+ CHAN(104, 5520, 106, 114),
+ CHAN(108, 5540, 106, 114),
+ CHAN(112, 5560, 106, 114),
+ CHAN(116, 5580, 122, 114),
+ CHAN(120, 5600, 122, 114),
+ CHAN(124, 5620, 122, 114),
+ CHAN(128, 5640, 122, 114),
+
+ CHAN(132, 5660, 138, 0),
+ CHAN(136, 5680, 138, 0),
+ CHAN(140, 5700, 138, 0),
+ CHAN(144, 5720, 138, 0),
+
+ CHAN(149, 5745, 155, 0),
+ CHAN(153, 5765, 155, 0),
+ CHAN(157, 5785, 155, 0),
+ CHAN(161, 5805, 155, 0),
+ CHAN(165, 5825, 0, 0, .ch_40 = 167),
+ CHAN(169, 5845, 0, 0, .ch_40 = 167),
+ CHAN(173, 5865, 0, 0),
+
+ CHAN(184, 4920, 0, 0),
+ CHAN(188, 4940, 0, 0),
+ CHAN(192, 4960, 0, 0),
+ CHAN(196, 4980, 0, 0),
+};
+
+static const struct atenl_channel atenl_channels_6ghz[] = {
+ /* UNII-5 */
+ CHAN(1, 5955, 7, 15),
+ CHAN(5, 5975, 7, 15),
+ CHAN(9, 5995, 7, 15),
+ CHAN(13, 6015, 7, 15),
+ CHAN(17, 6035, 23, 15),
+ CHAN(21, 6055, 23, 15),
+ CHAN(25, 6075, 23, 15),
+ CHAN(29, 6095, 23, 15),
+ CHAN(33, 6115, 39, 47),
+ CHAN(37, 6135, 39, 47),
+ CHAN(41, 6155, 39, 47),
+ CHAN(45, 6175, 39, 47),
+ CHAN(49, 6195, 55, 47),
+ CHAN(53, 6215, 55, 47),
+ CHAN(57, 6235, 55, 47),
+ CHAN(61, 6255, 55, 47),
+ CHAN(65, 6275, 71, 79),
+ CHAN(69, 6295, 71, 79),
+ CHAN(73, 6315, 71, 79),
+ CHAN(77, 6335, 71, 79),
+ CHAN(81, 6355, 87, 79),
+ CHAN(85, 6375, 87, 79),
+ CHAN(89, 6395, 87, 79),
+ CHAN(93, 6415, 87, 79),
+ /* UNII-6 */
+ CHAN(97, 6435, 103, 111),
+ CHAN(101, 6455, 103, 111),
+ CHAN(105, 6475, 103, 111),
+ CHAN(109, 6495, 103, 111),
+ CHAN(113, 6515, 119, 111),
+ CHAN(117, 6535, 119, 111),
+ /* UNII-7 */
+ CHAN(121, 6555, 119, 111),
+ CHAN(125, 6575, 119, 111),
+ CHAN(129, 6595, 135, 143),
+ CHAN(133, 6615, 135, 143),
+ CHAN(137, 6635, 135, 143),
+ CHAN(141, 6655, 135, 143),
+ CHAN(145, 6675, 151, 143),
+ CHAN(149, 6695, 151, 143),
+ CHAN(153, 6715, 151, 143),
+ CHAN(157, 6735, 151, 143),
+ CHAN(161, 6755, 167, 175),
+ CHAN(165, 6775, 167, 175),
+ CHAN(169, 6795, 167, 175),
+ CHAN(173, 6815, 167, 175),
+ CHAN(177, 6835, 183, 175),
+ CHAN(181, 6855, 183, 175),
+ CHAN(185, 6875, 183, 175),
+ /* UNII-8 */
+ CHAN(189, 6895, 183, 175),
+ CHAN(193, 6915, 199, 207),
+ CHAN(197, 6935, 199, 207),
+ CHAN(201, 6955, 199, 207),
+ CHAN(205, 6975, 199, 207),
+ CHAN(209, 6995, 215, 207),
+ CHAN(213, 7015, 215, 207),
+ CHAN(217, 7035, 215, 207),
+ CHAN(221, 7055, 215, 207),
+ CHAN(225, 7075, 0, 0, .ch_40 = 227),
+ CHAN(229, 7095, 0, 0, .ch_40 = 227),
+ CHAN(233, 7115, 0, 0),
+};
+
+static int
+atenl_hqa_adapter(struct atenl *an, struct atenl_data *data)
+{
+ char cmd[64];
+ u8 i;
+
+ for (i = 0; i < MAX_BAND_NUM; i++) {
+ u8 phy = get_band_val(an, i, phy_idx);
+
+ if (!get_band_val(an, i, valid))
+ continue;
+
+ if (data->cmd == HQA_CMD_OPEN_ADAPTER) {
+ sprintf(cmd, "iw phy phy%u interface add mon%u type monitor", phy, phy);
+ system(cmd);
+ sprintf(cmd, "iw dev wlan%u del", phy);
+ system(cmd);
+ sprintf(cmd, "ifconfig mon%u up", phy);
+ system(cmd);
+ /* set a special-defined country */
+ sprintf(cmd, "iw reg set VV");
+ system(cmd);
+ atenl_nl_set_state(an, i, MT76_TM_STATE_IDLE);
+ } else {
+ atenl_nl_set_state(an, i, MT76_TM_STATE_OFF);
+ sprintf(cmd, "iw reg set 00");
+ system(cmd);
+ sprintf(cmd, "iw dev mon%u del", phy);
+ system(cmd);
+ sprintf(cmd, "iw phy phy%u interface add wlan%u type managed", phy, phy);
+ system(cmd);
+ }
+ }
+
+ return 0;
+}
+
+static int
+atenl_hqa_set_rf_mode(struct atenl *an, struct atenl_data *data)
+{
+ struct atenl_cmd_hdr *hdr = atenl_hdr(data);
+
+ /* The testmode rf mode change applies to all bands */
+ set_band_val(an, 0, rf_mode, ntohl(*(u32 *)hdr->data));
+ set_band_val(an, 1, rf_mode, ntohl(*(u32 *)hdr->data));
+
+ return 0;
+}
+
+static int
+atenl_hqa_get_chip_id(struct atenl *an, struct atenl_data *data)
+{
+ struct atenl_cmd_hdr *hdr = atenl_hdr(data);
+
+ *(u32 *)(hdr->data + 2) = htonl(an->chip_id);
+
+ return 0;
+}
+
+static int
+atenl_hqa_get_sub_chip_id(struct atenl *an, struct atenl_data *data)
+{
+ struct atenl_cmd_hdr *hdr = atenl_hdr(data);
+
+ *(u32 *)(hdr->data + 2) = htonl(an->sub_chip_id);
+
+ return 0;
+}
+
+static int
+atenl_hqa_get_rf_cap(struct atenl *an, struct atenl_data *data)
+{
+ struct atenl_cmd_hdr *hdr = atenl_hdr(data);
+ u32 band = ntohl(*(u32 *)hdr->data);
+ struct atenl_band *anb;
+
+ if (band >= MAX_BAND_NUM)
+ return -EINVAL;
+
+ anb = &an->anb[band];
+ /* fill tx and rx ant */
+ *(u32 *)(hdr->data + 2) = htonl(__builtin_popcount(anb->chainmask));
+ *(u32 *)(hdr->data + 2 + 4) = *(u32 *)(hdr->data + 2);
+
+ return 0;
+}
+
+static int
+atenl_hqa_reset_counter(struct atenl *an, struct atenl_data *data)
+{
+ struct atenl_band *anb = &an->anb[an->cur_band];
+
+ anb->reset_tx_cnt = true;
+ anb->reset_rx_cnt = true;
+
+ memset(&anb->rx_stat, 0, sizeof(anb->rx_stat));
+
+ return 0;
+}
+
+static int
+atenl_hqa_mac_bbp_reg(struct atenl *an, struct atenl_data *data)
+{
+ struct atenl_cmd_hdr *hdr = atenl_hdr(data);
+ enum atenl_cmd cmd = data->cmd;
+ u32 *v = (u32 *)hdr->data;
+ u32 offset = ntohl(v[0]), res;
+ int ret;
+
+ if (cmd == HQA_CMD_READ_MAC_BBP_REG) {
+ u16 num = ntohs(*(u16 *)(hdr->data + 4));
+ u32 *ptr = (u32 *)(hdr->data + 2);
+ int i;
+
+ if (num > SHRT_MAX) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ hdr->len = htons(2 + num * 4);
+ for (i = 0; i < num && i < sizeof(hdr->data) / 4; i++) {
+ ret = atenl_reg_read(an, offset + i * 4, &res);
+ if (ret)
+ goto out;
+
+ res = htonl(res);
+ memcpy(ptr + i, &res, 4);
+ }
+ } else if (cmd == HQA_CMD_READ_MAC_BBP_REG_QA) {
+ ret = atenl_reg_read(an, offset, &res);
+ if (ret)
+ goto out;
+
+ res = htonl(res);
+ memcpy(hdr->data + 2, &res, 4);
+ } else {
+ u32 val = ntohl(v[1]);
+
+ ret = atenl_reg_write(an, offset, val);
+ if (ret)
+ goto out;
+ }
+
+ ret = 0;
+out:
+ memset(hdr->data, 0, 2);
+
+ return ret;
+}
+
+static int
+atenl_hqa_rf_reg(struct atenl *an, struct atenl_data *data)
+{
+ struct atenl_cmd_hdr *hdr = atenl_hdr(data);
+ enum atenl_cmd cmd = data->cmd;
+ u32 *v = (u32 *)hdr->data;
+ u32 wf_sel = ntohl(v[0]);
+ u32 offset = ntohl(v[1]);
+ u32 num = ntohl(v[2]);
+ int ret, i;
+
+ if (cmd == HQA_CMD_READ_RF_REG) {
+ u32 *ptr = (u32 *)(hdr->data + 2);
+ u32 res;
+
+ hdr->len = htons(2 + num * 4);
+ for (i = 0; i < num && i < sizeof(hdr->data) / 4; i++) {
+ ret = atenl_rf_read(an, wf_sel, offset + i * 4, &res);
+ if (ret)
+ goto out;
+
+ res = htonl(res);
+ memcpy(ptr + i, &res, 4);
+ }
+ } else {
+ u32 *ptr = (u32 *)(hdr->data + 12);
+
+ for (i = 0; i < num && i < sizeof(hdr->data) / 4; i++) {
+ u32 val = ntohl(ptr[i]);
+
+ ret = atenl_rf_write(an, wf_sel, offset + i * 4, val);
+ if (ret)
+ goto out;
+ }
+ }
+
+ ret = 0;
+out:
+ memset(hdr->data, 0, 2);
+
+ return ret;
+}
+
+static int
+atenl_hqa_eeprom_bulk(struct atenl *an, struct atenl_data *data)
+{
+ struct atenl_cmd_hdr *hdr = atenl_hdr(data);
+ enum atenl_cmd cmd = data->cmd;
+
+ if (cmd == HQA_CMD_WRITE_BUFFER_DONE) {
+ u32 buf_mode = ntohl(*(u32 *)hdr->data);
+
+ switch (buf_mode) {
+ case E2P_EFUSE_MODE:
+ atenl_nl_write_efuse_all(an);
+ break;
+ default:
+ break;
+ }
+ } else {
+ u16 *v = (u16 *)hdr->data;
+ u16 offset = ntohs(v[0]), len = ntohs(v[1]);
+ u16 val;
+ size_t i;
+
+ if (offset >= an->eeprom_size || (len > sizeof(hdr->data) - 2))
+ return -EINVAL;
+
+ if (cmd == HQA_CMD_READ_EEPROM_BULK) {
+ hdr->len = htons(len + 2);
+ for (i = 0; i < len; i += 2) {
+ if (offset + i >= an->eeprom_size)
+ val = 0;
+ else
+ val = ntohs(*(u16 *)(an->eeprom_data + offset + i));
+ *(u16 *)(hdr->data + 2 + i) = val;
+ }
+ } else { /* write eeprom */
+ for (i = 0; i < DIV_ROUND_UP(len, 2); i++) {
+ val = ntohs(v[i + 2]);
+ memcpy(&an->eeprom_data[offset + i * 2], &val, 2);
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int
+atenl_hqa_get_efuse_free_block(struct atenl *an, struct atenl_data *data)
+{
+ struct atenl_cmd_hdr *hdr = atenl_hdr(data);
+ u32 free_block = htonl(0x14);
+
+ /* TODO */
+ *(u32 *)(hdr->data + 2) = free_block;
+
+ return 0;
+}
+
+static int
+atenl_hqa_get_band(struct atenl *an, struct atenl_data *data)
+{
+ struct atenl_cmd_hdr *hdr = atenl_hdr(data);
+ u32 band = ntohl(*(u32 *)hdr->data);
+
+ if (band >= MAX_BAND_NUM)
+ return -EINVAL;
+
+ *(u32 *)(hdr->data + 2) = htonl(an->anb[band].cap);
+
+ return 0;
+}
+
+static int
+atenl_hqa_get_tx_power(struct atenl *an, struct atenl_data *data)
+{
+ struct atenl_cmd_hdr *hdr = atenl_hdr(data);
+ u32 tx_power = htonl(28);
+
+ memcpy(hdr->data + 6, &tx_power, 4);
+
+ return 0;
+}
+
+static int
+atenl_hqa_get_freq_offset(struct atenl *an, struct atenl_data *data)
+{
+ struct atenl_cmd_hdr *hdr = atenl_hdr(data);
+ u32 freq_offset = htonl(10);
+
+ /* TODO */
+ memcpy(hdr->data + 2, &freq_offset, 4);
+
+ return 0;
+}
+
+static int
+atenl_hqa_get_cfg(struct atenl *an, struct atenl_data *data)
+{
+ struct atenl_cmd_hdr *hdr = atenl_hdr(data);
+ u32 val = htonl(1);
+
+ /* TODO */
+ memcpy(hdr->data + 2, &val, 4);
+
+ return 0;
+}
+
+static int
+atenl_hqa_read_temperature(struct atenl *an, struct atenl_data *data)
+{
+ struct atenl_cmd_hdr *hdr = atenl_hdr(data);
+ char buf[64], *str;
+ int fd, ret;
+ u32 temp;
+ u8 phy_idx = get_band_val(an, an->cur_band, phy_idx);
+
+ ret = snprintf(buf, sizeof(buf),
+ "/sys/class/ieee80211/phy%u/hwmon%u/temp1_input",
+ phy_idx, phy_idx);
+ if (snprintf_error(sizeof(buf), ret))
+ return -1;
+
+ fd = open(buf, O_RDONLY);
+ if (fd < 0)
+ return fd;
+
+ ret = read(fd, buf, sizeof(buf) - 1);
+ if (ret < 0)
+ goto out;
+ buf[ret] = 0;
+
+ str = strchr(buf, ':');
+ str += 2;
+ temp = strtol(str, NULL, 10);
+ /* unit conversion */
+ *(u32 *)(hdr->data + 2) = htonl(temp / 1000);
+
+ ret = 0;
+out:
+ close(fd);
+
+ return ret;
+}
+
+static int
+atenl_hqa_check_efuse_mode(struct atenl *an, struct atenl_data *data)
+{
+ struct atenl_cmd_hdr *hdr = atenl_hdr(data);
+ bool flash_mode = an->mtd_part != NULL;
+ enum atenl_cmd cmd = data->cmd;
+ u32 mode;
+
+ switch (cmd) {
+ case HQA_CMD_CHECK_EFUSE_MODE:
+ mode = flash_mode ? 0 : 1;
+ break;
+ case HQA_CMD_CHECK_EFUSE_MODE_TYPE:
+ mode = flash_mode ? E2P_FLASH_MODE : E2P_BIN_MODE;
+ break;
+ case HQA_CMD_CHECK_EFUSE_MODE_NATIVE:
+ mode = flash_mode ? E2P_FLASH_MODE : E2P_EFUSE_MODE;
+ break;
+ default:
+ mode = E2P_BIN_MODE;
+ break;
+ }
+
+ *(u32 *)(hdr->data + 2) = htonl(mode);
+
+ return 0;
+}
+
+static inline u16
+atenl_get_freq_by_channel(u8 ch_band, u16 ch)
+{
+ u16 base;
+
+ if (ch_band == CH_BAND_6GHZ) {
+ base = 5950;
+ } else if (ch_band == CH_BAND_5GHZ) {
+ if (ch >= 184)
+ return 4920 + (ch - 184) * 5;
+
+ base = 5000;
+ } else {
+ base = 2407;
+ }
+
+ return base + ch * 5;
+}
+
+u16 atenl_get_center_channel(u8 bw, u8 ch_band, u16 ctrl_ch)
+{
+ const struct atenl_channel *chan = NULL;
+ const struct atenl_channel *ch_list;
+ u16 center_ch;
+ u8 ch_num;
+ int i;
+
+ if (ch_band == CH_BAND_2GHZ || bw <= TEST_CBW_40MHZ)
+ return 0;
+
+ if (ch_band == CH_BAND_6GHZ) {
+ ch_list = atenl_channels_6ghz;
+ ch_num = ARRAY_SIZE(atenl_channels_6ghz);
+ } else {
+ ch_list = atenl_channels_5ghz;
+ ch_num = ARRAY_SIZE(atenl_channels_5ghz);
+ }
+
+ for (i = 0; i < ch_num; i++) {
+ if (ctrl_ch == ch_list[i].ch) {
+ chan = &ch_list[i];
+ break;
+ }
+ }
+
+ if (!chan)
+ return 0;
+
+ switch (bw) {
+ case TEST_CBW_160MHZ:
+ center_ch = chan->ch_160;
+ break;
+ case TEST_CBW_80MHZ:
+ center_ch = chan->ch_80;
+ break;
+ default:
+ center_ch = 0;
+ break;
+ }
+
+ return center_ch;
+}
+
+static void atenl_get_bw_string(u8 bw, char *buf)
+{
+ switch (bw) {
+ case TEST_CBW_160MHZ:
+ sprintf(buf, "160");
+ break;
+ case TEST_CBW_80MHZ:
+ sprintf(buf, "80");
+ break;
+ case TEST_CBW_40MHZ:
+ sprintf(buf, "40");
+ break;
+ default:
+ sprintf(buf, "20");
+ break;
+ }
+}
+
+void atenl_set_channel(struct atenl *an, u8 bw, u8 ch_band,
+ u16 ch, u16 center_ch1, u16 center_ch2)
+{
+ char bw_str[8] = {};
+ char cmd[128];
+ u16 freq = atenl_get_freq_by_channel(ch_band, ch);
+ u16 freq_center1 = atenl_get_freq_by_channel(ch_band, center_ch1);
+ int ret;
+
+ if (bw > TEST_CBW_MAX)
+ return;
+
+ atenl_get_bw_string(bw, bw_str);
+
+ if (bw == TEST_CBW_20MHZ)
+ ret = snprintf(cmd, sizeof(cmd), "iw dev mon%d set freq %u %s",
+ get_band_val(an, an->cur_band, phy_idx),
+ freq, bw_str);
+ else
+ ret = snprintf(cmd, sizeof(cmd), "iw dev mon%d set freq %u %s %u",
+ get_band_val(an, an->cur_band, phy_idx),
+ freq, bw_str, freq_center1);
+ if (snprintf_error(sizeof(cmd), ret))
+ return;
+
+ atenl_dbg("%s: cmd: %s\n", __func__, cmd);
+
+ system(cmd);
+}
+
+static int
+atenl_hqa_set_channel(struct atenl *an, struct atenl_data *data)
+{
+ struct atenl_cmd_hdr *hdr = atenl_hdr(data);
+ u32 *v = (u32 *)hdr->data;
+ u8 band = ntohl(v[2]);
+ u16 ch1 = ntohl(v[3]); /* center */
+ u16 ch2 = ntohl(v[4]);
+ u8 bw = ntohl(v[5]);
+ u8 pri_sel = ntohl(v[7]);
+ u8 ch_band = ntohl(v[9]);
+ u16 ctrl_ch = 0;
+
+ if (band >= MAX_BAND_NUM)
+ return -EINVAL;
+
+ if ((bw == TEST_CBW_160MHZ && pri_sel > 7) ||
+ (bw == TEST_CBW_80MHZ && pri_sel > 3) ||
+ (bw == TEST_CBW_40MHZ && pri_sel > 1)) {
+ atenl_err("%s: ctrl channel select error\n", __func__);
+ return -EINVAL;
+ }
+
+ an->cur_band = band;
+
+ if (ch_band == CH_BAND_2GHZ) {
+ ctrl_ch = ch1;
+ switch (bw) {
+ case TEST_CBW_40MHZ:
+ if (pri_sel == 1)
+ ctrl_ch += 2;
+ else
+ ctrl_ch -= 2;
+ break;
+ default:
+ break;
+ }
+
+ atenl_set_channel(an, bw, CH_BAND_2GHZ, ctrl_ch, ch1, 0);
+ } else {
+ const struct atenl_channel *chan = NULL;
+ const struct atenl_channel *ch_list;
+ u8 ch_num;
+ int i;
+
+ if (ch_band == CH_BAND_6GHZ) {
+ ch_list = atenl_channels_6ghz;
+ ch_num = ARRAY_SIZE(atenl_channels_6ghz);
+ } else {
+ ch_list = atenl_channels_5ghz;
+ ch_num = ARRAY_SIZE(atenl_channels_5ghz);
+ }
+
+ if (bw == TEST_CBW_160MHZ) {
+ for (i = 0; i < ch_num; i++) {
+ if (ch1 == ch_list[i].ch_160) {
+ chan = &ch_list[i];
+ break;
+ } else if (ch1 < ch_list[i].ch_160) {
+ chan = &ch_list[i - 1];
+ break;
+ }
+ }
+ } else if (bw == TEST_CBW_80MHZ) {
+ for (i = 0; i < ch_num; i++) {
+ if (ch1 == ch_list[i].ch_80) {
+ chan = &ch_list[i];
+ break;
+ } else if (ch1 < ch_list[i].ch_80) {
+ chan = &ch_list[i - 1];
+ break;
+ }
+ }
+ } else {
+ for (i = 0; i < ch_num; i++) {
+ if (ch1 <= ch_list[i].ch) {
+ if (ch1 == ch_list[i].ch)
+ chan = &ch_list[i];
+ else
+ chan = &ch_list[i - 1];
+ break;
+ }
+ }
+ }
+
+ if (!chan)
+ return -EINVAL;
+
+ if (bw != TEST_CBW_20MHZ) {
+ chan += pri_sel;
+ if (chan > &ch_list[ch_num - 1])
+ return -EINVAL;
+ }
+ ctrl_ch = chan->ch;
+
+ atenl_set_channel(an, bw, ch_band, ctrl_ch, ch1, ch2);
+ }
+
+ *(u32 *)(hdr->data + 2) = data->ext_id;
+
+ atenl_nl_set_aid(an, band, 0);
+
+ return 0;
+}
+
+static int
+atenl_hqa_tx_time_option(struct atenl *an, struct atenl_data *data)
+{
+ struct atenl_cmd_hdr *hdr = atenl_hdr(data);
+ u32 *v = (u32 *)hdr->data;
+ u8 band = ntohl(v[1]);
+ u32 option = ntohl(v[2]);
+
+ if (band >= MAX_BAND_NUM)
+ return -EINVAL;
+
+ set_band_val(an, band, use_tx_time, !!option);
+ *(u32 *)(hdr->data + 2) = data->ext_id;
+
+ return 0;
+}
+
+/* should be placed in order for binary search */
+static const struct atenl_ops hqa_ops[] = {
+ {
+ .cmd = HQA_CMD_OPEN_ADAPTER,
+ .cmd_id = 0x1000,
+ .resp_len = 2,
+ .ops = atenl_hqa_adapter,
+ },
+ {
+ .cmd = HQA_CMD_CLOSE_ADAPTER,
+ .cmd_id = 0x1001,
+ .resp_len = 2,
+ .ops = atenl_hqa_adapter,
+ },
+ {
+ .cmd = HQA_CMD_SET_TX_PATH,
+ .cmd_id = 0x100b,
+ .resp_len = 2,
+ .ops = atenl_nl_process,
+ },
+ {
+ .cmd = HQA_CMD_SET_RX_PATH,
+ .cmd_id = 0x100c,
+ .resp_len = 2,
+ .ops = atenl_nl_process,
+ },
+ {
+ .cmd = HQA_CMD_LEGACY,
+ .cmd_id = 0x100d,
+ .resp_len = 2,
+ .flags = ATENL_OPS_FLAG_SKIP,
+ },
+ {
+ .cmd = HQA_CMD_SET_TX_POWER,
+ .cmd_id = 0x1011,
+ .resp_len = 2,
+ .ops = atenl_nl_process,
+ },
+ {
+ .cmd = HQA_CMD_SET_TX_POWER_MANUAL,
+ .cmd_id = 0x1018,
+ .resp_len = 2,
+ .flags = ATENL_OPS_FLAG_SKIP,
+ },
+ {
+ .cmd = HQA_CMD_LEGACY,
+ .cmd_id = 0x1101,
+ .resp_len = 2,
+ .flags = ATENL_OPS_FLAG_SKIP,
+ },
+ {
+ .cmd = HQA_CMD_LEGACY,
+ .cmd_id = 0x1102,
+ .resp_len = 2,
+ .flags = ATENL_OPS_FLAG_SKIP,
+ },
+ {
+ .cmd = HQA_CMD_SET_TX_BW,
+ .cmd_id = 0x1104,
+ .resp_len = 2,
+ .flags = ATENL_OPS_FLAG_SKIP,
+ },
+ {
+ .cmd = HQA_CMD_SET_TX_PKT_BW,
+ .cmd_id = 0x1105,
+ .resp_len = 2,
+ .flags = ATENL_OPS_FLAG_SKIP,
+ },
+ {
+ .cmd = HQA_CMD_SET_TX_PRI_BW,
+ .cmd_id = 0x1106,
+ .resp_len = 2,
+ .flags = ATENL_OPS_FLAG_SKIP,
+ },
+ {
+ .cmd = HQA_CMD_SET_FREQ_OFFSET,
+ .cmd_id = 0x1107,
+ .resp_len = 2,
+ .ops = atenl_nl_process,
+ },
+ {
+ .cmd = HQA_CMD_SET_TSSI,
+ .cmd_id = 0x1109,
+ .resp_len = 2,
+ .ops = atenl_nl_process,
+ },
+ {
+ .cmd = HQA_CMD_SET_EEPROM_TO_FW,
+ .cmd_id = 0x110c,
+ .resp_len = 2,
+ .flags = ATENL_OPS_FLAG_SKIP,
+ },
+ {
+ .cmd = HQA_CMD_ANT_SWAP_CAP,
+ .cmd_id = 0x110d,
+ .resp_len = 6,
+ .flags = ATENL_OPS_FLAG_SKIP,
+ },
+ {
+ .cmd = HQA_CMD_RESET_TX_RX_COUNTER,
+ .cmd_id = 0x1200,
+ .resp_len = 2,
+ .ops = atenl_hqa_reset_counter,
+ },
+ {
+ .cmd = HQA_CMD_READ_MAC_BBP_REG_QA,
+ .cmd_id = 0x1300,
+ .resp_len = 6,
+ .ops = atenl_hqa_mac_bbp_reg,
+ },
+ {
+ .cmd = HQA_CMD_WRITE_MAC_BBP_REG,
+ .cmd_id = 0x1301,
+ .resp_len = 2,
+ .ops = atenl_hqa_mac_bbp_reg,
+ },
+ {
+ .cmd = HQA_CMD_READ_MAC_BBP_REG,
+ .cmd_id = 0x1302,
+ .ops = atenl_hqa_mac_bbp_reg,
+ },
+ {
+ .cmd = HQA_CMD_READ_RF_REG,
+ .cmd_id = 0x1303,
+ .ops = atenl_hqa_rf_reg,
+ },
+ {
+ .cmd = HQA_CMD_WRITE_RF_REG,
+ .cmd_id = 0x1304,
+ .resp_len = 2,
+ .ops = atenl_hqa_rf_reg,
+ },
+ {
+ .cmd = HQA_CMD_WRITE_EEPROM_BULK,
+ .cmd_id = 0x1306,
+ .resp_len = 2,
+ .ops = atenl_hqa_eeprom_bulk,
+ },
+ {
+ .cmd = HQA_CMD_READ_EEPROM_BULK,
+ .cmd_id = 0x1307,
+ .ops = atenl_hqa_eeprom_bulk,
+ },
+ {
+ .cmd = HQA_CMD_WRITE_EEPROM_BULK,
+ .cmd_id = 0x1308,
+ .resp_len = 2,
+ .ops = atenl_hqa_eeprom_bulk,
+ },
+ {
+ .cmd = HQA_CMD_CHECK_EFUSE_MODE,
+ .cmd_id = 0x1309,
+ .resp_len = 6,
+ .ops = atenl_hqa_check_efuse_mode,
+ },
+ {
+ .cmd = HQA_CMD_GET_EFUSE_FREE_BLOCK,
+ .cmd_id = 0x130a,
+ .resp_len = 6,
+ .ops = atenl_hqa_get_efuse_free_block,
+ },
+ {
+ .cmd = HQA_CMD_GET_TX_POWER,
+ .cmd_id = 0x130d,
+ .resp_len = 10,
+ .ops = atenl_hqa_get_tx_power,
+ },
+ {
+ .cmd = HQA_CMD_SET_CFG,
+ .cmd_id = 0x130e,
+ .resp_len = 2,
+ .ops = atenl_nl_process,
+ },
+ {
+ .cmd = HQA_CMD_GET_FREQ_OFFSET,
+ .cmd_id = 0x130f,
+ .resp_len = 6,
+ .ops = atenl_hqa_get_freq_offset,
+ },
+ {
+ .cmd = HQA_CMD_CONTINUOUS_TX,
+ .cmd_id = 0x1311,
+ .resp_len = 6,
+ .ops = atenl_nl_process,
+ },
+ {
+ .cmd = HQA_CMD_SET_RX_PKT_LEN,
+ .cmd_id = 0x1312,
+ .resp_len = 2,
+ .flags = ATENL_OPS_FLAG_SKIP,
+ },
+ {
+ .cmd = HQA_CMD_GET_TX_INFO,
+ .cmd_id = 0x1313,
+ .resp_len = 10,
+ .ops = atenl_nl_process,
+ },
+ {
+ .cmd = HQA_CMD_GET_CFG,
+ .cmd_id = 0x1314,
+ .resp_len = 6,
+ .ops = atenl_hqa_get_cfg,
+ },
+ {
+ .cmd = HQA_CMD_GET_TX_TONE_POWER,
+ .cmd_id = 0x131a,
+ .resp_len = 6,
+ .flags = ATENL_OPS_FLAG_SKIP,
+ },
+ {
+ .cmd = HQA_CMD_UNKNOWN,
+ .cmd_id = 0x131f,
+ .resp_len = 1024,
+ .flags = ATENL_OPS_FLAG_SKIP,
+ },
+ {
+ .cmd = HQA_CMD_READ_TEMPERATURE,
+ .cmd_id = 0x1401,
+ .resp_len = 6,
+ .ops = atenl_hqa_read_temperature,
+ },
+ {
+ .cmd = HQA_CMD_GET_FW_INFO,
+ .cmd_id = 0x1500,
+ .resp_len = 32,
+ .flags = ATENL_OPS_FLAG_SKIP,
+ },
+ {
+ .cmd_id = 0x1502,
+ .flags = ATENL_OPS_FLAG_LEGACY,
+ },
+ {
+ .cmd = HQA_CMD_SET_TSSI,
+ .cmd_id = 0x1505,
+ .resp_len = 2,
+ .ops = atenl_nl_process,
+ },
+ {
+ .cmd = HQA_CMD_SET_RF_MODE,
+ .cmd_id = 0x1509,
+ .resp_len = 2,
+ .ops = atenl_hqa_set_rf_mode,
+ },
+ {
+ .cmd_id = 0x150b,
+ .flags = ATENL_OPS_FLAG_LEGACY,
+ },
+ {
+ .cmd = HQA_CMD_WRITE_BUFFER_DONE,
+ .cmd_id = 0x1511,
+ .resp_len = 2,
+ .ops = atenl_hqa_eeprom_bulk,
+ },
+ {
+ .cmd = HQA_CMD_GET_CHIP_ID,
+ .cmd_id = 0x1514,
+ .resp_len = 6,
+ .ops = atenl_hqa_get_chip_id,
+ },
+ {
+ .cmd = HQA_CMD_GET_SUB_CHIP_ID,
+ .cmd_id = 0x151b,
+ .resp_len = 6,
+ .ops = atenl_hqa_get_sub_chip_id,
+ },
+ {
+ .cmd = HQA_CMD_GET_RX_INFO,
+ .cmd_id = 0x151c,
+ .ops = atenl_nl_process,
+ },
+ {
+ .cmd = HQA_CMD_GET_RF_CAP,
+ .cmd_id = 0x151e,
+ .resp_len = 10,
+ .ops = atenl_hqa_get_rf_cap,
+ },
+ {
+ .cmd = HQA_CMD_CHECK_EFUSE_MODE_TYPE,
+ .cmd_id = 0x1522,
+ .resp_len = 6,
+ .ops = atenl_hqa_check_efuse_mode,
+ },
+ {
+ .cmd = HQA_CMD_CHECK_EFUSE_MODE_NATIVE,
+ .cmd_id = 0x1523,
+ .resp_len = 6,
+ .ops = atenl_hqa_check_efuse_mode,
+ },
+ {
+ .cmd = HQA_CMD_GET_BAND,
+ .cmd_id = 0x152d,
+ .resp_len = 6,
+ .ops = atenl_hqa_get_band,
+ },
+ {
+ .cmd = HQA_CMD_SET_RU,
+ .cmd_id = 0x1594,
+ .resp_len = 2,
+ .ops = atenl_nl_process_many,
+ },
+};
+
+static const struct atenl_ops hqa_ops_ext[] = {
+ {
+ .cmd = HQA_EXT_CMD_SET_CHANNEL,
+ .cmd_id = 0x01,
+ .resp_len = 6,
+ .ops = atenl_hqa_set_channel,
+ .flags = ATENL_OPS_FLAG_EXT_CMD,
+ },
+ {
+ .cmd = HQA_EXT_CMD_SET_TX,
+ .cmd_id = 0x02,
+ .resp_len = 6,
+ .ops = atenl_nl_process,
+ .flags = ATENL_OPS_FLAG_EXT_CMD,
+ },
+ {
+ .cmd = HQA_EXT_CMD_START_TX,
+ .cmd_id = 0x03,
+ .resp_len = 6,
+ .ops = atenl_nl_process,
+ .flags = ATENL_OPS_FLAG_EXT_CMD,
+ },
+ {
+ .cmd = HQA_EXT_CMD_START_RX,
+ .cmd_id = 0x04,
+ .resp_len = 6,
+ .ops = atenl_nl_process,
+ .flags = ATENL_OPS_FLAG_EXT_CMD,
+ },
+ {
+ .cmd = HQA_EXT_CMD_STOP_TX,
+ .cmd_id = 0x05,
+ .resp_len = 6,
+ .ops = atenl_nl_process,
+ .flags = ATENL_OPS_FLAG_EXT_CMD,
+ },
+ {
+ .cmd = HQA_EXT_CMD_STOP_RX,
+ .cmd_id = 0x06,
+ .resp_len = 6,
+ .ops = atenl_nl_process,
+ .flags = ATENL_OPS_FLAG_EXT_CMD,
+ },
+ {
+ .cmd = HQA_EXT_CMD_IBF_SET_VAL,
+ .cmd_id = 0x08,
+ .resp_len = 6,
+ .ops = atenl_nl_process,
+ .flags = ATENL_OPS_FLAG_EXT_CMD,
+ },
+ {
+ .cmd = HQA_EXT_CMD_IBF_GET_STATUS,
+ .cmd_id = 0x09,
+ .resp_len = 10,
+ .ops = atenl_nl_process,
+ .flags = ATENL_OPS_FLAG_EXT_CMD,
+ },
+ {
+ .cmd = HQA_EXT_CMD_IBF_PROF_UPDATE_ALL,
+ .cmd_id = 0x0c,
+ .resp_len = 6,
+ .ops = atenl_nl_process,
+ .flags = ATENL_OPS_FLAG_EXT_CMD,
+ },
+ {
+ .cmd = HQA_EXT_CMD_SET_TX_TIME_OPT,
+ .cmd_id = 0x26,
+ .resp_len = 6,
+ .ops = atenl_hqa_tx_time_option,
+ .flags = ATENL_OPS_FLAG_EXT_CMD,
+ },
+ {
+ .cmd = HQA_EXT_CMD_OFF_CH_SCAN,
+ .cmd_id = 0x27,
+ .resp_len = 6,
+ .ops = atenl_nl_process,
+ .flags = ATENL_OPS_FLAG_EXT_CMD,
+ },
+};
+
+static const struct atenl_ops *
+atenl_get_ops(struct atenl_data *data)
+{
+ const struct atenl_ops *group;
+ struct atenl_cmd_hdr *hdr = atenl_hdr(data);
+ u16 cmd_id = ntohs(hdr->cmd_id), id = cmd_id;
+ int size, low = 0, high;
+
+ switch (cmd_id) {
+ case 0x1600:
+ group = hqa_ops_ext;
+ size = ARRAY_SIZE(hqa_ops_ext);
+ break;
+ default:
+ group = hqa_ops;
+ size = ARRAY_SIZE(hqa_ops);
+ break;
+ }
+
+ if (group[0].flags & ATENL_OPS_FLAG_EXT_CMD)
+ id = ntohl(*(u32 *)hdr->data);
+
+ /* binary search */
+ high = size - 1;
+ while (low <= high) {
+ int mid = low + (high - low) / 2;
+
+ if (group[mid].cmd_id == id)
+ return &group[mid];
+ else if (group[mid].cmd_id > id)
+ high = mid - 1;
+ else
+ low = mid + 1;
+ }
+
+ return NULL;
+}
+
+static int
+atenl_hqa_handler(struct atenl *an, struct atenl_data *data)
+{
+ struct atenl_cmd_hdr *hdr = atenl_hdr(data);
+ const struct atenl_ops *ops = NULL;
+ u16 cmd_id = ntohs(hdr->cmd_id);
+ u16 status = 0;
+
+ atenl_dbg("handle command: 0x%x\n", cmd_id);
+
+ ops = atenl_get_ops(data);
+ if (!ops || (!ops->ops && !ops->flags)) {
+ atenl_err("Unknown command id: 0x%x\n", cmd_id);
+ goto done;
+ }
+
+ data->cmd = ops->cmd;
+ data->cmd_id = ops->cmd_id;
+ if (ops->flags & ATENL_OPS_FLAG_EXT_CMD) {
+ data->ext_cmd = ops->cmd;
+ data->ext_id = ops->cmd_id;
+ }
+
+ if (ops->flags & ATENL_OPS_FLAG_SKIP)
+ goto done;
+
+ atenl_dbg_print_data(data->buf, __func__,
+ ntohs(hdr->len) + ETH_HLEN + RACFG_HLEN);
+ if (ops->ops)
+ status = htons(ops->ops(an, data));
+ if (ops->resp_len)
+ hdr->len = htons(ops->resp_len);
+
+done:
+ *(u16 *)hdr->data = status;
+ data->len = ntohs(hdr->len) + ETH_HLEN + RACFG_HLEN;
+ hdr->cmd_type |= ~htons(RACFG_CMD_TYPE_MASK);
+
+ return 0;
+}
+
+int atenl_hqa_proc_cmd(struct atenl *an)
+{
+ struct atenl_data *data;
+ struct atenl_cmd_hdr *hdr;
+ u16 cmd_type;
+ int ret = -EINVAL;
+
+ data = calloc(1, sizeof(struct atenl_data));
+ if (!data)
+ return -ENOMEM;
+
+ ret = atenl_eth_recv(an, data);
+ if (ret)
+ goto out;
+
+ hdr = atenl_hdr(data);
+ if (ntohl(hdr->magic_no) != RACFG_MAGIC_NO)
+ goto out;
+
+ cmd_type = ntohs(hdr->cmd_type);
+ if (FIELD_GET(RACFG_CMD_TYPE_MASK, cmd_type) != RACFG_CMD_TYPE_ETHREQ &&
+ FIELD_GET(RACFG_CMD_TYPE_MASK, cmd_type) != RACFG_CMD_TYPE_PLATFORM_MODULE) {
+ atenl_err("cmd type error = 0x%x\n", cmd_type);
+ goto out;
+ }
+
+ ret = atenl_hqa_handler(an, data);
+ if (ret)
+ goto out;
+
+ ret = atenl_eth_send(an, data);
+ if (ret)
+ goto out;
+
+ ret = 0;
+out:
+ free(data);
+
+ return ret;
+}
diff --git a/recipes-devtools/atenl/files/src/main.c b/recipes-devtools/atenl/files/src/main.c
new file mode 100644
index 0000000..36ac8f3
--- /dev/null
+++ b/recipes-devtools/atenl/files/src/main.c
@@ -0,0 +1,214 @@
+/* Copyright (C) 2021-2022 Mediatek Inc. */
+
+#include <signal.h>
+#include <sys/select.h>
+#include <sys/wait.h>
+#include "atenl.h"
+
+static const char *progname;
+bool atenl_enable;
+
+void sig_handler(int signum)
+{
+ atenl_enable = false;
+}
+
+void atenl_init_signals()
+{
+ if (signal(SIGINT, sig_handler) == SIG_ERR)
+ goto out;
+ if (signal(SIGTERM, sig_handler) == SIG_ERR)
+ goto out;
+ if (signal(SIGABRT, sig_handler) == SIG_ERR)
+ goto out;
+ if (signal(SIGUSR1, sig_handler) == SIG_ERR)
+ goto out;
+ if (signal(SIGUSR2, sig_handler) == SIG_ERR)
+ goto out;
+
+ return;
+out:
+ perror("signal");
+}
+
+static int phy_lookup_idx(const char *name)
+{
+ char buf[128];
+ FILE *f;
+ size_t len;
+ int ret;
+
+ ret = snprintf(buf, sizeof(buf), "/sys/class/ieee80211/%s/index", name);
+ if (snprintf_error(sizeof(buf), ret))
+ return -1;
+
+ f = fopen(buf, "r");
+ if (!f)
+ return -1;
+
+ len = fread(buf, 1, sizeof(buf) - 1, f);
+ fclose(f);
+
+ if (!len)
+ return -1;
+
+ buf[len] = 0;
+ return atoi(buf);
+}
+
+static int get_default_bridge_name(struct atenl *an)
+{
+ char buf[128];
+ FILE *f;
+ size_t len;
+ int ret;
+
+ ret = snprintf(buf, sizeof(buf), "/sbin/procd");
+ if (snprintf_error(sizeof(buf), ret))
+ return -1;
+
+ f = fopen(buf, "r");
+
+ /* This procd is openwrt only */
+ if (f) {
+ an->bridge_name = BRIDGE_NAME_OPENWRT;
+ fclose(f);
+ } else {
+ an->bridge_name = BRIDGE_NAME_RDKB;
+ }
+
+ return 0;
+}
+
+static void usage(void)
+{
+ printf("Usage:\n");
+ printf(" %s [-u] [-i phyX]\n", progname);
+ printf("options:\n"
+ " -h = show help text\n"
+ " -i = phy name of driver interface, please use first phy for dbdc\n"
+ " -u = use unicast to respond to HQADLL\n"
+ " -b = specify your bridge name\n");
+ printf("examples:\n"
+ " %s -u -i phy0 -b br-lan\n", progname);
+
+ exit(EXIT_FAILURE);
+}
+
+static void atenl_handler_run(struct atenl *an)
+{
+ int count, sock_eth = an->sock_eth;
+ fd_set readfds;
+
+ atenl_info("Start atenl HQA command handler\n");
+
+ while (atenl_enable) {
+ FD_ZERO(&readfds);
+ FD_SET(sock_eth, &readfds);
+ count = select(sock_eth + 1, &readfds, NULL, NULL, NULL);
+
+ if (count < 0) {
+ atenl_err("%s: select failed, %s\n", __func__, strerror(errno));
+ } else if (count == 0) {
+ usleep(1000);
+ } else {
+ if (!FD_ISSET(sock_eth, &readfds))
+ continue;
+ atenl_hqa_proc_cmd(an);
+ }
+ }
+
+ atenl_dbg("HQA command handler end\n");
+}
+
+int main(int argc, char **argv)
+{
+ int opt, phy_idx, ret = 0;
+ char *phy = "phy0", *cmd = NULL;
+ struct atenl *an;
+
+ progname = argv[0];
+
+ an = calloc(1, sizeof(struct atenl));
+ if (!an)
+ return -ENOMEM;
+
+ while(1) {
+ opt = getopt(argc, argv, "hi:uc:b:");
+ if (opt == -1)
+ break;
+
+ switch (opt) {
+ case 'h':
+ usage();
+ goto out;
+ case 'i':
+ phy = optarg;
+ break;
+ case 'b':
+ an->bridge_name = optarg;
+ break;
+ case 'u':
+ an->unicast = true;
+ printf("Opt: use unicast to send response\n");
+ break;
+ case 'c':
+ cmd = optarg;
+ break;
+ default:
+ atenl_err("Not supported option: %c\n", opt);
+ goto out;
+ }
+ }
+
+ phy_idx = phy_lookup_idx(phy);
+ if (phy_idx < 0 || phy_idx > UCHAR_MAX) {
+ atenl_err("Could not find phy '%s'\n", phy);
+ goto out;
+ }
+
+ if (cmd) {
+ atenl_eeprom_cmd_handler(an, phy_idx, cmd);
+ goto out;
+ }
+
+ atenl_enable = true;
+ atenl_init_signals();
+
+ if (!an->bridge_name) {
+ ret = get_default_bridge_name(an);
+ if (ret) {
+ atenl_err("Get default bridge name failed\n");
+ goto out;
+ }
+
+ atenl_info("Bridge name is not specified, use default bridge name: %s\n", an->bridge_name);
+ } else {
+ atenl_info("Currently using bridge name: %s\n", an->bridge_name);
+ }
+
+ /* background ourself */
+ if (!fork()) {
+ ret = atenl_eeprom_init(an, phy_idx);
+ if (ret)
+ goto out;
+
+ ret = atenl_eth_init(an);
+ if (ret)
+ goto out;
+
+ atenl_handler_run(an);
+ } else {
+ usleep(800000);
+ }
+
+ ret = 0;
+out:
+ if (an->sock_eth)
+ close(an->sock_eth);
+ if (an->eeprom_fd || an->eeprom_data)
+ atenl_eeprom_close(an);
+ free(an);
+
+ return ret;
+}
diff --git a/recipes-devtools/atenl/files/src/nl.c b/recipes-devtools/atenl/files/src/nl.c
new file mode 100644
index 0000000..b919356
--- /dev/null
+++ b/recipes-devtools/atenl/files/src/nl.c
@@ -0,0 +1,1499 @@
+/* Copyright (C) 2021-2022 Mediatek Inc. */
+#define _GNU_SOURCE
+
+#include <unl.h>
+
+#include "atenl.h"
+
+#define to_rssi(_rcpi) ((_rcpi - 220) / 2)
+
+struct atenl_nl_priv {
+ struct atenl *an;
+ struct unl unl;
+ struct nl_msg *msg;
+ int attr;
+ void *res;
+};
+
+struct atenl_nl_ops {
+ int set;
+ int dump;
+ int (*ops)(struct atenl *an, struct atenl_data *data,
+ struct atenl_nl_priv *nl_priv);
+};
+
+static struct nla_policy testdata_policy[NUM_MT76_TM_ATTRS] = {
+ [MT76_TM_ATTR_STATE] = { .type = NLA_U8 },
+ [MT76_TM_ATTR_MTD_PART] = { .type = NLA_STRING },
+ [MT76_TM_ATTR_MTD_OFFSET] = { .type = NLA_U32 },
+ [MT76_TM_ATTR_IS_MAIN_PHY] = { .type = NLA_U8 },
+ [MT76_TM_ATTR_TX_COUNT] = { .type = NLA_U32 },
+ [MT76_TM_ATTR_TX_LENGTH] = { .type = NLA_U32 },
+ [MT76_TM_ATTR_TX_RATE_MODE] = { .type = NLA_U8 },
+ [MT76_TM_ATTR_TX_RATE_NSS] = { .type = NLA_U8 },
+ [MT76_TM_ATTR_TX_RATE_IDX] = { .type = NLA_U8 },
+ [MT76_TM_ATTR_TX_RATE_SGI] = { .type = NLA_U8 },
+ [MT76_TM_ATTR_TX_RATE_LDPC] = { .type = NLA_U8 },
+ [MT76_TM_ATTR_TX_RATE_STBC] = { .type = NLA_U8 },
+ [MT76_TM_ATTR_TX_LTF] = { .type = NLA_U8 },
+ [MT76_TM_ATTR_TX_POWER_CONTROL] = { .type = NLA_U8 },
+ [MT76_TM_ATTR_TX_ANTENNA] = { .type = NLA_U8 },
+ [MT76_TM_ATTR_FREQ_OFFSET] = { .type = NLA_U32 },
+ [MT76_TM_ATTR_STATS] = { .type = NLA_NESTED },
+ [MT76_TM_ATTR_PRECAL] = { .type = NLA_NESTED },
+ [MT76_TM_ATTR_PRECAL_INFO] = { .type = NLA_NESTED },
+};
+
+static struct nla_policy stats_policy[NUM_MT76_TM_STATS_ATTRS] = {
+ [MT76_TM_STATS_ATTR_TX_PENDING] = { .type = NLA_U32 },
+ [MT76_TM_STATS_ATTR_TX_QUEUED] = { .type = NLA_U32 },
+ [MT76_TM_STATS_ATTR_TX_DONE] = { .type = NLA_U32 },
+ [MT76_TM_STATS_ATTR_RX_PACKETS] = { .type = NLA_U64 },
+ [MT76_TM_STATS_ATTR_RX_FCS_ERROR] = { .type = NLA_U64 },
+};
+
+static struct nla_policy rx_policy[NUM_MT76_TM_RX_ATTRS] = {
+ [MT76_TM_RX_ATTR_FREQ_OFFSET] = { .type = NLA_U32 },
+ [MT76_TM_RX_ATTR_RCPI] = { .type = NLA_NESTED },
+ [MT76_TM_RX_ATTR_IB_RSSI] = { .type = NLA_NESTED },
+ [MT76_TM_RX_ATTR_WB_RSSI] = { .type = NLA_NESTED },
+ [MT76_TM_RX_ATTR_SNR] = { .type = NLA_U8 },
+};
+
+struct he_sgi {
+ enum mt76_testmode_tx_mode tx_mode;
+ u8 sgi;
+ u8 tx_ltf;
+};
+
+#define HE_SGI_GROUP(_tx_mode, _sgi, _tx_ltf) \
+ { .tx_mode = MT76_TM_TX_MODE_##_tx_mode, .sgi = _sgi, .tx_ltf = _tx_ltf }
+static const struct he_sgi he_sgi_groups[] = {
+ HE_SGI_GROUP(HE_SU, 0, 0),
+ HE_SGI_GROUP(HE_SU, 0, 1),
+ HE_SGI_GROUP(HE_SU, 1, 1),
+ HE_SGI_GROUP(HE_SU, 2, 2),
+ HE_SGI_GROUP(HE_SU, 0, 2),
+ HE_SGI_GROUP(HE_EXT_SU, 0, 0),
+ HE_SGI_GROUP(HE_EXT_SU, 0, 1),
+ HE_SGI_GROUP(HE_EXT_SU, 1, 1),
+ HE_SGI_GROUP(HE_EXT_SU, 2, 2),
+ HE_SGI_GROUP(HE_EXT_SU, 0, 2),
+ HE_SGI_GROUP(HE_TB, 1, 0),
+ HE_SGI_GROUP(HE_TB, 1, 1),
+ HE_SGI_GROUP(HE_TB, 2, 2),
+ HE_SGI_GROUP(HE_MU, 0, 2),
+ HE_SGI_GROUP(HE_MU, 0, 1),
+ HE_SGI_GROUP(HE_MU, 1, 1),
+ HE_SGI_GROUP(HE_MU, 2, 2),
+};
+#undef HE_SGI_LTF_GROUP
+
+static u8 phy_type_to_attr(u8 phy_type)
+{
+ static const u8 phy_type_to_attr[] = {
+ [ATENL_PHY_TYPE_CCK] = MT76_TM_TX_MODE_CCK,
+ [ATENL_PHY_TYPE_OFDM] = MT76_TM_TX_MODE_OFDM,
+ [ATENL_PHY_TYPE_HT] = MT76_TM_TX_MODE_HT,
+ [ATENL_PHY_TYPE_HT_GF] = MT76_TM_TX_MODE_HT,
+ [ATENL_PHY_TYPE_VHT] = MT76_TM_TX_MODE_VHT,
+ [ATENL_PHY_TYPE_HE_SU] = MT76_TM_TX_MODE_HE_SU,
+ [ATENL_PHY_TYPE_HE_EXT_SU] = MT76_TM_TX_MODE_HE_EXT_SU,
+ [ATENL_PHY_TYPE_HE_TB] = MT76_TM_TX_MODE_HE_TB,
+ [ATENL_PHY_TYPE_HE_MU] = MT76_TM_TX_MODE_HE_MU,
+ };
+
+ if (phy_type >= ARRAY_SIZE(phy_type_to_attr))
+ return 0;
+
+ return phy_type_to_attr[phy_type];
+}
+
+static void
+atenl_set_attr_state(struct atenl *an, struct nl_msg *msg,
+ u8 band, enum mt76_testmode_state state)
+{
+ if (get_band_val(an, band, cur_state) == state)
+ return;
+
+ nla_put_u8(msg, MT76_TM_ATTR_STATE, state);
+ set_band_val(an, band, cur_state, state);
+}
+
+static void
+atenl_set_attr_antenna(struct atenl *an, struct nl_msg *msg, u8 tx_antenna)
+{
+ if (!tx_antenna)
+ return;
+ if (is_mt7915(an))
+ nla_put_u8(msg, MT76_TM_ATTR_TX_ANTENNA,
+ tx_antenna << (2 * an->cur_band));
+ else if (is_mt7916(an) || is_mt7986(an))
+ nla_put_u8(msg, MT76_TM_ATTR_TX_ANTENNA, tx_antenna);
+}
+
+static int
+atenl_nl_set_attr(struct atenl *an, struct atenl_data *data,
+ struct atenl_nl_priv *nl_priv)
+{
+ struct atenl_cmd_hdr *hdr = atenl_hdr(data);
+ struct nl_msg *msg = nl_priv->msg;
+ u32 val = ntohl(*(u32 *)hdr->data);
+ int attr = nl_priv->attr;
+ void *ptr, *a;
+
+ ptr = nla_nest_start(msg, NL80211_ATTR_TESTDATA);
+ if (!ptr)
+ return -ENOMEM;
+
+ switch (attr) {
+ case MT76_TM_ATTR_TX_ANTENNA:
+ atenl_set_attr_antenna(an, msg, val);
+ break;
+ case MT76_TM_ATTR_FREQ_OFFSET:
+ nla_put_u32(msg, attr, val);
+ break;
+ case MT76_TM_ATTR_TX_POWER:
+ a = nla_nest_start(msg, MT76_TM_ATTR_TX_POWER);
+ if (!a)
+ return -ENOMEM;
+ nla_put_u8(msg, 0, val);
+ nla_nest_end(msg, a);
+ break;
+ default:
+ nla_put_u8(msg, attr, val);
+ break;
+ }
+
+ nla_nest_end(msg, ptr);
+
+ return unl_genl_request(&nl_priv->unl, msg, NULL, NULL);
+}
+
+static int
+atenl_nl_set_cfg(struct atenl *an, struct atenl_data *data,
+ struct atenl_nl_priv *nl_priv)
+{
+ struct atenl_cmd_hdr *hdr = atenl_hdr(data);
+ struct nl_msg *msg = nl_priv->msg;
+ enum atenl_cmd cmd = data->cmd;
+ u32 *v = (u32 *)hdr->data;
+ u8 type = ntohl(v[0]);
+ u8 enable = ntohl(v[1]);
+ void *ptr, *cfg;
+
+ if (cmd == HQA_CMD_SET_TSSI) {
+ type = 0;
+ enable = 1;
+ }
+
+ ptr = nla_nest_start(msg, NL80211_ATTR_TESTDATA);
+ if (!ptr)
+ return -ENOMEM;
+
+ cfg = nla_nest_start(msg, MT76_TM_ATTR_CFG);
+ if (!cfg)
+ return -ENOMEM;
+
+ if (nla_put_u8(msg, 0, type) ||
+ nla_put_u8(msg, 1, enable))
+ return -EINVAL;
+
+ nla_nest_end(msg, cfg);
+
+ nla_nest_end(msg, ptr);
+
+ return unl_genl_request(&nl_priv->unl, msg, NULL, NULL);
+}
+
+static int
+atenl_nl_set_tx(struct atenl *an, struct atenl_data *data,
+ struct atenl_nl_priv *nl_priv)
+{
+ struct atenl_cmd_hdr *hdr = atenl_hdr(data);
+ struct nl_msg *msg = nl_priv->msg;
+ u32 *v = (u32 *)hdr->data;
+ u8 *addr1 = hdr->data + 36;
+ u8 *addr2 = addr1 + ETH_ALEN;
+ u8 *addr3 = addr2 + ETH_ALEN;
+ u8 def_mac[ETH_ALEN] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55};
+ void *ptr, *a;
+
+ if (get_band_val(an, an->cur_band, use_tx_time))
+ set_band_val(an, an->cur_band, tx_time, ntohl(v[7]));
+ else
+ set_band_val(an, an->cur_band, tx_mpdu_len, ntohl(v[7]));
+
+ ptr = nla_nest_start(msg, NL80211_ATTR_TESTDATA);
+ if (!ptr)
+ return -ENOMEM;
+
+ a = nla_nest_start(msg, MT76_TM_ATTR_MAC_ADDRS);
+ if (!a)
+ return -ENOMEM;
+
+ nla_put(msg, 0, ETH_ALEN, use_default_addr(addr1) ? def_mac : addr1);
+ nla_put(msg, 1, ETH_ALEN, use_default_addr(addr2) ? def_mac : addr2);
+ nla_put(msg, 2, ETH_ALEN, use_default_addr(addr3) ? def_mac : addr3);
+
+ nla_nest_end(msg, a);
+
+ nla_nest_end(msg, ptr);
+
+ *(u32 *)(hdr->data + 2) = data->ext_id;
+
+ return unl_genl_request(&nl_priv->unl, msg, NULL, NULL);
+}
+
+static int
+atenl_nl_tx(struct atenl *an, struct atenl_data *data, struct atenl_nl_priv *nl_priv)
+{
+#define USE_SPE_IDX BIT(31)
+ struct atenl_cmd_hdr *hdr = atenl_hdr(data);
+ struct nl_msg *msg = nl_priv->msg;
+ u32 *v = (u32 *)hdr->data;
+ u8 band = ntohl(v[2]);
+ void *ptr;
+ int ret;
+
+ if (band >= MAX_BAND_NUM)
+ return -EINVAL;
+
+ ptr = nla_nest_start(msg, NL80211_ATTR_TESTDATA);
+ if (!ptr)
+ return -ENOMEM;
+
+ if (data->ext_cmd == HQA_EXT_CMD_STOP_TX) {
+ atenl_set_attr_state(an, msg, band, MT76_TM_STATE_IDLE);
+ } else {
+ u32 tx_count = ntohl(v[3]);
+ u8 tx_rate_mode = phy_type_to_attr(ntohl(v[4]));
+ u8 aid = ntohl(v[11]);
+ u8 sgi = ntohl(v[13]);
+ u32 tx_antenna = ntohl(v[14]);
+ void *a;
+
+ if (sgi > 5)
+ return -EINVAL;
+
+ if (!tx_count)
+ tx_count = 10000000;
+
+ nla_put_u32(msg, MT76_TM_ATTR_TX_COUNT, tx_count);
+ nla_put_u32(msg, MT76_TM_ATTR_TX_IPG, ntohl(v[12]));
+ nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_MODE, tx_rate_mode);
+ nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_IDX, ntohl(v[5]));
+ nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_STBC, ntohl(v[7]));
+ nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_LDPC, ntohl(v[8]));
+ nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_NSS, ntohl(v[15]));
+
+ if (get_band_val(an, band, use_tx_time))
+ nla_put_u32(msg, MT76_TM_ATTR_TX_TIME,
+ get_band_val(an, band, tx_time));
+ else
+ nla_put_u32(msg, MT76_TM_ATTR_TX_LENGTH,
+ get_band_val(an, band, tx_mpdu_len));
+
+ /* for chips after 7915, tx need to use at least wcid = 1 */
+ if (!is_mt7915(an) && !aid)
+ aid = 1;
+ nla_put_u8(msg, MT76_TM_ATTR_AID, aid);
+
+ if (tx_antenna & USE_SPE_IDX) {
+ nla_put_u8(msg, MT76_TM_ATTR_TX_SPE_IDX,
+ tx_antenna & ~USE_SPE_IDX);
+ } else {
+ nla_put_u8(msg, MT76_TM_ATTR_TX_SPE_IDX, 0);
+ atenl_set_attr_antenna(an, msg, tx_antenna);
+ }
+
+ if (tx_rate_mode >= MT76_TM_TX_MODE_HE_SU) {
+ u8 ofs = sgi;
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(he_sgi_groups); i++)
+ if (he_sgi_groups[i].tx_mode == tx_rate_mode)
+ break;
+
+ if ((i + ofs) >= ARRAY_SIZE(he_sgi_groups))
+ return -EINVAL;
+
+ sgi = he_sgi_groups[i + ofs].sgi;
+ nla_put_u8(msg, MT76_TM_ATTR_TX_LTF,
+ he_sgi_groups[i + ofs].tx_ltf);
+ }
+ nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_SGI, sgi);
+
+ a = nla_nest_start(msg, MT76_TM_ATTR_TX_POWER);
+ if (!a)
+ return -ENOMEM;
+ nla_put_u8(msg, 0, ntohl(v[6]));
+ nla_nest_end(msg, a);
+
+ atenl_set_attr_state(an, msg, band, MT76_TM_STATE_TX_FRAMES);
+ }
+
+ nla_nest_end(msg, ptr);
+
+ ret = unl_genl_request(&nl_priv->unl, msg, NULL, NULL);
+ if (ret)
+ return ret;
+
+ *(u32 *)(hdr->data + 2) = data->ext_id;
+
+ return 0;
+}
+
+static int
+atenl_nl_rx(struct atenl *an, struct atenl_data *data, struct atenl_nl_priv *nl_priv)
+{
+ struct atenl_cmd_hdr *hdr = atenl_hdr(data);
+ struct atenl_band *anb = &an->anb[an->cur_band];
+ struct nl_msg *msg = nl_priv->msg;
+ u32 *v = (u32 *)hdr->data;
+ u8 band = ntohl(v[2]);
+ void *ptr;
+
+ if (band >= MAX_BAND_NUM)
+ return -EINVAL;
+
+ ptr = nla_nest_start(msg, NL80211_ATTR_TESTDATA);
+ if (!ptr)
+ return -ENOMEM;
+
+ if (data->ext_cmd == HQA_EXT_CMD_STOP_RX) {
+ atenl_set_attr_state(an, msg, band, MT76_TM_STATE_IDLE);
+ } else {
+ v = (u32 *)(hdr->data + 18);
+
+ atenl_set_attr_antenna(an, msg, ntohl(v[0]));
+ nla_put_u8(msg, MT76_TM_ATTR_AID, ntohl(v[1]));
+ atenl_set_attr_state(an, msg, band, MT76_TM_STATE_RX_FRAMES);
+
+ anb->reset_rx_cnt = false;
+
+ /* clear history buffer */
+ memset(&anb->rx_stat, 0, sizeof(anb->rx_stat));
+ }
+
+ nla_nest_end(msg, ptr);
+
+ *(u32 *)(hdr->data + 2) = data->ext_id;
+
+ return unl_genl_request(&nl_priv->unl, msg, NULL, NULL);
+}
+
+static int
+atenl_off_ch_scan(struct atenl *an, struct atenl_data *data,
+ struct atenl_nl_priv *nl_priv)
+{
+ struct atenl_cmd_hdr *hdr = atenl_hdr(data);
+ struct nl_msg *msg = nl_priv->msg;
+ u32 *v = (u32 *)hdr->data;
+ u8 ch = ntohl(v[2]);
+ u8 bw = ntohl(v[4]);
+ u8 tx_path = ntohl(v[5]);
+ u8 status = ntohl(v[6]);
+ void *ptr;
+
+ if (!status)
+ ch = 0; /* stop */
+
+ ptr = nla_nest_start(msg, NL80211_ATTR_TESTDATA);
+ if (!ptr)
+ return -ENOMEM;
+
+ nla_put_u8(msg, MT76_TM_ATTR_OFF_CH_SCAN_CH, ch);
+ nla_put_u8(msg, MT76_TM_ATTR_OFF_CH_SCAN_CENTER_CH,
+ atenl_get_center_channel(bw, CH_BAND_5GHZ, ch));
+ nla_put_u8(msg, MT76_TM_ATTR_OFF_CH_SCAN_BW, bw);
+ nla_put_u8(msg, MT76_TM_ATTR_OFF_CH_SCAN_PATH, tx_path);
+
+ nla_nest_end(msg, ptr);
+
+ *(u32 *)(hdr->data + 2) = data->ext_id;
+
+ return 0;
+}
+
+static int atenl_nl_dump_cb(struct nl_msg *msg, void *arg)
+{
+ struct atenl_nl_priv *nl_priv = (struct atenl_nl_priv *)arg;
+ struct nlattr *tb1[NUM_MT76_TM_ATTRS];
+ struct nlattr *tb2[NUM_MT76_TM_STATS_ATTRS];
+ struct nlattr *nl_attr;
+ int attr = nl_priv->attr;
+ u64 *res = nl_priv->res;
+
+ nl_attr = unl_find_attr(&nl_priv->unl, msg, NL80211_ATTR_TESTDATA);
+ if (!nl_attr) {
+ atenl_err("Testdata attribute not found\n");
+ return NL_SKIP;
+ }
+
+ nla_parse_nested(tb1, MT76_TM_ATTR_MAX, nl_attr, testdata_policy);
+ nla_parse_nested(tb2, MT76_TM_STATS_ATTR_MAX,
+ tb1[MT76_TM_ATTR_STATS], stats_policy);
+
+ if (attr == MT76_TM_STATS_ATTR_TX_DONE)
+ *res = nla_get_u32(tb2[MT76_TM_STATS_ATTR_TX_DONE]);
+
+ return NL_SKIP;
+}
+
+static int
+atenl_nl_dump_attr(struct atenl *an, struct atenl_data *data,
+ struct atenl_nl_priv *nl_priv)
+{
+ struct atenl_cmd_hdr *hdr = atenl_hdr(data);
+ struct nl_msg *msg = nl_priv->msg;
+ void *ptr;
+ u64 res = 0;
+
+ nl_priv->res = (void *)&res;
+
+ ptr = nla_nest_start(msg, NL80211_ATTR_TESTDATA);
+ if (!ptr)
+ return -ENOMEM;
+ nla_put_flag(msg, MT76_TM_ATTR_STATS);
+ nla_nest_end(msg, ptr);
+
+ unl_genl_request(&nl_priv->unl, msg, atenl_nl_dump_cb, (void *)nl_priv);
+
+ if (nl_priv->attr == MT76_TM_STATS_ATTR_TX_DONE)
+ *(u32 *)(hdr->data + 2 + 4 * an->cur_band) = htonl(res);
+
+ return 0;
+}
+
+static int atenl_nl_continuous_tx(struct atenl *an,
+ struct atenl_data *data,
+ struct atenl_nl_priv *nl_priv)
+{
+ struct atenl_cmd_hdr *hdr = atenl_hdr(data);
+ struct nl_msg *msg = nl_priv->msg;
+ u32 *v = (u32 *)hdr->data;
+ u8 band = ntohl(v[0]);
+ bool enable = ntohl(v[1]);
+ void *ptr;
+
+ ptr = nla_nest_start(msg, NL80211_ATTR_TESTDATA);
+ if (!ptr)
+ return -ENOMEM;
+
+ if (band >= MAX_BAND_NUM)
+ return -EINVAL;
+
+ if (!enable) {
+ int phy = get_band_val(an, band, phy_idx);
+ char cmd[64];
+
+ atenl_set_attr_state(an, msg, band, MT76_TM_STATE_IDLE);
+ nla_nest_end(msg, ptr);
+ unl_genl_request(&nl_priv->unl, msg, NULL, NULL);
+
+ sprintf(cmd, "iw dev mon%d del", phy);
+ system(cmd);
+ sprintf(cmd, "iw phy phy%d interface add mon%d type monitor", phy, phy);
+ system(cmd);
+ sprintf(cmd, "ifconfig mon%d up", phy);
+ system(cmd);
+
+ return 0;
+ }
+
+ if (get_band_val(an, band, rf_mode) != ATENL_RF_MODE_TEST)
+ return 0;
+
+ nla_put_u8(msg, MT76_TM_ATTR_TX_ANTENNA, ntohl(v[2]));
+ nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_MODE, phy_type_to_attr(ntohl(v[3])));
+ nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_IDX, ntohl(v[6]));
+
+ atenl_dbg("%s: enable = %d, ant=%u, tx_rate_mode=%u, rate_idx=%u\n",
+ __func__, enable, ntohl(v[2]), ntohl(v[3]), ntohl(v[6]));
+
+ atenl_set_attr_state(an, msg, band, MT76_TM_STATE_TX_CONT);
+
+ nla_nest_end(msg, ptr);
+
+ return unl_genl_request(&nl_priv->unl, msg, NULL, NULL);
+}
+
+static int atenl_nl_get_rx_info_cb(struct nl_msg *msg, void *arg)
+{
+ struct atenl_nl_priv *nl_priv = (struct atenl_nl_priv *)arg;
+ struct atenl *an = nl_priv->an;
+ struct atenl_band *anb = &an->anb[an->cur_band];
+ struct atenl_data *data = nl_priv->res;
+ struct atenl_cmd_hdr *hdr = atenl_hdr(data);
+ struct atenl_rx_info_hdr *rx_hdr;
+ struct atenl_rx_info_band *rx_band;
+ struct atenl_rx_info_user *rx_user;
+ struct atenl_rx_info_path *rx_path;
+ struct atenl_rx_info_comm *rx_comm;
+ struct nlattr *tb1[NUM_MT76_TM_ATTRS];
+ struct nlattr *tb2[NUM_MT76_TM_STATS_ATTRS];
+ struct nlattr *tb3[NUM_MT76_TM_RX_ATTRS];
+ struct nlattr *nl_attr, *cur;
+ struct atenl_rx_stat rx_cur, rx_diff = {};
+ u32 rcpi[4] = {};
+ u32 type_num = htonl(4);
+ s32 ib_rssi[4] = {}, wb_rssi[4] = {};
+ u8 path = an->anb[an->cur_band].chainmask;
+ u8 path_num = __builtin_popcount(path);
+ u8 *buf = hdr->data + 2;
+ int i, rem;
+
+ *(u32 *)buf = type_num;
+ buf += sizeof(type_num);
+
+#define RX_PUT_HDR(_hdr, _type, _val, _size) do { \
+ _hdr->type = htonl(_type); \
+ _hdr->val = htonl(_val); \
+ _hdr->len = htonl(_size); \
+ buf += sizeof(*_hdr); \
+ } while (0)
+
+ rx_hdr = (struct atenl_rx_info_hdr *)buf;
+ RX_PUT_HDR(rx_hdr, 0, BIT(an->cur_band), sizeof(*rx_band));
+ rx_band = (struct atenl_rx_info_band *)buf;
+ buf += sizeof(*rx_band);
+
+ rx_hdr = (struct atenl_rx_info_hdr *)buf;
+ RX_PUT_HDR(rx_hdr, 1, path, path_num * sizeof(*rx_path));
+ rx_path = (struct atenl_rx_info_path *)buf;
+ buf += path_num * sizeof(*rx_path);
+
+ rx_hdr = (struct atenl_rx_info_hdr *)buf;
+ RX_PUT_HDR(rx_hdr, 2, GENMASK(15, 0), 16 * sizeof(*rx_user));
+ rx_user = (struct atenl_rx_info_user *)buf;
+ buf += 16 * sizeof(*rx_user);
+
+ rx_hdr = (struct atenl_rx_info_hdr *)buf;
+ RX_PUT_HDR(rx_hdr, 3, BIT(0), sizeof(*rx_comm));
+ rx_comm = (struct atenl_rx_info_comm *)buf;
+ buf += sizeof(*rx_comm);
+
+ hdr->len = htons(buf - hdr->data);
+
+ nl_attr = unl_find_attr(&nl_priv->unl, msg, NL80211_ATTR_TESTDATA);
+ if (!nl_attr) {
+ atenl_err("Testdata attribute not found\n");
+ return NL_SKIP;
+ }
+
+ nla_parse_nested(tb1, MT76_TM_ATTR_MAX, nl_attr, testdata_policy);
+ nla_parse_nested(tb2, MT76_TM_STATS_ATTR_MAX,
+ tb1[MT76_TM_ATTR_STATS], stats_policy);
+
+ rx_cur.total = nla_get_u64(tb2[MT76_TM_STATS_ATTR_RX_PACKETS]);
+ rx_cur.err_cnt = nla_get_u64(tb2[MT76_TM_STATS_ATTR_RX_FCS_ERROR]);
+ rx_cur.len_mismatch = nla_get_u64(tb2[MT76_TM_STATS_ATTR_RX_LEN_MISMATCH]);
+ rx_cur.ok_cnt = rx_cur.total - rx_cur.err_cnt - rx_cur.len_mismatch;
+
+ if (!anb->reset_rx_cnt ||
+ get_band_val(an, an->cur_band, cur_state) == MT76_TM_STATE_RX_FRAMES) {
+#define RX_COUNT_DIFF(_field) \
+ rx_diff._field = (rx_cur._field) - (anb->rx_stat._field);
+ RX_COUNT_DIFF(total);
+ RX_COUNT_DIFF(err_cnt);
+ RX_COUNT_DIFF(len_mismatch);
+ RX_COUNT_DIFF(ok_cnt);
+#undef RX_COUNT_DIFF
+
+ memcpy(&anb->rx_stat, &rx_cur, sizeof(anb->rx_stat));
+ }
+
+ rx_band->mac_rx_mdrdy_cnt = htonl((u32)rx_diff.total);
+ rx_band->mac_rx_fcs_err_cnt = htonl((u32)rx_diff.err_cnt);
+ rx_band->mac_rx_fcs_ok_cnt = htonl((u32)rx_diff.ok_cnt);
+ rx_band->mac_rx_len_mismatch = htonl((u32)rx_diff.len_mismatch);
+ rx_user->fcs_error_cnt = htonl((u32)rx_diff.err_cnt);
+
+ nla_parse_nested(tb3, MT76_TM_RX_ATTR_MAX,
+ tb2[MT76_TM_STATS_ATTR_LAST_RX], rx_policy);
+
+ rx_user->freq_offset = htonl(nla_get_u32(tb3[MT76_TM_RX_ATTR_FREQ_OFFSET]));
+ rx_user->snr = htonl(nla_get_u8(tb3[MT76_TM_RX_ATTR_SNR]));
+
+ i = 0;
+ nla_for_each_nested(cur, tb3[MT76_TM_RX_ATTR_RCPI], rem) {
+ if (nla_len(cur) != 1 || i >= 4)
+ break;
+
+ rcpi[i++] = nla_get_u8(cur);
+ }
+
+ i = 0;
+ nla_for_each_nested(cur, tb3[MT76_TM_RX_ATTR_IB_RSSI], rem) {
+ if (nla_len(cur) != 1 || i >= 4)
+ break;
+
+ ib_rssi[i++] = (s8)nla_get_u8(cur);
+ }
+
+ i = 0;
+ nla_for_each_nested(cur, tb3[MT76_TM_RX_ATTR_WB_RSSI], rem) {
+ if (nla_len(cur) != 1 || i >= 4)
+ break;
+
+ wb_rssi[i++] = (s8)nla_get_u8(cur);
+ }
+
+ for (i = 0; i < 4; i++) {
+ struct atenl_rx_info_path *path = &rx_path[i];
+
+ path->rcpi = htonl(rcpi[i]);
+ path->rssi = htonl(to_rssi((u8)rcpi[i]));
+ path->fagc_ib_rssi = htonl(ib_rssi[i]);
+ path->fagc_wb_rssi = htonl(wb_rssi[i]);
+ }
+
+ return NL_SKIP;
+}
+
+static int atenl_nl_get_rx_info(struct atenl *an, struct atenl_data *data,
+ struct atenl_nl_priv *nl_priv)
+{
+ struct nl_msg *msg = nl_priv->msg;
+ void *ptr;
+
+ nl_priv->an = an;
+ nl_priv->res = (void *)data;
+
+ ptr = nla_nest_start(msg, NL80211_ATTR_TESTDATA);
+ if (!ptr)
+ return -ENOMEM;
+
+ nla_put_flag(msg, MT76_TM_ATTR_STATS);
+
+ nla_nest_end(msg, ptr);
+
+ return unl_genl_request(&nl_priv->unl, msg, atenl_nl_get_rx_info_cb,
+ (void *)nl_priv);
+}
+
+static int
+atenl_nl_set_ru(struct atenl *an, struct atenl_data *data,
+ struct atenl_nl_priv *nl_priv)
+{
+ struct atenl_cmd_hdr *hdr = atenl_hdr(data);
+ struct nl_msg *msg;
+ u32 *v = (u32 *)(hdr->data + 4);
+ u32 seg0_num = ntohl(v[0]); /* v[1] seg1_num unused */
+ void *ptr;
+ int i, ret;
+
+ if (seg0_num > 8)
+ return -EINVAL;
+
+ for (i = 0, v = &v[2]; i < seg0_num; i++, v += 11) {
+ u32 ru_alloc = ntohl(v[1]);
+ u32 aid = ntohl(v[2]);
+ u32 ru_idx = ntohl(v[3]);
+ u32 mcs = ntohl(v[4]);
+ u32 ldpc = ntohl(v[5]);
+ u32 nss = ntohl(v[6]);
+ u32 tx_length = ntohl(v[8]);
+ char buf[10];
+
+ if (unl_genl_init(&nl_priv->unl, "nl80211") < 0) {
+ atenl_err("Failed to connect to nl80211\n");
+ return 2;
+ }
+
+ msg = unl_genl_msg(&nl_priv->unl, NL80211_CMD_TESTMODE, false);
+ nla_put_u32(msg, NL80211_ATTR_WIPHY, get_band_val(an, an->cur_band, phy_idx));
+
+ ptr = nla_nest_start(msg, NL80211_ATTR_TESTDATA);
+ if (!ptr)
+ return -ENOMEM;
+
+ if (i == 0)
+ atenl_set_attr_state(an, msg, an->cur_band, MT76_TM_STATE_IDLE);
+
+ nla_put_u8(msg, MT76_TM_ATTR_AID, aid);
+ nla_put_u8(msg, MT76_TM_ATTR_RU_IDX, ru_idx);
+ nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_IDX, mcs);
+ nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_LDPC, ldpc);
+ nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_NSS, nss);
+ nla_put_u32(msg, MT76_TM_ATTR_TX_LENGTH, tx_length);
+
+ ret = snprintf(buf, sizeof(buf), "%x", ru_alloc);
+ if (snprintf_error(sizeof(buf), ret))
+ return -EINVAL;
+
+ nla_put_u8(msg, MT76_TM_ATTR_RU_ALLOC, strtol(buf, NULL, 2));
+
+ nla_nest_end(msg, ptr);
+
+ unl_genl_request(&nl_priv->unl, msg, NULL, NULL);
+
+ unl_free(&nl_priv->unl);
+ }
+
+ return 0;
+}
+
+static int
+atenl_nl_ibf_init(struct atenl *an, u8 band)
+{
+ struct atenl_nl_priv nl_priv = {};
+ struct nl_msg *msg;
+ void *ptr, *a;
+ int ret;
+
+ if (unl_genl_init(&nl_priv.unl, "nl80211") < 0) {
+ atenl_err("Failed to connect to nl80211\n");
+ return 2;
+ }
+
+ msg = unl_genl_msg(&nl_priv.unl, NL80211_CMD_TESTMODE, false);
+ nla_put_u32(msg, NL80211_ATTR_WIPHY, get_band_val(an, band, phy_idx));
+
+ ptr = nla_nest_start(msg, NL80211_ATTR_TESTDATA);
+ if (!ptr) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_MODE, MT76_TM_TX_MODE_HT);
+ nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_IDX, an->ibf_mcs);
+ nla_put_u8(msg, MT76_TM_ATTR_TX_ANTENNA, an->ibf_ant);
+ nla_put_u8(msg, MT76_TM_ATTR_TXBF_ACT, MT76_TM_TXBF_ACT_INIT);
+
+ a = nla_nest_start(msg, MT76_TM_ATTR_TXBF_PARAM);
+ if (!a) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ nla_put_u16(msg, 0, 1);
+ nla_nest_end(msg, a);
+
+ nla_nest_end(msg, ptr);
+
+ ret = unl_genl_request(&nl_priv.unl, msg, NULL, NULL);
+
+out:
+ unl_free(&nl_priv.unl);
+ return ret;
+}
+
+static int
+atenl_nl_ibf_e2p_update(struct atenl *an)
+{
+ struct atenl_nl_priv nl_priv = {};
+ struct nl_msg *msg;
+ void *ptr, *a;
+ int ret;
+
+ if (unl_genl_init(&nl_priv.unl, "nl80211") < 0) {
+ atenl_err("Failed to connect to nl80211\n");
+ return 2;
+ }
+
+ msg = unl_genl_msg(&nl_priv.unl, NL80211_CMD_TESTMODE, false);
+ nla_put_u32(msg, NL80211_ATTR_WIPHY, get_band_val(an, an->cur_band, phy_idx));
+
+ ptr = nla_nest_start(msg, NL80211_ATTR_TESTDATA);
+ if (!ptr) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ nla_put_u8(msg, MT76_TM_ATTR_TXBF_ACT, MT76_TM_TXBF_ACT_E2P_UPDATE);
+ a = nla_nest_start(msg, MT76_TM_ATTR_TXBF_PARAM);
+ if (!a) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ nla_put_u16(msg, 0, 0);
+ nla_nest_end(msg, a);
+
+ nla_nest_end(msg, ptr);
+
+ ret = unl_genl_request(&nl_priv.unl, msg, NULL, NULL);
+
+out:
+ unl_free(&nl_priv.unl);
+ return ret;
+}
+
+static void
+atenl_get_ibf_cal_result(struct atenl *an)
+{
+ u16 offset;
+
+ if (an->adie_id == 0x7975)
+ offset = 0x651;
+ else if (an->adie_id == 0x7976)
+ offset = 0x60a;
+
+ /* per group size = 40, for group 0-8 */
+ atenl_eeprom_read_from_driver(an, offset, 40 * 9);
+}
+
+static int
+atenl_nl_ibf_set_val(struct atenl *an, struct atenl_data *data,
+ struct atenl_nl_priv *nl_priv)
+{
+#define MT_IBF(_act) MT76_TM_TXBF_ACT_##_act
+ static const u8 bf_act_map[] = {
+ [TXBF_ACT_IBF_PHASE_COMP] = MT_IBF(PHASE_COMP),
+ [TXBF_ACT_IBF_PROF_UPDATE] = MT_IBF(IBF_PROF_UPDATE),
+ [TXBF_ACT_EBF_PROF_UPDATE] = MT_IBF(EBF_PROF_UPDATE),
+ [TXBF_ACT_IBF_PHASE_CAL] = MT_IBF(PHASE_CAL),
+ };
+#undef MT_IBF
+ struct atenl_cmd_hdr *hdr = atenl_hdr(data);
+ struct nl_msg *msg = nl_priv->msg;
+ u32 *v = (u32 *)(hdr->data + 4);
+ u32 action = ntohl(v[0]);
+ u16 val[8], is_atenl = 1;
+ u8 tmp_ant;
+ void *ptr, *a;
+ char cmd[64];
+ int i;
+
+ for (i = 0; i < 8; i++)
+ val[i] = ntohl(v[i + 1]);
+
+ atenl_dbg("%s: action = %u, val = %u, %u, %u, %u, %u\n",
+ __func__, action, val[0], val[1], val[2], val[3], val[4]);
+
+ ptr = nla_nest_start(msg, NL80211_ATTR_TESTDATA);
+ if (!ptr)
+ return -ENOMEM;
+
+ switch (action) {
+ case TXBF_ACT_CHANNEL:
+ an->cur_band = val[1];
+ /* a sanity to prevent script band idx error */
+ if (val[0] > 14)
+ an->cur_band = 1;
+ atenl_nl_ibf_init(an, an->cur_band);
+ atenl_set_channel(an, 0, an->cur_band, val[0], 0, 0);
+
+ nla_put_u8(msg, MT76_TM_ATTR_AID, 0);
+ nla_put_u8(msg, MT76_TM_ATTR_TXBF_ACT, MT76_TM_TXBF_ACT_UPDATE_CH);
+ a = nla_nest_start(msg, MT76_TM_ATTR_TXBF_PARAM);
+ if (!a)
+ return -ENOMEM;
+ nla_put_u16(msg, 0, 0);
+ nla_nest_end(msg, a);
+ break;
+ case TXBF_ACT_MCS:
+ tmp_ant = (1 << DIV_ROUND_UP(val[0], 8)) - 1 ?: 1;
+ /* sometimes the correct band idx will be set after this action,
+ * so maintain a temp variable to allow mcs update in anthor action.
+ */
+ an->ibf_mcs = val[0];
+ an->ibf_ant = tmp_ant;
+ nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_IDX, an->ibf_mcs);
+ nla_put_u8(msg, MT76_TM_ATTR_TX_ANTENNA, an->ibf_ant);
+ break;
+ case TXBF_ACT_TX_ANT:
+ nla_put_u8(msg, MT76_TM_ATTR_TX_ANTENNA, val[0]);
+ break;
+ case TXBF_ACT_RX_START:
+ atenl_set_attr_state(an, msg, an->cur_band, MT76_TM_STATE_RX_FRAMES);
+ break;
+ case TXBF_ACT_RX_ANT:
+ nla_put_u8(msg, MT76_TM_ATTR_TX_ANTENNA, val[0]);
+ break;
+ case TXBF_ACT_TX_PKT:
+ nla_put_u8(msg, MT76_TM_ATTR_AID, val[1]);
+ nla_put_u8(msg, MT76_TM_ATTR_TXBF_ACT, MT76_TM_TXBF_ACT_TX_PREP);
+ nla_put_u32(msg, MT76_TM_ATTR_TX_COUNT, 10000000);
+ nla_put_u32(msg, MT76_TM_ATTR_TX_LENGTH, 1024);
+ a = nla_nest_start(msg, MT76_TM_ATTR_TXBF_PARAM);
+ if (!a)
+ return -ENOMEM;
+
+ for (i = 0; i < 5; i++)
+ nla_put_u16(msg, i, val[i]);
+ nla_nest_end(msg, a);
+
+ atenl_set_attr_state(an, msg, an->cur_band, MT76_TM_STATE_TX_FRAMES);
+ break;
+ case TXBF_ACT_IBF_PHASE_COMP:
+ nla_put_u8(msg, MT76_TM_ATTR_AID, 1);
+ case TXBF_ACT_IBF_PROF_UPDATE:
+ case TXBF_ACT_EBF_PROF_UPDATE:
+ case TXBF_ACT_IBF_PHASE_CAL:
+ nla_put_u8(msg, MT76_TM_ATTR_TXBF_ACT, bf_act_map[action]);
+ a = nla_nest_start(msg, MT76_TM_ATTR_TXBF_PARAM);
+ if (!a)
+ return -ENOMEM;
+ /* Note: litepoint may send random number for lna_gain_level, reset to 0 */
+ if (action == TXBF_ACT_IBF_PHASE_CAL)
+ val[4] = 0;
+ for (i = 0; i < 5; i++)
+ nla_put_u16(msg, i, val[i]);
+ /* Used to distinguish between command mode and HQADLL mode */
+ nla_put_u16(msg, 5, is_atenl);
+ nla_nest_end(msg, a);
+ break;
+ case TXBF_ACT_IBF_PHASE_E2P_UPDATE:
+ atenl_nl_ibf_e2p_update(an);
+ atenl_get_ibf_cal_result(an);
+
+ nla_put_u8(msg, MT76_TM_ATTR_AID, 0);
+ nla_put_u8(msg, MT76_TM_ATTR_TXBF_ACT, MT76_TM_TXBF_ACT_INIT);
+
+ a = nla_nest_start(msg, MT76_TM_ATTR_TXBF_PARAM);
+ if (!a)
+ return -ENOMEM;
+ nla_put_u16(msg, 0, 0);
+ nla_nest_end(msg, a);
+ break;
+ case TXBF_ACT_INIT:
+ case TXBF_ACT_POWER:
+ default:
+ break;
+ }
+
+ nla_nest_end(msg, ptr);
+
+ *(u32 *)(hdr->data + 2) = data->ext_id;
+
+ return unl_genl_request(&nl_priv->unl, msg, NULL, NULL);
+}
+
+static int
+atenl_nl_ibf_get_status(struct atenl *an, struct atenl_data *data,
+ struct atenl_nl_priv *nl_priv)
+{
+ struct atenl_cmd_hdr *hdr = atenl_hdr(data);
+ u32 status = htonl(1);
+
+ *(u32 *)(hdr->data + 2) = data->ext_id;
+ memcpy(hdr->data + 6, &status, 4);
+
+ return 0;
+}
+
+static int
+atenl_nl_ibf_profile_update_all(struct atenl *an, struct atenl_data *data,
+ struct atenl_nl_priv *nl_priv)
+{
+ struct atenl_cmd_hdr *hdr = atenl_hdr(data);
+ struct nl_msg *msg;
+ void *ptr, *a;
+ u32 *v = (u32 *)(hdr->data + 4);
+ u16 pfmu_idx = ntohl(v[0]);
+ int i;
+
+ for (i = 0, v = &v[5]; i < 64; i++, v += 5) {
+ int j;
+
+ if (unl_genl_init(&nl_priv->unl, "nl80211") < 0) {
+ atenl_err("Failed to connect to nl80211\n");
+ return 2;
+ }
+
+ msg = unl_genl_msg(&nl_priv->unl, NL80211_CMD_TESTMODE, false);
+ nla_put_u32(msg, NL80211_ATTR_WIPHY,
+ get_band_val(an, an->cur_band, phy_idx));
+
+ ptr = nla_nest_start(msg, NL80211_ATTR_TESTDATA);
+ if (!ptr)
+ return -ENOMEM;
+
+ nla_put_u8(msg, MT76_TM_ATTR_TXBF_ACT, MT76_TM_TXBF_ACT_PROF_UPDATE_ALL);
+ a = nla_nest_start(msg, MT76_TM_ATTR_TXBF_PARAM);
+ if (!a)
+ return -ENOMEM;
+ nla_put_u16(msg, 0, pfmu_idx);
+
+ for (j = 0; j < 5; j++)
+ nla_put_u16(msg, j + 1, ntohl(v[j]));
+ nla_nest_end(msg, a);
+
+ nla_nest_end(msg, ptr);
+
+ unl_genl_request(&nl_priv->unl, msg, NULL, NULL);
+
+ unl_free(&nl_priv->unl);
+ }
+
+ *(u32 *)(hdr->data + 2) = data->ext_id;
+
+ return 0;
+}
+
+#define NL_OPS_GROUP(cmd, ...) [HQA_CMD_##cmd] = { __VA_ARGS__ }
+static const struct atenl_nl_ops nl_ops[] = {
+ NL_OPS_GROUP(SET_TX_PATH, .set=MT76_TM_ATTR_TX_ANTENNA),
+ NL_OPS_GROUP(SET_TX_POWER, .set=MT76_TM_ATTR_TX_POWER),
+ NL_OPS_GROUP(SET_RX_PATH, .set=MT76_TM_ATTR_TX_ANTENNA),
+ NL_OPS_GROUP(SET_FREQ_OFFSET, .set=MT76_TM_ATTR_FREQ_OFFSET),
+ NL_OPS_GROUP(SET_CFG, .ops=atenl_nl_set_cfg),
+ NL_OPS_GROUP(SET_TSSI, .ops=atenl_nl_set_cfg),
+ NL_OPS_GROUP(CONTINUOUS_TX, .ops=atenl_nl_continuous_tx),
+ NL_OPS_GROUP(GET_TX_INFO, .dump=MT76_TM_STATS_ATTR_TX_DONE),
+ NL_OPS_GROUP(GET_RX_INFO, .ops=atenl_nl_get_rx_info, .dump=true),
+ NL_OPS_GROUP(SET_RU, .ops=atenl_nl_set_ru),
+};
+#undef NL_OPS_GROUP
+
+#define NL_OPS_EXT(cmd, ...) [HQA_EXT_CMD_##cmd] = { __VA_ARGS__ }
+static const struct atenl_nl_ops nl_ops_ext[] = {
+ NL_OPS_EXT(SET_TX, .ops=atenl_nl_set_tx),
+ NL_OPS_EXT(START_TX, .ops=atenl_nl_tx),
+ NL_OPS_EXT(STOP_TX, .ops=atenl_nl_tx),
+ NL_OPS_EXT(START_RX, .ops=atenl_nl_rx),
+ NL_OPS_EXT(STOP_RX, .ops=atenl_nl_rx),
+ NL_OPS_EXT(OFF_CH_SCAN, .ops=atenl_off_ch_scan),
+ NL_OPS_EXT(IBF_SET_VAL, .ops=atenl_nl_ibf_set_val),
+ NL_OPS_EXT(IBF_GET_STATUS, .ops=atenl_nl_ibf_get_status),
+ NL_OPS_EXT(IBF_PROF_UPDATE_ALL, .ops=atenl_nl_ibf_profile_update_all),
+};
+#undef NL_OPS_EXT
+
+int atenl_nl_process(struct atenl *an, struct atenl_data *data)
+{
+ struct atenl_nl_priv nl_priv = {};
+ const struct atenl_nl_ops *ops;
+ struct nl_msg *msg;
+ int ret = 0;
+
+ if (data->ext_cmd != 0)
+ ops = &nl_ops_ext[data->ext_cmd];
+ else
+ ops = &nl_ops[data->cmd];
+
+ if (unl_genl_init(&nl_priv.unl, "nl80211") < 0) {
+ atenl_err("Failed to connect to nl80211\n");
+ return -1;
+ }
+
+ msg = unl_genl_msg(&nl_priv.unl, NL80211_CMD_TESTMODE, !!ops->dump);
+ nla_put_u32(msg, NL80211_ATTR_WIPHY, get_band_val(an, an->cur_band, phy_idx));
+ nl_priv.msg = msg;
+
+ if (ops->ops) {
+ ret = ops->ops(an, data, &nl_priv);
+ } else if (ops->dump) {
+ nl_priv.attr = ops->dump;
+ ret = atenl_nl_dump_attr(an, data, &nl_priv);
+ } else {
+ nl_priv.attr = ops->set;
+ ret = atenl_nl_set_attr(an, data, &nl_priv);
+ }
+
+ if (ret)
+ atenl_err("command process error: 0x%x (0x%x)\n", data->cmd_id, data->ext_id);
+
+ unl_free(&nl_priv.unl);
+
+ return ret;
+}
+
+int atenl_nl_process_many(struct atenl *an, struct atenl_data *data)
+{
+ struct atenl_nl_priv nl_priv = {};
+ const struct atenl_nl_ops *ops;
+ int ret = 0;
+
+ if (data->ext_cmd != 0)
+ ops = &nl_ops_ext[data->ext_cmd];
+ else
+ ops = &nl_ops[data->cmd];
+
+ if (ops->ops)
+ ret = ops->ops(an, data, &nl_priv);
+
+ return ret;
+}
+
+int atenl_nl_set_state(struct atenl *an, u8 band,
+ enum mt76_testmode_state state)
+{
+ struct atenl_nl_priv nl_priv = {};
+ struct nl_msg *msg;
+ void *ptr;
+
+ if (unl_genl_init(&nl_priv.unl, "nl80211") < 0) {
+ atenl_err("Failed to connect to nl80211\n");
+ return 2;
+ }
+
+ msg = unl_genl_msg(&nl_priv.unl, NL80211_CMD_TESTMODE, false);
+ nla_put_u32(msg, NL80211_ATTR_WIPHY, get_band_val(an, band, phy_idx));
+
+ ptr = nla_nest_start(msg, NL80211_ATTR_TESTDATA);
+ if (!ptr)
+ return -ENOMEM;
+
+ atenl_set_attr_state(an, msg, band, state);
+
+ nla_nest_end(msg, ptr);
+
+ unl_genl_request(&nl_priv.unl, msg, NULL, NULL);
+
+ unl_free(&nl_priv.unl);
+
+ return 0;
+}
+
+int atenl_nl_set_aid(struct atenl *an, u8 band, u8 aid)
+{
+ struct atenl_nl_priv nl_priv = {};
+ struct nl_msg *msg;
+ void *ptr;
+
+ if (unl_genl_init(&nl_priv.unl, "nl80211") < 0) {
+ atenl_err("Failed to connect to nl80211\n");
+ return 2;
+ }
+
+ msg = unl_genl_msg(&nl_priv.unl, NL80211_CMD_TESTMODE, false);
+ nla_put_u32(msg, NL80211_ATTR_WIPHY, get_band_val(an, band, phy_idx));
+
+ ptr = nla_nest_start(msg, NL80211_ATTR_TESTDATA);
+ if (!ptr)
+ return -ENOMEM;
+
+ nla_put_u8(msg, MT76_TM_ATTR_AID, aid);
+
+ nla_nest_end(msg, ptr);
+
+ unl_genl_request(&nl_priv.unl, msg, NULL, NULL);
+
+ unl_free(&nl_priv.unl);
+
+ return 0;
+}
+
+static int atenl_nl_check_mtd_cb(struct nl_msg *msg, void *arg)
+{
+ struct atenl_nl_priv *nl_priv = (struct atenl_nl_priv *)arg;
+ struct atenl *an = nl_priv->an;
+ struct nlattr *tb[NUM_MT76_TM_ATTRS];
+ struct nlattr *attr;
+
+ attr = unl_find_attr(&nl_priv->unl, msg, NL80211_ATTR_TESTDATA);
+ if (!attr)
+ return NL_SKIP;
+
+ nla_parse_nested(tb, MT76_TM_ATTR_MAX, attr, testdata_policy);
+ if (!tb[MT76_TM_ATTR_MTD_PART] || !tb[MT76_TM_ATTR_MTD_OFFSET])
+ return NL_SKIP;
+
+ an->mtd_part = strdup(nla_get_string(tb[MT76_TM_ATTR_MTD_PART]));
+ an->mtd_offset = nla_get_u32(tb[MT76_TM_ATTR_MTD_OFFSET]);
+ an->is_main_phy = nla_get_u32(tb[MT76_TM_ATTR_IS_MAIN_PHY]);
+
+ return NL_SKIP;
+}
+
+int atenl_nl_check_mtd(struct atenl *an)
+{
+ struct atenl_nl_priv nl_priv = { .an = an };
+ struct nl_msg *msg;
+
+ if (unl_genl_init(&nl_priv.unl, "nl80211") < 0) {
+ atenl_err("Failed to connect to nl80211\n");
+ return 2;
+ }
+
+ msg = unl_genl_msg(&nl_priv.unl, NL80211_CMD_TESTMODE, true);
+ nla_put_u32(msg, NL80211_ATTR_WIPHY, get_band_val(an, 0, phy_idx));
+ unl_genl_request(&nl_priv.unl, msg, atenl_nl_check_mtd_cb, (void *)&nl_priv);
+
+ unl_free(&nl_priv.unl);
+
+ return 0;
+}
+
+int atenl_nl_write_eeprom(struct atenl *an, u32 offset, u8 *val, int len)
+{
+ struct atenl_nl_priv nl_priv = {};
+ struct nl_msg *msg;
+ void *ptr, *a;
+ int i;
+
+ if (unl_genl_init(&nl_priv.unl, "nl80211") < 0) {
+ atenl_err("Failed to connect to nl80211\n");
+ return 2;
+ }
+
+ if (len > 16)
+ return -EINVAL;
+
+ msg = unl_genl_msg(&nl_priv.unl, NL80211_CMD_TESTMODE, false);
+ nla_put_u32(msg, NL80211_ATTR_WIPHY, get_band_val(an, 0, phy_idx));
+
+ ptr = nla_nest_start(msg, NL80211_ATTR_TESTDATA);
+ if (!ptr)
+ return -ENOMEM;
+
+ nla_put_u8(msg, MT76_TM_ATTR_EEPROM_ACTION,
+ MT76_TM_EEPROM_ACTION_UPDATE_DATA);
+ nla_put_u32(msg, MT76_TM_ATTR_EEPROM_OFFSET, offset);
+
+ a = nla_nest_start(msg, MT76_TM_ATTR_EEPROM_VAL);
+ if (!a)
+ return -ENOMEM;
+
+ for (i = 0; i < len; i++)
+ if (nla_put_u8(msg, i, val[i]))
+ goto out;
+
+ nla_nest_end(msg, a);
+
+ nla_nest_end(msg, ptr);
+
+ unl_genl_request(&nl_priv.unl, msg, NULL, NULL);
+
+ unl_free(&nl_priv.unl);
+
+out:
+ return 0;
+}
+
+int atenl_nl_write_efuse_all(struct atenl *an)
+{
+ struct atenl_nl_priv nl_priv = {};
+ struct nl_msg *msg;
+ void *ptr;
+
+ if (unl_genl_init(&nl_priv.unl, "nl80211") < 0) {
+ atenl_err("Failed to connect to nl80211\n");
+ return 2;
+ }
+
+ msg = unl_genl_msg(&nl_priv.unl, NL80211_CMD_TESTMODE, false);
+ nla_put_u32(msg, NL80211_ATTR_WIPHY, get_band_val(an, 0, phy_idx));
+
+ ptr = nla_nest_start(msg, NL80211_ATTR_TESTDATA);
+ if (!ptr)
+ return -ENOMEM;
+
+ nla_put_u8(msg, MT76_TM_ATTR_EEPROM_ACTION,
+ MT76_TM_EEPROM_ACTION_WRITE_TO_EFUSE);
+
+ nla_nest_end(msg, ptr);
+
+ unl_genl_request(&nl_priv.unl, msg, NULL, NULL);
+
+ unl_free(&nl_priv.unl);
+
+ return 0;
+}
+
+int atenl_nl_update_buffer_mode(struct atenl *an)
+{
+ struct atenl_nl_priv nl_priv = {};
+ struct nl_msg *msg;
+ void *ptr;
+
+ if (unl_genl_init(&nl_priv.unl, "nl80211") < 0) {
+ atenl_err("Failed to connect to nl80211\n");
+ return 2;
+ }
+
+ msg = unl_genl_msg(&nl_priv.unl, NL80211_CMD_TESTMODE, false);
+ nla_put_u32(msg, NL80211_ATTR_WIPHY, get_band_val(an, 0, phy_idx));
+
+ ptr = nla_nest_start(msg, NL80211_ATTR_TESTDATA);
+ if (!ptr)
+ return -ENOMEM;
+
+ nla_put_u8(msg, MT76_TM_ATTR_EEPROM_ACTION,
+ MT76_TM_EEPROM_ACTION_UPDATE_BUFFER_MODE);
+
+ nla_nest_end(msg, ptr);
+
+ unl_genl_request(&nl_priv.unl, msg, NULL, NULL);
+
+ unl_free(&nl_priv.unl);
+
+ return 0;
+}
+
+static int atenl_nl_precal_sync_from_driver_cb(struct nl_msg *msg, void *arg)
+{
+ struct atenl_nl_priv *nl_priv = (struct atenl_nl_priv *)arg;
+ struct atenl *an = nl_priv->an;
+ struct nlattr *tb[NUM_MT76_TM_ATTRS];
+ struct nlattr *attr, *cur;
+ int i, rem, prek_offset = nl_priv->attr;
+
+
+ attr = unl_find_attr(&nl_priv->unl, msg, NL80211_ATTR_TESTDATA);
+ if (!attr)
+ return NL_SKIP;
+
+ nla_parse_nested(tb, MT76_TM_ATTR_MAX, attr, testdata_policy);
+
+ if (!tb[MT76_TM_ATTR_PRECAL_INFO] && !tb[MT76_TM_ATTR_PRECAL]) {
+ atenl_info("No Pre cal data or info!\n");
+ return NL_SKIP;
+ }
+
+ if (tb[MT76_TM_ATTR_PRECAL_INFO]) {
+ i = 0;
+ nla_for_each_nested(cur, tb[MT76_TM_ATTR_PRECAL_INFO], rem) {
+ an->cal_info[i] = (u32) nla_get_u32(cur);
+ i++;
+ }
+ return NL_SKIP;
+ }
+
+ if (tb[MT76_TM_ATTR_PRECAL] && an->cal) {
+ i = prek_offset;
+ nla_for_each_nested(cur, tb[MT76_TM_ATTR_PRECAL], rem) {
+ an->cal[i] = (u8) nla_get_u8(cur);
+ i++;
+ }
+ return NL_SKIP;
+ }
+ atenl_info("No data found for pre-cal!\n");
+
+ return NL_SKIP;
+}
+
+static int
+atenl_nl_precal_sync_partition(struct atenl_nl_priv *nl_priv, enum mt76_testmode_attr attr,
+ int prek_type, int prek_offset)
+{
+ int ret;
+ void *ptr;
+ struct nl_msg *msg;
+ struct atenl *an = nl_priv->an;
+
+ msg = unl_genl_msg(&(nl_priv->unl), NL80211_CMD_TESTMODE, true);
+ nla_put_u32(msg, NL80211_ATTR_WIPHY, get_band_val(an, an->cur_band, phy_idx));
+ nl_priv->msg = msg;
+ nl_priv->attr = prek_offset;
+
+ ptr = nla_nest_start(msg, NL80211_ATTR_TESTDATA);
+ if (!ptr)
+ return -ENOMEM;
+
+ nla_put_flag(msg, attr);
+ if (attr == MT76_TM_ATTR_PRECAL)
+ nla_put_u8(msg, MT76_TM_ATTR_PRECAL_INFO, prek_type);
+ nla_nest_end(msg, ptr);
+
+ ret = unl_genl_request(&(nl_priv->unl), msg, atenl_nl_precal_sync_from_driver_cb, (void *)nl_priv);
+
+ if (ret) {
+ atenl_err("command process error!\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+int atenl_nl_precal_sync_from_driver(struct atenl *an, enum prek_ops ops)
+{
+#define GROUP_IND_MASK BIT(0)
+#define DPD_IND_MASK GENMASK(3, 1)
+ int ret;
+ u32 i, times, group_size, dpd_size, total_size, transmit_size, offs;
+ u32 dpd_per_chan_size, dpd_chan_num[3], total_chan_num;
+ u32 size, base, base_idx, *size_ptr;
+ u8 cal_indicator, *precal_info;
+ struct atenl_nl_priv nl_priv = { .an = an };
+
+ offs = an->eeprom_prek_offs;
+ cal_indicator = an->eeprom_data[offs];
+
+ if (cal_indicator) {
+ precal_info = an->eeprom_data + an->eeprom_size;
+ memcpy(an->cal_info, precal_info, PRE_CAL_INFO);
+ group_size = an->cal_info[0];
+ dpd_size = an->cal_info[1];
+ total_size = group_size + dpd_size;
+ dpd_chan_num[0] = (an->cal_info[2] >> DPD_INFO_6G_SHIFT) & DPD_INFO_MASK;
+ dpd_chan_num[1] = (an->cal_info[2] >> DPD_INFO_5G_SHIFT) & DPD_INFO_MASK;
+ dpd_chan_num[2] = (an->cal_info[2] >> DPD_INFO_2G_SHIFT) & DPD_INFO_MASK;
+ dpd_per_chan_size = (an->cal_info[2] >> DPD_INFO_CH_SHIFT) & DPD_INFO_MASK;
+ total_chan_num = dpd_chan_num[0] + dpd_chan_num[1] + dpd_chan_num[2];
+ }
+
+ switch (ops){
+ case PREK_SYNC_ALL:
+ size_ptr = &total_size;
+ base_idx = 0;
+ goto start;
+ case PREK_SYNC_GROUP:
+ size_ptr = &group_size;
+ base_idx = 0;
+ goto start;
+ case PREK_SYNC_DPD_6G:
+ size_ptr = &dpd_size;
+ base_idx = 0;
+ goto start;
+ case PREK_SYNC_DPD_5G:
+ size_ptr = &dpd_size;
+ base_idx = 1;
+ goto start;
+ case PREK_SYNC_DPD_2G:
+ size_ptr = &dpd_size;
+ base_idx = 2;
+
+start:
+ if (unl_genl_init(&nl_priv.unl, "nl80211") < 0) {
+ atenl_err("Failed to connect to nl80211\n");
+ return 2;
+ }
+
+ ret = atenl_nl_precal_sync_partition(&nl_priv, MT76_TM_ATTR_PRECAL_INFO, 0, 0);
+ if (ret || !an->cal_info)
+ goto out;
+
+ group_size = an->cal_info[0];
+ dpd_size = an->cal_info[1];
+ total_size = group_size + dpd_size;
+ dpd_chan_num[0] = (an->cal_info[2] >> DPD_INFO_6G_SHIFT) & DPD_INFO_MASK;
+ dpd_chan_num[1] = (an->cal_info[2] >> DPD_INFO_5G_SHIFT) & DPD_INFO_MASK;
+ dpd_chan_num[2] = (an->cal_info[2] >> DPD_INFO_2G_SHIFT) & DPD_INFO_MASK;
+ dpd_per_chan_size = (an->cal_info[2] >> DPD_INFO_CH_SHIFT) & DPD_INFO_MASK;
+ total_chan_num = dpd_chan_num[0] + dpd_chan_num[1] + dpd_chan_num[2];
+ transmit_size = an->cal_info[3];
+
+ size = *size_ptr;
+ size = (size_ptr == &dpd_size) ? (size / total_chan_num * dpd_chan_num[base_idx]) :
+ size;
+ base = 0;
+ for (i = 0; i < base_idx; i++) {
+ base += dpd_chan_num[i] * dpd_per_chan_size * MT_EE_CAL_UNIT;
+ }
+ base += (size_ptr == &dpd_size) ? group_size : 0;
+
+ if (!an->cal)
+ an->cal = (u8 *) calloc(size, sizeof(u8));
+ times = size / transmit_size + 1;
+ for (i = 0; i < times; i++) {
+ ret = atenl_nl_precal_sync_partition(&nl_priv, MT76_TM_ATTR_PRECAL, ops,
+ i * transmit_size);
+ if (ret)
+ goto out;
+ }
+
+ ret = atenl_eeprom_update_precal(an, base, size);
+ break;
+ case PREK_CLEAN_GROUP:
+ if (!(cal_indicator & GROUP_IND_MASK))
+ return 0;
+ an->cal_info[4] = cal_indicator & (u8) ~GROUP_IND_MASK;
+ ret = atenl_eeprom_update_precal(an, 0, group_size);
+ break;
+ case PREK_CLEAN_DPD:
+ if (!(cal_indicator & DPD_IND_MASK))
+ return 0;
+ an->cal_info[4] = cal_indicator & (u8) ~DPD_IND_MASK;
+ ret = atenl_eeprom_update_precal(an, group_size, dpd_size);
+ break;
+ default:
+ break;
+ }
+
+out:
+ unl_free(&nl_priv.unl);
+ return ret;
+}
diff --git a/recipes-devtools/atenl/files/src/nl.h b/recipes-devtools/atenl/files/src/nl.h
new file mode 100644
index 0000000..27336bd
--- /dev/null
+++ b/recipes-devtools/atenl/files/src/nl.h
@@ -0,0 +1,254 @@
+/* Copyright (C) 2021-2022 Mediatek Inc. */
+#ifndef __ATENL_NL_H
+#define __ATENL_NL_H
+
+/* This is copied from mt76/testmode.h */
+
+/**
+ * enum mt76_testmode_attr - testmode attributes inside NL80211_ATTR_TESTDATA
+ *
+ * @MT76_TM_ATTR_UNSPEC: (invalid attribute)
+ *
+ * @MT76_TM_ATTR_RESET: reset parameters to default (flag)
+ * @MT76_TM_ATTR_STATE: test state (u32), see &enum mt76_testmode_state
+ *
+ * @MT76_TM_ATTR_MTD_PART: mtd partition used for eeprom data (string)
+ * @MT76_TM_ATTR_MTD_OFFSET: offset of eeprom data within the partition (u32)
+ * @MT76_TM_ATTR_IS_MAIN_PHY: Is current phy index the main phy or the ext phy (u8)
+ *
+ * @MT76_TM_ATTR_TX_COUNT: configured number of frames to send when setting
+ * state to MT76_TM_STATE_TX_FRAMES (u32)
+ * @MT76_TM_ATTR_TX_PENDING: pending frames during MT76_TM_STATE_TX_FRAMES (u32)
+ * @MT76_TM_ATTR_TX_LENGTH: packet tx msdu length (u32)
+ * @MT76_TM_ATTR_TX_RATE_MODE: packet tx mode (u8, see &enum mt76_testmode_tx_mode)
+ * @MT76_TM_ATTR_TX_RATE_NSS: packet tx number of spatial streams (u8)
+ * @MT76_TM_ATTR_TX_RATE_IDX: packet tx rate/MCS index (u8)
+ * @MT76_TM_ATTR_TX_RATE_SGI: packet tx use short guard interval (u8)
+ * @MT76_TM_ATTR_TX_RATE_LDPC: packet tx enable LDPC (u8)
+ * @MT76_TM_ATTR_TX_RATE_STBC: packet tx enable STBC (u8)
+ * @MT76_TM_ATTR_TX_LTF: packet tx LTF, set 0 to 2 for 1x, 2x, and 4x LTF (u8)
+ *
+ * @MT76_TM_ATTR_TX_ANTENNA: tx antenna mask (u8)
+ * @MT76_TM_ATTR_TX_POWER_CONTROL: enable tx power control (u8)
+ * @MT76_TM_ATTR_TX_POWER: per-antenna tx power array (nested, u8 attrs)
+ *
+ * @MT76_TM_ATTR_FREQ_OFFSET: RF frequency offset (u32)
+ *
+ * @MT76_TM_ATTR_STATS: statistics (nested, see &enum mt76_testmode_stats_attr)
+ *
+ * @MT76_TM_ATTR_PRECAL: Pre-cal data (u8)
+ * @MT76_TM_ATTR_PRECAL: Pre-cal data (u8)
+ * @MT76_TM_ATTR_PRECAL_INFO: group size, dpd size, dpd_info, transmit size,
+ * eeprom cal indicator (u32),
+ * dpd_info = [dpd_per_chan_size, chan_num_2g,
+ * chan_num_5g, chan_num_6g]
+ * @MT76_TM_ATTR_TX_SPE_IDX: tx spatial extension index (u8)
+ *
+ * @MT76_TM_ATTR_TX_DUTY_CYCLE: packet tx duty cycle (u8)
+ * @MT76_TM_ATTR_TX_IPG: tx inter-packet gap, in unit of us (u32)
+ * @MT76_TM_ATTR_TX_TIME: packet transmission time, in unit of us (u32)
+ *
+ */
+enum mt76_testmode_attr {
+ MT76_TM_ATTR_UNSPEC,
+
+ MT76_TM_ATTR_RESET,
+ MT76_TM_ATTR_STATE,
+
+ MT76_TM_ATTR_MTD_PART,
+ MT76_TM_ATTR_MTD_OFFSET,
+ MT76_TM_ATTR_IS_MAIN_PHY,
+
+ MT76_TM_ATTR_TX_COUNT,
+ MT76_TM_ATTR_TX_LENGTH,
+ MT76_TM_ATTR_TX_RATE_MODE,
+ MT76_TM_ATTR_TX_RATE_NSS,
+ MT76_TM_ATTR_TX_RATE_IDX,
+ MT76_TM_ATTR_TX_RATE_SGI,
+ MT76_TM_ATTR_TX_RATE_LDPC,
+ MT76_TM_ATTR_TX_RATE_STBC,
+ MT76_TM_ATTR_TX_LTF,
+
+ MT76_TM_ATTR_TX_ANTENNA,
+ MT76_TM_ATTR_TX_POWER_CONTROL,
+ MT76_TM_ATTR_TX_POWER,
+
+ MT76_TM_ATTR_FREQ_OFFSET,
+
+ MT76_TM_ATTR_STATS,
+
+ MT76_TM_ATTR_PRECAL,
+ MT76_TM_ATTR_PRECAL_INFO,
+
+ MT76_TM_ATTR_TX_SPE_IDX,
+
+ MT76_TM_ATTR_TX_DUTY_CYCLE,
+ MT76_TM_ATTR_TX_IPG,
+ MT76_TM_ATTR_TX_TIME,
+
+ MT76_TM_ATTR_DRV_DATA,
+
+ MT76_TM_ATTR_MAC_ADDRS,
+ MT76_TM_ATTR_AID,
+ MT76_TM_ATTR_RU_ALLOC,
+ MT76_TM_ATTR_RU_IDX,
+
+ MT76_TM_ATTR_EEPROM_ACTION,
+ MT76_TM_ATTR_EEPROM_OFFSET,
+ MT76_TM_ATTR_EEPROM_VAL,
+
+ MT76_TM_ATTR_CFG,
+ MT76_TM_ATTR_TXBF_ACT,
+ MT76_TM_ATTR_TXBF_PARAM,
+
+ MT76_TM_ATTR_OFF_CH_SCAN_CH,
+ MT76_TM_ATTR_OFF_CH_SCAN_CENTER_CH,
+ MT76_TM_ATTR_OFF_CH_SCAN_BW,
+ MT76_TM_ATTR_OFF_CH_SCAN_PATH,
+
+ /* keep last */
+ NUM_MT76_TM_ATTRS,
+ MT76_TM_ATTR_MAX = NUM_MT76_TM_ATTRS - 1,
+};
+
+/**
+ * enum mt76_testmode_state - statistics attributes
+ *
+ * @MT76_TM_STATS_ATTR_TX_PENDING: pending tx frames (u32)
+ * @MT76_TM_STATS_ATTR_TX_QUEUED: queued tx frames (u32)
+ * @MT76_TM_STATS_ATTR_TX_QUEUED: completed tx frames (u32)
+ *
+ * @MT76_TM_STATS_ATTR_RX_PACKETS: number of rx packets (u64)
+ * @MT76_TM_STATS_ATTR_RX_FCS_ERROR: number of rx packets with FCS error (u64)
+ * @MT76_TM_STATS_ATTR_LAST_RX: information about the last received packet
+ * see &enum mt76_testmode_rx_attr
+ */
+enum mt76_testmode_stats_attr {
+ MT76_TM_STATS_ATTR_UNSPEC,
+ MT76_TM_STATS_ATTR_PAD,
+
+ MT76_TM_STATS_ATTR_TX_PENDING,
+ MT76_TM_STATS_ATTR_TX_QUEUED,
+ MT76_TM_STATS_ATTR_TX_DONE,
+
+ MT76_TM_STATS_ATTR_RX_PACKETS,
+ MT76_TM_STATS_ATTR_RX_FCS_ERROR,
+ MT76_TM_STATS_ATTR_LAST_RX,
+ MT76_TM_STATS_ATTR_RX_LEN_MISMATCH,
+
+ /* keep last */
+ NUM_MT76_TM_STATS_ATTRS,
+ MT76_TM_STATS_ATTR_MAX = NUM_MT76_TM_STATS_ATTRS - 1,
+};
+
+
+/**
+ * enum mt76_testmode_rx_attr - packet rx information
+ *
+ * @MT76_TM_RX_ATTR_FREQ_OFFSET: frequency offset (s32)
+ * @MT76_TM_RX_ATTR_RCPI: received channel power indicator (array, u8)
+ * @MT76_TM_RX_ATTR_IB_RSSI: internal inband RSSI (array, s8)
+ * @MT76_TM_RX_ATTR_WB_RSSI: internal wideband RSSI (array, s8)
+ * @MT76_TM_RX_ATTR_SNR: signal-to-noise ratio (u8)
+ */
+enum mt76_testmode_rx_attr {
+ MT76_TM_RX_ATTR_UNSPEC,
+
+ MT76_TM_RX_ATTR_FREQ_OFFSET,
+ MT76_TM_RX_ATTR_RCPI,
+ MT76_TM_RX_ATTR_IB_RSSI,
+ MT76_TM_RX_ATTR_WB_RSSI,
+ MT76_TM_RX_ATTR_SNR,
+
+ /* keep last */
+ NUM_MT76_TM_RX_ATTRS,
+ MT76_TM_RX_ATTR_MAX = NUM_MT76_TM_RX_ATTRS - 1,
+};
+
+/**
+ * enum mt76_testmode_state - phy test state
+ *
+ * @MT76_TM_STATE_OFF: test mode disabled (normal operation)
+ * @MT76_TM_STATE_IDLE: test mode enabled, but idle
+ * @MT76_TM_STATE_TX_FRAMES: send a fixed number of test frames
+ * @MT76_TM_STATE_RX_FRAMES: receive packets and keep statistics
+ * @MT76_TM_STATE_TX_CONT: waveform tx without time gap
+ * @MT76_TM_STATE_ON: test mode enabled used in offload firmware
+ */
+enum mt76_testmode_state {
+ MT76_TM_STATE_OFF,
+ MT76_TM_STATE_IDLE,
+ MT76_TM_STATE_TX_FRAMES,
+ MT76_TM_STATE_RX_FRAMES,
+ MT76_TM_STATE_TX_CONT,
+ MT76_TM_STATE_ON,
+
+ /* keep last */
+ NUM_MT76_TM_STATES,
+ MT76_TM_STATE_MAX = NUM_MT76_TM_STATES - 1,
+};
+
+/**
+ * enum mt76_testmode_tx_mode - packet tx phy mode
+ *
+ * @MT76_TM_TX_MODE_CCK: legacy CCK mode
+ * @MT76_TM_TX_MODE_OFDM: legacy OFDM mode
+ * @MT76_TM_TX_MODE_HT: 802.11n MCS
+ * @MT76_TM_TX_MODE_VHT: 802.11ac MCS
+ * @MT76_TM_TX_MODE_HE_SU: 802.11ax single-user MIMO
+ * @MT76_TM_TX_MODE_HE_EXT_SU: 802.11ax extended-range SU
+ * @MT76_TM_TX_MODE_HE_TB: 802.11ax trigger-based
+ * @MT76_TM_TX_MODE_HE_MU: 802.11ax multi-user MIMO
+ */
+enum mt76_testmode_tx_mode {
+ MT76_TM_TX_MODE_CCK,
+ MT76_TM_TX_MODE_OFDM,
+ MT76_TM_TX_MODE_HT,
+ MT76_TM_TX_MODE_VHT,
+ MT76_TM_TX_MODE_HE_SU,
+ MT76_TM_TX_MODE_HE_EXT_SU,
+ MT76_TM_TX_MODE_HE_TB,
+ MT76_TM_TX_MODE_HE_MU,
+
+ /* keep last */
+ NUM_MT76_TM_TX_MODES,
+ MT76_TM_TX_MODE_MAX = NUM_MT76_TM_TX_MODES - 1,
+};
+
+/**
+ * enum mt76_testmode_eeprom_action - eeprom setting actions
+ *
+ * @MT76_TM_EEPROM_ACTION_UPDATE_DATA: update rf values to specific
+ * eeprom data block
+ * @MT76_TM_EEPROM_ACTION_UPDATE_BUFFER_MODE: send updated eeprom data to fw
+ * @MT76_TM_EEPROM_ACTION_WRITE_TO_EFUSE: write eeprom data back to efuse
+ */
+enum mt76_testmode_eeprom_action {
+ MT76_TM_EEPROM_ACTION_UPDATE_DATA,
+ MT76_TM_EEPROM_ACTION_UPDATE_BUFFER_MODE,
+ MT76_TM_EEPROM_ACTION_WRITE_TO_EFUSE,
+
+ /* keep last */
+ NUM_MT76_TM_EEPROM_ACTION,
+ MT76_TM_EEPROM_ACTION_MAX = NUM_MT76_TM_EEPROM_ACTION - 1,
+};
+
+enum mt76_testmode_txbf_act {
+ MT76_TM_TXBF_ACT_INIT,
+ MT76_TM_TXBF_ACT_UPDATE_CH,
+ MT76_TM_TXBF_ACT_PHASE_COMP,
+ MT76_TM_TXBF_ACT_TX_PREP,
+ MT76_TM_TXBF_ACT_IBF_PROF_UPDATE,
+ MT76_TM_TXBF_ACT_EBF_PROF_UPDATE,
+ MT76_TM_TXBF_ACT_APPLY_TX,
+ MT76_TM_TXBF_ACT_PHASE_CAL,
+ MT76_TM_TXBF_ACT_PROF_UPDATE_ALL,
+ MT76_TM_TXBF_ACT_PROF_UPDATE_ALL_CMD,
+ MT76_TM_TXBF_ACT_E2P_UPDATE,
+
+ /* keep last */
+ NUM_MT76_TM_TXBF_ACT,
+ MT76_TM_TXBF_ACT_MAX = NUM_MT76_TM_TXBF_ACT - 1,
+};
+
+#endif
diff --git a/recipes-devtools/atenl/files/src/util.c b/recipes-devtools/atenl/files/src/util.c
new file mode 100644
index 0000000..b224040
--- /dev/null
+++ b/recipes-devtools/atenl/files/src/util.c
@@ -0,0 +1,217 @@
+/* Copyright (C) 2021-2022 Mediatek Inc. */
+
+#include "atenl.h"
+
+int atenl_reg_read(struct atenl *an, u32 offset, u32 *res)
+{
+ char dir[64], buf[16];
+ unsigned long val;
+ int fd, ret;
+
+ /* write offset into regidx */
+ ret = snprintf(dir, sizeof(dir),
+ "/sys/kernel/debug/ieee80211/phy%d/mt76/regidx",
+ get_band_val(an, 0, phy_idx));
+ if (snprintf_error(sizeof(dir), ret))
+ return ret;
+
+ fd = open(dir, O_WRONLY);
+ if (fd < 0)
+ return fd;
+
+ ret = snprintf(buf, sizeof(buf), "0x%x", offset);
+ if (snprintf_error(sizeof(buf), ret))
+ goto out;
+
+ lseek(fd, 0, SEEK_SET);
+ write(fd, buf, sizeof(buf));
+ close(fd);
+
+ /* read value from regval */
+ ret = snprintf(dir, sizeof(dir),
+ "/sys/kernel/debug/ieee80211/phy%d/mt76/regval",
+ get_band_val(an, 0, phy_idx));
+ if (snprintf_error(sizeof(dir), ret))
+ return ret;
+
+ fd = open(dir, O_RDONLY);
+ if (fd < 0)
+ return fd;
+
+ ret = read(fd, buf, sizeof(buf) - 1);
+ if (ret < 0)
+ goto out;
+ buf[ret] = 0;
+
+ val = strtoul(buf, NULL, 16);
+ if (val > (u32) -1)
+ return -EINVAL;
+
+ *res = val;
+ ret = 0;
+out:
+ close(fd);
+
+ return ret;
+}
+
+int atenl_reg_write(struct atenl *an, u32 offset, u32 val)
+{
+ char dir[64], buf[16];
+ int fd, ret;
+
+ /* write offset into regidx */
+ ret = snprintf(dir, sizeof(dir),
+ "/sys/kernel/debug/ieee80211/phy%d/mt76/regidx",
+ get_band_val(an, 0, phy_idx));
+ if (snprintf_error(sizeof(dir), ret))
+ return ret;
+
+ fd = open(dir, O_WRONLY);
+ if (fd < 0)
+ return fd;
+
+ ret = snprintf(buf, sizeof(buf), "0x%x", offset);
+ if (snprintf_error(sizeof(buf), ret))
+ goto out;
+
+ lseek(fd, 0, SEEK_SET);
+ write(fd, buf, sizeof(buf));
+ close(fd);
+
+ /* write value into regval */
+ ret = snprintf(dir, sizeof(dir),
+ "/sys/kernel/debug/ieee80211/phy%d/mt76/regval",
+ get_band_val(an, 0, phy_idx));
+ if (snprintf_error(sizeof(dir), ret))
+ return ret;
+
+ fd = open(dir, O_WRONLY);
+ if (fd < 0)
+ return fd;
+
+ ret = snprintf(buf, sizeof(buf), "0x%x", val);
+ if (snprintf_error(sizeof(buf), ret))
+ goto out;
+ buf[ret] = 0;
+
+ lseek(fd, 0, SEEK_SET);
+ write(fd, buf, sizeof(buf));
+ ret = 0;
+out:
+ close(fd);
+
+ return ret;
+}
+
+int atenl_rf_read(struct atenl *an, u32 wf_sel, u32 offset, u32 *res)
+{
+ char dir[64], buf[16];
+ unsigned long val;
+ int fd, ret;
+ u32 regidx;
+
+ /* merge wf_sel and offset into regidx */
+ regidx = FIELD_PREP(GENMASK(31, 28), wf_sel) |
+ FIELD_PREP(GENMASK(27, 0), offset);
+
+ /* write regidx */
+ ret = snprintf(dir, sizeof(dir),
+ "/sys/kernel/debug/ieee80211/phy%d/mt76/regidx",
+ get_band_val(an, 0, phy_idx));
+ if (snprintf_error(sizeof(dir), ret))
+ return ret;
+
+ fd = open(dir, O_WRONLY);
+ if (fd < 0)
+ return fd;
+
+ ret = snprintf(buf, sizeof(buf), "0x%x", regidx);
+ if (snprintf_error(sizeof(buf), ret))
+ goto out;
+
+ lseek(fd, 0, SEEK_SET);
+ write(fd, buf, sizeof(buf));
+ close(fd);
+
+ /* read from rf_regval */
+ ret = snprintf(dir, sizeof(dir),
+ "/sys/kernel/debug/ieee80211/phy%d/mt76/rf_regval",
+ get_band_val(an, 0, phy_idx));
+ if (snprintf_error(sizeof(dir), ret))
+ return ret;
+
+ fd = open(dir, O_RDONLY);
+ if (fd < 0)
+ return fd;
+
+ ret = read(fd, buf, sizeof(buf) - 1);
+ if (ret < 0)
+ goto out;
+ buf[ret] = 0;
+
+ val = strtoul(buf, NULL, 16);
+ if (val > (u32) -1)
+ return -EINVAL;
+
+ *res = val;
+ ret = 0;
+out:
+ close(fd);
+
+ return ret;
+}
+
+int atenl_rf_write(struct atenl *an, u32 wf_sel, u32 offset, u32 val)
+{
+ char dir[64], buf[16];
+ int fd, ret;
+ u32 regidx;
+
+ /* merge wf_sel and offset into regidx */
+ regidx = FIELD_PREP(GENMASK(31, 28), wf_sel) |
+ FIELD_PREP(GENMASK(27, 0), offset);
+
+ /* write regidx */
+ ret = snprintf(dir, sizeof(dir),
+ "/sys/kernel/debug/ieee80211/phy%d/mt76/regidx",
+ get_band_val(an, 0, phy_idx));
+ if (snprintf_error(sizeof(dir), ret))
+ return ret;
+
+ fd = open(dir, O_WRONLY);
+ if (fd < 0)
+ return fd;
+
+ ret = snprintf(buf, sizeof(buf), "0x%x", regidx);
+ if (snprintf_error(sizeof(buf), ret))
+ goto out;
+
+ lseek(fd, 0, SEEK_SET);
+ write(fd, buf, sizeof(buf));
+ close(fd);
+
+ /* write value into rf_val */
+ ret = snprintf(dir, sizeof(dir),
+ "/sys/kernel/debug/ieee80211/phy%d/mt76/rf_regval",
+ get_band_val(an, 0, phy_idx));
+ if (snprintf_error(sizeof(dir), ret))
+ return ret;
+
+ fd = open(dir, O_WRONLY);
+ if (fd < 0)
+ return fd;
+
+ ret = snprintf(buf, sizeof(buf), "0x%x", val);
+ if (snprintf_error(sizeof(buf), ret))
+ goto out;
+ buf[ret] = 0;
+
+ lseek(fd, 0, SEEK_SET);
+ write(fd, buf, sizeof(buf));
+ ret = 0;
+out:
+ close(fd);
+
+ return ret;
+}
diff --git a/recipes-devtools/atenl/files/src/util.h b/recipes-devtools/atenl/files/src/util.h
new file mode 100644
index 0000000..45c97b5
--- /dev/null
+++ b/recipes-devtools/atenl/files/src/util.h
@@ -0,0 +1,123 @@
+/* Copyright (C) 2021-2022 Mediatek Inc. */
+#ifndef __ATENL_UTIL_H
+#define __ATENL_UTIL_H
+
+#include <linux/const.h>
+#include <linux/if_arp.h>
+#include <linux/if_ether.h>
+#include <linux/if_packet.h>
+#include <stdint.h>
+#include <string.h>
+
+typedef uint8_t u8;
+typedef uint16_t u16;
+typedef uint32_t u32;
+typedef uint64_t u64;
+typedef int8_t s8;
+typedef int16_t s16;
+typedef int32_t s32;
+typedef int64_t s64, ktime_t;
+
+#ifndef __WORDSIZE
+#define __WORDSIZE (__SIZEOF_LONG__ * 8)
+#endif
+
+#ifndef BITS_PER_LONG
+#define BITS_PER_LONG __WORDSIZE
+#endif
+
+#define UL(x) (_UL(x))
+#define ULL(x) (_ULL(x))
+
+#define BIT(nr) (1UL << (nr))
+
+#define GENMASK_INPUT_CHECK(h, l) 0
+#define __GENMASK(h, l) \
+ (((~UL(0)) - (UL(1) << (l)) + 1) & \
+ (~UL(0) >> (BITS_PER_LONG - 1 - (h))))
+#define GENMASK(h, l) \
+ (GENMASK_INPUT_CHECK(h, l) + __GENMASK(h, l))
+
+#define __bf_shf(x) (__builtin_ffsll(x) - 1)
+#define FIELD_GET(_mask, _reg) \
+ ({ \
+ (typeof(_mask))(((_reg) & (_mask)) >> __bf_shf(_mask)); \
+ })
+#define FIELD_PREP(_mask, _val) \
+ ({ \
+ ((typeof(_mask))(_val) << __bf_shf(_mask)) & (_mask); \
+ })
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
+#endif
+
+#ifndef DIV_ROUND_UP
+#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
+#endif
+
+#define PIPE_READ 0
+#define PIPE_WRITE 1
+
+#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
+#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x"
+
+static inline bool ether_addr_equal(const u8 *addr1, const u8 *addr2)
+{
+ const u16 *a = (const u16 *)addr1;
+ const u16 *b = (const u16 *)addr2;
+
+ return ((a[0] ^ b[0]) | (a[1] ^ b[1]) | (a[2] ^ b[2])) == 0;
+}
+
+static inline bool is_broadcast_ether_addr(const u8 *addr)
+{
+ return (*(const u16 *)(addr + 0) &
+ *(const u16 *)(addr + 2) &
+ *(const u16 *)(addr + 4)) == 0xffff;
+}
+
+static inline bool is_multicast_ether_addr(const u8 *addr)
+{
+ return 0x01 & addr[0];
+}
+
+static inline bool is_unicast_ether_addr(const u8 *addr)
+{
+ return !is_multicast_ether_addr(addr);
+}
+
+static inline bool is_zero_ether_addr(const u8 *addr)
+{
+ return (*(const u16 *)(addr + 0) |
+ *(const u16 *)(addr + 2) |
+ *(const u16 *)(addr + 4)) == 0;
+}
+
+static inline bool use_default_addr(const u8 *addr)
+{
+ return !is_unicast_ether_addr(addr) ||
+ is_zero_ether_addr(addr);
+}
+
+static inline void eth_broadcast_addr(u8 *addr)
+{
+ memset(addr, 0xff, ETH_ALEN);
+}
+
+static inline void ether_addr_copy(u8 *dst, const u8 *src)
+{
+ u16 *a = (u16 *)dst;
+ const u16 *b = (const u16 *)src;
+
+ a[0] = b[0];
+ a[1] = b[1];
+ a[2] = b[2];
+}
+
+static inline int snprintf_error(size_t size, int res)
+{
+ return res < 0 || (unsigned int) res >= size;
+}
+
+#endif